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

! This file was ported from Lean 3 source module logic.embedding.basic
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.FunLike.Embedding
import Mathbin.Data.Prod.Pprod
import Mathbin.Data.Sigma.Basic
import Mathbin.Data.Option.Basic
import Mathbin.Data.Subtype
import Mathbin.Logic.Equiv.Basic

/-!
# Injective functions

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

universe u v w x

namespace Function

/- warning: function.embedding -> Function.Embedding is a dubious translation:
lean 3 declaration is
  Sort.{u1} -> Sort.{u2} -> Sort.{max 1 (imax u1 u2)}
but is expected to have type
  Sort.{u1} -> Sort.{u2} -> Sort.{max (max 1 u1) u2}
Case conversion may be inaccurate. Consider using '#align function.embedding Function.Embeddingₓ'. -/
-- depending on cardinalities, an injective function may not exist
/-- `α ↪ β` is a bundled injective function. -/
@[nolint has_nonempty_instance]
structure Embedding (α : Sort _) (β : Sort _) where
  toFun : α → β
  inj' : Injective to_fun
#align function.embedding Function.Embedding

-- mathport name: «expr ↪ »
infixr:25 " ↪ " => Embedding

instance {α : Sort u} {β : Sort v} : CoeFun (α ↪ β) fun _ => α → β :=
  ⟨Embedding.toFun⟩

initialize_simps_projections Embedding (toFun → apply)

instance {α : Sort u} {β : Sort v} : EmbeddingLike (α ↪ β) α β
    where
  coe := Embedding.toFun
  injective' := Embedding.inj'
  coe_injective' f g h := by
    cases f
    cases g
    congr

instance {α β : Sort _} : CanLift (α → β) (α ↪ β) coeFn Injective where prf f hf := ⟨⟨f, hf⟩, rfl⟩

end Function

section Equiv

variable {α : Sort u} {β : Sort v} (f : α ≃ β)

#print Equiv.toEmbedding /-
/-- Convert an `α ≃ β` to `α ↪ β`.

This is also available as a coercion `equiv.coe_embedding`.
The explicit `equiv.to_embedding` version is preferred though, since the coercion can have issues
inferring the type of the resulting embedding. For example:

```lean
-- Works:
example (s : finset (fin 3)) (f : equiv.perm (fin 3)) : s.map f.to_embedding = s.map f := by simp
-- Error, `f` has type `fin 3 ≃ fin 3` but is expected to have type `fin 3 ↪ ?m_1 : Type ?`
example (s : finset (fin 3)) (f : equiv.perm (fin 3)) : s.map f = s.map f.to_embedding := by simp
```
-/
protected def Equiv.toEmbedding : α ↪ β :=
  ⟨f, f.Injective⟩
#align equiv.to_embedding Equiv.toEmbedding
-/

#print Equiv.coe_toEmbedding /-
@[simp]
theorem Equiv.coe_toEmbedding : ⇑f.toEmbedding = f :=
  rfl
#align equiv.coe_to_embedding Equiv.coe_toEmbedding
-/

#print Equiv.toEmbedding_apply /-
theorem Equiv.toEmbedding_apply (a : α) : f.toEmbedding a = f a :=
  rfl
#align equiv.to_embedding_apply Equiv.toEmbedding_apply
-/

/- warning: equiv.coe_embedding -> Equiv.coeEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}}, Coe.{max 1 (imax u1 u2) (imax u2 u1), max 1 (imax u1 u2)} (Equiv.{u1, u2} α β) (Function.Embedding.{u1, u2} α β)
but is expected to have type
  forall {α : Sort.{u1}} {β : Sort.{u2}}, Coe.{max (max 1 u2) u1, max (max 1 u2) u1} (Equiv.{u1, u2} α β) (Function.Embedding.{u1, u2} α β)
Case conversion may be inaccurate. Consider using '#align equiv.coe_embedding Equiv.coeEmbeddingₓ'. -/
instance Equiv.coeEmbedding : Coe (α ≃ β) (α ↪ β) :=
  ⟨Equiv.toEmbedding⟩
#align equiv.coe_embedding Equiv.coeEmbedding

#print Equiv.Perm.coeEmbedding /-
@[reducible]
instance Equiv.Perm.coeEmbedding : Coe (Equiv.Perm α) (α ↪ α) :=
  Equiv.coeEmbedding
#align equiv.perm.coe_embedding Equiv.Perm.coeEmbedding
-/

@[simp]
theorem Equiv.coe_eq_toEmbedding : ↑f = f.toEmbedding :=
  rfl
#align equiv.coe_eq_to_embedding Equiv.coe_eq_toEmbedding

/- warning: equiv.as_embedding -> Equiv.asEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {p : β -> Prop}, (Equiv.{u1, max 1 u2} α (Subtype.{u2} β p)) -> (Function.Embedding.{u1, u2} α β)
but is expected to have type
  forall {α : Sort.{u1}} {β : Sort.{u2}} {p : α -> Prop}, (Equiv.{u2, max 1 u1} β (Subtype.{u1} α p)) -> (Function.Embedding.{u2, u1} β α)
Case conversion may be inaccurate. Consider using '#align equiv.as_embedding Equiv.asEmbeddingₓ'. -/
/-- Given an equivalence to a subtype, produce an embedding to the elements of the corresponding
set. -/
@[simps]
def Equiv.asEmbedding {p : β → Prop} (e : α ≃ Subtype p) : α ↪ β :=
  ⟨coe ∘ e, Subtype.coe_injective.comp e.Injective⟩
#align equiv.as_embedding Equiv.asEmbedding

end Equiv

namespace Function

namespace Embedding

/- warning: function.embedding.coe_injective -> Function.Embedding.coe_injective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}}, Function.Injective.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (α -> β) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (ᾰ : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}}, Function.Injective.{max (max 1 u1) u2, imax u2 u1} (Function.Embedding.{u2, u1} α β) (α -> β) (fun (f : Function.Embedding.{u2, u1} α β) => FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f)
Case conversion may be inaccurate. Consider using '#align function.embedding.coe_injective Function.Embedding.coe_injectiveₓ'. -/
theorem coe_injective {α β} : @Function.Injective (α ↪ β) (α → β) coeFn :=
  FunLike.coe_injective
#align function.embedding.coe_injective Function.Embedding.coe_injective

/- warning: function.embedding.ext -> Function.Embedding.ext is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {f : Function.Embedding.{u1, u2} α β} {g : Function.Embedding.{u1, u2} α β}, (forall (x : α), Eq.{u2} β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f x) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) g x)) -> (Eq.{max 1 (imax u1 u2)} (Function.Embedding.{u1, u2} α β) f g)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {f : Function.Embedding.{u2, u1} α β} {g : Function.Embedding.{u2, u1} α β}, (forall (x : α), Eq.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) g x)) -> (Eq.{max (max 1 u1) u2} (Function.Embedding.{u2, u1} α β) f g)
Case conversion may be inaccurate. Consider using '#align function.embedding.ext Function.Embedding.extₓ'. -/
@[ext]
theorem ext {α β} {f g : Embedding α β} (h : ∀ x, f x = g x) : f = g :=
  FunLike.ext f g h
#align function.embedding.ext Function.Embedding.ext

/- warning: function.embedding.ext_iff -> Function.Embedding.ext_iff is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {f : Function.Embedding.{u1, u2} α β} {g : Function.Embedding.{u1, u2} α β}, Iff (forall (x : α), Eq.{u2} β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f x) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) g x)) (Eq.{max 1 (imax u1 u2)} (Function.Embedding.{u1, u2} α β) f g)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {f : Function.Embedding.{u2, u1} α β} {g : Function.Embedding.{u2, u1} α β}, Iff (forall (x : α), Eq.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) g x)) (Eq.{max (max 1 u1) u2} (Function.Embedding.{u2, u1} α β) f g)
Case conversion may be inaccurate. Consider using '#align function.embedding.ext_iff Function.Embedding.ext_iffₓ'. -/
theorem ext_iff {α β} {f g : Embedding α β} : (∀ x, f x = g x) ↔ f = g :=
  FunLike.ext_iff.symm
#align function.embedding.ext_iff Function.Embedding.ext_iff

/- warning: function.embedding.to_fun_eq_coe -> Function.Embedding.toFun_eq_coe is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (f : Function.Embedding.{u1, u2} α β), Eq.{imax u1 u2} (α -> β) (Function.Embedding.toFun.{u1, u2} α β f) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (f : Function.Embedding.{u2, u1} α β), Eq.{imax u2 u1} (α -> β) (Function.Embedding.toFun.{u2, u1} α β f) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f)
Case conversion may be inaccurate. Consider using '#align function.embedding.to_fun_eq_coe Function.Embedding.toFun_eq_coeₓ'. -/
@[simp]
theorem toFun_eq_coe {α β} (f : α ↪ β) : toFun f = f :=
  rfl
#align function.embedding.to_fun_eq_coe Function.Embedding.toFun_eq_coe

/- warning: function.embedding.coe_fn_mk -> Function.Embedding.coeFn_mk is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (f : α -> β) (i : Function.Injective.{u1, u2} α β f), Eq.{imax u1 u2} ((fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.mk.{u1, u2} α β f i)) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) (Function.Embedding.mk.{u1, u2} α β f i)) f
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (f : α -> β) (i : Function.Injective.{u2, u1} α β f), Eq.{imax u2 u1} (forall (a : α), (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (FunLike.coe.{max (max 1 u2) u1, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u2) u1, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) (Function.Embedding.mk.{u2, u1} α β f i)) f
Case conversion may be inaccurate. Consider using '#align function.embedding.coe_fn_mk Function.Embedding.coeFn_mkₓ'. -/
@[simp]
theorem coeFn_mk {α β} (f : α → β) (i) : (@mk _ _ f i : α → β) = f :=
  rfl
#align function.embedding.coe_fn_mk Function.Embedding.coeFn_mk

/- warning: function.embedding.mk_coe -> Function.Embedding.mk_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Function.Embedding.{succ u1, succ u2} α β) (inj : Function.Injective.{succ u1, succ u2} α β (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f)), Eq.{max 1 (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (Function.Embedding.mk.{succ u1, succ u2} α β (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f) inj) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Function.Embedding.{succ u2, succ u1} α β) (inj : Function.Injective.{succ u2, succ u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f)), Eq.{max (succ u2) (succ u1)} (Function.Embedding.{succ u2, succ u1} α β) (Function.Embedding.mk.{succ u2, succ u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f) inj) f
Case conversion may be inaccurate. Consider using '#align function.embedding.mk_coe Function.Embedding.mk_coeₓ'. -/
@[simp]
theorem mk_coe {α β : Type _} (f : α ↪ β) (inj) : (⟨f, inj⟩ : α ↪ β) = f :=
  by
  ext
  simp
#align function.embedding.mk_coe Function.Embedding.mk_coe

/- warning: function.embedding.injective -> Function.Embedding.injective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (f : Function.Embedding.{u1, u2} α β), Function.Injective.{u1, u2} α β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (f : Function.Embedding.{u2, u1} α β), Function.Injective.{u2, u1} α β (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f)
Case conversion may be inaccurate. Consider using '#align function.embedding.injective Function.Embedding.injectiveₓ'. -/
protected theorem injective {α β} (f : α ↪ β) : Injective f :=
  EmbeddingLike.injective f
#align function.embedding.injective Function.Embedding.injective

/- warning: function.embedding.apply_eq_iff_eq -> Function.Embedding.apply_eq_iff_eq is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (f : Function.Embedding.{u1, u2} α β) (x : α) (y : α), Iff (Eq.{u2} β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f x) (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f y)) (Eq.{u1} α x y)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (f : Function.Embedding.{u2, u1} α β) (x : α) (y : α), Iff (Eq.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f x) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f y)) (Eq.{u2} α x y)
Case conversion may be inaccurate. Consider using '#align function.embedding.apply_eq_iff_eq Function.Embedding.apply_eq_iff_eqₓ'. -/
theorem apply_eq_iff_eq {α β} (f : α ↪ β) (x y : α) : f x = f y ↔ x = y :=
  EmbeddingLike.apply_eq_iff_eq f
#align function.embedding.apply_eq_iff_eq Function.Embedding.apply_eq_iff_eq

#print Function.Embedding.refl /-
/-- The identity map as a `function.embedding`. -/
@[refl, simps (config := { simpRhs := true })]
protected def refl (α : Sort _) : α ↪ α :=
  ⟨id, injective_id⟩
#align function.embedding.refl Function.Embedding.refl
-/

#print Function.Embedding.trans /-
/-- Composition of `f : α ↪ β` and `g : β ↪ γ`. -/
@[trans, simps (config := { simpRhs := true })]
protected def trans {α β γ} (f : α ↪ β) (g : β ↪ γ) : α ↪ γ :=
  ⟨g ∘ f, g.Injective.comp f.Injective⟩
#align function.embedding.trans Function.Embedding.trans
-/

/- warning: function.embedding.equiv_to_embedding_trans_symm_to_embedding -> Function.Embedding.equiv_toEmbedding_trans_symm_toEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (e : Equiv.{u1, u2} α β), Eq.{max 1 u1} (Function.Embedding.{u1, u1} α α) (Function.Embedding.trans.{u1, u2, u1} α β α (Equiv.toEmbedding.{u1, u2} α β e) (Equiv.toEmbedding.{u2, u1} β α (Equiv.symm.{u1, u2} α β e))) (Function.Embedding.refl.{u1} α)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (e : Equiv.{u2, u1} α β), Eq.{max 1 u2} (Function.Embedding.{u2, u2} α α) (Function.Embedding.trans.{u2, u1, u2} α β α (Equiv.toEmbedding.{u2, u1} α β e) (Equiv.toEmbedding.{u1, u2} β α (Equiv.symm.{u2, u1} α β e))) (Function.Embedding.refl.{u2} α)
Case conversion may be inaccurate. Consider using '#align function.embedding.equiv_to_embedding_trans_symm_to_embedding Function.Embedding.equiv_toEmbedding_trans_symm_toEmbeddingₓ'. -/
@[simp]
theorem equiv_toEmbedding_trans_symm_toEmbedding {α β : Sort _} (e : α ≃ β) :
    e.toEmbedding.trans e.symm.toEmbedding = Embedding.refl _ :=
  by
  ext
  simp
#align function.embedding.equiv_to_embedding_trans_symm_to_embedding Function.Embedding.equiv_toEmbedding_trans_symm_toEmbedding

/- warning: function.embedding.equiv_symm_to_embedding_trans_to_embedding -> Function.Embedding.equiv_symm_toEmbedding_trans_toEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (e : Equiv.{u1, u2} α β), Eq.{max 1 u2} (Function.Embedding.{u2, u2} β β) (Function.Embedding.trans.{u2, u1, u2} β α β (Equiv.toEmbedding.{u2, u1} β α (Equiv.symm.{u1, u2} α β e)) (Equiv.toEmbedding.{u1, u2} α β e)) (Function.Embedding.refl.{u2} β)
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (e : Equiv.{u2, u1} α β), Eq.{max 1 u1} (Function.Embedding.{u1, u1} β β) (Function.Embedding.trans.{u1, u2, u1} β α β (Equiv.toEmbedding.{u1, u2} β α (Equiv.symm.{u2, u1} α β e)) (Equiv.toEmbedding.{u2, u1} α β e)) (Function.Embedding.refl.{u1} β)
Case conversion may be inaccurate. Consider using '#align function.embedding.equiv_symm_to_embedding_trans_to_embedding Function.Embedding.equiv_symm_toEmbedding_trans_toEmbeddingₓ'. -/
@[simp]
theorem equiv_symm_toEmbedding_trans_toEmbedding {α β : Sort _} (e : α ≃ β) :
    e.symm.toEmbedding.trans e.toEmbedding = Embedding.refl _ :=
  by
  ext
  simp
#align function.embedding.equiv_symm_to_embedding_trans_to_embedding Function.Embedding.equiv_symm_toEmbedding_trans_toEmbedding

#print Function.Embedding.congr /-
/-- Transfer an embedding along a pair of equivalences. -/
@[simps (config := { fullyApplied := false })]
protected def congr {α : Sort u} {β : Sort v} {γ : Sort w} {δ : Sort x} (e₁ : α ≃ β) (e₂ : γ ≃ δ)
    (f : α ↪ γ) : β ↪ δ :=
  (Equiv.toEmbedding e₁.symm).trans (f.trans e₂.toEmbedding)
#align function.embedding.congr Function.Embedding.congr
-/

#print Function.Embedding.ofSurjective /-
/-- A right inverse `surj_inv` of a surjective function as an `embedding`. -/
protected noncomputable def ofSurjective {α β} (f : β → α) (hf : Surjective f) : α ↪ β :=
  ⟨surjInv hf, injective_surjInv _⟩
#align function.embedding.of_surjective Function.Embedding.ofSurjective
-/

#print Function.Embedding.equivOfSurjective /-
/-- Convert a surjective `embedding` to an `equiv` -/
protected noncomputable def equivOfSurjective {α β} (f : α ↪ β) (hf : Surjective f) : α ≃ β :=
  Equiv.ofBijective f ⟨f.Injective, hf⟩
#align function.embedding.equiv_of_surjective Function.Embedding.equivOfSurjective
-/

#print Function.Embedding.ofIsEmpty /-
/-- There is always an embedding from an empty type. -/
protected def ofIsEmpty {α β} [IsEmpty α] : α ↪ β :=
  ⟨isEmptyElim, isEmptyElim⟩
#align function.embedding.of_is_empty Function.Embedding.ofIsEmpty
-/

#print Function.Embedding.setValue /-
/-- Change the value of an embedding `f` at one point. If the prescribed image
is already occupied by some `f a'`, then swap the values at these two points. -/
def setValue {α β} (f : α ↪ β) (a : α) (b : β) [∀ a', Decidable (a' = a)]
    [∀ a', Decidable (f a' = b)] : α ↪ β :=
  ⟨fun a' => if a' = a then b else if f a' = b then f a else f a',
    by
    intro x y h
    dsimp at h
    split_ifs  at h <;> try subst b <;> try simp only [f.injective.eq_iff] at * <;> cc⟩
#align function.embedding.set_value Function.Embedding.setValue
-/

/- warning: function.embedding.set_value_eq -> Function.Embedding.setValue_eq is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} (f : Function.Embedding.{u1, u2} α β) (a : α) (b : β) [_inst_1 : forall (a' : α), Decidable (Eq.{u1} α a' a)] [_inst_2 : forall (a' : α), Decidable (Eq.{u2} β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) f a') b)], Eq.{u2} β (coeFn.{max 1 (imax u1 u2), imax u1 u2} (Function.Embedding.{u1, u2} α β) (fun (_x : Function.Embedding.{u1, u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{u1, u2} α β) (Function.Embedding.setValue.{u1, u2} α β f a b (fun (a' : α) => _inst_1 a') (fun (a' : α) => _inst_2 a')) a) b
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} (f : Function.Embedding.{u2, u1} α β) (a : α) (b : β) [_inst_1 : forall (a' : α), Decidable (Eq.{u2} α a' a)] [_inst_2 : forall (a' : α), Decidable (Eq.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a') (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) f a') b)], Eq.{u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (FunLike.coe.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (max 1 u1) u2, u2, u1} (Function.Embedding.{u2, u1} α β) α β (Function.instEmbeddingLikeEmbedding.{u2, u1} α β)) (Function.Embedding.setValue.{u2, u1} α β f a b (fun (a' : α) => _inst_1 a') (fun (a' : α) => _inst_2 a')) a) b
Case conversion may be inaccurate. Consider using '#align function.embedding.set_value_eq Function.Embedding.setValue_eqₓ'. -/
theorem setValue_eq {α β} (f : α ↪ β) (a : α) (b : β) [∀ a', Decidable (a' = a)]
    [∀ a', Decidable (f a' = b)] : setValue f a b a = b := by simp [set_value]
#align function.embedding.set_value_eq Function.Embedding.setValue_eq

#print Function.Embedding.some /-
/-- Embedding into `option α` using `some`. -/
@[simps (config := { fullyApplied := false })]
protected def some {α} : α ↪ Option α :=
  ⟨some, Option.some_injective α⟩
#align function.embedding.some Function.Embedding.some
-/

/- warning: function.embedding.coe_option clashes with function.embedding.some -> Function.Embedding.some
Case conversion may be inaccurate. Consider using '#align function.embedding.coe_option Function.Embedding.someₓ'. -/
#print Function.Embedding.some /-
/-- Embedding into `option α` using `coe`. Usually the correct synctatical form for `simp`. -/
@[simps (config := { fullyApplied := false })]
def some {α} : α ↪ Option α :=
  ⟨coe, Option.some_injective α⟩
#align function.embedding.coe_option Function.Embedding.some
-/

#print Function.Embedding.optionMap /-
/-- A version of `option.map` for `function.embedding`s. -/
@[simps (config := { fullyApplied := false })]
def optionMap {α β} (f : α ↪ β) : Option α ↪ Option β :=
  ⟨Option.map f, Option.map_injective f.Injective⟩
#align function.embedding.option_map Function.Embedding.optionMap
-/

#print Function.Embedding.subtype /-
/-- Embedding of a `subtype`. -/
def subtype {α} (p : α → Prop) : Subtype p ↪ α :=
  ⟨coe, fun _ _ => Subtype.ext_val⟩
#align function.embedding.subtype Function.Embedding.subtype
-/

#print Function.Embedding.coe_subtype /-
@[simp]
theorem coe_subtype {α} (p : α → Prop) : ⇑(subtype p) = coe :=
  rfl
#align function.embedding.coe_subtype Function.Embedding.coe_subtype
-/

#print Function.Embedding.quotientOut /-
/-- `quotient.out` as an embedding. -/
noncomputable def quotientOut (α) [s : Setoid α] : Quotient s ↪ α :=
  ⟨_, Quotient.out_injective⟩
#align function.embedding.quotient_out Function.Embedding.quotientOut
-/

#print Function.Embedding.coe_quotientOut /-
@[simp]
theorem coe_quotientOut (α) [s : Setoid α] : ⇑(quotientOut α) = Quotient.out :=
  rfl
#align function.embedding.coe_quotient_out Function.Embedding.coe_quotientOut
-/

#print Function.Embedding.punit /-
/-- Choosing an element `b : β` gives an embedding of `punit` into `β`. -/
def punit {β : Sort _} (b : β) : PUnit ↪ β :=
  ⟨fun _ => b, by
    rintro ⟨⟩ ⟨⟩ _
    rfl⟩
#align function.embedding.punit Function.Embedding.punit
-/

#print Function.Embedding.sectl /-
/-- Fixing an element `b : β` gives an embedding `α ↪ α × β`. -/
@[simps]
def sectl (α : Sort _) {β : Sort _} (b : β) : α ↪ α × β :=
  ⟨fun a => (a, b), fun a a' h => congr_arg Prod.fst h⟩
#align function.embedding.sectl Function.Embedding.sectl
-/

#print Function.Embedding.sectr /-
/-- Fixing an element `a : α` gives an embedding `β ↪ α × β`. -/
@[simps]
def sectr {α : Sort _} (a : α) (β : Sort _) : β ↪ α × β :=
  ⟨fun b => (a, b), fun b b' h => congr_arg Prod.snd h⟩
#align function.embedding.sectr Function.Embedding.sectr
-/

#print Function.Embedding.prodMap /-
/-- If `e₁` and `e₂` are embeddings, then so is `prod.map e₁ e₂ : (a, b) ↦ (e₁ a, e₂ b)`. -/
def prodMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : α × γ ↪ β × δ :=
  ⟨Prod.map e₁ e₂, e₁.Injective.Prod_map e₂.Injective⟩
#align function.embedding.prod_map Function.Embedding.prodMap
-/

/- warning: function.embedding.coe_prod_map -> Function.Embedding.coe_prodMap is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} (e₁ : Function.Embedding.{succ u1, succ u2} α β) (e₂ : Function.Embedding.{succ u3, succ u4} γ δ), Eq.{max (max (succ u1) (succ u3)) (succ u2) (succ u4)} ((Prod.{u1, u3} α γ) -> (Prod.{u2, u4} β δ)) (coeFn.{max 1 (max (succ u1) (succ u3)) (succ u2) (succ u4), max (max (succ u1) (succ u3)) (succ u2) (succ u4)} (Function.Embedding.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ)) (fun (_x : Function.Embedding.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ)) => (Prod.{u1, u3} α γ) -> (Prod.{u2, u4} β δ)) (Function.Embedding.hasCoeToFun.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ)) (Function.Embedding.prodMap.{u1, u2, u3, u4} α β γ δ e₁ e₂)) (Prod.map.{u1, u2, u3, u4} α β γ δ (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) e₁) (coeFn.{max 1 (succ u3) (succ u4), max (succ u3) (succ u4)} (Function.Embedding.{succ u3, succ u4} γ δ) (fun (_x : Function.Embedding.{succ u3, succ u4} γ δ) => γ -> δ) (Function.Embedding.hasCoeToFun.{succ u3, succ u4} γ δ) e₂))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} (e₁ : Function.Embedding.{succ u4, succ u3} α β) (e₂ : Function.Embedding.{succ u2, succ u1} γ δ), Eq.{max (max (max (succ u4) (succ u3)) (succ u2)) (succ u1)} (forall (ᾰ : Prod.{u4, u2} α γ), (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Prod.{u4, u2} α γ) => Prod.{u3, u1} β δ) ᾰ) (FunLike.coe.{max (max (max (succ u4) (succ u3)) (succ u2)) (succ u1), max (succ u4) (succ u2), max (succ u3) (succ u1)} (Function.Embedding.{max (succ u2) (succ u4), max (succ u1) (succ u3)} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ)) (Prod.{u4, u2} α γ) (fun (_x : Prod.{u4, u2} α γ) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Prod.{u4, u2} α γ) => Prod.{u3, u1} β δ) _x) (EmbeddingLike.toFunLike.{max (max (max (succ u4) (succ u3)) (succ u2)) (succ u1), max (succ u4) (succ u2), max (succ u3) (succ u1)} (Function.Embedding.{max (succ u2) (succ u4), max (succ u1) (succ u3)} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ)) (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (Function.instEmbeddingLikeEmbedding.{max (succ u4) (succ u2), max (succ u3) (succ u1)} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ))) (Function.Embedding.prodMap.{u4, u3, u2, u1} α β γ δ e₁ e₂)) (Prod.map.{u4, u3, u2, u1} α β γ δ (FunLike.coe.{max (succ u4) (succ u3), succ u4, succ u3} (Function.Embedding.{succ u4, succ u3} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u4) (succ u3), succ u4, succ u3} (Function.Embedding.{succ u4, succ u3} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u4, succ u3} α β)) e₁) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} γ δ) γ (fun (_x : γ) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : γ) => δ) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} γ δ) γ δ (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} γ δ)) e₂))
Case conversion may be inaccurate. Consider using '#align function.embedding.coe_prod_map Function.Embedding.coe_prodMapₓ'. -/
@[simp]
theorem coe_prodMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) :
    ⇑(e₁.Prod_map e₂) = Prod.map e₁ e₂ :=
  rfl
#align function.embedding.coe_prod_map Function.Embedding.coe_prodMap

#print Function.Embedding.pprodMap /-
/-- If `e₁` and `e₂` are embeddings, then so is `λ ⟨a, b⟩, ⟨e₁ a, e₂ b⟩ : pprod α γ → pprod β δ`. -/
def pprodMap {α β γ δ : Sort _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : PProd α γ ↪ PProd β δ :=
  ⟨fun x => ⟨e₁ x.1, e₂ x.2⟩, e₁.Injective.pprod_map e₂.Injective⟩
#align function.embedding.pprod_map Function.Embedding.pprodMap
-/

section Sum

open Sum

#print Function.Embedding.sumMap /-
/-- If `e₁` and `e₂` are embeddings, then so is `sum.map e₁ e₂`. -/
def sumMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : Sum α γ ↪ Sum β δ :=
  ⟨Sum.map e₁ e₂, fun s₁ s₂ h =>
    match s₁, s₂, h with
    | inl a₁, inl a₂, h => congr_arg inl <| e₁.Injective <| inl.inj h
    | inr b₁, inr b₂, h => congr_arg inr <| e₂.Injective <| inr.inj h⟩
#align function.embedding.sum_map Function.Embedding.sumMap
-/

/- warning: function.embedding.coe_sum_map -> Function.Embedding.coe_sumMap is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} (e₁ : Function.Embedding.{succ u1, succ u2} α β) (e₂ : Function.Embedding.{succ u3, succ u4} γ δ), Eq.{max (max (succ u1) (succ u3)) (succ u2) (succ u4)} ((Sum.{u1, u3} α γ) -> (Sum.{u2, u4} β δ)) (coeFn.{max 1 (max (succ u1) (succ u3)) (succ u2) (succ u4), max (max (succ u1) (succ u3)) (succ u2) (succ u4)} (Function.Embedding.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Sum.{u1, u3} α γ) (Sum.{u2, u4} β δ)) (fun (_x : Function.Embedding.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Sum.{u1, u3} α γ) (Sum.{u2, u4} β δ)) => (Sum.{u1, u3} α γ) -> (Sum.{u2, u4} β δ)) (Function.Embedding.hasCoeToFun.{max (succ u1) (succ u3), max (succ u2) (succ u4)} (Sum.{u1, u3} α γ) (Sum.{u2, u4} β δ)) (Function.Embedding.sumMap.{u1, u2, u3, u4} α β γ δ e₁ e₂)) (Sum.map.{u1, u3, u2, u4} α β γ δ (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) e₁) (coeFn.{max 1 (succ u3) (succ u4), max (succ u3) (succ u4)} (Function.Embedding.{succ u3, succ u4} γ δ) (fun (_x : Function.Embedding.{succ u3, succ u4} γ δ) => γ -> δ) (Function.Embedding.hasCoeToFun.{succ u3, succ u4} γ δ) e₂))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} (e₁ : Function.Embedding.{succ u4, succ u3} α β) (e₂ : Function.Embedding.{succ u2, succ u1} γ δ), Eq.{max (max (max (succ u1) (succ u2)) (succ u3)) (succ u4)} (forall (ᾰ : Sum.{u4, u2} α γ), (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Sum.{u4, u2} α γ) => Sum.{u3, u1} β δ) ᾰ) (FunLike.coe.{max (max (max (succ u1) (succ u2)) (succ u3)) (succ u4), max (succ u2) (succ u4), max (succ u1) (succ u3)} (Function.Embedding.{max (succ u2) (succ u4), max (succ u1) (succ u3)} (Sum.{u4, u2} α γ) (Sum.{u3, u1} β δ)) (Sum.{u4, u2} α γ) (fun (_x : Sum.{u4, u2} α γ) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Sum.{u4, u2} α γ) => Sum.{u3, u1} β δ) _x) (EmbeddingLike.toFunLike.{max (max (max (succ u1) (succ u2)) (succ u3)) (succ u4), max (succ u2) (succ u4), max (succ u1) (succ u3)} (Function.Embedding.{max (succ u2) (succ u4), max (succ u1) (succ u3)} (Sum.{u4, u2} α γ) (Sum.{u3, u1} β δ)) (Sum.{u4, u2} α γ) (Sum.{u3, u1} β δ) (Function.instEmbeddingLikeEmbedding.{max (succ u2) (succ u4), max (succ u1) (succ u3)} (Sum.{u4, u2} α γ) (Sum.{u3, u1} β δ))) (Function.Embedding.sumMap.{u4, u3, u2, u1} α β γ δ e₁ e₂)) (Sum.map.{u4, u2, u3, u1} α β γ δ (FunLike.coe.{max (succ u3) (succ u4), succ u4, succ u3} (Function.Embedding.{succ u4, succ u3} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u3) (succ u4), succ u4, succ u3} (Function.Embedding.{succ u4, succ u3} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u4, succ u3} α β)) e₁) (FunLike.coe.{max (succ u1) (succ u2), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} γ δ) γ (fun (_x : γ) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : γ) => δ) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} γ δ) γ δ (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} γ δ)) e₂))
Case conversion may be inaccurate. Consider using '#align function.embedding.coe_sum_map Function.Embedding.coe_sumMapₓ'. -/
@[simp]
theorem coe_sumMap {α β γ δ} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : ⇑(sumMap e₁ e₂) = Sum.map e₁ e₂ :=
  rfl
#align function.embedding.coe_sum_map Function.Embedding.coe_sumMap

#print Function.Embedding.inl /-
/-- The embedding of `α` into the sum `α ⊕ β`. -/
@[simps]
def inl {α β : Type _} : α ↪ Sum α β :=
  ⟨Sum.inl, fun a b => Sum.inl.inj⟩
#align function.embedding.inl Function.Embedding.inl
-/

#print Function.Embedding.inr /-
/-- The embedding of `β` into the sum `α ⊕ β`. -/
@[simps]
def inr {α β : Type _} : β ↪ Sum α β :=
  ⟨Sum.inr, fun a b => Sum.inr.inj⟩
#align function.embedding.inr Function.Embedding.inr
-/

end Sum

section Sigma

variable {α α' : Type _} {β : α → Type _} {β' : α' → Type _}

#print Function.Embedding.sigmaMk /-
/-- `sigma.mk` as an `function.embedding`. -/
@[simps apply]
def sigmaMk (a : α) : β a ↪ Σx, β x :=
  ⟨Sigma.mk a, sigma_mk_injective⟩
#align function.embedding.sigma_mk Function.Embedding.sigmaMk
-/

#print Function.Embedding.sigmaMap /-
/-- If `f : α ↪ α'` is an embedding and `g : Π a, β α ↪ β' (f α)` is a family
of embeddings, then `sigma.map f g` is an embedding. -/
@[simps apply]
def sigmaMap (f : α ↪ α') (g : ∀ a, β a ↪ β' (f a)) : (Σa, β a) ↪ Σa', β' a' :=
  ⟨Sigma.map f fun a => g a, f.Injective.sigma_map fun a => (g a).Injective⟩
#align function.embedding.sigma_map Function.Embedding.sigmaMap
-/

end Sigma

#print Function.Embedding.piCongrRight /-
/-- Define an embedding `(Π a : α, β a) ↪ (Π a : α, γ a)` from a family of embeddings
`e : Π a, (β a ↪ γ a)`. This embedding sends `f` to `λ a, e a (f a)`. -/
@[simps]
def piCongrRight {α : Sort _} {β γ : α → Sort _} (e : ∀ a, β a ↪ γ a) : (∀ a, β a) ↪ ∀ a, γ a :=
  ⟨fun f a => e a (f a), fun f₁ f₂ h => funext fun a => (e a).Injective (congr_fun h a)⟩
#align function.embedding.Pi_congr_right Function.Embedding.piCongrRight
-/

#print Function.Embedding.arrowCongrRight /-
/-- An embedding `e : α ↪ β` defines an embedding `(γ → α) ↪ (γ → β)` that sends each `f`
to `e ∘ f`. -/
def arrowCongrRight {α : Sort u} {β : Sort v} {γ : Sort w} (e : α ↪ β) : (γ → α) ↪ γ → β :=
  piCongrRight fun _ => e
#align function.embedding.arrow_congr_right Function.Embedding.arrowCongrRight
-/

#print Function.Embedding.arrowCongrRight_apply /-
@[simp]
theorem arrowCongrRight_apply {α : Sort u} {β : Sort v} {γ : Sort w} (e : α ↪ β) (f : γ ↪ α) :
    arrowCongrRight e f = e ∘ f :=
  rfl
#align function.embedding.arrow_congr_right_apply Function.Embedding.arrowCongrRight_apply
-/

#print Function.Embedding.arrowCongrLeft /-
/-- An embedding `e : α ↪ β` defines an embedding `(α → γ) ↪ (β → γ)` for any inhabited type `γ`.
This embedding sends each `f : α → γ` to a function `g : β → γ` such that `g ∘ e = f` and
`g y = default` whenever `y ∉ range e`. -/
noncomputable def arrowCongrLeft {α : Sort u} {β : Sort v} {γ : Sort w} [Inhabited γ] (e : α ↪ β) :
    (α → γ) ↪ β → γ :=
  ⟨fun f => extend e f default, fun f₁ f₂ h =>
    funext fun x => by simpa only [e.injective.extend_apply] using congr_fun h (e x)⟩
#align function.embedding.arrow_congr_left Function.Embedding.arrowCongrLeft
-/

#print Function.Embedding.subtypeMap /-
/-- Restrict both domain and codomain of an embedding. -/
protected def subtypeMap {α β} {p : α → Prop} {q : β → Prop} (f : α ↪ β)
    (h : ∀ ⦃x⦄, p x → q (f x)) : { x : α // p x } ↪ { y : β // q y } :=
  ⟨Subtype.map f h, Subtype.map_injective h f.2⟩
#align function.embedding.subtype_map Function.Embedding.subtypeMap
-/

open Set

/- warning: function.embedding.swap_apply -> Function.Embedding.swap_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : DecidableEq.{succ u2} β] (f : Function.Embedding.{succ u1, succ u2} α β) (x : α) (y : α) (z : α), Eq.{succ u2} β (coeFn.{succ u2, succ u2} (Equiv.Perm.{succ u2} β) (fun (_x : Equiv.{succ u2, succ u2} β β) => β -> β) (Equiv.hasCoeToFun.{succ u2, succ u2} β β) (Equiv.swap.{succ u2} β (fun (a : β) (b : β) => _inst_2 a b) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f x) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f y)) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f z)) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f (coeFn.{succ u1, succ u1} (Equiv.Perm.{succ u1} α) (fun (_x : Equiv.{succ u1, succ u1} α α) => α -> α) (Equiv.hasCoeToFun.{succ u1, succ u1} α α) (Equiv.swap.{succ u1} α (fun (a : α) (b : α) => _inst_1 a b) x y) z))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : DecidableEq.{succ u2} α] [_inst_2 : DecidableEq.{succ u1} β] (f : Function.Embedding.{succ u2, succ u1} α β) (x : α) (y : α) (z : α), Eq.{succ u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (a : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f z)) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.Perm.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x)) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (fun (_x : β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x)) (Equiv.swap.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (fun (a : β) (b : β) => _inst_2 a b) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f x) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f y)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f z)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f (FunLike.coe.{succ u2, succ u2, succ u2} (Equiv.Perm.{succ u2} α) α (fun (_x : α) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : α) => α) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u2} α α) (Equiv.swap.{succ u2} α (fun (a : α) (b : α) => _inst_1 a b) x y) z))
Case conversion may be inaccurate. Consider using '#align function.embedding.swap_apply Function.Embedding.swap_applyₓ'. -/
theorem swap_apply {α β : Type _} [DecidableEq α] [DecidableEq β] (f : α ↪ β) (x y z : α) :
    Equiv.swap (f x) (f y) (f z) = f (Equiv.swap x y z) :=
  f.Injective.swap_apply x y z
#align function.embedding.swap_apply Function.Embedding.swap_apply

/- warning: function.embedding.swap_comp -> Function.Embedding.swap_comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : DecidableEq.{succ u2} β] (f : Function.Embedding.{succ u1, succ u2} α β) (x : α) (y : α), Eq.{max (succ u1) (succ u2)} (α -> β) (Function.comp.{succ u1, succ u2, succ u2} α β β (coeFn.{succ u2, succ u2} (Equiv.Perm.{succ u2} β) (fun (_x : Equiv.{succ u2, succ u2} β β) => β -> β) (Equiv.hasCoeToFun.{succ u2, succ u2} β β) (Equiv.swap.{succ u2} β (fun (a : β) (b : β) => _inst_2 a b) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f x) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f y))) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f)) (Function.comp.{succ u1, succ u1, succ u2} α α β (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Function.Embedding.{succ u1, succ u2} α β) (fun (_x : Function.Embedding.{succ u1, succ u2} α β) => α -> β) (Function.Embedding.hasCoeToFun.{succ u1, succ u2} α β) f) (coeFn.{succ u1, succ u1} (Equiv.Perm.{succ u1} α) (fun (_x : Equiv.{succ u1, succ u1} α α) => α -> α) (Equiv.hasCoeToFun.{succ u1, succ u1} α α) (Equiv.swap.{succ u1} α (fun (a : α) (b : α) => _inst_1 a b) x y)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : DecidableEq.{succ u2} α] [_inst_2 : DecidableEq.{succ u1} β] (f : Function.Embedding.{succ u2, succ u1} α β) (x : α) (y : α), Eq.{max (succ u2) (succ u1)} (α -> ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x)) (Function.comp.{succ u2, succ u1, succ u1} α ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.Perm.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x)) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (fun (_x : β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x)) (Equiv.swap.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) x) (fun (a : β) (b : β) => _inst_2 a b) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f x) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f y))) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f)) (Function.comp.{succ u2, succ u2, succ u1} α α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) f) (FunLike.coe.{succ u2, succ u2, succ u2} (Equiv.Perm.{succ u2} α) α (fun (_x : α) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : α) => α) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u2} α α) (Equiv.swap.{succ u2} α (fun (a : α) (b : α) => _inst_1 a b) x y)))
Case conversion may be inaccurate. Consider using '#align function.embedding.swap_comp Function.Embedding.swap_compₓ'. -/
theorem swap_comp {α β : Type _} [DecidableEq α] [DecidableEq β] (f : α ↪ β) (x y : α) :
    Equiv.swap (f x) (f y) ∘ f = f ∘ Equiv.swap x y :=
  f.Injective.swap_comp x y
#align function.embedding.swap_comp Function.Embedding.swap_comp

end Embedding

end Function

namespace Equiv

open Function.Embedding

/- warning: equiv.subtype_injective_equiv_embedding -> Equiv.subtypeInjectiveEquivEmbedding is a dubious translation:
lean 3 declaration is
  forall (α : Sort.{u1}) (β : Sort.{u2}), Equiv.{max 1 (imax u1 u2), max 1 (imax u1 u2)} (Subtype.{imax u1 u2} (α -> β) (fun (f : α -> β) => Function.Injective.{u1, u2} α β f)) (Function.Embedding.{u1, u2} α β)
but is expected to have type
  forall (α : Sort.{u1}) (β : Sort.{u2}), Equiv.{max 1 (imax u1 u2), max (max 1 u2) u1} (Subtype.{imax u1 u2} (α -> β) (fun (f : α -> β) => Function.Injective.{u1, u2} α β f)) (Function.Embedding.{u1, u2} α β)
Case conversion may be inaccurate. Consider using '#align equiv.subtype_injective_equiv_embedding Equiv.subtypeInjectiveEquivEmbeddingₓ'. -/
/-- The type of embeddings `α ↪ β` is equivalent to
    the subtype of all injective functions `α → β`. -/
def subtypeInjectiveEquivEmbedding (α β : Sort _) : { f : α → β // Function.Injective f } ≃ (α ↪ β)
    where
  toFun f := ⟨f.val, f.property⟩
  invFun f := ⟨f, f.Injective⟩
  left_inv f := by simp
  right_inv f := by
    ext
    rfl
#align equiv.subtype_injective_equiv_embedding Equiv.subtypeInjectiveEquivEmbedding

/- warning: equiv.embedding_congr -> Equiv.embeddingCongr is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {γ : Sort.{u3}} {δ : Sort.{u4}}, (Equiv.{u1, u2} α β) -> (Equiv.{u3, u4} γ δ) -> (Equiv.{max 1 (imax u1 u3), max 1 (imax u2 u4)} (Function.Embedding.{u1, u3} α γ) (Function.Embedding.{u2, u4} β δ))
but is expected to have type
  forall {α : Sort.{u1}} {β : Sort.{u2}} {γ : Sort.{u3}} {δ : Sort.{u4}}, (Equiv.{u1, u2} α β) -> (Equiv.{u3, u4} γ δ) -> (Equiv.{max (max 1 u3) u1, max (max 1 u4) u2} (Function.Embedding.{u1, u3} α γ) (Function.Embedding.{u2, u4} β δ))
Case conversion may be inaccurate. Consider using '#align equiv.embedding_congr Equiv.embeddingCongrₓ'. -/
/-- If `α₁ ≃ α₂` and `β₁ ≃ β₂`, then the type of embeddings `α₁ ↪ β₁`
is equivalent to the type of embeddings `α₂ ↪ β₂`. -/
@[congr, simps apply]
def embeddingCongr {α β γ δ : Sort _} (h : α ≃ β) (h' : γ ≃ δ) : (α ↪ γ) ≃ (β ↪ δ)
    where
  toFun f := f.congr h h'
  invFun f := f.congr h.symm h'.symm
  left_inv x := by
    ext
    simp
  right_inv x := by
    ext
    simp
#align equiv.embedding_congr Equiv.embeddingCongr

/- warning: equiv.embedding_congr_refl -> Equiv.embeddingCongr_refl is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}}, Eq.{max 1 (imax u1 u2)} (Equiv.{max 1 (imax u1 u2), max 1 (imax u1 u2)} (Function.Embedding.{u1, u2} α β) (Function.Embedding.{u1, u2} α β)) (Equiv.embeddingCongr.{u1, u1, u2, u2} α α β β (Equiv.refl.{u1} α) (Equiv.refl.{u2} β)) (Equiv.refl.{max 1 (imax u1 u2)} (Function.Embedding.{u1, u2} α β))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}}, Eq.{max (max 1 u2) u1} (Equiv.{max (max 1 u1) u2, max (max 1 u1) u2} (Function.Embedding.{u2, u1} α β) (Function.Embedding.{u2, u1} α β)) (Equiv.embeddingCongr.{u2, u2, u1, u1} α α β β (Equiv.refl.{u2} α) (Equiv.refl.{u1} β)) (Equiv.refl.{max (max 1 u1) u2} (Function.Embedding.{u2, u1} α β))
Case conversion may be inaccurate. Consider using '#align equiv.embedding_congr_refl Equiv.embeddingCongr_reflₓ'. -/
@[simp]
theorem embeddingCongr_refl {α β : Sort _} :
    embeddingCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (α ↪ β) :=
  by
  ext
  rfl
#align equiv.embedding_congr_refl Equiv.embeddingCongr_refl

/- warning: equiv.embedding_congr_trans -> Equiv.embeddingCongr_trans is a dubious translation:
lean 3 declaration is
  forall {α₁ : Sort.{u1}} {β₁ : Sort.{u2}} {α₂ : Sort.{u3}} {β₂ : Sort.{u4}} {α₃ : Sort.{u5}} {β₃ : Sort.{u6}} (e₁ : Equiv.{u1, u3} α₁ α₂) (e₁' : Equiv.{u2, u4} β₁ β₂) (e₂ : Equiv.{u3, u5} α₂ α₃) (e₂' : Equiv.{u4, u6} β₂ β₃), Eq.{max 1 (max (max 1 (imax u1 u2)) 1 (imax u5 u6)) (max 1 (imax u5 u6)) 1 (imax u1 u2)} (Equiv.{max 1 (imax u1 u2), max 1 (imax u5 u6)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u5, u6} α₃ β₃)) (Equiv.embeddingCongr.{u1, u5, u2, u6} α₁ α₃ β₁ β₃ (Equiv.trans.{u1, u3, u5} α₁ α₂ α₃ e₁ e₂) (Equiv.trans.{u2, u4, u6} β₁ β₂ β₃ e₁' e₂')) (Equiv.trans.{max 1 (imax u1 u2), max 1 (imax u3 u4), max 1 (imax u5 u6)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u3, u4} α₂ β₂) (Function.Embedding.{u5, u6} α₃ β₃) (Equiv.embeddingCongr.{u1, u3, u2, u4} α₁ α₂ β₁ β₂ e₁ e₁') (Equiv.embeddingCongr.{u3, u5, u4, u6} α₂ α₃ β₂ β₃ e₂ e₂'))
but is expected to have type
  forall {α₁ : Sort.{u6}} {β₁ : Sort.{u5}} {α₂ : Sort.{u4}} {β₂ : Sort.{u3}} {α₃ : Sort.{u2}} {β₃ : Sort.{u1}} (e₁ : Equiv.{u6, u4} α₁ α₂) (e₁' : Equiv.{u5, u3} β₁ β₂) (e₂ : Equiv.{u4, u2} α₂ α₃) (e₂' : Equiv.{u3, u1} β₂ β₃), Eq.{max (max (max (max 1 u6) u5) u2) u1} (Equiv.{max (max 1 u5) u6, max (max 1 u1) u2} (Function.Embedding.{u6, u5} α₁ β₁) (Function.Embedding.{u2, u1} α₃ β₃)) (Equiv.embeddingCongr.{u6, u2, u5, u1} α₁ α₃ β₁ β₃ (Equiv.trans.{u6, u4, u2} α₁ α₂ α₃ e₁ e₂) (Equiv.trans.{u5, u3, u1} β₁ β₂ β₃ e₁' e₂')) (Equiv.trans.{max (max 1 u6) u5, max (max 1 u4) u3, max (max 1 u1) u2} (Function.Embedding.{u6, u5} α₁ β₁) (Function.Embedding.{u4, u3} α₂ β₂) (Function.Embedding.{u2, u1} α₃ β₃) (Equiv.embeddingCongr.{u6, u4, u5, u3} α₁ α₂ β₁ β₂ e₁ e₁') (Equiv.embeddingCongr.{u4, u2, u3, u1} α₂ α₃ β₂ β₃ e₂ e₂'))
Case conversion may be inaccurate. Consider using '#align equiv.embedding_congr_trans Equiv.embeddingCongr_transₓ'. -/
@[simp]
theorem embeddingCongr_trans {α₁ β₁ α₂ β₂ α₃ β₃ : Sort _} (e₁ : α₁ ≃ α₂) (e₁' : β₁ ≃ β₂)
    (e₂ : α₂ ≃ α₃) (e₂' : β₂ ≃ β₃) :
    embeddingCongr (e₁.trans e₂) (e₁'.trans e₂') =
      (embeddingCongr e₁ e₁').trans (embeddingCongr e₂ e₂') :=
  rfl
#align equiv.embedding_congr_trans Equiv.embeddingCongr_trans

/- warning: equiv.embedding_congr_symm -> Equiv.embeddingCongr_symm is a dubious translation:
lean 3 declaration is
  forall {α₁ : Sort.{u1}} {β₁ : Sort.{u2}} {α₂ : Sort.{u3}} {β₂ : Sort.{u4}} (e₁ : Equiv.{u1, u3} α₁ α₂) (e₂ : Equiv.{u2, u4} β₁ β₂), Eq.{max 1 (max (max 1 (imax u3 u4)) 1 (imax u1 u2)) (max 1 (imax u1 u2)) 1 (imax u3 u4)} (Equiv.{max 1 (imax u3 u4), max 1 (imax u1 u2)} (Function.Embedding.{u3, u4} α₂ β₂) (Function.Embedding.{u1, u2} α₁ β₁)) (Equiv.symm.{max 1 (imax u1 u2), max 1 (imax u3 u4)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u3, u4} α₂ β₂) (Equiv.embeddingCongr.{u1, u3, u2, u4} α₁ α₂ β₁ β₂ e₁ e₂)) (Equiv.embeddingCongr.{u3, u1, u4, u2} α₂ α₁ β₂ β₁ (Equiv.symm.{u1, u3} α₁ α₂ e₁) (Equiv.symm.{u2, u4} β₁ β₂ e₂))
but is expected to have type
  forall {α₁ : Sort.{u4}} {β₁ : Sort.{u3}} {α₂ : Sort.{u2}} {β₂ : Sort.{u1}} (e₁ : Equiv.{u4, u2} α₁ α₂) (e₂ : Equiv.{u3, u1} β₁ β₂), Eq.{max (max (max (max 1 u4) u3) u2) u1} (Equiv.{max (max 1 u2) u1, max (max 1 u4) u3} (Function.Embedding.{u2, u1} α₂ β₂) (Function.Embedding.{u4, u3} α₁ β₁)) (Equiv.symm.{max (max 1 u4) u3, max (max 1 u2) u1} (Function.Embedding.{u4, u3} α₁ β₁) (Function.Embedding.{u2, u1} α₂ β₂) (Equiv.embeddingCongr.{u4, u2, u3, u1} α₁ α₂ β₁ β₂ e₁ e₂)) (Equiv.embeddingCongr.{u2, u4, u1, u3} α₂ α₁ β₂ β₁ (Equiv.symm.{u4, u2} α₁ α₂ e₁) (Equiv.symm.{u3, u1} β₁ β₂ e₂))
Case conversion may be inaccurate. Consider using '#align equiv.embedding_congr_symm Equiv.embeddingCongr_symmₓ'. -/
@[simp]
theorem embeddingCongr_symm {α₁ β₁ α₂ β₂ : Sort _} (e₁ : α₁ ≃ α₂) (e₂ : β₁ ≃ β₂) :
    (embeddingCongr e₁ e₂).symm = embeddingCongr e₁.symm e₂.symm :=
  rfl
#align equiv.embedding_congr_symm Equiv.embeddingCongr_symm

/- warning: equiv.embedding_congr_apply_trans -> Equiv.embeddingCongr_apply_trans is a dubious translation:
lean 3 declaration is
  forall {α₁ : Sort.{u1}} {β₁ : Sort.{u2}} {γ₁ : Sort.{u3}} {α₂ : Sort.{u4}} {β₂ : Sort.{u5}} {γ₂ : Sort.{u6}} (ea : Equiv.{u1, u4} α₁ α₂) (eb : Equiv.{u2, u5} β₁ β₂) (ec : Equiv.{u3, u6} γ₁ γ₂) (f : Function.Embedding.{u1, u2} α₁ β₁) (g : Function.Embedding.{u2, u3} β₁ γ₁), Eq.{max 1 (imax u4 u6)} (Function.Embedding.{u4, u6} α₂ γ₂) (coeFn.{max 1 (max (max 1 (imax u1 u3)) 1 (imax u4 u6)) (max 1 (imax u4 u6)) 1 (imax u1 u3), max (max 1 (imax u1 u3)) 1 (imax u4 u6)} (Equiv.{max 1 (imax u1 u3), max 1 (imax u4 u6)} (Function.Embedding.{u1, u3} α₁ γ₁) (Function.Embedding.{u4, u6} α₂ γ₂)) (fun (_x : Equiv.{max 1 (imax u1 u3), max 1 (imax u4 u6)} (Function.Embedding.{u1, u3} α₁ γ₁) (Function.Embedding.{u4, u6} α₂ γ₂)) => (Function.Embedding.{u1, u3} α₁ γ₁) -> (Function.Embedding.{u4, u6} α₂ γ₂)) (Equiv.hasCoeToFun.{max 1 (imax u1 u3), max 1 (imax u4 u6)} (Function.Embedding.{u1, u3} α₁ γ₁) (Function.Embedding.{u4, u6} α₂ γ₂)) (Equiv.embeddingCongr.{u1, u4, u3, u6} α₁ α₂ γ₁ γ₂ ea ec) (Function.Embedding.trans.{u1, u2, u3} α₁ β₁ γ₁ f g)) (Function.Embedding.trans.{u4, u5, u6} α₂ β₂ γ₂ (coeFn.{max 1 (max (max 1 (imax u1 u2)) 1 (imax u4 u5)) (max 1 (imax u4 u5)) 1 (imax u1 u2), max (max 1 (imax u1 u2)) 1 (imax u4 u5)} (Equiv.{max 1 (imax u1 u2), max 1 (imax u4 u5)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u4, u5} α₂ β₂)) (fun (_x : Equiv.{max 1 (imax u1 u2), max 1 (imax u4 u5)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u4, u5} α₂ β₂)) => (Function.Embedding.{u1, u2} α₁ β₁) -> (Function.Embedding.{u4, u5} α₂ β₂)) (Equiv.hasCoeToFun.{max 1 (imax u1 u2), max 1 (imax u4 u5)} (Function.Embedding.{u1, u2} α₁ β₁) (Function.Embedding.{u4, u5} α₂ β₂)) (Equiv.embeddingCongr.{u1, u4, u2, u5} α₁ α₂ β₁ β₂ ea eb) f) (coeFn.{max 1 (max (max 1 (imax u2 u3)) 1 (imax u5 u6)) (max 1 (imax u5 u6)) 1 (imax u2 u3), max (max 1 (imax u2 u3)) 1 (imax u5 u6)} (Equiv.{max 1 (imax u2 u3), max 1 (imax u5 u6)} (Function.Embedding.{u2, u3} β₁ γ₁) (Function.Embedding.{u5, u6} β₂ γ₂)) (fun (_x : Equiv.{max 1 (imax u2 u3), max 1 (imax u5 u6)} (Function.Embedding.{u2, u3} β₁ γ₁) (Function.Embedding.{u5, u6} β₂ γ₂)) => (Function.Embedding.{u2, u3} β₁ γ₁) -> (Function.Embedding.{u5, u6} β₂ γ₂)) (Equiv.hasCoeToFun.{max 1 (imax u2 u3), max 1 (imax u5 u6)} (Function.Embedding.{u2, u3} β₁ γ₁) (Function.Embedding.{u5, u6} β₂ γ₂)) (Equiv.embeddingCongr.{u2, u5, u3, u6} β₁ β₂ γ₁ γ₂ eb ec) g))
but is expected to have type
  forall {α₁ : Sort.{u6}} {β₁ : Sort.{u5}} {γ₁ : Sort.{u4}} {α₂ : Sort.{u3}} {β₂ : Sort.{u2}} {γ₂ : Sort.{u1}} (ea : Equiv.{u6, u3} α₁ α₂) (eb : Equiv.{u5, u2} β₁ β₂) (ec : Equiv.{u4, u1} γ₁ γ₂) (f : Function.Embedding.{u6, u5} α₁ β₁) (g : Function.Embedding.{u5, u4} β₁ γ₁), Eq.{max (max 1 u3) u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Function.Embedding.{u6, u4} α₁ γ₁) => Function.Embedding.{u3, u1} α₂ γ₂) (Function.Embedding.trans.{u6, u5, u4} α₁ β₁ γ₁ f g)) (FunLike.coe.{max (max (max (max 1 u6) u4) u3) u1, max (max 1 u6) u4, max (max 1 u3) u1} (Equiv.{max (max 1 u4) u6, max (max 1 u1) u3} (Function.Embedding.{u6, u4} α₁ γ₁) (Function.Embedding.{u3, u1} α₂ γ₂)) (Function.Embedding.{u6, u4} α₁ γ₁) (fun (_x : Function.Embedding.{u6, u4} α₁ γ₁) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Function.Embedding.{u6, u4} α₁ γ₁) => Function.Embedding.{u3, u1} α₂ γ₂) _x) (Equiv.instFunLikeEquiv.{max (max 1 u6) u4, max (max 1 u3) u1} (Function.Embedding.{u6, u4} α₁ γ₁) (Function.Embedding.{u3, u1} α₂ γ₂)) (Equiv.embeddingCongr.{u6, u3, u4, u1} α₁ α₂ γ₁ γ₂ ea ec) (Function.Embedding.trans.{u6, u5, u4} α₁ β₁ γ₁ f g)) (Function.Embedding.trans.{u3, u2, u1} α₂ β₂ γ₂ (FunLike.coe.{max (max (max (max 1 u6) u5) u3) u2, max (max 1 u6) u5, max (max 1 u3) u2} (Equiv.{max (max 1 u5) u6, max (max 1 u2) u3} (Function.Embedding.{u6, u5} α₁ β₁) (Function.Embedding.{u3, u2} α₂ β₂)) (Function.Embedding.{u6, u5} α₁ β₁) (fun (_x : Function.Embedding.{u6, u5} α₁ β₁) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Function.Embedding.{u6, u5} α₁ β₁) => Function.Embedding.{u3, u2} α₂ β₂) _x) (Equiv.instFunLikeEquiv.{max (max 1 u6) u5, max (max 1 u3) u2} (Function.Embedding.{u6, u5} α₁ β₁) (Function.Embedding.{u3, u2} α₂ β₂)) (Equiv.embeddingCongr.{u6, u3, u5, u2} α₁ α₂ β₁ β₂ ea eb) f) (FunLike.coe.{max (max (max (max 1 u5) u4) u2) u1, max (max 1 u5) u4, max (max 1 u2) u1} (Equiv.{max (max 1 u4) u5, max (max 1 u1) u2} (Function.Embedding.{u5, u4} β₁ γ₁) (Function.Embedding.{u2, u1} β₂ γ₂)) (Function.Embedding.{u5, u4} β₁ γ₁) (fun (_x : Function.Embedding.{u5, u4} β₁ γ₁) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Function.Embedding.{u5, u4} β₁ γ₁) => Function.Embedding.{u2, u1} β₂ γ₂) _x) (Equiv.instFunLikeEquiv.{max (max 1 u5) u4, max (max 1 u2) u1} (Function.Embedding.{u5, u4} β₁ γ₁) (Function.Embedding.{u2, u1} β₂ γ₂)) (Equiv.embeddingCongr.{u5, u2, u4, u1} β₁ β₂ γ₁ γ₂ eb ec) g))
Case conversion may be inaccurate. Consider using '#align equiv.embedding_congr_apply_trans Equiv.embeddingCongr_apply_transₓ'. -/
theorem embeddingCongr_apply_trans {α₁ β₁ γ₁ α₂ β₂ γ₂ : Sort _} (ea : α₁ ≃ α₂) (eb : β₁ ≃ β₂)
    (ec : γ₁ ≃ γ₂) (f : α₁ ↪ β₁) (g : β₁ ↪ γ₁) :
    Equiv.embeddingCongr ea ec (f.trans g) =
      (Equiv.embeddingCongr ea eb f).trans (Equiv.embeddingCongr eb ec g) :=
  by
  ext
  simp
#align equiv.embedding_congr_apply_trans Equiv.embeddingCongr_apply_trans

#print Equiv.refl_toEmbedding /-
@[simp]
theorem refl_toEmbedding {α : Type _} : (Equiv.refl α).toEmbedding = Function.Embedding.refl α :=
  rfl
#align equiv.refl_to_embedding Equiv.refl_toEmbedding
-/

/- warning: equiv.trans_to_embedding -> Equiv.trans_toEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (e : Equiv.{succ u1, succ u2} α β) (f : Equiv.{succ u2, succ u3} β γ), Eq.{max 1 (succ u1) (succ u3)} (Function.Embedding.{succ u1, succ u3} α γ) (Equiv.toEmbedding.{succ u1, succ u3} α γ (Equiv.trans.{succ u1, succ u2, succ u3} α β γ e f)) (Function.Embedding.trans.{succ u1, succ u2, succ u3} α β γ (Equiv.toEmbedding.{succ u1, succ u2} α β e) (Equiv.toEmbedding.{succ u2, succ u3} β γ f))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (e : Equiv.{succ u3, succ u2} α β) (f : Equiv.{succ u2, succ u1} β γ), Eq.{max (succ u3) (succ u1)} (Function.Embedding.{succ u3, succ u1} α γ) (Equiv.toEmbedding.{succ u3, succ u1} α γ (Equiv.trans.{succ u3, succ u2, succ u1} α β γ e f)) (Function.Embedding.trans.{succ u3, succ u2, succ u1} α β γ (Equiv.toEmbedding.{succ u3, succ u2} α β e) (Equiv.toEmbedding.{succ u2, succ u1} β γ f))
Case conversion may be inaccurate. Consider using '#align equiv.trans_to_embedding Equiv.trans_toEmbeddingₓ'. -/
@[simp]
theorem trans_toEmbedding {α β γ : Type _} (e : α ≃ β) (f : β ≃ γ) :
    (e.trans f).toEmbedding = e.toEmbedding.trans f.toEmbedding :=
  rfl
#align equiv.trans_to_embedding Equiv.trans_toEmbedding

end Equiv

section Subtype

variable {α : Type _}

#print subtypeOrLeftEmbedding /-
/-- A subtype `{x // p x ∨ q x}` over a disjunction of `p q : α → Prop` can be injectively split
into a sum of subtypes `{x // p x} ⊕ {x // q x}` such that `¬ p x` is sent to the right. -/
def subtypeOrLeftEmbedding (p q : α → Prop) [DecidablePred p] :
    { x // p x ∨ q x } ↪ Sum { x // p x } { x // q x } :=
  ⟨fun x => if h : p x then Sum.inl ⟨x, h⟩ else Sum.inr ⟨x, x.Prop.resolve_left h⟩,
    by
    intro x y
    dsimp only
    split_ifs <;> simp [Subtype.ext_iff]⟩
#align subtype_or_left_embedding subtypeOrLeftEmbedding
-/

#print subtypeOrLeftEmbedding_apply_left /-
theorem subtypeOrLeftEmbedding_apply_left {p q : α → Prop} [DecidablePred p]
    (x : { x // p x ∨ q x }) (hx : p x) : subtypeOrLeftEmbedding p q x = Sum.inl ⟨x, hx⟩ :=
  dif_pos hx
#align subtype_or_left_embedding_apply_left subtypeOrLeftEmbedding_apply_left
-/

#print subtypeOrLeftEmbedding_apply_right /-
theorem subtypeOrLeftEmbedding_apply_right {p q : α → Prop} [DecidablePred p]
    (x : { x // p x ∨ q x }) (hx : ¬p x) :
    subtypeOrLeftEmbedding p q x = Sum.inr ⟨x, x.Prop.resolve_left hx⟩ :=
  dif_neg hx
#align subtype_or_left_embedding_apply_right subtypeOrLeftEmbedding_apply_right
-/

#print Subtype.impEmbedding /-
/-- A subtype `{x // p x}` can be injectively sent to into a subtype `{x // q x}`,
if `p x → q x` for all `x : α`. -/
@[simps]
def Subtype.impEmbedding (p q : α → Prop) (h : ∀ x, p x → q x) : { x // p x } ↪ { x // q x } :=
  ⟨fun x => ⟨x, h x x.Prop⟩, fun x y => by simp [Subtype.ext_iff]⟩
#align subtype.imp_embedding Subtype.impEmbedding
-/

end Subtype


/-
Copyright (c) 2021 Ashvni Narayanan. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ashvni Narayanan
-/
import number_theory.padics.padic_integers
import topology.continuous_function.compact
import topology.continuous_function.locally_constant

/-!
# p-adic measure theory

This file defines p-adic distributions and measure on the space of locally constant functions
from a profinite space to a normed ring. We then use the measure to construct the p-adic integral.
In fact, we prove that this integral is linearly and continuously extended on `C(X, A`.

## Main definitions and theorems
 * `exists_finset_clopen`
 * `measures`
 * `integral`

## Implementation notes
TODO (optional)

## References
Introduction to Cyclotomic Fields, Washington (Chapter 12)

## Tags
p-adic L-function, p-adic integral, measure, totally disconnected, locally constant, compact,
Hausdorff

###############
Note (jmc): this file was copied with permission of Ashvni Narayan from
https://github.com/leanprover-community/mathlib/blob/f2fd1fb4507431cf2f2a873db4b97d360633fb69/src/number_theory/L_functions.lean#L453
and subsequently mildly modified.
###############

-/

variables (X : Type*) [topological_space X]
variables (A : Type*) [normed_group A]

variable {X}
variables [compact_space X]

namespace set
lemma diff_inter_eq_empty {α : Type*} (a : set α) {b c : set α} (h : c ⊆ b) :
  a \ b ∩ c = ∅ :=
begin
  ext x,
  simp only [and_imp, mem_empty_eq, mem_inter_eq, not_and, mem_diff, iff_false],
  intro _,
  exact mt (@h x),
end

lemma diff_inter_mem_sUnion {α : Type*} {s : set (set α)} (a y : set α) (h : y ∈ s) :
  (a \ ⋃₀ s) ∩ y = ∅ :=
diff_inter_eq_empty a $ subset_sUnion_of_mem h

end set

namespace is_clopen

lemma is_closed_sUnion {H : Type*} [topological_space H]
  {s : finset(set H)} (hs : ∀ x ∈ s, is_closed x) :
  is_closed ⋃₀ (s : set(set H)) :=
by { simpa only [← is_open_compl_iff, set.compl_sUnion, set.sInter_image] using is_open_bInter
    (finset.finite_to_set s) (λ i hi, _), apply is_open_compl_iff.2 (hs i hi), }

lemma is_clopen_sUnion {H : Type*} [topological_space H]
  (s : finset(set H)) (hs : ∀ x ∈ s, is_clopen x) :
  is_clopen ⋃₀ (s : set(set H)) :=
⟨is_open_sUnion (λ t ht, (hs t ht).1), is_closed_sUnion (λ t ht, (hs t ht).2) ⟩

/-- The finite union of clopen sets is clopen. -/
lemma clopen_finite_Union {H : Type*} [topological_space H]
  (s : finset(set H)) (hs : ∀ x ∈ s, is_clopen x) :
  is_clopen ⋃₀ (s : set(set H)) :=
  by { rw set.sUnion_eq_bUnion, apply is_clopen_bUnion hs, }

/-- Given a finite set of clopens, one can find a finite disjoint set of clopens contained in
  it. -/
lemma clopen_Union_disjoint {H : Type*} [topological_space H]
  (s : finset(set H)) (hs : ∀ x ∈ s, is_clopen x) :
  ∃ (t : finset (set H)),
  (∀ (x ∈ (t : set (set H))), is_clopen x) ∧
  ⋃₀ (s : set(set H)) = ⋃₀ (t : set(set H)) ∧
  (∀ (x : set H) (hx : x ∈ t), ∃ z ∈ s, x ⊆ z) ∧
  ∀ (x y : set H) (hx : x ∈ t) (hy : y ∈ t) (h : x ≠ y), x ∩ y = ∅ :=
begin
  classical,
  apply finset.induction_on' s,
  { use ∅, simp only [finset.not_mem_empty, forall_false_left, set.mem_empty_eq, forall_const,
      finset.coe_empty, eq_self_iff_true, and_self], },
  { rintros a S h's hS aS ⟨t, clo, union, sub, disj⟩,
    set b := a \ ⋃₀ S with hb,
    refine ⟨insert b t, _, _, ⟨λ x hx, _, λ x y hx hy ne, _⟩⟩,
    { rintros x hx,
      simp only [finset.coe_insert, set.mem_insert_iff, finset.mem_coe] at hx,
      cases hx,
      { rw hx, apply is_clopen.diff (hs a h's) (clopen_finite_Union _ (λ y hy, (hs y (hS hy)))), },
      { apply clo x hx, }, },
    { simp only [finset.coe_insert, set.sUnion_insert], rw [←union, set.diff_union_self], },
    { simp only [finset.mem_insert] at hx, cases hx,
      { use a, rw hx, simp only [true_and, true_or, eq_self_iff_true, finset.mem_insert],
        apply set.diff_subset, },
      { rcases sub x hx with ⟨z, hz, xz⟩, refine ⟨z, _, xz⟩,
        rw finset.mem_insert, right, assumption, }, },
    { rw finset.mem_insert at hx, rw finset.mem_insert at hy,
      have : ∀ y ∈ t, b ∩ y = ∅,
      { rintros y hy, rw [hb, union], apply set.diff_inter_mem_sUnion, assumption, },
      cases hx,
      { cases hy,
        { exfalso, apply ne, rw [hx, hy], },
        { rw hx, apply this y hy, }, },
      { cases hy,
        { rw set.inter_comm, rw hy, apply this x hx, },
        { apply disj x y hx hy ne, }, }, }, },
end

end is_clopen

namespace locally_constant.density

variables (ε : ℝ)

/-- Takes an element of `A` to an `ε/4`-ball centered around it. -/
abbreviation h {A : Type*} [normed_group A] : A → set A :=
  λ (x : A), metric.ball x (ε / 4)

/-- The set of (ε/4)-balls. -/
abbreviation S {A : Type*} [normed_group A] : set (set A) := set.range (h ε)

variables {A} (f : C(X, A))

/-- Preimage of (ε/4)-balls. -/
abbreviation B : set(set X) := { j : set X | ∃ (U ∈ ((S ε) : set(set A))), j = f ⁻¹' U }

lemma opens {j : set X} (hj : j ∈ (B ε f)) : is_open j :=
begin
  rcases hj with ⟨hj_w, ⟨hj_h_w_w, rfl⟩, rfl⟩,
  exact continuous.is_open_preimage f.2 _ (metric.is_open_ball),
end

variable [fact (0 < ε)]
/-- `X` is covered by a union of preimage of finitely many elements of `S` under `f` -/
lemma exists_finset_univ_sub : ∃ (t : finset (set A)), set.univ ⊆ ⨆ (i : set A) (H : i ∈ t)
  (H : i ∈ ((S ε) : set(set A))), f ⁻¹' i :=
begin
  have g : (⋃₀ S ε) = (set.univ : set A),
  { rw set.sUnion_eq_univ_iff, rintros, refine ⟨metric.ball a (ε/4), _, _⟩,
    { simp only [set.mem_range, exists_apply_eq_apply], },
    { simp only [metric.mem_ball, dist_self],
      refine div_pos (fact.out _) zero_lt_four, }, },
  have g' : set.preimage f (⋃₀ S ε) = set.univ,
  { rw g, exact set.preimage_univ, },
  rw [set.preimage_sUnion, set.subset.antisymm_iff] at g',
  refine is_compact.elim_finite_subcover compact_univ _ (λ i, is_open_Union
    (λ hi, continuous.is_open_preimage (continuous_map.continuous f) i _)) g'.2,
  cases hi with y hy, rw [←hy], refine @metric.is_open_ball A _ y (ε/4),
end

/-- Choosing a finset as given in `exists_finset_univ_sub` -/
noncomputable abbreviation t : finset (set A) := classical.some (exists_finset_univ_sub ε f)

lemma exists_finset_univ_sub_prop : set.univ ⊆ ⨆ (i : set A) (H : i ∈ t ε f)
  (H : i ∈ ((S ε) : set(set A))), f ⁻¹' i := classical.some_spec (exists_finset_univ_sub ε f)

/-- If there is a finite set of sets from `S` whose preimage forms a cover for `X`,
  then the union of the preimages of all the sets from `S` also forms a cover. -/
lemma sUnion_sub_of_finset_sub : set.univ ⊆ set.sUnion (B ε f) :=
begin
  rintros x hx,
  obtain ⟨-, ⟨j, rfl⟩, -, ⟨hj, rfl⟩, -, ⟨⟨a, jS⟩, rfl⟩, fj⟩ := (exists_finset_univ_sub_prop ε f) hx,
  exact ⟨f⁻¹' j, ⟨j, ⟨_, jS⟩, rfl⟩, fj⟩,
end

variables [t2_space X] [totally_disconnected_space X]

/-- If there is a finite set of sets from `S` whose preimage forms a cover for `X`,
  then there is a cover of `X` by clopen sets, with the image of each set being
  contained in an element of `S`. -/
def set_clopen : set (set X) := {j : set X | ∃ (U : set X) (hU : U ∈ (B ε f)),
    j ∈ classical.some (topological_space.is_topological_basis.open_eq_sUnion
    (@loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _) (opens ε f hU))}

lemma mem_set_clopen {x : set X} : x ∈ (set_clopen ε f) ↔ ∃ (U : set X) (hU : U ∈ (B ε f)),
    x ∈ classical.some (topological_space.is_topological_basis.open_eq_sUnion
    (@loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _) (opens ε f hU)) := iff.rfl

/-- Elements of `set_clopen` are clopen. -/
lemma set_clopen_sub_clopen_set : (set_clopen ε f) ⊆ {s : set X | is_clopen s} :=
begin
  intros j hj,
  obtain ⟨W, hW, hj⟩ := (mem_set_clopen ε f).1 hj,
  obtain ⟨H, -⟩ := classical.some_spec (topological_space.is_topological_basis.open_eq_sUnion
    (@loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _) (opens ε f hW)),
  exact H hj,
end

/-- `set_clopen` covers X. -/
lemma univ_sub_sUnion_set_clopen : set.univ ⊆ ⋃₀ (set_clopen ε f) :=
begin
  rintros x hx, rw set.mem_sUnion,
  have f' := @loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _,
  have sUnion_sub_of_finset_sub := sUnion_sub_of_finset_sub ε f,
-- writing `f⁻¹' U` as a union of basis elements (clopen sets)
  conv at sUnion_sub_of_finset_sub { congr, skip, rw set.sUnion_eq_Union, congr, funext,
    apply_congr classical.some_spec (classical.some_spec
    (topological_space.is_topological_basis.open_eq_sUnion f' (opens ε f i.prop))), },
  rw set.Union at sUnion_sub_of_finset_sub,
  have g3 := sUnion_sub_of_finset_sub hx,
  simp only [exists_prop, set.mem_Union, set.mem_range, set_coe.exists, exists_exists_eq_and,
    set.supr_eq_Union, set.mem_set_of_eq, subtype.coe_mk] at g3,
  rcases g3 with ⟨U, hU, a, ha, xa⟩,
  refine ⟨a, _, xa⟩,
  rw mem_set_clopen,
  simp only [exists_prop, set.mem_range, exists_exists_eq_and, set.mem_set_of_eq],
  refine ⟨U, hU, ha⟩,
end

/-- The image of each element of `set_clopen` is contained in an element of `S`. -/
lemma exists_B_of_mem_clopen {x : set X} (hx : x ∈ set_clopen ε f) :
  ∃ (U : set X) (H : U ∈ B ε f), x ⊆ U :=
begin
  rcases hx with ⟨U, hU, xU⟩, refine ⟨U, hU, _⟩,
  obtain ⟨H, H1⟩ := classical.some_spec
    (topological_space.is_topological_basis.open_eq_sUnion
    (@loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _) (opens ε f hU)),
  rw H1, intros u hu, simp only [exists_prop, set.mem_set_of_eq],
  refine ⟨x, _, hu⟩,
  convert xU,
  ext, simp only [exists_prop, iff_self],
end

/-- Every element of `set_clopen` is open. -/
lemma mem_set_clopen_is_open (i : (set_clopen ε f)) : is_open (i : set X) :=
 topological_space.is_topological_basis.is_open (@loc_compact_Haus_tot_disc_of_zero_dim X _ _ _ _)
  ((set_clopen_sub_clopen_set ε f) i.2)

/-- A restatement of `univ_sub_sUnion_set_clopen`. -/
lemma cover : (set.univ : set X) ⊆ ⋃ (i : (set_clopen ε f)), ↑i :=
by { convert univ_sub_sUnion_set_clopen ε f, rw set.sUnion_eq_Union, }

/-- Obtain a finite subcover of `set_clopen` using the compactness of `X`. -/
noncomputable abbreviation s' := classical.some (is_compact.elim_finite_subcover
  (@compact_univ X _ _) _ (mem_set_clopen_is_open ε f) (cover ε f))

/-- Coercing a subset of `set_clopen` in `s'` to `set X`. -/
abbreviation s1 := λ (x : s' ε f), (x.1 : set X)

/-- The range of `s1` is finite. -/
lemma fin : (set.range (s1 ε f)).finite :=
by { apply set.finite_range _, exact plift.fintype (s' ε f), }

/-- Any element in the range of `s1` is clopen. -/
lemma is_clopen_x {x : set X} (hx : x ∈ (fin ε f).to_finset) : is_clopen x :=
begin
  simp only [set.mem_range, set_coe.exists, set.finite.mem_to_finset, finset.mem_coe] at hx,
  rcases hx with ⟨⟨⟨v, hv⟩, hw⟩, hU⟩,
  convert (set_clopen_sub_clopen_set ε f) hv,
  rw ←hU,
  delta s1,
  simp,
end

/-- If there is a finite set of sets from `S` whose preimage forms a cover for `X`,
  then there is a finset of `sets X` containing clopen sets, with the image of each set being
  contained in an element of `S`. We use `s'` to get a finite disjoint clopen cover of `X`;
  note : it is not a partition -/
noncomputable def finset_clopen : finset (set X) :=
  classical.some (is_clopen.clopen_Union_disjoint
    (set.finite.to_finset (fin ε f)) (λ x hx, (is_clopen_x ε f hx)))

/-- Elements of `finset_clopen` are clopen. -/
lemma finset_clopen_is_clopen {x : set X} (hx : x ∈ finset_clopen ε f) : is_clopen x :=
  (classical.some_spec (is_clopen.clopen_Union_disjoint (set.finite.to_finset (fin ε f))
    (λ x hx, (is_clopen_x ε f hx)))).1 x hx

/-- The image of every element of `finset_clopen` is contained in some element of `S`. -/
lemma exists_sub_S {x : set X} (hx : x ∈ finset_clopen ε f) :
  ∃ U ∈ ((S ε) : set(set A)), (set.image f x : set A) ⊆ U :=
begin
  rcases (classical.some_spec (is_clopen.clopen_Union_disjoint
    (set.finite.to_finset (fin ε f)) (λ x hx, (is_clopen_x ε f hx)))).2.2.1 x hx with ⟨z, hz, wz⟩,
  simp only [set.mem_range, set_coe.exists, set.finite.mem_to_finset, finset.mem_coe] at hz,
  -- `z'` is a lift of `x` in `V`
  rcases hz with ⟨⟨⟨z', h1⟩, h2⟩, h3⟩,
  rcases exists_B_of_mem_clopen ε f h1 with ⟨U, BU, xU⟩,
  simp only [exists_prop, exists_exists_eq_and, set.mem_set_of_eq] at BU,
  cases BU with U' h4,
  refine ⟨U', h4.1, _⟩, transitivity (set.image f z),
  { apply set.image_subset _ wz, },
  { simp only [set.image_subset_iff], rw [←h4.2, ←h3],
    delta s1,
    simp only [xU, subtype.coe_mk], },
end

/-- Showing that `finset_clopen` is a disjoint cover of `X`. -/
lemma finset_clopen_prop (a : X) : ∃! (b ∈ finset_clopen ε f), a ∈ b :=
begin
-- proving that every element `a : X` is contained in a unique element `j` of `s`
  obtain ⟨j, hj, aj⟩ : ∃ j ∈ finset_clopen ε f, a ∈ j,
  { -- `s'` covers `X`
    have ha := classical.some_spec (is_compact.elim_finite_subcover
      (@compact_univ X _ _) _ (mem_set_clopen_is_open ε f) (cover ε f)) (set.mem_univ a),
    have hs := (classical.some_spec (is_clopen.clopen_Union_disjoint
      (set.finite.to_finset (fin ε f)) (λ x hx, (is_clopen_x ε f hx)))).2.1,
    delta s1 at hs,
    suffices : a ∈ ⋃₀ (finset_clopen ε f : set(set X)),
    { simp only [exists_prop, set.mem_set_of_eq, finset.mem_coe] at this,
      cases this with j hj, refine ⟨j, hj.1, hj.2⟩, },
    { rw finset_clopen,
      rw ←hs,
      simp only [set.mem_Union, set.finite.coe_to_finset, subtype.val_eq_coe, set.sUnion_range],
      simp only [exists_prop, set.mem_Union, set_coe.exists, exists_and_distrib_right,
        subtype.coe_mk] at ha,
      -- have the element `U` of `V`, now translate it to `s`
      rcases ha with ⟨U, ⟨hU, s'U⟩, aU⟩,
      delta s',
      refine ⟨⟨⟨U, hU⟩, s'U⟩, aU⟩, }, },
  refine ⟨j, _, λ y hy, _⟩,
  { -- existence
    simp only [exists_prop, set.image_subset_iff, set.mem_range, exists_exists_eq_and,
      exists_unique_iff_exists],
    refine ⟨hj, aj⟩, },
  { -- uniqueness, coming from the disjointness of the clopen cover, `disj`
    simp only [exists_prop, exists_unique_iff_exists] at hy,
    cases hy with h1 h2,
    have disj := (classical.some_spec (is_clopen.clopen_Union_disjoint
      (set.finite.to_finset (fin ε f)) (λ x hx, (is_clopen_x ε f hx)))).2.2.2 j y hj h1,
    by_cases h : j = y,
    { rw h.symm, },
    { exfalso, specialize disj h, rw ←set.mem_empty_eq, rw ←disj,
      apply set.mem_inter aj _,
      simp only [and_true, implies_true_iff, eq_iff_true_of_subsingleton] at h2,
      exact h2, }, },
end

/-- Takes a nonempty `s` in `finset_clopen` and returns an element of it. -/
noncomputable abbreviation c' := λ (s : set X) (H : s ∈ (finset_clopen ε f) ∧ nonempty s),
  classical.choice (H.2)

/-- Any `x` in `X` must belong to a unique `s` in `finset_clopen`. `c2` takes `x` to the image of
  any element of `s` under `f`, which is the same `f x`. -/
noncomputable abbreviation c2 (f : C(X, A)) : X → A :=
λ x, f (c' ε f (classical.some (exists_of_exists_unique (finset_clopen_prop ε f x)) )
begin
  have := (exists_prop.1 (exists_of_exists_unique (classical.some_spec
    (exists_of_exists_unique (finset_clopen_prop ε f x))))),
  split,
  refine finset.mem_coe.1 (this).1,
  apply set.nonempty.to_subtype,
  refine ⟨x, this.2⟩,
end).

/-- Any element of `finset_clopen` is open. -/
lemma mem_finset_clopen_is_open {U : set X} (hU : U ∈ finset_clopen ε f) : is_open U :=
by { rw finset_clopen at hU, apply (finset_clopen_is_clopen ε f hU).1, }

/-- An equivalent version of `disj`. -/
lemma mem_finset_clopen_unique' {U V : set X} {y : X}
  (hU : U ∈ finset_clopen ε f) (hUy : y ∈ U) (hVy : y ∈ V) (hV : V ∈ finset_clopen ε f) : V = U :=
begin
  by_contra,
  have := (classical.some_spec (is_clopen.clopen_Union_disjoint
    (set.finite.to_finset (fin ε f)) (λ x hx, (is_clopen_x ε f hx)))).2.2.2 _ _ hV hU h,
  revert this,
  --change (V ∩ U) ≠ ∅,
  refine set.nonempty.ne_empty ⟨y, set.mem_inter hVy hUy⟩,
end

/-- Given `x` in `X`, there is a unique element `U` of `finset_clopen` such that `x ∈ U`. For any
  `y ∈ U`, `y` is contained in any other element `V` of `finset_clopen` containing `x`. -/
lemma mem_finset_clopen_unique {U V : set X} {x y : X}
  (U_prop : (U ∈ finset_clopen ε f ∧ x ∈ U) ∧ ∀ (y : set X), y ∈ finset_clopen ε f →
    x ∈ y → y = U) (hy : y ∈ U) (hV : V ∈ finset_clopen ε f) : x ∈ V ↔ y ∈ V :=
begin
  obtain ⟨W, hW⟩ := finset_clopen_prop ε f y,
  simp only [and_imp, exists_prop, exists_unique_iff_exists] at hW,
  split; intro h,
  { rw U_prop.2 V hV h, assumption, },
  { rw hW.2 V hV h, rw ←(hW.2 U U_prop.1.1 hy), apply U_prop.1.2, },
end

/-- `c2` is locally constant -/
lemma loc_const : is_locally_constant (c2 ε f) :=
begin
  rw is_locally_constant.iff_exists_open, rintros x,
  obtain ⟨U, hU⟩ := finset_clopen_prop ε f x,
  simp only [and_imp, exists_prop, exists_unique_iff_exists] at hU,
  refine ⟨U, mem_finset_clopen_is_open ε f hU.1.1, hU.1.2, λ x' hx', _⟩,
  delta c2,
  congr',
  swap 4, ext y, revert y, rw ←set.ext_iff, congr, -- is there a better way to do this?
  any_goals
  { ext y, simp only [exists_prop, and.congr_right_iff, exists_unique_iff_exists],
    intro hy, symmetry, apply mem_finset_clopen_unique ε f hU hx' hy, },
end

/-- Given an `f ∈ C(X, A)` and an `ε > 0`, one can find a locally constant function `b` which is in
  an ε-ball with center `f`, `b` is precisely `c2`. -/
theorem loc_const_dense' : ∃ (b : C(X, A))
  (H : b ∈ set.range (@locally_constant.to_continuous_map X A _ _)),
  dist f b < ε := ⟨@locally_constant.to_continuous_map X A _ _ ⟨c2 ε f, loc_const ε f⟩, ⟨⟨c2 ε f, loc_const ε f⟩, rfl⟩,
  gt_of_gt_of_ge (half_lt_self (fact.out _))
begin
-- showing that the distance between `f` and `c2` is less than or equal to `ε/2`
  rw [dist_eq_norm, continuous_map.norm_eq_supr_norm],
  -- empty type is special case
  cases is_empty_or_nonempty X with hempty hnonempty,
  { change _ ≥ dite _ _ _,
    split_ifs with h,
    { rcases h with ⟨⟨_, x, _⟩, _⟩,
      exact (@is_empty.false _ hempty x).elim },
    exact le_of_lt (half_pos (fact.out _)) },
-- writing the distance in terms of the sup norm
  refine cSup_le _ (λ m hm, _),
  { rw set.range_nonempty_iff_nonempty, assumption, }, -- this is where `nonempty X` is needed
  { cases hm with y hy,
    simp only [continuous_map.coe_sub, locally_constant.coe_mk,
      locally_constant.to_continuous_map_linear_map_apply, pi.sub_apply,
      locally_constant.coe_continuous_map] at hy,
    rw ←hy,
    -- reduced to proving ∥f(y) - c2(y)∥ ≤ ε/2
    obtain ⟨w, wT, hw⟩ := finset_clopen_prop ε f y,
    -- `w` is the unique element of `finset_clopen` to which `y` belongs
    simp only [exists_prop, exists_unique_iff_exists] at wT,
    simp only [and_imp, exists_prop, exists_unique_iff_exists] at hw,
    have : c2 ε f y = f (c' ε f w ⟨wT.1, ⟨⟨y, wT.2⟩⟩⟩),
    -- showing that `w` is the same as the `classical.some _` used in `c2`
    { delta c2, congr',
      any_goals
      { have := classical.some_spec (exists_of_exists_unique (finset_clopen_prop ε f y)),
        simp only [exists_prop, exists_unique_iff_exists] at *,
        apply hw _ (this.1) (this.2), }, },
    dsimp,
    rw this,
    obtain ⟨U, hU, wU⟩ := exists_sub_S ε f wT.1,
    -- `U` is a set of `A` which is an element of `S` and contains `f(w)`
    cases hU with z hz,
    -- `U` is the `ε/4`-ball centered at `z`
    have mem_U : f (c' ε f w ⟨wT.1, ⟨⟨y, wT.2⟩⟩⟩) ∈ U :=
      wU ⟨(c' ε f w ⟨wT.1, ⟨⟨y, wT.2⟩⟩⟩), subtype.coe_prop _, rfl⟩,
    have tS : f y ∈ U := wU ⟨y, wT.2, rfl⟩,
    rw [hz.symm, mem_ball_iff_norm] at *,
    conv_lhs { rw sub_eq_sub_add_sub _ _ z, },
    -- unfolding everything in terms of `z`, and then using `mem_U` and `tS`
    have : ε/2 = ε/4 + ε/4, { rw div_add_div_same, linarith, },
    rw this, apply norm_add_le_of_le (le_of_lt _) (le_of_lt tS),
    rw ←norm_neg _, simp only [mem_U, neg_sub], },
end ⟩

variable (X)
/-- The locally constant functions from `X` to `A` (viewed as a subset of C(X, A)) are dense
  in C(X, A). -/
theorem loc_const_dense : dense (set.range (@locally_constant.to_continuous_map X A _ _)) :=
  λ f, begin
  rw metric.mem_closure_iff,
  rintros ε hε,
  haveI : fact (0 < ε) := fact.mk hε,
-- we have all the ingredients from `loc_const_dense'`, only need `exists_finset_univ_sub_prop`
  apply loc_const_dense' ε f,
end

end locally_constant.density