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

import linear_algebra.finite_dimensional
import ring_theory.adjoin.basic
import linear_algebra.direct_sum.finsupp

/-!
# The tensor product of R-algebras

Let `R` be a (semi)ring and `A` an `R`-algebra.
In this file we:

- Define the `A`-module structure on `A ⊗ M`, for an `R`-module `M`.
- Define the `R`-algebra structure on `A ⊗ B`, for another `R`-algebra `B`.
  and provide the structure isomorphisms
  * `R ⊗[R] A ≃ₐ[R] A`
  * `A ⊗[R] R ≃ₐ[R] A`
  * `A ⊗[R] B ≃ₐ[R] B ⊗[R] A`
  * `((A ⊗[R] B) ⊗[R] C) ≃ₐ[R] (A ⊗[R] (B ⊗[R] C))`

## Main declaration

- `linear_map.base_change A f` is the `A`-linear map `A ⊗ f`, for an `R`-linear map `f`.

## Implementation notes

The heterobasic definitions below such as:
 * `tensor_product.algebra_tensor_module.curry`
 * `tensor_product.algebra_tensor_module.uncurry`
 * `tensor_product.algebra_tensor_module.lcurry`
 * `tensor_product.algebra_tensor_module.lift`
 * `tensor_product.algebra_tensor_module.lift.equiv`
 * `tensor_product.algebra_tensor_module.mk`
 * `tensor_product.algebra_tensor_module.assoc`

are just more general versions of the definitions already in `linear_algebra/tensor_product`. We
could thus consider replacing the less general definitions with these ones. If we do this, we
probably should still implement the less general ones as abbreviations to the more general ones with
fewer type arguments.
-/

universes u v₁ v₂ v₃ v₄

open_locale tensor_product
open tensor_product

namespace tensor_product

variables {R A M N P : Type*}

/-!
### The `A`-module structure on `A ⊗[R] M`
-/

open linear_map
open algebra (lsmul)

namespace algebra_tensor_module

section semiring
variables [comm_semiring R] [semiring A] [algebra R A]
variables [add_comm_monoid M] [module R M] [module A M] [is_scalar_tower R A M]
variables [add_comm_monoid N] [module R N]
variables [add_comm_monoid P] [module R P] [module A P] [is_scalar_tower R A P]

lemma smul_eq_lsmul_rtensor (a : A) (x : M ⊗[R] N) : a • x = (lsmul R M a).rtensor N x := rfl

/-- Heterobasic version of `tensor_product.curry`:

Given a linear map `M ⊗[R] N →[A] P`, compose it with the canonical
bilinear map `M →[A] N →[R] M ⊗[R] N` to form a bilinear map `M →[A] N →[R] P`. -/
@[simps] def curry (f : (M ⊗[R] N) →ₗ[A] P) : M →ₗ[A] (N →ₗ[R] P) :=
{ to_fun := curry (f.restrict_scalars R),
  map_smul' := λ c x, linear_map.ext $ λ y, f.map_smul c (x ⊗ₜ y),
  .. curry (f.restrict_scalars R) }

lemma restrict_scalars_curry (f : (M ⊗[R] N) →ₗ[A] P) :
  restrict_scalars R (curry f) = curry (f.restrict_scalars R) :=
rfl

/-- Just as `tensor_product.ext` is marked `ext` instead of `tensor_product.ext'`, this is
a better `ext` lemma than `tensor_product.algebra_tensor_module.ext` below.

See note [partially-applied ext lemmas]. -/
@[ext] lemma curry_injective :
  function.injective (curry : (M ⊗ N →ₗ[A] P) → (M →ₗ[A] N →ₗ[R] P)) :=
λ x y h, linear_map.restrict_scalars_injective R $ curry_injective $
  (congr_arg (linear_map.restrict_scalars R) h : _)

theorem ext {g h : (M ⊗[R] N) →ₗ[A] P}
  (H : ∀ x y, g (x ⊗ₜ y) = h (x ⊗ₜ y)) : g = h :=
curry_injective $ linear_map.ext₂ H

end semiring

section comm_semiring
variables [comm_semiring R] [comm_semiring A] [algebra R A]
variables [add_comm_monoid M] [module R M] [module A M] [is_scalar_tower R A M]
variables [add_comm_monoid N] [module R N]
variables [add_comm_monoid P] [module R P] [module A P] [is_scalar_tower R A P]

/-- Heterobasic version of `tensor_product.lift`:

Constructing a linear map `M ⊗[R] N →[A] P` given a bilinear map `M →[A] N →[R] P` with the
property that its composition with the canonical bilinear map `M →[A] N →[R] M ⊗[R] N` is
the given bilinear map `M →[A] N →[R] P`. -/
@[simps] def lift (f : M →ₗ[A] (N →ₗ[R] P)) : (M ⊗[R] N) →ₗ[A] P :=
{ map_smul' := λ c, show ∀ x : M ⊗[R] N, (lift (f.restrict_scalars R)).comp (lsmul R _ c) x =
      (lsmul R _ c).comp (lift (f.restrict_scalars R)) x,
    from ext_iff.1 $ tensor_product.ext' $ λ x y,
    by simp only [comp_apply, algebra.lsmul_coe, smul_tmul', lift.tmul, coe_restrict_scalars_eq_coe,
        f.map_smul, smul_apply],
  .. lift (f.restrict_scalars R) }

@[simp] lemma lift_tmul (f : M →ₗ[A] (N →ₗ[R] P)) (x : M) (y : N) :
  lift f (x ⊗ₜ y) = f x y :=
rfl

variables (R A M N P)
/-- Heterobasic version of `tensor_product.uncurry`:

Linearly constructing a linear map `M ⊗[R] N →[A] P` given a bilinear map `M →[A] N →[R] P`
with the property that its composition with the canonical bilinear map `M →[A] N →[R] M ⊗[R] N` is
the given bilinear map `M →[A] N →[R] P`. -/
@[simps] def uncurry : (M →ₗ[A] (N →ₗ[R] P)) →ₗ[A] ((M ⊗[R] N) →ₗ[A] P) :=
{ to_fun := lift,
  map_add' := λ f g, ext $ λ x y, by simp only [lift_tmul, add_apply],
  map_smul' := λ c f, ext $ λ x y, by simp only [lift_tmul, smul_apply, ring_hom.id_apply] }

/-- Heterobasic version of `tensor_product.lcurry`:

Given a linear map `M ⊗[R] N →[A] P`, compose it with the canonical
bilinear map `M →[A] N →[R] M ⊗[R] N` to form a bilinear map `M →[A] N →[R] P`. -/
@[simps] def lcurry : ((M ⊗[R] N) →ₗ[A] P) →ₗ[A] (M →ₗ[A] (N →ₗ[R] P)) :=
{ to_fun := curry,
  map_add' := λ f g, rfl,
  map_smul' := λ c f, rfl }

/-- Heterobasic version of `tensor_product.lift.equiv`:

A linear equivalence constructing a linear map `M ⊗[R] N →[A] P` given a
bilinear map `M →[A] N →[R] P` with the property that its composition with the
canonical bilinear map `M →[A] N →[R] M ⊗[R] N` is the given bilinear map `M →[A] N →[R] P`. -/
def lift.equiv : (M →ₗ[A] (N →ₗ[R] P)) ≃ₗ[A] ((M ⊗[R] N) →ₗ[A] P) :=
linear_equiv.of_linear (uncurry R A M N P) (lcurry R A M N P)
  (linear_map.ext $ λ f, ext $ λ x y, lift_tmul _ x y)
  (linear_map.ext $ λ f, linear_map.ext $ λ x, linear_map.ext $ λ y, lift_tmul f x y)

variables (R A M N P)
/-- Heterobasic version of `tensor_product.mk`:

The canonical bilinear map `M →[A] N →[R] M ⊗[R] N`. -/
@[simps] def mk : M →ₗ[A] N →ₗ[R] M ⊗[R] N :=
{ map_smul' := λ c x, rfl,
  .. mk R M N }

local attribute [ext] tensor_product.ext

/-- Heterobasic version of `tensor_product.assoc`:

Linear equivalence between `(M ⊗[A] N) ⊗[R] P` and `M ⊗[A] (N ⊗[R] P)`. -/
def assoc : ((M ⊗[A] P) ⊗[R] N) ≃ₗ[A] (M ⊗[A] (P ⊗[R] N)) :=
linear_equiv.of_linear
  (lift $ tensor_product.uncurry A _ _ _ $ comp (lcurry R A _ _ _) $
    tensor_product.mk A M (P ⊗[R] N))
  (tensor_product.uncurry A _ _ _ $ comp (uncurry R A _ _ _) $
    by { apply tensor_product.curry, exact (mk R A _ _) })
  (by { ext, refl, })
  (by { ext, simp only [curry_apply, tensor_product.curry_apply, mk_apply, tensor_product.mk_apply,
              uncurry_apply, tensor_product.uncurry_apply, id_apply, lift_tmul, compr₂_apply,
              restrict_scalars_apply, function.comp_app, to_fun_eq_coe, lcurry_apply,
              linear_map.comp_apply] })

end comm_semiring

end algebra_tensor_module

end tensor_product

namespace linear_map
open tensor_product

/-!
### The base-change of a linear map of `R`-modules to a linear map of `A`-modules
-/

section semiring

variables {R A B M N : Type*} [comm_semiring R]
variables [semiring A] [algebra R A] [semiring B] [algebra R B]
variables [add_comm_monoid M] [module R M] [add_comm_monoid N] [module R N]
variables (r : R) (f g : M →ₗ[R] N)

variables (A)

/-- `base_change A f` for `f : M →ₗ[R] N` is the `A`-linear map `A ⊗[R] M →ₗ[A] A ⊗[R] N`. -/
def base_change (f : M →ₗ[R] N) : A ⊗[R] M →ₗ[A] A ⊗[R] N :=
{ to_fun := f.ltensor A,
  map_add' := (f.ltensor A).map_add,
  map_smul' := λ a x,
    show (f.ltensor A) (rtensor M (linear_map.mul R A a) x) =
      (rtensor N ((linear_map.mul R A) a)) ((ltensor A f) x),
    by { rw [← comp_apply, ← comp_apply],
      simp only [ltensor_comp_rtensor, rtensor_comp_ltensor] } }

variables {A}

@[simp] lemma base_change_tmul (a : A) (x : M) :
  f.base_change A (a ⊗ₜ x) = a ⊗ₜ (f x) := rfl

lemma base_change_eq_ltensor :
  (f.base_change A : A ⊗ M → A ⊗ N) = f.ltensor A := rfl

@[simp] lemma base_change_add :
  (f + g).base_change A = f.base_change A + g.base_change A :=
by { ext, simp [base_change_eq_ltensor], }

@[simp] lemma base_change_zero : base_change A (0 : M →ₗ[R] N) = 0 :=
by { ext, simp [base_change_eq_ltensor], }

@[simp] lemma base_change_smul : (r • f).base_change A = r • (f.base_change A) :=
by { ext, simp [base_change_tmul], }

variables (R A M N)
/-- `base_change` as a linear map. -/
@[simps] def base_change_hom : (M →ₗ[R] N) →ₗ[R] A ⊗[R] M →ₗ[A] A ⊗[R] N :=
{ to_fun := base_change A,
  map_add' := base_change_add,
  map_smul' := base_change_smul }

end semiring

section ring

variables {R A B M N : Type*} [comm_ring R]
variables [ring A] [algebra R A] [ring B] [algebra R B]
variables [add_comm_group M] [module R M] [add_comm_group N] [module R N]
variables (f g : M →ₗ[R] N)

@[simp] lemma base_change_sub :
  (f - g).base_change A = f.base_change A - g.base_change A :=
by { ext, simp [base_change_eq_ltensor], }

@[simp] lemma base_change_neg : (-f).base_change A = -(f.base_change A) :=
by { ext, simp [base_change_eq_ltensor], }

end ring

end linear_map

namespace algebra
namespace tensor_product

section semiring

variables {R : Type u} [comm_semiring R]
variables {A : Type v₁} [semiring A] [algebra R A]
variables {B : Type v₂} [semiring B] [algebra R B]

/-!
### The `R`-algebra structure on `A ⊗[R] B`
-/

/--
(Implementation detail)
The multiplication map on `A ⊗[R] B`,
for a fixed pure tensor in the first argument,
as an `R`-linear map.
-/
def mul_aux (a₁ : A) (b₁ : B) : (A ⊗[R] B) →ₗ[R] (A ⊗[R] B) :=
tensor_product.map (linear_map.mul_left R a₁) (linear_map.mul_left R b₁)

@[simp]
lemma mul_aux_apply (a₁ a₂ : A) (b₁ b₂ : B) :
  (mul_aux a₁ b₁) (a₂ ⊗ₜ[R] b₂) = (a₁ * a₂) ⊗ₜ[R] (b₁ * b₂) :=
rfl

/--
(Implementation detail)
The multiplication map on `A ⊗[R] B`,
as an `R`-bilinear map.
-/
def mul : (A ⊗[R] B) →ₗ[R] (A ⊗[R] B) →ₗ[R] (A ⊗[R] B) :=
tensor_product.lift $ linear_map.mk₂ R mul_aux
  (λ x₁ x₂ y, tensor_product.ext' $ λ x' y',
    by simp only [mul_aux_apply, linear_map.add_apply, add_mul, add_tmul])
  (λ c x y, tensor_product.ext' $ λ x' y',
    by simp only [mul_aux_apply, linear_map.smul_apply, smul_tmul', smul_mul_assoc])
  (λ x y₁ y₂, tensor_product.ext' $ λ x' y',
    by simp only [mul_aux_apply, linear_map.add_apply, add_mul, tmul_add])
  (λ c x y, tensor_product.ext' $ λ x' y',
    by simp only [mul_aux_apply, linear_map.smul_apply, smul_tmul, smul_tmul', smul_mul_assoc])

@[simp]
lemma mul_apply (a₁ a₂ : A) (b₁ b₂ : B) :
  mul (a₁ ⊗ₜ[R] b₁) (a₂ ⊗ₜ[R] b₂) = (a₁ * a₂) ⊗ₜ[R] (b₁ * b₂) :=
rfl

lemma mul_assoc' (mul : (A ⊗[R] B) →ₗ[R] (A ⊗[R] B) →ₗ[R] (A ⊗[R] B))
  (h : ∀ (a₁ a₂ a₃ : A) (b₁ b₂ b₃ : B),
    mul (mul (a₁ ⊗ₜ[R] b₁) (a₂ ⊗ₜ[R] b₂)) (a₃ ⊗ₜ[R] b₃) =
      mul (a₁ ⊗ₜ[R] b₁) (mul (a₂ ⊗ₜ[R] b₂) (a₃ ⊗ₜ[R] b₃))) :
  ∀ (x y z : A ⊗[R] B), mul (mul x y) z = mul x (mul y z) :=
begin
  intros,
  apply tensor_product.induction_on x,
  { simp only [linear_map.map_zero, linear_map.zero_apply], },
  apply tensor_product.induction_on y,
  { simp only [linear_map.map_zero, forall_const, linear_map.zero_apply], },
  apply tensor_product.induction_on z,
  { simp only [linear_map.map_zero, forall_const], },
  { intros, simp only [h], },
  { intros, simp only [linear_map.map_add, *], },
  { intros, simp only [linear_map.map_add, *, linear_map.add_apply], },
  { intros, simp only [linear_map.map_add, *, linear_map.add_apply], },
end

lemma mul_assoc (x y z : A ⊗[R] B) : mul (mul x y) z = mul x (mul y z) :=
mul_assoc' mul (by { intros, simp only [mul_apply, mul_assoc], }) x y z

lemma one_mul (x : A ⊗[R] B) : mul (1 ⊗ₜ 1) x = x :=
begin
  apply tensor_product.induction_on x;
  simp {contextual := tt},
end

lemma mul_one (x : A ⊗[R] B) : mul x (1 ⊗ₜ 1) = x :=
begin
  apply tensor_product.induction_on x;
  simp {contextual := tt},
end

instance : has_one (A ⊗[R] B) :=
{ one := 1 ⊗ₜ 1 }

instance : add_monoid_with_one (A ⊗[R] B) := add_monoid_with_one.unary

instance : semiring (A ⊗[R] B) :=
{ zero := 0,
  add := (+),
  one := 1,
  mul := λ a b, mul a b,
  one_mul := one_mul,
  mul_one := mul_one,
  mul_assoc := mul_assoc,
  zero_mul := by simp,
  mul_zero := by simp,
  left_distrib := by simp,
  right_distrib := by simp,
  .. (by apply_instance : add_monoid_with_one (A ⊗[R] B)),
  .. (by apply_instance : add_comm_monoid (A ⊗[R] B)) }.

lemma one_def : (1 : A ⊗[R] B) = (1 : A) ⊗ₜ (1 : B) := rfl

@[simp]
lemma tmul_mul_tmul (a₁ a₂ : A) (b₁ b₂ : B) :
  (a₁ ⊗ₜ[R] b₁) * (a₂ ⊗ₜ[R] b₂) = (a₁ * a₂) ⊗ₜ[R] (b₁ * b₂) :=
rfl

@[simp]
lemma tmul_pow (a : A) (b : B) (k : ℕ) :
  (a ⊗ₜ[R] b)^k = (a^k) ⊗ₜ[R] (b^k) :=
begin
  induction k with k ih,
  { simp [one_def], },
  { simp [pow_succ, ih], }
end

/-- The ring morphism `A →+* A ⊗[R] B` sending `a` to `a ⊗ₜ 1`. -/
@[simps]
def include_left_ring_hom : A →+* A ⊗[R] B :=
{ to_fun := λ a, a ⊗ₜ 1,
  map_zero' := by simp,
  map_add' := by simp [add_tmul],
  map_one' := rfl,
  map_mul' := by simp }

variables {S : Type*} [comm_semiring S] [algebra R S] [algebra S A] [is_scalar_tower R S A]

instance left_algebra : algebra S (A ⊗[R] B) :=
{ commutes' := λ r x,
  begin
    apply tensor_product.induction_on x,
    { simp, },
    { intros a b, dsimp, rw [algebra.commutes, _root_.mul_one, _root_.one_mul], },
    { intros y y' h h', dsimp at h h' ⊢, simp only [mul_add, add_mul, h, h'], },
  end,
  smul_def' := λ r x,
  begin
    apply tensor_product.induction_on x,
    { simp [smul_zero], },
    { intros a b, dsimp, rw [tensor_product.smul_tmul', algebra.smul_def r a, _root_.one_mul] },
    { intros, dsimp, simp [smul_add, mul_add, *], },
  end,
  .. tensor_product.include_left_ring_hom.comp (algebra_map S A),
  .. (by apply_instance : module S (A ⊗[R] B)) }.

-- This is for the `undergrad.yaml` list.
/-- The tensor product of two `R`-algebras is an `R`-algebra. -/
instance : algebra R (A ⊗[R] B) := infer_instance

@[simp]
lemma algebra_map_apply (r : S) :
  (algebra_map S (A ⊗[R] B)) r = ((algebra_map S A) r) ⊗ₜ 1 := rfl

instance : is_scalar_tower R S (A ⊗[R] B) := ⟨λ a b c, by simp⟩

variables {C : Type v₃} [semiring C] [algebra R C]

@[ext]
theorem ext {g h : (A ⊗[R] B) →ₐ[R] C}
  (H : ∀ a b, g (a ⊗ₜ b) = h (a ⊗ₜ b)) : g = h :=
begin
  apply @alg_hom.to_linear_map_injective R (A ⊗[R] B) C _ _ _ _ _ _ _ _,
  ext,
  simp [H],
end

/-- The `R`-algebra morphism `A →ₐ[R] A ⊗[R] B` sending `a` to `a ⊗ₜ 1`. -/
def include_left : A →ₐ[R] A ⊗[R] B :=
{ commutes' := by simp,
  ..include_left_ring_hom }

@[simp]
lemma include_left_apply (a : A) : (include_left : A →ₐ[R] A ⊗[R] B) a = a ⊗ₜ 1 := rfl

/-- The algebra morphism `B →ₐ[R] A ⊗[R] B` sending `b` to `1 ⊗ₜ b`. -/
def include_right : B →ₐ[R] A ⊗[R] B :=
{ to_fun := λ b, 1 ⊗ₜ b,
  map_zero' := by simp,
  map_add' := by simp [tmul_add],
  map_one' := rfl,
  map_mul' := by simp,
  commutes' := λ r,
  begin
    simp only [algebra_map_apply],
    transitivity r • ((1 : A) ⊗ₜ[R] (1 : B)),
    { rw [←tmul_smul, algebra.smul_def], simp, },
    { simp [algebra.smul_def], },
  end, }

@[simp]
lemma include_right_apply (b : B) : (include_right : B →ₐ[R] A ⊗[R] B) b = 1 ⊗ₜ b := rfl

lemma include_left_comp_algebra_map {R S T : Type*} [comm_ring R] [comm_ring S] [comm_ring T]
  [algebra R S] [algebra R T] :
    (include_left.to_ring_hom.comp (algebra_map R S) : R →+* S ⊗[R] T) =
      include_right.to_ring_hom.comp (algebra_map R T) :=
by { ext, simp }

end semiring

section ring

variables {R : Type u} [comm_ring R]
variables {A : Type v₁} [ring A] [algebra R A]
variables {B : Type v₂} [ring B] [algebra R B]

instance : ring (A ⊗[R] B) :=
{ .. (by apply_instance : add_comm_group (A ⊗[R] B)),
  .. (by apply_instance : semiring (A ⊗[R] B)) }.

end ring

section comm_ring

variables {R : Type u} [comm_ring R]
variables {A : Type v₁} [comm_ring A] [algebra R A]
variables {B : Type v₂} [comm_ring B] [algebra R B]

instance : comm_ring (A ⊗[R] B) :=
{ mul_comm := λ x y,
  begin
    apply tensor_product.induction_on x,
    { simp, },
    { intros a₁ b₁,
      apply tensor_product.induction_on y,
      { simp, },
      { intros a₂ b₂,
        simp [mul_comm], },
      { intros a₂ b₂ ha hb,
        simp [mul_add, add_mul, ha, hb], }, },
    { intros x₁ x₂ h₁ h₂,
      simp [mul_add, add_mul, h₁, h₂], },
  end
  .. (by apply_instance : ring (A ⊗[R] B)) }.

section right_algebra

/-- `S ⊗[R] T` has a `T`-algebra structure. This is not a global instance or else the action of
`S` on `S ⊗[R] S` would be ambiguous. -/
@[reducible] def right_algebra : algebra B (A ⊗[R] B) :=
(algebra.tensor_product.include_right.to_ring_hom : B →+* A ⊗[R] B).to_algebra

local attribute [instance] tensor_product.right_algebra

instance right_is_scalar_tower : is_scalar_tower R B (A ⊗[R] B) :=
is_scalar_tower.of_algebra_map_eq (λ r, (algebra.tensor_product.include_right.commutes r).symm)

end right_algebra

end comm_ring

/--
Verify that typeclass search finds the ring structure on `A ⊗[ℤ] B`
when `A` and `B` are merely rings, by treating both as `ℤ`-algebras.
-/
example {A : Type v₁} [ring A] {B : Type v₂} [ring B] : ring (A ⊗[ℤ] B) :=
by apply_instance

/--
Verify that typeclass search finds the comm_ring structure on `A ⊗[ℤ] B`
when `A` and `B` are merely comm_rings, by treating both as `ℤ`-algebras.
-/
example {A : Type v₁} [comm_ring A] {B : Type v₂} [comm_ring B] : comm_ring (A ⊗[ℤ] B) :=
by apply_instance

/-!
We now build the structure maps for the symmetric monoidal category of `R`-algebras.
-/
section monoidal

section
variables {R : Type u} [comm_semiring R]
variables {A : Type v₁} [semiring A] [algebra R A]
variables {B : Type v₂} [semiring B] [algebra R B]
variables {C : Type v₃} [semiring C] [algebra R C]
variables {D : Type v₄} [semiring D] [algebra R D]

/--
Build an algebra morphism from a linear map out of a tensor product,
and evidence of multiplicativity on pure tensors.
-/
def alg_hom_of_linear_map_tensor_product
  (f : A ⊗[R] B →ₗ[R] C)
  (w₁ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), f ((a₁ * a₂) ⊗ₜ (b₁ * b₂)) = f (a₁ ⊗ₜ b₁) * f (a₂ ⊗ₜ b₂))
  (w₂ : ∀ r, f ((algebra_map R A) r ⊗ₜ[R] 1) = (algebra_map R C) r):
  A ⊗[R] B →ₐ[R] C :=
{ map_one' := by rw [←(algebra_map R C).map_one, ←w₂, (algebra_map R A).map_one]; refl,
  map_zero' := by rw [linear_map.to_fun_eq_coe, map_zero],
  map_mul' := λ x y, by
  { rw linear_map.to_fun_eq_coe,
    apply tensor_product.induction_on x,
    { rw [zero_mul, map_zero, zero_mul] },
    { intros a₁ b₁,
      apply tensor_product.induction_on y,
      { rw [mul_zero, map_zero, mul_zero] },
      { intros a₂ b₂,
        rw [tmul_mul_tmul, w₁] },
      { intros x₁ x₂ h₁ h₂,
        rw [mul_add, map_add, map_add, mul_add, h₁, h₂] } },
    { intros x₁ x₂ h₁ h₂,
      rw [add_mul, map_add, map_add, add_mul, h₁, h₂] } },
  commutes' := λ r, by rw [linear_map.to_fun_eq_coe, algebra_map_apply, w₂],
  .. f }

@[simp]
lemma alg_hom_of_linear_map_tensor_product_apply (f w₁ w₂ x) :
  (alg_hom_of_linear_map_tensor_product f w₁ w₂ : A ⊗[R] B →ₐ[R] C) x = f x := rfl

/--
Build an algebra equivalence from a linear equivalence out of a tensor product,
and evidence of multiplicativity on pure tensors.
-/
def alg_equiv_of_linear_equiv_tensor_product
  (f : A ⊗[R] B ≃ₗ[R] C)
  (w₁ : ∀ (a₁ a₂ : A) (b₁ b₂ : B), f ((a₁ * a₂) ⊗ₜ (b₁ * b₂)) = f (a₁ ⊗ₜ b₁) * f (a₂ ⊗ₜ b₂))
  (w₂ : ∀ r, f ((algebra_map R A) r ⊗ₜ[R] 1) = (algebra_map R C) r):
  A ⊗[R] B ≃ₐ[R] C :=
{ .. alg_hom_of_linear_map_tensor_product (f : A ⊗[R] B →ₗ[R] C) w₁ w₂,
  .. f }

@[simp]
lemma alg_equiv_of_linear_equiv_tensor_product_apply (f w₁ w₂ x) :
  (alg_equiv_of_linear_equiv_tensor_product f w₁ w₂ : A ⊗[R] B ≃ₐ[R] C) x = f x := rfl

/--
Build an algebra equivalence from a linear equivalence out of a triple tensor product,
and evidence of multiplicativity on pure tensors.
-/
def alg_equiv_of_linear_equiv_triple_tensor_product
  (f : ((A ⊗[R] B) ⊗[R] C) ≃ₗ[R] D)
  (w₁ : ∀ (a₁ a₂ : A) (b₁ b₂ : B) (c₁ c₂ : C),
    f ((a₁ * a₂) ⊗ₜ (b₁ * b₂) ⊗ₜ (c₁ * c₂)) = f (a₁ ⊗ₜ b₁ ⊗ₜ c₁) * f (a₂ ⊗ₜ b₂ ⊗ₜ c₂))
  (w₂ : ∀ r, f (((algebra_map R A) r ⊗ₜ[R] (1 : B)) ⊗ₜ[R] (1 : C)) = (algebra_map R D) r) :
  (A ⊗[R] B) ⊗[R] C ≃ₐ[R] D :=
{ to_fun := f,
  map_mul' := λ x y,
  begin
    apply tensor_product.induction_on x,
    { simp only [map_zero, zero_mul] },
    { intros ab₁ c₁,
      apply tensor_product.induction_on y,
      { simp only [map_zero, mul_zero] },
      { intros ab₂ c₂,
        apply tensor_product.induction_on ab₁,
        { simp only [zero_tmul, map_zero, zero_mul] },
        { intros a₁ b₁,
          apply tensor_product.induction_on ab₂,
          { simp only [zero_tmul, map_zero, mul_zero] },
          { intros, simp only [tmul_mul_tmul, w₁] },
          { intros x₁ x₂ h₁ h₂,
            simp only [tmul_mul_tmul] at h₁ h₂,
            simp only [tmul_mul_tmul, mul_add, add_tmul, map_add, h₁, h₂] } },
        { intros x₁ x₂ h₁ h₂,
          simp only [tmul_mul_tmul] at h₁ h₂,
          simp only [tmul_mul_tmul, add_mul, add_tmul, map_add, h₁, h₂] } },
      { intros x₁ x₂ h₁ h₂,
        simp only [tmul_mul_tmul, map_add, mul_add, add_mul, h₁, h₂], }, },
    { intros x₁ x₂ h₁ h₂,
      simp only [tmul_mul_tmul, map_add, mul_add, add_mul, h₁, h₂], }
  end,
  commutes' := λ r, by simp [w₂],
  .. f }

@[simp]
lemma alg_equiv_of_linear_equiv_triple_tensor_product_apply (f w₁ w₂ x) :
  (alg_equiv_of_linear_equiv_triple_tensor_product f w₁ w₂ : (A ⊗[R] B) ⊗[R] C ≃ₐ[R] D) x = f x :=
rfl

end

variables {R : Type u} [comm_semiring R]
variables {A : Type v₁} [semiring A] [algebra R A]
variables {B : Type v₂} [semiring B] [algebra R B]
variables {C : Type v₃} [semiring C] [algebra R C]
variables {D : Type v₄} [semiring D] [algebra R D]

section
variables (R A)
/--
The base ring is a left identity for the tensor product of algebra, up to algebra isomorphism.
-/
protected def lid : R ⊗[R] A ≃ₐ[R] A :=
alg_equiv_of_linear_equiv_tensor_product (tensor_product.lid R A)
(by simp [mul_smul]) (by simp [algebra.smul_def])

@[simp]

/--
The base ring is a right identity for the tensor product of algebra, up to algebra isomorphism.
-/
protected def rid : A ⊗[R] R ≃ₐ[R] A :=
alg_equiv_of_linear_equiv_tensor_product (tensor_product.rid R A)
(by simp [mul_smul]) (by simp [algebra.smul_def])

@[simp] theorem rid_tmul (r : R) (a : A) :
  (tensor_product.rid R A : (A ⊗ R → A)) (a ⊗ₜ r) = r • a :=
by simp [tensor_product.rid]

section
variables (R A B)

/--
The tensor product of R-algebras is commutative, up to algebra isomorphism.
-/
protected def comm : A ⊗[R] B ≃ₐ[R] B ⊗[R] A :=
alg_equiv_of_linear_equiv_tensor_product (tensor_product.comm R A B)
(by simp)
(λ r, begin
  transitivity r • ((1 : B) ⊗ₜ[R] (1 : A)),
  { rw [←tmul_smul, algebra.smul_def], simp, },
  { simp [algebra.smul_def], },
end)

@[simp]
theorem comm_tmul (a : A) (b : B) :
  (tensor_product.comm R A B : (A ⊗[R] B → B ⊗[R] A)) (a ⊗ₜ b) = (b ⊗ₜ a) :=
by simp [tensor_product.comm]

lemma adjoin_tmul_eq_top : adjoin R {t : A ⊗[R] B | ∃ a b, a ⊗ₜ[R] b = t} = ⊤ :=
top_le_iff.mp $ (top_le_iff.mpr $ span_tmul_eq_top R A B).trans (span_le_adjoin R _)

end

section
variables {R A B C}

lemma assoc_aux_1 (a₁ a₂ : A) (b₁ b₂ : B) (c₁ c₂ : C) :
  (tensor_product.assoc R A B C) (((a₁ * a₂) ⊗ₜ[R] (b₁ * b₂)) ⊗ₜ[R] (c₁ * c₂)) =
    (tensor_product.assoc R A B C) ((a₁ ⊗ₜ[R] b₁) ⊗ₜ[R] c₁) *
      (tensor_product.assoc R A B C) ((a₂ ⊗ₜ[R] b₂) ⊗ₜ[R] c₂) :=
rfl

lemma assoc_aux_2 (r : R) :
  (tensor_product.assoc R A B C) (((algebra_map R A) r ⊗ₜ[R] 1) ⊗ₜ[R] 1) =
    (algebra_map R (A ⊗ (B ⊗ C))) r := rfl

variables (R A B C)

/-- The associator for tensor product of R-algebras, as an algebra isomorphism. -/
protected def assoc : ((A ⊗[R] B) ⊗[R] C) ≃ₐ[R] (A ⊗[R] (B ⊗[R] C)) :=
alg_equiv_of_linear_equiv_triple_tensor_product
  (tensor_product.assoc.{u v₁ v₂ v₃} R A B C : (A ⊗ B ⊗ C) ≃ₗ[R] (A ⊗ (B ⊗ C)))
  (@algebra.tensor_product.assoc_aux_1.{u v₁ v₂ v₃} R _ A _ _ B _ _ C _ _)
  (@algebra.tensor_product.assoc_aux_2.{u v₁ v₂ v₃} R _ A _ _ B _ _ C _ _)

variables {R A B C}

@[simp] theorem assoc_tmul (a : A) (b : B) (c : C) :
  ((tensor_product.assoc R A B C) :
  (A ⊗[R] B) ⊗[R] C → A ⊗[R] (B ⊗[R] C)) ((a ⊗ₜ b) ⊗ₜ c) = a ⊗ₜ (b ⊗ₜ c) :=
rfl

end

variables {R A B C D}

/-- The tensor product of a pair of algebra morphisms. -/
def map (f : A →ₐ[R] B) (g : C →ₐ[R] D) : A ⊗[R] C →ₐ[R] B ⊗[R] D :=
alg_hom_of_linear_map_tensor_product
  (tensor_product.map f.to_linear_map g.to_linear_map)
  (by simp)
  (by simp [alg_hom.commutes])

@[simp] theorem map_tmul (f : A →ₐ[R] B) (g : C →ₐ[R] D) (a : A) (c : C) :
  map f g (a ⊗ₜ c) = f a ⊗ₜ g c :=
rfl

@[simp] lemma map_comp_include_left (f : A →ₐ[R] B) (g : C →ₐ[R] D) :
  (map f g).comp include_left = include_left.comp f := alg_hom.ext $ by simp

@[simp] lemma map_comp_include_right (f : A →ₐ[R] B) (g : C →ₐ[R] D) :
  (map f g).comp include_right = include_right.comp g := alg_hom.ext $ by simp

lemma map_range (f : A →ₐ[R] B) (g : C →ₐ[R] D) :
  (map f g).range = (include_left.comp f).range ⊔ (include_right.comp g).range :=
begin
  apply le_antisymm,
  { rw [←map_top, ←adjoin_tmul_eq_top, ←adjoin_image, adjoin_le_iff],
    rintros _ ⟨_, ⟨a, b, rfl⟩, rfl⟩,
    rw [map_tmul, ←_root_.mul_one (f a), ←_root_.one_mul (g b), ←tmul_mul_tmul],
    exact mul_mem_sup (alg_hom.mem_range_self _ a) (alg_hom.mem_range_self _ b) },
  { rw [←map_comp_include_left f g, ←map_comp_include_right f g],
    exact sup_le (alg_hom.range_comp_le_range _ _) (alg_hom.range_comp_le_range _ _) },
end

/--
Construct an isomorphism between tensor products of R-algebras
from isomorphisms between the tensor factors.
-/
def congr (f : A ≃ₐ[R] B) (g : C ≃ₐ[R] D) : A ⊗[R] C ≃ₐ[R] B ⊗[R] D :=
alg_equiv.of_alg_hom (map f g) (map f.symm g.symm)
  (ext $ λ b d, by simp)
  (ext $ λ a c, by simp)

@[simp]
lemma congr_apply (f : A ≃ₐ[R] B) (g : C ≃ₐ[R] D) (x) :
  congr f g x = (map (f : A →ₐ[R] B) (g : C →ₐ[R] D)) x := rfl

@[simp]
lemma congr_symm_apply (f : A ≃ₐ[R] B) (g : C ≃ₐ[R] D) (x) :
  (congr f g).symm x = (map (f.symm : B →ₐ[R] A) (g.symm : D →ₐ[R] C)) x := rfl

end

end monoidal

section

variables {R A B S : Type*} [comm_semiring R] [semiring A] [semiring B] [comm_semiring S]
variables [algebra R A] [algebra R B] [algebra R S]
variables (f : A →ₐ[R] S) (g : B →ₐ[R] S)

variables (R)

/-- `linear_map.mul'` is an alg_hom on commutative rings. -/
def lmul' : S ⊗[R] S →ₐ[R] S :=
alg_hom_of_linear_map_tensor_product (linear_map.mul' R S)
  (λ a₁ a₂ b₁ b₂, by simp only [linear_map.mul'_apply, mul_mul_mul_comm])
  (λ r, by simp only [linear_map.mul'_apply, _root_.mul_one])

variables {R}

lemma lmul'_to_linear_map : (lmul' R : _ →ₐ[R] S).to_linear_map = linear_map.mul' R S := rfl

@[simp] lemma lmul'_apply_tmul (a b : S) : lmul' R (a ⊗ₜ[R] b) = a * b := rfl

@[simp]
lemma lmul'_comp_include_left : (lmul' R : _ →ₐ[R] S).comp include_left = alg_hom.id R S :=
alg_hom.ext $ _root_.mul_one

@[simp]
lemma lmul'_comp_include_right : (lmul' R : _ →ₐ[R] S).comp include_right = alg_hom.id R S :=
alg_hom.ext $ _root_.one_mul

/--
If `S` is commutative, for a pair of morphisms `f : A →ₐ[R] S`, `g : B →ₐ[R] S`,
We obtain a map `A ⊗[R] B →ₐ[R] S` that commutes with `f`, `g` via `a ⊗ b ↦ f(a) * g(b)`.
-/
def product_map : A ⊗[R] B →ₐ[R] S := (lmul' R).comp (tensor_product.map f g)

@[simp] lemma product_map_apply_tmul (a : A) (b : B) : product_map f g (a ⊗ₜ b) = f a * g b :=
by { unfold product_map lmul', simp }

lemma product_map_left_apply (a : A) :
  product_map f g ((include_left : A →ₐ[R] A ⊗ B) a) = f a := by simp

@[simp] lemma product_map_left : (product_map f g).comp include_left = f := alg_hom.ext $ by simp

lemma product_map_right_apply (b : B) : product_map f g (include_right b) = g b := by simp

@[simp] lemma product_map_right : (product_map f g).comp include_right = g := alg_hom.ext $ by simp

lemma product_map_range : (product_map f g).range = f.range ⊔ g.range :=
by rw [product_map, alg_hom.range_comp, map_range, map_sup, ←alg_hom.range_comp,
    ←alg_hom.range_comp, ←alg_hom.comp_assoc, ←alg_hom.comp_assoc, lmul'_comp_include_left,
    lmul'_comp_include_right, alg_hom.id_comp, alg_hom.id_comp]

end
section

variables {R A A' B S : Type*}
variables [comm_semiring R] [comm_semiring A] [semiring A'] [semiring B] [comm_semiring S]
variables [algebra R A] [algebra R A'] [algebra A A'] [is_scalar_tower R A A'] [algebra R B]
variables [algebra R S] [algebra A S] [is_scalar_tower R A S]

/-- If `A`, `B` are `R`-algebras, `A'` is an `A`-algebra, then the product map of `f : A' →ₐ[A] S`
and `g : B →ₐ[R] S` is an `A`-algebra homomorphism. -/
@[simps] def product_left_alg_hom (f : A' →ₐ[A] S) (g : B →ₐ[R] S) : A' ⊗[R] B →ₐ[A] S :=
{ commutes' := λ r, by { dsimp, simp },
  ..(product_map (f.restrict_scalars R) g).to_ring_hom }

end
section basis

variables {k : Type*} [comm_ring k] (R : Type*) [ring R] [algebra k R] {M : Type*}
  [add_comm_monoid M] [module k M] {ι : Type*} (b : basis ι k M)

/-- Given a `k`-algebra `R` and a `k`-basis of `M,` this is a `k`-linear isomorphism
`R ⊗[k] M ≃ (ι →₀ R)` (which is in fact `R`-linear). -/
noncomputable def basis_aux : R ⊗[k] M ≃ₗ[k] (ι →₀ R) :=
(_root_.tensor_product.congr (finsupp.linear_equiv.finsupp_unique k R punit).symm b.repr) ≪≫ₗ
  (finsupp_tensor_finsupp k R k punit ι).trans (finsupp.lcongr (equiv.unique_prod ι punit)
  (_root_.tensor_product.rid k R))

variables {R}

lemma basis_aux_tmul (r : R) (m : M) :
  basis_aux R b (r ⊗ₜ m) = r • (finsupp.map_range (algebra_map k R)
    (map_zero _) (b.repr m)) :=
begin
  ext,
  simp [basis_aux, ←algebra.commutes, algebra.smul_def],
end

lemma basis_aux_map_smul (r : R) (x : R ⊗[k] M) :
  basis_aux R b (r • x) = r • basis_aux R b x :=
tensor_product.induction_on x (by simp) (λ x y, by simp only [tensor_product.smul_tmul',
  basis_aux_tmul, smul_assoc]) (λ x y hx hy, by simp [hx, hy])

variables (R)

/-- Given a `k`-algebra `R`, this is the `R`-basis of `R ⊗[k] M` induced by a `k`-basis of `M`. -/
noncomputable def basis : basis ι R (R ⊗[k] M) :=
{ repr := { map_smul' := basis_aux_map_smul b, .. basis_aux R b } }

variables {R}

@[simp] lemma basis_repr_tmul (r : R) (m : M) :
  (basis R b).repr (r ⊗ₜ m) = r • (finsupp.map_range (algebra_map k R) (map_zero _) (b.repr m)) :=
basis_aux_tmul _ _ _

@[simp] lemma basis_repr_symm_apply (r : R) (i : ι) :
  (basis R b).repr.symm (finsupp.single i r) = r ⊗ₜ b.repr.symm (finsupp.single i 1) :=
by simp [basis, equiv.unique_prod_symm_apply, basis_aux]

end basis
end tensor_product
end algebra

namespace module

variables {R M N : Type*} [comm_semiring R]
variables [add_comm_monoid M] [add_comm_monoid N]
variables [module R M] [module R N]

/-- The algebra homomorphism from `End M ⊗ End N` to `End (M ⊗ N)` sending `f ⊗ₜ g` to
the `tensor_product.map f g`, the tensor product of the two maps. -/
def End_tensor_End_alg_hom : (End R M) ⊗[R] (End R N) →ₐ[R] End R (M ⊗[R] N) :=
begin
  refine algebra.tensor_product.alg_hom_of_linear_map_tensor_product
    (hom_tensor_hom_map R M N M N) _ _,
  { intros f₁ f₂ g₁ g₂,
    simp only [hom_tensor_hom_map_apply, tensor_product.map_mul] },
  { intro r,
    simp only [hom_tensor_hom_map_apply],
    ext m n, simp [smul_tmul] }
end

lemma End_tensor_End_alg_hom_apply (f : End R M) (g : End R N) :
  End_tensor_End_alg_hom (f ⊗ₜ[R] g) = tensor_product.map f g :=
by simp only [End_tensor_End_alg_hom,
  algebra.tensor_product.alg_hom_of_linear_map_tensor_product_apply, hom_tensor_hom_map_apply]

end module

lemma subalgebra.finite_dimensional_sup {K L : Type*} [field K] [comm_ring L] [algebra K L]
  (E1 E2 : subalgebra K L) [finite_dimensional K E1] [finite_dimensional K E2] :
  finite_dimensional K ↥(E1 ⊔ E2) :=
begin
  rw [←E1.range_val, ←E2.range_val, ←algebra.tensor_product.product_map_range],
  exact (algebra.tensor_product.product_map E1.val E2.val).to_linear_map.finite_dimensional_range,
end

namespace tensor_product.algebra

variables {R A B M : Type*}
variables [comm_semiring R] [add_comm_monoid M] [module R M]
variables [semiring A] [semiring B] [module A M] [module B M]
variables [algebra R A] [algebra R B]
variables [is_scalar_tower R A M] [is_scalar_tower R B M]

/-- An auxiliary definition, used for constructing the `module (A ⊗[R] B) M` in
`tensor_product.algebra.module` below. -/
def module_aux : A ⊗[R] B →ₗ[R] M →ₗ[R] M :=
tensor_product.lift
{ to_fun := λ a, a • (algebra.lsmul R M : B →ₐ[R] module.End R M).to_linear_map,
  map_add' := λ r t, by { ext, simp only [add_smul, linear_map.add_apply] },
  map_smul' := λ n r, by { ext, simp only [ring_hom.id_apply, linear_map.smul_apply, smul_assoc] } }

lemma module_aux_apply (a : A) (b : B) (m : M) :
  module_aux (a ⊗ₜ[R] b) m = a • b • m := rfl

variables [smul_comm_class A B M]

/-- If `M` is a representation of two different `R`-algebras `A` and `B` whose actions commute,
then it is a representation the `R`-algebra `A ⊗[R] B`.

An important example arises from a semiring `S`; allowing `S` to act on itself via left and right
multiplication, the roles of `R`, `A`, `B`, `M` are played by `ℕ`, `S`, `Sᵐᵒᵖ`, `S`. This example
is important because a submodule of `S` as a `module` over `S ⊗[ℕ] Sᵐᵒᵖ` is a two-sided ideal.

NB: This is not an instance because in the case `B = A` and `M = A ⊗[R] A` we would have a diamond
of `smul` actions. Furthermore, this would not be a mere definitional diamond but a true
mathematical diamond in which `A ⊗[R] A` had two distinct scalar actions on itself: one from its
multiplication, and one from this would-be instance. Arguably we could live with this but in any
case the real fix is to address the ambiguity in notation, probably along the lines outlined here:
https://leanprover.zulipchat.com/#narrow/stream/144837-PR-reviews/topic/.234773.20base.20change/near/240929258
-/
protected def module : module (A ⊗[R] B) M :=
{ smul := λ x m, module_aux x m,
  zero_smul := λ m, by simp only [map_zero, linear_map.zero_apply],
  smul_zero := λ x, by simp only [map_zero],
  smul_add := λ x m₁ m₂, by simp only [map_add],
  add_smul := λ x y m, by simp only [map_add, linear_map.add_apply],
  one_smul := λ m, by simp only [module_aux_apply, algebra.tensor_product.one_def, one_smul],
  mul_smul := λ x y m,
  begin
    apply tensor_product.induction_on x;
    apply tensor_product.induction_on y,
    { simp only [mul_zero, map_zero, linear_map.zero_apply], },
    { intros a b, simp only [zero_mul, map_zero, linear_map.zero_apply], },
    { intros z w hz hw, simp only [zero_mul, map_zero, linear_map.zero_apply], },
    { intros a b, simp only [mul_zero, map_zero, linear_map.zero_apply], },
    { intros a₁ b₁ a₂ b₂,
      simp only [module_aux_apply, mul_smul, smul_comm a₁ b₂, algebra.tensor_product.tmul_mul_tmul,
        linear_map.mul_apply], },
    { intros z w hz hw a b,
      simp only at hz hw,
      simp only [mul_add, hz, hw, map_add, linear_map.add_apply], },
    { intros z w hz hw, simp only [mul_zero, map_zero, linear_map.zero_apply], },
    { intros a b z w hz hw,
      simp only at hz hw,
      simp only [map_add, add_mul, linear_map.add_apply, hz, hw], },
    { intros u v hu hv z w hz hw,
      simp only at hz hw,
      simp only [add_mul, hz, hw, map_add, linear_map.add_apply], },
  end }

local attribute [instance] tensor_product.algebra.module

lemma smul_def (a : A) (b : B) (m : M) : (a ⊗ₜ[R] b) • m = a • b • m := rfl

end tensor_product.algebra


lemma add_assoc (a b c : mynat) : (a + b) + c = a + (b + c) :=
begin
induction c with h hd,
rw add_zero,
rw add_zero,
refl,
rw add_succ,
rw add_succ,
rw add_succ,
rw hd,
refl,
end


-- Desigualdad de naturales
-- ========================

import data.pnat.basic
import tactic

-- 1ª demostración
lemma larger_ineq
  (x y z: ℕ+)
  (h: x + z < y)
  : ¬ x + 2 * z + (y - x - z) < z :=
begin
  cases x with x hx,
  cases y with y hy,
  cases z with z hz,
  simp [← pnat.coe_lt_coe] at ⊢ h,
  linarith,
end


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

! This file was ported from Lean 3 source module topology.dense_embedding
! leanprover-community/mathlib commit 0ebfdb71919ac6ca5d7fbc61a082fa2519556818
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Separation
import Mathbin.Topology.Bases

/-!
# Dense embeddings

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

This file defines three properties of functions:

* `dense_range f`      means `f` has dense image;
* `dense_inducing i`   means `i` is also `inducing`;
* `dense_embedding e`  means `e` is also an `embedding`.

The main theorem `continuous_extend` gives a criterion for a function
`f : X → Z` to a T₃ space Z to extend along a dense embedding
`i : X → Y` to a continuous function `g : Y → Z`. Actually `i` only
has to be `dense_inducing` (not necessarily injective).

-/

noncomputable section

open Set Filter

open Classical Topology Filter

variable {α : Type _} {β : Type _} {γ : Type _} {δ : Type _}

#print DenseInducing /-
/-- `i : α → β` is "dense inducing" if it has dense range and the topology on `α`
  is the one induced by `i` from the topology on `β`. -/
@[protect_proj]
structure DenseInducing [TopologicalSpace α] [TopologicalSpace β] (i : α → β) extends Inducing i :
  Prop where
  dense : DenseRange i
#align dense_inducing DenseInducing
-/

namespace DenseInducing

variable [TopologicalSpace α] [TopologicalSpace β]

variable {i : α → β} (di : DenseInducing i)

/- warning: dense_inducing.nhds_eq_comap -> DenseInducing.nhds_eq_comap is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (forall (a : α), Eq.{succ u1} (Filter.{u1} α) (nhds.{u1} α _inst_1 a) (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 (i a))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (forall (a : α), Eq.{succ u2} (Filter.{u2} α) (nhds.{u2} α _inst_1 a) (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 (i a))))
Case conversion may be inaccurate. Consider using '#align dense_inducing.nhds_eq_comap DenseInducing.nhds_eq_comapₓ'. -/
theorem nhds_eq_comap (di : DenseInducing i) : ∀ a : α, 𝓝 a = comap i (𝓝 <| i a) :=
  di.to_inducing.nhds_eq_comap
#align dense_inducing.nhds_eq_comap DenseInducing.nhds_eq_comap

/- warning: dense_inducing.continuous -> DenseInducing.continuous is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (Continuous.{u1, u2} α β _inst_1 _inst_2 i)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (Continuous.{u2, u1} α β _inst_1 _inst_2 i)
Case conversion may be inaccurate. Consider using '#align dense_inducing.continuous DenseInducing.continuousₓ'. -/
protected theorem continuous (di : DenseInducing i) : Continuous i :=
  di.to_inducing.Continuous
#align dense_inducing.continuous DenseInducing.continuous

#print DenseInducing.closure_range /-
theorem closure_range : closure (range i) = univ :=
  di.dense.closure_range
#align dense_inducing.closure_range DenseInducing.closure_range
-/

/- warning: dense_inducing.preconnected_space -> DenseInducing.preconnectedSpace is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_3 : PreconnectedSpace.{u1} α _inst_1], (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (PreconnectedSpace.{u2} β _inst_2)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_3 : PreconnectedSpace.{u2} α _inst_1], (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (PreconnectedSpace.{u1} β _inst_2)
Case conversion may be inaccurate. Consider using '#align dense_inducing.preconnected_space DenseInducing.preconnectedSpaceₓ'. -/
protected theorem preconnectedSpace [PreconnectedSpace α] (di : DenseInducing i) :
    PreconnectedSpace β :=
  di.dense.PreconnectedSpace di.Continuous
#align dense_inducing.preconnected_space DenseInducing.preconnectedSpace

/- warning: dense_inducing.closure_image_mem_nhds -> DenseInducing.closure_image_mem_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} {s : Set.{u1} α} {a : α}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α _inst_1 a)) -> (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) (closure.{u2} β _inst_2 (Set.image.{u1, u2} α β i s)) (nhds.{u2} β _inst_2 (i a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} {s : Set.{u2} α} {a : α}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α _inst_1 a)) -> (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) (closure.{u1} β _inst_2 (Set.image.{u2, u1} α β i s)) (nhds.{u1} β _inst_2 (i a)))
Case conversion may be inaccurate. Consider using '#align dense_inducing.closure_image_mem_nhds DenseInducing.closure_image_mem_nhdsₓ'. -/
theorem closure_image_mem_nhds {s : Set α} {a : α} (di : DenseInducing i) (hs : s ∈ 𝓝 a) :
    closure (i '' s) ∈ 𝓝 (i a) :=
  by
  rw [di.nhds_eq_comap a, ((nhds_basis_opens _).comap _).mem_iff] at hs
  rcases hs with ⟨U, ⟨haU, hUo⟩, sub : i ⁻¹' U ⊆ s⟩
  refine' mem_of_superset (hUo.mem_nhds haU) _
  calc
    U ⊆ closure (i '' (i ⁻¹' U)) := di.dense.subset_closure_image_preimage_of_is_open hUo
    _ ⊆ closure (i '' s) := closure_mono (image_subset i sub)

#align dense_inducing.closure_image_mem_nhds DenseInducing.closure_image_mem_nhds

/- warning: dense_inducing.dense_image -> DenseInducing.dense_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (forall {s : Set.{u1} α}, Iff (Dense.{u2} β _inst_2 (Set.image.{u1, u2} α β i s)) (Dense.{u1} α _inst_1 s))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (forall {s : Set.{u2} α}, Iff (Dense.{u1} β _inst_2 (Set.image.{u2, u1} α β i s)) (Dense.{u2} α _inst_1 s))
Case conversion may be inaccurate. Consider using '#align dense_inducing.dense_image DenseInducing.dense_imageₓ'. -/
theorem dense_image (di : DenseInducing i) {s : Set α} : Dense (i '' s) ↔ Dense s :=
  by
  refine' ⟨fun H x => _, di.dense.dense_image di.continuous⟩
  rw [di.to_inducing.closure_eq_preimage_closure_image, H.closure_eq, preimage_univ]
  trivial
#align dense_inducing.dense_image DenseInducing.dense_image

/- warning: dense_inducing.interior_compact_eq_empty -> DenseInducing.interior_compact_eq_empty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_3 : T2Space.{u2} β _inst_2], (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (Dense.{u2} β _inst_2 (HasCompl.compl.{u2} (Set.{u2} β) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} β) (Set.booleanAlgebra.{u2} β)) (Set.range.{u2, succ u1} β α i))) -> (forall {s : Set.{u1} α}, (IsCompact.{u1} α _inst_1 s) -> (Eq.{succ u1} (Set.{u1} α) (interior.{u1} α _inst_1 s) (EmptyCollection.emptyCollection.{u1} (Set.{u1} α) (Set.hasEmptyc.{u1} α))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_3 : T2Space.{u2} β _inst_2], (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (Dense.{u2} β _inst_2 (HasCompl.compl.{u2} (Set.{u2} β) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} β) (Set.instBooleanAlgebraSet.{u2} β)) (Set.range.{u2, succ u1} β α i))) -> (forall {s : Set.{u1} α}, (IsCompact.{u1} α _inst_1 s) -> (Eq.{succ u1} (Set.{u1} α) (interior.{u1} α _inst_1 s) (EmptyCollection.emptyCollection.{u1} (Set.{u1} α) (Set.instEmptyCollectionSet.{u1} α))))
Case conversion may be inaccurate. Consider using '#align dense_inducing.interior_compact_eq_empty DenseInducing.interior_compact_eq_emptyₓ'. -/
/-- If `i : α → β` is a dense embedding with dense complement of the range, then any compact set in
`α` has empty interior. -/
theorem interior_compact_eq_empty [T2Space β] (di : DenseInducing i) (hd : Dense (range iᶜ))
    {s : Set α} (hs : IsCompact s) : interior s = ∅ :=
  by
  refine' eq_empty_iff_forall_not_mem.2 fun x hx => _
  rw [mem_interior_iff_mem_nhds] at hx
  have := di.closure_image_mem_nhds hx
  rw [(hs.image di.continuous).IsClosed.closure_eq] at this
  rcases hd.inter_nhds_nonempty this with ⟨y, hyi, hys⟩
  exact hyi (image_subset_range _ _ hys)
#align dense_inducing.interior_compact_eq_empty DenseInducing.interior_compact_eq_empty

/- warning: dense_inducing.prod -> DenseInducing.prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] [_inst_4 : TopologicalSpace.{u4} δ] {e₁ : α -> β} {e₂ : γ -> δ}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 e₁) -> (DenseInducing.{u3, u4} γ δ _inst_3 _inst_4 e₂) -> (DenseInducing.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Prod.topologicalSpace.{u1, u3} α γ _inst_1 _inst_3) (Prod.topologicalSpace.{u2, u4} β δ _inst_2 _inst_4) (fun (p : Prod.{u1, u3} α γ) => Prod.mk.{u2, u4} β δ (e₁ (Prod.fst.{u1, u3} α γ p)) (e₂ (Prod.snd.{u1, u3} α γ p))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u4}} {δ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] [_inst_3 : TopologicalSpace.{u4} γ] [_inst_4 : TopologicalSpace.{u3} δ] {e₁ : α -> β} {e₂ : γ -> δ}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 e₁) -> (DenseInducing.{u4, u3} γ δ _inst_3 _inst_4 e₂) -> (DenseInducing.{max u2 u4, max u3 u1} (Prod.{u2, u4} α γ) (Prod.{u1, u3} β δ) (instTopologicalSpaceProd.{u2, u4} α γ _inst_1 _inst_3) (instTopologicalSpaceProd.{u1, u3} β δ _inst_2 _inst_4) (fun (p : Prod.{u2, u4} α γ) => Prod.mk.{u1, u3} β δ (e₁ (Prod.fst.{u2, u4} α γ p)) (e₂ (Prod.snd.{u2, u4} α γ p))))
Case conversion may be inaccurate. Consider using '#align dense_inducing.prod DenseInducing.prodₓ'. -/
/-- The product of two dense inducings is a dense inducing -/
protected theorem prod [TopologicalSpace γ] [TopologicalSpace δ] {e₁ : α → β} {e₂ : γ → δ}
    (de₁ : DenseInducing e₁) (de₂ : DenseInducing e₂) :
    DenseInducing fun p : α × γ => (e₁ p.1, e₂ p.2) :=
  { induced := (de₁.to_inducing.prod_mk de₂.to_inducing).induced
    dense := de₁.dense.Prod_map de₂.dense }
#align dense_inducing.prod DenseInducing.prod

open TopologicalSpace

/- warning: dense_inducing.separable_space -> DenseInducing.separableSpace is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (forall [_inst_3 : TopologicalSpace.SeparableSpace.{u1} α _inst_1], TopologicalSpace.SeparableSpace.{u2} β _inst_2)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (forall [_inst_3 : TopologicalSpace.SeparableSpace.{u2} α _inst_1], TopologicalSpace.SeparableSpace.{u1} β _inst_2)
Case conversion may be inaccurate. Consider using '#align dense_inducing.separable_space DenseInducing.separableSpaceₓ'. -/
/-- If the domain of a `dense_inducing` map is a separable space, then so is the codomain. -/
protected theorem separableSpace [SeparableSpace α] : SeparableSpace β :=
  di.dense.SeparableSpace di.Continuous
#align dense_inducing.separable_space DenseInducing.separableSpace

variable [TopologicalSpace δ] {f : γ → α} {g : γ → δ} {h : δ → β}

/- warning: dense_inducing.tendsto_comap_nhds_nhds -> DenseInducing.tendsto_comap_nhds_nhds is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_3 : TopologicalSpace.{u4} δ] {f : γ -> α} {g : γ -> δ} {h : δ -> β} {d : δ} {a : α}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (Filter.Tendsto.{u4, u2} δ β h (nhds.{u4} δ _inst_3 d) (nhds.{u2} β _inst_2 (i a))) -> (Eq.{max (succ u3) (succ u2)} (γ -> β) (Function.comp.{succ u3, succ u4, succ u2} γ δ β h g) (Function.comp.{succ u3, succ u1, succ u2} γ α β i f)) -> (Filter.Tendsto.{u3, u1} γ α f (Filter.comap.{u3, u4} γ δ g (nhds.{u4} δ _inst_3 d)) (nhds.{u1} α _inst_1 a))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u1}} {δ : Type.{u2}} [_inst_1 : TopologicalSpace.{u4} α] [_inst_2 : TopologicalSpace.{u3} β] {i : α -> β} [_inst_3 : TopologicalSpace.{u2} δ] {f : γ -> α} {g : γ -> δ} {h : δ -> β} {d : δ} {a : α}, (DenseInducing.{u4, u3} α β _inst_1 _inst_2 i) -> (Filter.Tendsto.{u2, u3} δ β h (nhds.{u2} δ _inst_3 d) (nhds.{u3} β _inst_2 (i a))) -> (Eq.{max (succ u3) (succ u1)} (γ -> β) (Function.comp.{succ u1, succ u2, succ u3} γ δ β h g) (Function.comp.{succ u1, succ u4, succ u3} γ α β i f)) -> (Filter.Tendsto.{u1, u4} γ α f (Filter.comap.{u1, u2} γ δ g (nhds.{u2} δ _inst_3 d)) (nhds.{u4} α _inst_1 a))
Case conversion may be inaccurate. Consider using '#align dense_inducing.tendsto_comap_nhds_nhds DenseInducing.tendsto_comap_nhds_nhdsₓ'. -/
/-- ```
 γ -f→ α
g↓     ↓e
 δ -h→ β
```
-/
theorem tendsto_comap_nhds_nhds {d : δ} {a : α} (di : DenseInducing i)
    (H : Tendsto h (𝓝 d) (𝓝 (i a))) (comm : h ∘ g = i ∘ f) : Tendsto f (comap g (𝓝 d)) (𝓝 a) :=
  by
  have lim1 : map g (comap g (𝓝 d)) ≤ 𝓝 d := map_comap_le
  replace lim1 : map h (map g (comap g (𝓝 d))) ≤ map h (𝓝 d) := map_mono lim1
  rw [Filter.map_map, comm, ← Filter.map_map, map_le_iff_le_comap] at lim1
  have lim2 : comap i (map h (𝓝 d)) ≤ comap i (𝓝 (i a)) := comap_mono H
  rw [← di.nhds_eq_comap] at lim2
  exact le_trans lim1 lim2
#align dense_inducing.tendsto_comap_nhds_nhds DenseInducing.tendsto_comap_nhds_nhds

/- warning: dense_inducing.nhds_within_ne_bot -> DenseInducing.nhdsWithin_neBot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (forall (b : β), Filter.NeBot.{u2} β (nhdsWithin.{u2} β _inst_2 b (Set.range.{u2, succ u1} β α i)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (forall (b : β), Filter.NeBot.{u1} β (nhdsWithin.{u1} β _inst_2 b (Set.range.{u1, succ u2} β α i)))
Case conversion may be inaccurate. Consider using '#align dense_inducing.nhds_within_ne_bot DenseInducing.nhdsWithin_neBotₓ'. -/
protected theorem nhdsWithin_neBot (di : DenseInducing i) (b : β) : NeBot (𝓝[range i] b) :=
  di.dense.nhdsWithin_neBot b
#align dense_inducing.nhds_within_ne_bot DenseInducing.nhdsWithin_neBot

/- warning: dense_inducing.comap_nhds_ne_bot -> DenseInducing.comap_nhds_neBot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) -> (forall (b : β), Filter.NeBot.{u1} α (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 b)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β}, (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) -> (forall (b : β), Filter.NeBot.{u2} α (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 b)))
Case conversion may be inaccurate. Consider using '#align dense_inducing.comap_nhds_ne_bot DenseInducing.comap_nhds_neBotₓ'. -/
theorem comap_nhds_neBot (di : DenseInducing i) (b : β) : NeBot (comap i (𝓝 b)) :=
  comap_neBot fun s hs =>
    let ⟨_, ⟨ha, a, rfl⟩⟩ := mem_closure_iff_nhds.1 (di.dense b) s hs
    ⟨a, ha⟩
#align dense_inducing.comap_nhds_ne_bot DenseInducing.comap_nhds_neBot

variable [TopologicalSpace γ]

#print DenseInducing.extend /-
/-- If `i : α → β` is a dense inducing, then any function `f : α → γ` "extends"
  to a function `g = extend di f : β → γ`. If `γ` is Hausdorff and `f` has a
  continuous extension, then `g` is the unique such extension. In general,
  `g` might not be continuous or even extend `f`. -/
def extend (di : DenseInducing i) (f : α → γ) (b : β) : γ :=
  @limUnder _ ⟨f (di.dense.some b)⟩ (comap i (𝓝 b)) f
#align dense_inducing.extend DenseInducing.extend
-/

/- warning: dense_inducing.extend_eq_of_tendsto -> DenseInducing.extend_eq_of_tendsto is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {b : β} {c : γ} {f : α -> γ}, (Filter.Tendsto.{u1, u3} α γ f (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 b)) (nhds.{u3} γ _inst_4 c)) -> (Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f b) c)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {b : β} {c : γ} {f : α -> γ}, (Filter.Tendsto.{u2, u3} α γ f (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 b)) (nhds.{u3} γ _inst_4 c)) -> (Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f b) c)
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_eq_of_tendsto DenseInducing.extend_eq_of_tendstoₓ'. -/
theorem extend_eq_of_tendsto [T2Space γ] {b : β} {c : γ} {f : α → γ}
    (hf : Tendsto f (comap i (𝓝 b)) (𝓝 c)) : di.extend f b = c :=
  haveI := di.comap_nhds_ne_bot
  hf.lim_eq
#align dense_inducing.extend_eq_of_tendsto DenseInducing.extend_eq_of_tendsto

/- warning: dense_inducing.extend_eq_at -> DenseInducing.extend_eq_at is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {a : α}, (ContinuousAt.{u1, u3} α γ _inst_1 _inst_4 f a) -> (Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {a : α}, (ContinuousAt.{u2, u3} α γ _inst_1 _inst_4 f a) -> (Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_eq_at DenseInducing.extend_eq_atₓ'. -/
theorem extend_eq_at [T2Space γ] {f : α → γ} {a : α} (hf : ContinuousAt f a) :
    di.extend f (i a) = f a :=
  extend_eq_of_tendsto _ <| di.nhds_eq_comap a ▸ hf
#align dense_inducing.extend_eq_at DenseInducing.extend_eq_at

/- warning: dense_inducing.extend_eq_at' -> DenseInducing.extend_eq_at' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {a : α} (c : γ), (Filter.Tendsto.{u1, u3} α γ f (nhds.{u1} α _inst_1 a) (nhds.{u3} γ _inst_4 c)) -> (Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {a : α} (c : γ), (Filter.Tendsto.{u2, u3} α γ f (nhds.{u2} α _inst_1 a) (nhds.{u3} γ _inst_4 c)) -> (Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_eq_at' DenseInducing.extend_eq_at'ₓ'. -/
theorem extend_eq_at' [T2Space γ] {f : α → γ} {a : α} (c : γ) (hf : Tendsto f (𝓝 a) (𝓝 c)) :
    di.extend f (i a) = f a :=
  di.extend_eq_at (continuousAt_of_tendsto_nhds hf)
#align dense_inducing.extend_eq_at' DenseInducing.extend_eq_at'

/- warning: dense_inducing.extend_eq -> DenseInducing.extend_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ}, (Continuous.{u1, u3} α γ _inst_1 _inst_4 f) -> (forall (a : α), Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i) [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ}, (Continuous.{u2, u3} α γ _inst_1 _inst_4 f) -> (forall (a : α), Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_eq DenseInducing.extend_eqₓ'. -/
theorem extend_eq [T2Space γ] {f : α → γ} (hf : Continuous f) (a : α) : di.extend f (i a) = f a :=
  di.extend_eq_at hf.ContinuousAt
#align dense_inducing.extend_eq DenseInducing.extend_eq

/- warning: dense_inducing.extend_eq' -> DenseInducing.extend_eq' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i), (forall (b : β), Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u1, u3} α γ f (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 b)) (nhds.{u3} γ _inst_4 c))) -> (forall (a : α), Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i), (forall (b : β), Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u2, u3} α γ f (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 b)) (nhds.{u3} γ _inst_4 c))) -> (forall (a : α), Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f (i a)) (f a))
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_eq' DenseInducing.extend_eq'ₓ'. -/
/-- Variation of `extend_eq` where we ask that `f` has a limit along `comap i (𝓝 b)` for each
`b : β`. This is a strictly stronger assumption than continuity of `f`, but in a lot of cases
you'd have to prove it anyway to use `continuous_extend`, so this avoids doing the work twice. -/
theorem extend_eq' [T2Space γ] {f : α → γ} (di : DenseInducing i)
    (hf : ∀ b, ∃ c, Tendsto f (comap i (𝓝 b)) (𝓝 c)) (a : α) : di.extend f (i a) = f a :=
  by
  rcases hf (i a) with ⟨b, hb⟩
  refine' di.extend_eq_at' b _
  rwa [← di.to_inducing.nhds_eq_comap] at hb
#align dense_inducing.extend_eq' DenseInducing.extend_eq'

/- warning: dense_inducing.extend_unique_at -> DenseInducing.extend_unique_at is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {b : β} {f : α -> γ} {g : β -> γ} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i), (Filter.Eventually.{u1} α (fun (x : α) => Eq.{succ u3} γ (g (i x)) (f x)) (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 b))) -> (ContinuousAt.{u2, u3} β γ _inst_2 _inst_4 g b) -> (Eq.{succ u3} γ (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f b) (g b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {b : β} {f : α -> γ} {g : β -> γ} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i), (Filter.Eventually.{u2} α (fun (x : α) => Eq.{succ u3} γ (g (i x)) (f x)) (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 b))) -> (ContinuousAt.{u1, u3} β γ _inst_2 _inst_4 g b) -> (Eq.{succ u3} γ (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f b) (g b))
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_unique_at DenseInducing.extend_unique_atₓ'. -/
theorem extend_unique_at [T2Space γ] {b : β} {f : α → γ} {g : β → γ} (di : DenseInducing i)
    (hf : ∀ᶠ x in comap i (𝓝 b), g (i x) = f x) (hg : ContinuousAt g b) : di.extend f b = g b :=
  by
  refine' di.extend_eq_of_tendsto fun s hs => mem_map.2 _
  suffices : ∀ᶠ x : α in comap i (𝓝 b), g (i x) ∈ s
  exact hf.mp (this.mono fun x hgx hfx => hfx ▸ hgx)
  clear hf f
  refine' eventually_comap.2 ((hg.eventually hs).mono _)
  rintro _ hxs x rfl
  exact hxs
#align dense_inducing.extend_unique_at DenseInducing.extend_unique_at

/- warning: dense_inducing.extend_unique -> DenseInducing.extend_unique is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {g : β -> γ} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i), (forall (x : α), Eq.{succ u3} γ (g (i x)) (f x)) -> (Continuous.{u2, u3} β γ _inst_2 _inst_4 g) -> (Eq.{max (succ u2) (succ u3)} (β -> γ) (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f) g)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T2Space.{u3} γ _inst_4] {f : α -> γ} {g : β -> γ} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i), (forall (x : α), Eq.{succ u3} γ (g (i x)) (f x)) -> (Continuous.{u1, u3} β γ _inst_2 _inst_4 g) -> (Eq.{max (succ u1) (succ u3)} (β -> γ) (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f) g)
Case conversion may be inaccurate. Consider using '#align dense_inducing.extend_unique DenseInducing.extend_uniqueₓ'. -/
theorem extend_unique [T2Space γ] {f : α → γ} {g : β → γ} (di : DenseInducing i)
    (hf : ∀ x, g (i x) = f x) (hg : Continuous g) : di.extend f = g :=
  funext fun b => extend_unique_at di (eventually_of_forall hf) hg.ContinuousAt
#align dense_inducing.extend_unique DenseInducing.extend_unique

/- warning: dense_inducing.continuous_at_extend -> DenseInducing.continuousAt_extend is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T3Space.{u3} γ _inst_4] {b : β} {f : α -> γ} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i), (Filter.Eventually.{u2} β (fun (x : β) => Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u1, u3} α γ f (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 x)) (nhds.{u3} γ _inst_4 c))) (nhds.{u2} β _inst_2 b)) -> (ContinuousAt.{u2, u3} β γ _inst_2 _inst_4 (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f) b)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T3Space.{u3} γ _inst_4] {b : β} {f : α -> γ} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i), (Filter.Eventually.{u1} β (fun (x : β) => Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u2, u3} α γ f (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 x)) (nhds.{u3} γ _inst_4 c))) (nhds.{u1} β _inst_2 b)) -> (ContinuousAt.{u1, u3} β γ _inst_2 _inst_4 (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f) b)
Case conversion may be inaccurate. Consider using '#align dense_inducing.continuous_at_extend DenseInducing.continuousAt_extendₓ'. -/
theorem continuousAt_extend [T3Space γ] {b : β} {f : α → γ} (di : DenseInducing i)
    (hf : ∀ᶠ x in 𝓝 b, ∃ c, Tendsto f (comap i <| 𝓝 x) (𝓝 c)) : ContinuousAt (di.extend f) b :=
  by
  set φ := di.extend f
  haveI := di.comap_nhds_ne_bot
  suffices ∀ V' ∈ 𝓝 (φ b), IsClosed V' → φ ⁻¹' V' ∈ 𝓝 b by
    simpa [ContinuousAt, (closed_nhds_basis _).tendsto_right_iff]
  intro V' V'_in V'_closed
  set V₁ := { x | tendsto f (comap i <| 𝓝 x) (𝓝 <| φ x) }
  have V₁_in : V₁ ∈ 𝓝 b := by
    filter_upwards [hf]
    rintro x ⟨c, hc⟩
    dsimp [V₁, φ]
    rwa [di.extend_eq_of_tendsto hc]
  obtain ⟨V₂, V₂_in, V₂_op, hV₂⟩ : ∃ V₂ ∈ 𝓝 b, IsOpen V₂ ∧ ∀ x ∈ i ⁻¹' V₂, f x ∈ V' := by
    simpa [and_assoc'] using
      ((nhds_basis_opens' b).comap i).tendsto_left_iffₓ.mp (mem_of_mem_nhds V₁_in : b ∈ V₁) V' V'_in
  suffices ∀ x ∈ V₁ ∩ V₂, φ x ∈ V' by filter_upwards [inter_mem V₁_in V₂_in]using this
  rintro x ⟨x_in₁, x_in₂⟩
  have hV₂x : V₂ ∈ 𝓝 x := IsOpen.mem_nhds V₂_op x_in₂
  apply V'_closed.mem_of_tendsto x_in₁
  use V₂
  tauto
#align dense_inducing.continuous_at_extend DenseInducing.continuousAt_extend

/- warning: dense_inducing.continuous_extend -> DenseInducing.continuous_extend is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T3Space.{u3} γ _inst_4] {f : α -> γ} (di : DenseInducing.{u1, u2} α β _inst_1 _inst_2 i), (forall (b : β), Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u1, u3} α γ f (Filter.comap.{u1, u2} α β i (nhds.{u2} β _inst_2 b)) (nhds.{u3} γ _inst_4 c))) -> (Continuous.{u2, u3} β γ _inst_2 _inst_4 (DenseInducing.extend.{u1, u2, u3} α β γ _inst_1 _inst_2 i _inst_4 di f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {i : α -> β} [_inst_4 : TopologicalSpace.{u3} γ] [_inst_5 : T3Space.{u3} γ _inst_4] {f : α -> γ} (di : DenseInducing.{u2, u1} α β _inst_1 _inst_2 i), (forall (b : β), Exists.{succ u3} γ (fun (c : γ) => Filter.Tendsto.{u2, u3} α γ f (Filter.comap.{u2, u1} α β i (nhds.{u1} β _inst_2 b)) (nhds.{u3} γ _inst_4 c))) -> (Continuous.{u1, u3} β γ _inst_2 _inst_4 (DenseInducing.extend.{u2, u1, u3} α β γ _inst_1 _inst_2 i _inst_4 di f))
Case conversion may be inaccurate. Consider using '#align dense_inducing.continuous_extend DenseInducing.continuous_extendₓ'. -/
theorem continuous_extend [T3Space γ] {f : α → γ} (di : DenseInducing i)
    (hf : ∀ b, ∃ c, Tendsto f (comap i (𝓝 b)) (𝓝 c)) : Continuous (di.extend f) :=
  continuous_iff_continuousAt.mpr fun b => di.continuousAt_extend <| univ_mem' hf
#align dense_inducing.continuous_extend DenseInducing.continuous_extend

/- warning: dense_inducing.mk' -> DenseInducing.mk' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (i : α -> β), (Continuous.{u1, u2} α β _inst_1 _inst_2 i) -> (forall (x : β), Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (closure.{u2} β _inst_2 (Set.range.{u2, succ u1} β α i))) -> (forall (a : α) (s : Set.{u1} α), (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α _inst_1 a)) -> (Exists.{succ u2} (Set.{u2} β) (fun (t : Set.{u2} β) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t (nhds.{u2} β _inst_2 (i a))) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t (nhds.{u2} β _inst_2 (i a))) => forall (b : α), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (i b) t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b s))))) -> (DenseInducing.{u1, u2} α β _inst_1 _inst_2 i)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (i : α -> β), (Continuous.{u2, u1} α β _inst_1 _inst_2 i) -> (forall (x : β), Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x (closure.{u1} β _inst_2 (Set.range.{u1, succ u2} β α i))) -> (forall (a : α) (s : Set.{u2} α), (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α _inst_1 a)) -> (Exists.{succ u1} (Set.{u1} β) (fun (t : Set.{u1} β) => And (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) t (nhds.{u1} β _inst_2 (i a))) (forall (b : α), (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) (i b) t) -> (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) b s))))) -> (DenseInducing.{u2, u1} α β _inst_1 _inst_2 i)
Case conversion may be inaccurate. Consider using '#align dense_inducing.mk' DenseInducing.mk'ₓ'. -/
theorem mk' (i : α → β) (c : Continuous i) (dense : ∀ x, x ∈ closure (range i))
    (H : ∀ (a : α), ∀ s ∈ 𝓝 a, ∃ t ∈ 𝓝 (i a), ∀ b, i b ∈ t → b ∈ s) : DenseInducing i :=
  { induced :=
      (induced_iff_nhds_eq i).2 fun a =>
        le_antisymm (tendsto_iff_comap.1 <| c.Tendsto _) (by simpa [Filter.le_def] using H a)
    dense }
#align dense_inducing.mk' DenseInducing.mk'

end DenseInducing

#print DenseEmbedding /-
/-- A dense embedding is an embedding with dense image. -/
structure DenseEmbedding [TopologicalSpace α] [TopologicalSpace β] (e : α → β) extends
  DenseInducing e : Prop where
  inj : Function.Injective e
#align dense_embedding DenseEmbedding
-/

/- warning: dense_embedding.mk' -> DenseEmbedding.mk' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (e : α -> β), (Continuous.{u1, u2} α β _inst_1 _inst_2 e) -> (DenseRange.{u2, u1} β _inst_2 α e) -> (Function.Injective.{succ u1, succ u2} α β e) -> (forall (a : α) (s : Set.{u1} α), (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (nhds.{u1} α _inst_1 a)) -> (Exists.{succ u2} (Set.{u2} β) (fun (t : Set.{u2} β) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t (nhds.{u2} β _inst_2 (e a))) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t (nhds.{u2} β _inst_2 (e a))) => forall (b : α), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (e b) t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b s))))) -> (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (e : α -> β), (Continuous.{u2, u1} α β _inst_1 _inst_2 e) -> (DenseRange.{u1, u2} β _inst_2 α e) -> (Function.Injective.{succ u2, succ u1} α β e) -> (forall (a : α) (s : Set.{u2} α), (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s (nhds.{u2} α _inst_1 a)) -> (Exists.{succ u1} (Set.{u1} β) (fun (t : Set.{u1} β) => And (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) t (nhds.{u1} β _inst_2 (e a))) (forall (b : α), (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) (e b) t) -> (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) b s))))) -> (DenseEmbedding.{u2, u1} α β _inst_1 _inst_2 e)
Case conversion may be inaccurate. Consider using '#align dense_embedding.mk' DenseEmbedding.mk'ₓ'. -/
theorem DenseEmbedding.mk' [TopologicalSpace α] [TopologicalSpace β] (e : α → β) (c : Continuous e)
    (dense : DenseRange e) (inj : Function.Injective e)
    (H : ∀ (a : α), ∀ s ∈ 𝓝 a, ∃ t ∈ 𝓝 (e a), ∀ b, e b ∈ t → b ∈ s) : DenseEmbedding e :=
  { DenseInducing.mk' e c Dense H with inj }
#align dense_embedding.mk' DenseEmbedding.mk'

namespace DenseEmbedding

open TopologicalSpace

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] [TopologicalSpace δ]

variable {e : α → β} (de : DenseEmbedding e)

#print DenseEmbedding.inj_iff /-
theorem inj_iff {x y} : e x = e y ↔ x = y :=
  de.inj.eq_iff
#align dense_embedding.inj_iff DenseEmbedding.inj_iff
-/

/- warning: dense_embedding.to_embedding -> DenseEmbedding.to_embedding is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {e : α -> β}, (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e) -> (Embedding.{u1, u2} α β _inst_1 _inst_2 e)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {e : α -> β}, (DenseEmbedding.{u2, u1} α β _inst_1 _inst_2 e) -> (Embedding.{u2, u1} α β _inst_1 _inst_2 e)
Case conversion may be inaccurate. Consider using '#align dense_embedding.to_embedding DenseEmbedding.to_embeddingₓ'. -/
theorem to_embedding : Embedding e :=
  { induced := de.induced
    inj := de.inj }
#align dense_embedding.to_embedding DenseEmbedding.to_embedding

/- warning: dense_embedding.separable_space -> DenseEmbedding.separableSpace is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {e : α -> β}, (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e) -> (forall [_inst_5 : TopologicalSpace.SeparableSpace.{u1} α _inst_1], TopologicalSpace.SeparableSpace.{u2} β _inst_2)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {e : α -> β}, (DenseEmbedding.{u2, u1} α β _inst_1 _inst_2 e) -> (forall [_inst_5 : TopologicalSpace.SeparableSpace.{u2} α _inst_1], TopologicalSpace.SeparableSpace.{u1} β _inst_2)
Case conversion may be inaccurate. Consider using '#align dense_embedding.separable_space DenseEmbedding.separableSpaceₓ'. -/
/-- If the domain of a `dense_embedding` is a separable space, then so is its codomain. -/
protected theorem separableSpace [SeparableSpace α] : SeparableSpace β :=
  de.to_denseInducing.SeparableSpace
#align dense_embedding.separable_space DenseEmbedding.separableSpace

/- warning: dense_embedding.prod -> DenseEmbedding.prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] [_inst_4 : TopologicalSpace.{u4} δ] {e₁ : α -> β} {e₂ : γ -> δ}, (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e₁) -> (DenseEmbedding.{u3, u4} γ δ _inst_3 _inst_4 e₂) -> (DenseEmbedding.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Prod.topologicalSpace.{u1, u3} α γ _inst_1 _inst_3) (Prod.topologicalSpace.{u2, u4} β δ _inst_2 _inst_4) (fun (p : Prod.{u1, u3} α γ) => Prod.mk.{u2, u4} β δ (e₁ (Prod.fst.{u1, u3} α γ p)) (e₂ (Prod.snd.{u1, u3} α γ p))))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} [_inst_1 : TopologicalSpace.{u4} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] [_inst_4 : TopologicalSpace.{u1} δ] {e₁ : α -> β} {e₂ : γ -> δ}, (DenseEmbedding.{u4, u3} α β _inst_1 _inst_2 e₁) -> (DenseEmbedding.{u2, u1} γ δ _inst_3 _inst_4 e₂) -> (DenseEmbedding.{max u4 u2, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (instTopologicalSpaceProd.{u4, u2} α γ _inst_1 _inst_3) (instTopologicalSpaceProd.{u3, u1} β δ _inst_2 _inst_4) (fun (p : Prod.{u4, u2} α γ) => Prod.mk.{u3, u1} β δ (e₁ (Prod.fst.{u4, u2} α γ p)) (e₂ (Prod.snd.{u4, u2} α γ p))))
Case conversion may be inaccurate. Consider using '#align dense_embedding.prod DenseEmbedding.prodₓ'. -/
/-- The product of two dense embeddings is a dense embedding. -/
protected theorem prod {e₁ : α → β} {e₂ : γ → δ} (de₁ : DenseEmbedding e₁)
    (de₂ : DenseEmbedding e₂) : DenseEmbedding fun p : α × γ => (e₁ p.1, e₂ p.2) :=
  { DenseInducing.prod de₁.to_denseInducing de₂.to_denseInducing with
    inj := fun ⟨x₁, x₂⟩ ⟨y₁, y₂⟩ => by simp <;> exact fun h₁ h₂ => ⟨de₁.inj h₁, de₂.inj h₂⟩ }
#align dense_embedding.prod DenseEmbedding.prod

#print DenseEmbedding.subtypeEmb /-
/-- The dense embedding of a subtype inside its closure. -/
@[simps]
def subtypeEmb {α : Type _} (p : α → Prop) (e : α → β) (x : { x // p x }) :
    { x // x ∈ closure (e '' { x | p x }) } :=
  ⟨e x, subset_closure <| mem_image_of_mem e x.Prop⟩
#align dense_embedding.subtype_emb DenseEmbedding.subtypeEmb
-/

/- warning: dense_embedding.subtype -> DenseEmbedding.subtype is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {e : α -> β}, (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e) -> (forall (p : α -> Prop), DenseEmbedding.{u1, u2} (Subtype.{succ u1} α (fun (x : α) => p x)) (Subtype.{succ u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (closure.{u2} β _inst_2 (Set.image.{u1, u2} α β e (setOf.{u1} α (fun (x : α) => p x)))))) (Subtype.topologicalSpace.{u1} α (fun (x : α) => p x) _inst_1) (Subtype.topologicalSpace.{u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (closure.{u2} β _inst_2 (Set.image.{u1, u2} α β e (setOf.{u1} α (fun (x : α) => p x))))) _inst_2) (DenseEmbedding.subtypeEmb.{u2, u1} β _inst_2 α p e))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {e : α -> β}, (DenseEmbedding.{u2, u1} α β _inst_1 _inst_2 e) -> (forall (p : α -> Prop), DenseEmbedding.{u2, u1} (Subtype.{succ u2} α (fun (x : α) => p x)) (Subtype.{succ u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x (closure.{u1} β _inst_2 (Set.image.{u2, u1} α β e (setOf.{u2} α (fun (x : α) => p x)))))) (instTopologicalSpaceSubtype.{u2} α (fun (x : α) => p x) _inst_1) (instTopologicalSpaceSubtype.{u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x (closure.{u1} β _inst_2 (Set.image.{u2, u1} α β e (setOf.{u2} α (fun (x : α) => p x))))) _inst_2) (DenseEmbedding.subtypeEmb.{u1, u2} β _inst_2 α p e))
Case conversion may be inaccurate. Consider using '#align dense_embedding.subtype DenseEmbedding.subtypeₓ'. -/
protected theorem subtype (p : α → Prop) : DenseEmbedding (subtypeEmb p e) :=
  { dense :=
      dense_iff_closure_eq.2 <| by
        ext ⟨x, hx⟩
        rw [image_eq_range] at hx
        simpa [closure_subtype, ← range_comp, (· ∘ ·)]
    inj := (de.inj.comp Subtype.coe_injective).codRestrict _
    induced :=
      (induced_iff_nhds_eq _).2 fun ⟨x, hx⟩ => by
        simp [subtype_emb, nhds_subtype_eq_comap, de.to_inducing.nhds_eq_comap, comap_comap,
          (· ∘ ·)] }
#align dense_embedding.subtype DenseEmbedding.subtype

/- warning: dense_embedding.dense_image -> DenseEmbedding.dense_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {e : α -> β}, (DenseEmbedding.{u1, u2} α β _inst_1 _inst_2 e) -> (forall {s : Set.{u1} α}, Iff (Dense.{u2} β _inst_2 (Set.image.{u1, u2} α β e s)) (Dense.{u1} α _inst_1 s))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {e : α -> β}, (DenseEmbedding.{u2, u1} α β _inst_1 _inst_2 e) -> (forall {s : Set.{u2} α}, Iff (Dense.{u1} β _inst_2 (Set.image.{u2, u1} α β e s)) (Dense.{u2} α _inst_1 s))
Case conversion may be inaccurate. Consider using '#align dense_embedding.dense_image DenseEmbedding.dense_imageₓ'. -/
theorem dense_image {s : Set α} : Dense (e '' s) ↔ Dense s :=
  de.to_denseInducing.dense_image
#align dense_embedding.dense_image DenseEmbedding.dense_image

end DenseEmbedding

#print denseEmbedding_id /-
theorem denseEmbedding_id {α : Type _} [TopologicalSpace α] : DenseEmbedding (id : α → α) :=
  { embedding_id with dense := denseRange_id }
#align dense_embedding_id denseEmbedding_id
-/

#print Dense.denseEmbedding_val /-
theorem Dense.denseEmbedding_val [TopologicalSpace α] {s : Set α} (hs : Dense s) :
    DenseEmbedding (coe : s → α) :=
  { embedding_subtype_val with dense := hs.denseRange_val }
#align dense.dense_embedding_coe Dense.denseEmbedding_val
-/

#print isClosed_property /-
theorem isClosed_property [TopologicalSpace β] {e : α → β} {p : β → Prop} (he : DenseRange e)
    (hp : IsClosed { x | p x }) (h : ∀ a, p (e a)) : ∀ b, p b :=
  have : univ ⊆ { b | p b } :=
    calc
      univ = closure (range e) := he.closure_range.symm
      _ ⊆ closure { b | p b } := (closure_mono <| range_subset_iff.mpr h)
      _ = _ := hp.closure_eq

  fun b => this trivial
#align is_closed_property isClosed_property
-/

/- warning: is_closed_property2 -> isClosed_property2 is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β β) (Prod.topologicalSpace.{u2, u2} β β _inst_1 _inst_1) (setOf.{u2} (Prod.{u2, u2} β β) (fun (q : Prod.{u2, u2} β β) => p (Prod.fst.{u2, u2} β β q) (Prod.snd.{u2, u2} β β q)))) -> (forall (a₁ : α) (a₂ : α), p (e a₁) (e a₂)) -> (forall (b₁ : β) (b₂ : β), p b₁ b₂)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β β) (instTopologicalSpaceProd.{u2, u2} β β _inst_1 _inst_1) (setOf.{u2} (Prod.{u2, u2} β β) (fun (q : Prod.{u2, u2} β β) => p (Prod.fst.{u2, u2} β β q) (Prod.snd.{u2, u2} β β q)))) -> (forall (a₁ : α) (a₂ : α), p (e a₁) (e a₂)) -> (forall (b₁ : β) (b₂ : β), p b₁ b₂)
Case conversion may be inaccurate. Consider using '#align is_closed_property2 isClosed_property2ₓ'. -/
theorem isClosed_property2 [TopologicalSpace β] {e : α → β} {p : β → β → Prop} (he : DenseRange e)
    (hp : IsClosed { q : β × β | p q.1 q.2 }) (h : ∀ a₁ a₂, p (e a₁) (e a₂)) : ∀ b₁ b₂, p b₁ b₂ :=
  have : ∀ q : β × β, p q.1 q.2 := isClosed_property (he.Prod_map he) hp fun _ => h _ _
  fun b₁ b₂ => this ⟨b₁, b₂⟩
#align is_closed_property2 isClosed_property2

/- warning: is_closed_property3 -> isClosed_property3 is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (Prod.topologicalSpace.{u2, u2} β (Prod.{u2, u2} β β) _inst_1 (Prod.topologicalSpace.{u2, u2} β β _inst_1 _inst_1)) (setOf.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (fun (q : Prod.{u2, u2} β (Prod.{u2, u2} β β)) => p (Prod.fst.{u2, u2} β (Prod.{u2, u2} β β) q) (Prod.fst.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q)) (Prod.snd.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q))))) -> (forall (a₁ : α) (a₂ : α) (a₃ : α), p (e a₁) (e a₂) (e a₃)) -> (forall (b₁ : β) (b₂ : β) (b₃ : β), p b₁ b₂ b₃)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (instTopologicalSpaceProd.{u2, u2} β (Prod.{u2, u2} β β) _inst_1 (instTopologicalSpaceProd.{u2, u2} β β _inst_1 _inst_1)) (setOf.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (fun (q : Prod.{u2, u2} β (Prod.{u2, u2} β β)) => p (Prod.fst.{u2, u2} β (Prod.{u2, u2} β β) q) (Prod.fst.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q)) (Prod.snd.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q))))) -> (forall (a₁ : α) (a₂ : α) (a₃ : α), p (e a₁) (e a₂) (e a₃)) -> (forall (b₁ : β) (b₂ : β) (b₃ : β), p b₁ b₂ b₃)
Case conversion may be inaccurate. Consider using '#align is_closed_property3 isClosed_property3ₓ'. -/
theorem isClosed_property3 [TopologicalSpace β] {e : α → β} {p : β → β → β → Prop}
    (he : DenseRange e) (hp : IsClosed { q : β × β × β | p q.1 q.2.1 q.2.2 })
    (h : ∀ a₁ a₂ a₃, p (e a₁) (e a₂) (e a₃)) : ∀ b₁ b₂ b₃, p b₁ b₂ b₃ :=
  have : ∀ q : β × β × β, p q.1 q.2.1 q.2.2 :=
    isClosed_property (he.Prod_map <| he.Prod_map he) hp fun _ => h _ _ _
  fun b₁ b₂ b₃ => this ⟨b₁, b₂, b₃⟩
#align is_closed_property3 isClosed_property3

#print DenseRange.induction_on /-
@[elab_as_elim]
theorem DenseRange.induction_on [TopologicalSpace β] {e : α → β} (he : DenseRange e) {p : β → Prop}
    (b₀ : β) (hp : IsClosed { b | p b }) (ih : ∀ a : α, p <| e a) : p b₀ :=
  isClosed_property he hp ih b₀
#align dense_range.induction_on DenseRange.induction_on
-/

/- warning: dense_range.induction_on₂ -> DenseRange.induction_on₂ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β β) (Prod.topologicalSpace.{u2, u2} β β _inst_1 _inst_1) (setOf.{u2} (Prod.{u2, u2} β β) (fun (q : Prod.{u2, u2} β β) => p (Prod.fst.{u2, u2} β β q) (Prod.snd.{u2, u2} β β q)))) -> (forall (a₁ : α) (a₂ : α), p (e a₁) (e a₂)) -> (forall (b₁ : β) (b₂ : β), p b₁ b₂)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β β) (instTopologicalSpaceProd.{u2, u2} β β _inst_1 _inst_1) (setOf.{u2} (Prod.{u2, u2} β β) (fun (q : Prod.{u2, u2} β β) => p (Prod.fst.{u2, u2} β β q) (Prod.snd.{u2, u2} β β q)))) -> (forall (a₁ : α) (a₂ : α), p (e a₁) (e a₂)) -> (forall (b₁ : β) (b₂ : β), p b₁ b₂)
Case conversion may be inaccurate. Consider using '#align dense_range.induction_on₂ DenseRange.induction_on₂ₓ'. -/
@[elab_as_elim]
theorem DenseRange.induction_on₂ [TopologicalSpace β] {e : α → β} {p : β → β → Prop}
    (he : DenseRange e) (hp : IsClosed { q : β × β | p q.1 q.2 }) (h : ∀ a₁ a₂, p (e a₁) (e a₂))
    (b₁ b₂ : β) : p b₁ b₂ :=
  isClosed_property2 he hp h _ _
#align dense_range.induction_on₂ DenseRange.induction_on₂

/- warning: dense_range.induction_on₃ -> DenseRange.induction_on₃ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (Prod.topologicalSpace.{u2, u2} β (Prod.{u2, u2} β β) _inst_1 (Prod.topologicalSpace.{u2, u2} β β _inst_1 _inst_1)) (setOf.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (fun (q : Prod.{u2, u2} β (Prod.{u2, u2} β β)) => p (Prod.fst.{u2, u2} β (Prod.{u2, u2} β β) q) (Prod.fst.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q)) (Prod.snd.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q))))) -> (forall (a₁ : α) (a₂ : α) (a₃ : α), p (e a₁) (e a₂) (e a₃)) -> (forall (b₁ : β) (b₂ : β) (b₃ : β), p b₁ b₂ b₃)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} β] {e : α -> β} {p : β -> β -> β -> Prop}, (DenseRange.{u2, u1} β _inst_1 α e) -> (IsClosed.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (instTopologicalSpaceProd.{u2, u2} β (Prod.{u2, u2} β β) _inst_1 (instTopologicalSpaceProd.{u2, u2} β β _inst_1 _inst_1)) (setOf.{u2} (Prod.{u2, u2} β (Prod.{u2, u2} β β)) (fun (q : Prod.{u2, u2} β (Prod.{u2, u2} β β)) => p (Prod.fst.{u2, u2} β (Prod.{u2, u2} β β) q) (Prod.fst.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q)) (Prod.snd.{u2, u2} β β (Prod.snd.{u2, u2} β (Prod.{u2, u2} β β) q))))) -> (forall (a₁ : α) (a₂ : α) (a₃ : α), p (e a₁) (e a₂) (e a₃)) -> (forall (b₁ : β) (b₂ : β) (b₃ : β), p b₁ b₂ b₃)
Case conversion may be inaccurate. Consider using '#align dense_range.induction_on₃ DenseRange.induction_on₃ₓ'. -/
@[elab_as_elim]
theorem DenseRange.induction_on₃ [TopologicalSpace β] {e : α → β} {p : β → β → β → Prop}
    (he : DenseRange e) (hp : IsClosed { q : β × β × β | p q.1 q.2.1 q.2.2 })
    (h : ∀ a₁ a₂ a₃, p (e a₁) (e a₂) (e a₃)) (b₁ b₂ b₃ : β) : p b₁ b₂ b₃ :=
  isClosed_property3 he hp h _ _ _
#align dense_range.induction_on₃ DenseRange.induction_on₃

section

variable [TopologicalSpace β] [TopologicalSpace γ] [T2Space γ]

variable {f : α → β}

/- warning: dense_range.equalizer -> DenseRange.equalizer is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} β] [_inst_2 : TopologicalSpace.{u3} γ] [_inst_3 : T2Space.{u3} γ _inst_2] {f : α -> β}, (DenseRange.{u2, u1} β _inst_1 α f) -> (forall {g : β -> γ} {h : β -> γ}, (Continuous.{u2, u3} β γ _inst_1 _inst_2 g) -> (Continuous.{u2, u3} β γ _inst_1 _inst_2 h) -> (Eq.{max (succ u1) (succ u3)} (α -> γ) (Function.comp.{succ u1, succ u2, succ u3} α β γ g f) (Function.comp.{succ u1, succ u2, succ u3} α β γ h f)) -> (Eq.{max (succ u2) (succ u3)} (β -> γ) g h))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u3}} {γ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} β] [_inst_2 : TopologicalSpace.{u1} γ] [_inst_3 : T2Space.{u1} γ _inst_2] {f : α -> β}, (DenseRange.{u3, u2} β _inst_1 α f) -> (forall {g : β -> γ} {h : β -> γ}, (Continuous.{u3, u1} β γ _inst_1 _inst_2 g) -> (Continuous.{u3, u1} β γ _inst_1 _inst_2 h) -> (Eq.{max (succ u2) (succ u1)} (α -> γ) (Function.comp.{succ u2, succ u3, succ u1} α β γ g f) (Function.comp.{succ u2, succ u3, succ u1} α β γ h f)) -> (Eq.{max (succ u3) (succ u1)} (β -> γ) g h))
Case conversion may be inaccurate. Consider using '#align dense_range.equalizer DenseRange.equalizerₓ'. -/
/-- Two continuous functions to a t2-space that agree on the dense range of a function are equal. -/
theorem DenseRange.equalizer (hfd : DenseRange f) {g h : β → γ} (hg : Continuous g)
    (hh : Continuous h) (H : g ∘ f = h ∘ f) : g = h :=
  funext fun y => hfd.inductionOn y (isClosed_eq hg hh) <| congr_fun H
#align dense_range.equalizer DenseRange.equalizer

end

/- warning: filter.has_basis.has_basis_of_dense_inducing -> Filter.HasBasis.hasBasis_of_denseInducing is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : T3Space.{u2} β _inst_2] {ι : Type.{u3}} {s : ι -> (Set.{u1} α)} {p : ι -> Prop} {x : α}, (Filter.HasBasis.{u1, succ u3} α ι (nhds.{u1} α _inst_1 x) p s) -> (forall {f : α -> β}, (DenseInducing.{u1, u2} α β _inst_1 _inst_2 f) -> (Filter.HasBasis.{u2, succ u3} β ι (nhds.{u2} β _inst_2 (f x)) p (fun (i : ι) => closure.{u2} β _inst_2 (Set.image.{u1, u2} α β f (s i)))))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : T3Space.{u2} β _inst_2] {ι : Type.{u1}} {s : ι -> (Set.{u3} α)} {p : ι -> Prop} {x : α}, (Filter.HasBasis.{u3, succ u1} α ι (nhds.{u3} α _inst_1 x) p s) -> (forall {f : α -> β}, (DenseInducing.{u3, u2} α β _inst_1 _inst_2 f) -> (Filter.HasBasis.{u2, succ u1} β ι (nhds.{u2} β _inst_2 (f x)) p (fun (i : ι) => closure.{u2} β _inst_2 (Set.image.{u3, u2} α β f (s i)))))
Case conversion may be inaccurate. Consider using '#align filter.has_basis.has_basis_of_dense_inducing Filter.HasBasis.hasBasis_of_denseInducingₓ'. -/
-- Bourbaki GT III §3 no.4 Proposition 7 (generalised to any dense-inducing map to a T₃ space)
theorem Filter.HasBasis.hasBasis_of_denseInducing [TopologicalSpace α] [TopologicalSpace β]
    [T3Space β] {ι : Type _} {s : ι → Set α} {p : ι → Prop} {x : α} (h : (𝓝 x).HasBasis p s)
    {f : α → β} (hf : DenseInducing f) : (𝓝 (f x)).HasBasis p fun i => closure <| f '' s i :=
  by
  rw [Filter.hasBasis_iff] at h⊢
  intro T
  refine' ⟨fun hT => _, fun hT => _⟩
  · obtain ⟨T', hT₁, hT₂, hT₃⟩ := exists_mem_nhds_isClosed_subset hT
    have hT₄ : f ⁻¹' T' ∈ 𝓝 x := by
      rw [hf.to_inducing.nhds_eq_comap x]
      exact ⟨T', hT₁, subset.rfl⟩
    obtain ⟨i, hi, hi'⟩ := (h _).mp hT₄
    exact
      ⟨i, hi,
        (closure_mono (image_subset f hi')).trans
          (subset.trans (closure_minimal (image_subset_iff.mpr subset.rfl) hT₂) hT₃)⟩
  · obtain ⟨i, hi, hi'⟩ := hT
    suffices closure (f '' s i) ∈ 𝓝 (f x) by filter_upwards [this]using hi'
    replace h := (h (s i)).mpr ⟨i, hi, subset.rfl⟩
    exact hf.closure_image_mem_nhds h
#align filter.has_basis.has_basis_of_dense_inducing Filter.HasBasis.hasBasis_of_denseInducing