import Smt

theorem neq_zero : ∀ (x : Nat), x ≠ 0 := by
  smt
  admit

theorem succ_neq_zero : ∀ (x : Nat), x + 1 ≠ 0 := by
  smt
  admit


import pq_like_equalizer_util

universe u

section pq_like_equalizer_split

-- Show that Gr Q' ~= ker phi x Gr Q

variables {Q : Type u} [power_quandle Q]

def eq_left_split_fun : (pq_group (eta_equalizer Q)) → (eta_equalizer_iso_forward : pq_group (eta_equalizer Q) →* pq_group Q).ker :=
begin
  intro x,
  fconstructor,
  exact x * ((eta_equalizer_iso_backward) (eta_equalizer_iso_forward x))⁻¹,
  refine eta_equalizer_iso_forward.mem_ker.mpr _,
  simp only [monoid_hom.map_mul, monoid_hom.map_inv],
  rw eta_equalizer_iso_forward_of_backward,
  simp only [mul_right_inv],
end

lemma eq_left_split_fun_def (x : pq_group (eta_equalizer Q)) : x * ((eta_equalizer_iso_backward) (eta_equalizer_iso_forward x))⁻¹  = ↑(eq_left_split_fun x) := rfl

lemma eq_left_split_coe_commutes (x y : pq_group (eta_equalizer Q)) : x * ↑(eq_left_split_fun y) = ↑(eq_left_split_fun y) * x :=
begin
  rw ←eq_left_split_fun_def,
  revert x y,
  refine pq_group_word_induction _ _,
  {
    intros y,
    simp only [mul_one, one_mul],
  },
  {
    intros x a hx y,
    assoc_rw hx,
    rw ←mul_assoc,
    simp only [mul_left_inj],
    clear hx x _inst,
    sorry,
  },
  /-
  rw ←eq_left_split_fun_def,
  revert y x,
  refine pq_group_word_induction _ _,
  {
    intro y,
    simp only [one_inv, mul_one, one_mul, monoid_hom.map_one],
  },
  {
    intros y a hy x,
    simp only [mul_inv_rev, monoid_hom.map_mul],
    assoc_rw hy,
    assoc_rw hy,
    assoc_rw hy,
    clear _inst,
    suffices : x * of a * (eta_equalizer_iso_backward (eta_equalizer_iso_forward (of a)))⁻¹ =  of a * (eta_equalizer_iso_backward (eta_equalizer_iso_forward (of a)))⁻¹ * x,
    assoc_rw this,
    clear hy y,
    rw eta_equalizer_iso_forward_of,
    cases a with a ha,
    simp only,
    revert a,
    refine pq_group_word_induction _ _,
    {
      intro ha,
      simp only [one_inv, mul_one, monoid_hom.map_one],
      suffices : of (⟨1, ha⟩ : eta_equalizer Q) = 1,
      rw this,
      simp only [mul_one, one_mul],
      simp_rw ←of_one,
      congr,
      rw of_one,
    },
    {
      intros z y hz haz,
      simp only [mul_inv_rev, monoid_hom.map_mul],
      rw eta_equalizer_iso_backward_of,
      sorry,
    },
  },
  -/
end

def eq_left_split_hom : (pq_group (pq_group Q)) →* (counit : pq_group (pq_group Q) →* (pq_group Q)).ker :=
begin
  fconstructor,
  exact left_split_fun,
  {
    unfold left_split_fun,
    simp only [one_inv, mul_one, monoid_hom.map_one],
    refl,
  },
  {
    intros a b,
    unfold left_split_fun,
    ext1,
    simp only [mul_inv_rev, monoid_hom.map_mul, subgroup.coe_mul, subtype.coe_mk],
    rw mul_assoc,
    rw ←mul_assoc b,
    rw left_split_fun_def b,
    rw ←mul_assoc,
    rw left_split_coe_commutes,
    rw left_split_coe_commutes,
    rw mul_assoc,
  },
end

theorem eq_left_split_hom_is_left_split (x : (counit : pq_group (pq_group Q) →* (pq_group Q)).ker) : left_split_hom (↑x) = x :=
begin
  cases x with x hx,
  unfold left_split_hom,
  simp only [monoid_hom.coe_mk, subtype.coe_mk],
  unfold left_split_fun,
  ext1,
  simp only [subtype.coe_mk],
  suffices : counit x = 1,
  rw this,
  simp only [mul_one, monoid_hom.map_one],
  simp only [one_inv, mul_one],
  exact hx,
end

def eq_pq_group_prod_ker_G : pq_group (pq_group Q) ≃* pq_group Q × (counit : pq_group (pq_group Q) →* pq_group Q).ker := {
  to_fun := λ x, ⟨counit x, left_split_hom x⟩,
  inv_fun := λ a, a.2 * (L_of_morph of of_is_pq_morphism a.1),
  left_inv := begin
    intro x,
    simp only,
    unfold left_split_hom,
    simp only [monoid_hom.coe_mk],
    unfold left_split_fun,
    simp only [subgroup.coe_mk, inv_mul_cancel_right],
  end,
  right_inv := begin
    intro x,
    cases x with x1 x2,
    simp only [monoid_hom.map_mul],
    ext1,
    {
      simp only,
      rw counit_L_of,
      simp only [mul_left_eq_self],
      cases x2 with x2 hx2,
      simp only [subtype.coe_mk],
      exact hx2,
    },
    {
      simp only,
      rw left_split_hom_is_left_split,
      simp only [mul_right_eq_self],
      unfold left_split_hom,
      simp only [monoid_hom.coe_mk],
      unfold left_split_fun,
      simp_rw counit_L_of,
      group,
      refl,
    },
  end,
  map_mul' := begin
    intros x y,
    ext1,
    simp only [monoid_hom.map_mul, prod.mk_mul_mk],
    simp only [monoid_hom.map_mul, prod.mk_mul_mk],
  end }

end pq_like_equalizer_split


/-
Copyright (c) 2019 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.category.Cat
import Mathlib.category_theory.groupoid
import Mathlib.PostPort

universes v u

namespace Mathlib

/-!
# Objects of a category up to an isomorphism

`is_isomorphic X Y := nonempty (X ≅ Y)` is an equivalence relation on the objects of a category.
The quotient with respect to this relation defines a functor from our category to `Type`.
-/

namespace category_theory

/-- An object `X` is isomorphic to an object `Y`, if `X ≅ Y` is not empty. -/
def is_isomorphic {C : Type u} [category C] : C → C → Prop := fun (X Y : C) => Nonempty (X ≅ Y)

/-- `is_isomorphic` defines a setoid. -/
def is_isomorphic_setoid (C : Type u) [category C] : setoid C := setoid.mk is_isomorphic sorry

/--
The functor that sends each category to the quotient space of its objects up to an isomorphism.
-/
def isomorphism_classes : Cat ⥤ Type u :=
  functor.mk (fun (C : Cat) => quotient (is_isomorphic_setoid (bundled.α C)))
    fun (C D : Cat) (F : C ⟶ D) => quot.map (functor.obj F) sorry

theorem groupoid.is_isomorphic_iff_nonempty_hom {C : Type u} [groupoid C] {X : C} {Y : C} :
    is_isomorphic X Y ↔ Nonempty (X ⟶ Y) :=
  equiv.nonempty_iff_nonempty (groupoid.iso_equiv_hom X Y)

end Mathlib


/-
Copyright (c) 2015 Nathaniel Thomas. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nathaniel Thomas, Jeremy Avigad, Johannes Hölzl, Mario Carneiro
-/
import algebra.module.linear_map
import data.equiv.module
import group_theory.group_action.sub_mul_action
/-!

# Submodules of a module

In this file we define

* `submodule R M` : a subset of a `module` `M` that contains zero and is closed with respect to
  addition and scalar multiplication.

* `subspace k M` : an abbreviation for `submodule` assuming that `k` is a `field`.

## Tags

submodule, subspace, linear map
-/

open function
open_locale big_operators

universes u'' u' u v w
variables {G : Type u''} {S : Type u'} {R : Type u} {M : Type v} {ι : Type w}

set_option old_structure_cmd true

/-- A submodule of a module is one which is closed under vector operations.
  This is a sufficient condition for the subset of vectors in the submodule
  to themselves form a module. -/
structure submodule (R : Type u) (M : Type v) [semiring R]
  [add_comm_monoid M] [module R M] extends add_submonoid M, sub_mul_action R M : Type v.

/-- Reinterpret a `submodule` as an `add_submonoid`. -/
add_decl_doc submodule.to_add_submonoid

/-- Reinterpret a `submodule` as an `sub_mul_action`. -/
add_decl_doc submodule.to_sub_mul_action

namespace submodule

variables [semiring R] [add_comm_monoid M] [module R M]

instance : set_like (submodule R M) M :=
⟨submodule.carrier, λ p q h, by cases p; cases q; congr'⟩

@[simp] theorem mem_to_add_submonoid (p : submodule R M) (x : M) : x ∈ p.to_add_submonoid ↔ x ∈ p :=
iff.rfl

variables {p q : submodule R M}

@[simp]
lemma mem_mk {S : set M} {x : M} (h₁ h₂ h₃) : x ∈ (⟨S, h₁, h₂, h₃⟩ : submodule R M) ↔ x ∈ S :=
iff.rfl

@[simp] lemma coe_set_mk (S : set M) (h₁ h₂ h₃) :
  ((⟨S, h₁, h₂, h₃⟩ : submodule R M) : set M) = S := rfl

@[simp]
lemma mk_le_mk {S S' : set M} (h₁ h₂ h₃ h₁' h₂' h₃') :
  (⟨S, h₁, h₂, h₃⟩ : submodule R M) ≤ (⟨S', h₁', h₂', h₃'⟩ : submodule R M) ↔ S ⊆ S' := iff.rfl

@[ext] theorem ext (h : ∀ x, x ∈ p ↔ x ∈ q) : p = q := set_like.ext h

/-- Copy of a submodule with a new `carrier` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy (p : submodule R M) (s : set M) (hs : s = ↑p) : submodule R M :=
{ carrier := s,
  zero_mem' := hs.symm ▸ p.zero_mem',
  add_mem' := hs.symm ▸ p.add_mem',
  smul_mem' := hs.symm ▸ p.smul_mem' }

@[simp] lemma coe_copy (S : submodule R M) (s : set M) (hs : s = ↑S) :
  (S.copy s hs : set M) = s := rfl

lemma copy_eq (S : submodule R M) (s : set M) (hs : s = ↑S) : S.copy s hs = S :=
set_like.coe_injective hs

theorem to_add_submonoid_injective :
  injective (to_add_submonoid : submodule R M → add_submonoid M) :=
λ p q h, set_like.ext'_iff.2 (show _, from set_like.ext'_iff.1 h)

@[simp] theorem to_add_submonoid_eq : p.to_add_submonoid = q.to_add_submonoid ↔ p = q :=
to_add_submonoid_injective.eq_iff

@[mono] lemma to_add_submonoid_strict_mono :
  strict_mono (to_add_submonoid : submodule R M → add_submonoid M) := λ _ _, id

@[mono]
lemma to_add_submonoid_mono : monotone (to_add_submonoid : submodule R M → add_submonoid M) :=
to_add_submonoid_strict_mono.monotone

@[simp] theorem coe_to_add_submonoid (p : submodule R M) :
  (p.to_add_submonoid : set M) = p := rfl

theorem to_sub_mul_action_injective :
  injective (to_sub_mul_action : submodule R M → sub_mul_action R M) :=
λ p q h, set_like.ext'_iff.2 (show _, from set_like.ext'_iff.1 h)

@[simp] theorem to_sub_mul_action_eq : p.to_sub_mul_action = q.to_sub_mul_action ↔ p = q :=
to_sub_mul_action_injective.eq_iff

@[mono] lemma to_sub_mul_action_strict_mono :
  strict_mono (to_sub_mul_action : submodule R M → sub_mul_action R M) := λ _ _, id

@[mono]
lemma to_sub_mul_action_mono : monotone (to_sub_mul_action : submodule R M → sub_mul_action R M) :=
to_sub_mul_action_strict_mono.monotone

@[simp] theorem coe_to_sub_mul_action (p : submodule R M) :
  (p.to_sub_mul_action : set M) = p := rfl

end submodule

namespace submodule

section add_comm_monoid

variables [semiring R] [add_comm_monoid M]

-- We can infer the module structure implicitly from the bundled submodule,
-- rather than via typeclass resolution.
variables {module_M : module R M}
variables {p q : submodule R M}
variables {r : R} {x y : M}

variables (p)
@[simp] lemma mem_carrier : x ∈ p.carrier ↔ x ∈ (p : set M) := iff.rfl

@[simp] lemma zero_mem : (0 : M) ∈ p := p.zero_mem'

lemma add_mem (h₁ : x ∈ p) (h₂ : y ∈ p) : x + y ∈ p := p.add_mem' h₁ h₂

lemma smul_mem (r : R) (h : x ∈ p) : r • x ∈ p := p.smul_mem' r h
lemma smul_of_tower_mem [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M]
  (r : S) (h : x ∈ p) : r • x ∈ p :=
p.to_sub_mul_action.smul_of_tower_mem r h

lemma sum_mem {t : finset ι} {f : ι → M} : (∀c∈t, f c ∈ p) → (∑ i in t, f i) ∈ p :=
p.to_add_submonoid.sum_mem

lemma sum_smul_mem {t : finset ι} {f : ι → M} (r : ι → R)
    (hyp : ∀ c ∈ t, f c ∈ p) : (∑ i in t, r i • f i) ∈ p :=
submodule.sum_mem _ (λ i hi, submodule.smul_mem  _ _ (hyp i hi))

@[simp] lemma smul_mem_iff' [group G] [mul_action G M] [has_scalar G R] [is_scalar_tower G R M]
  (g : G) : g • x ∈ p ↔ x ∈ p :=
p.to_sub_mul_action.smul_mem_iff' g

instance : has_add p := ⟨λx y, ⟨x.1 + y.1, add_mem _ x.2 y.2⟩⟩
instance : has_zero p := ⟨⟨0, zero_mem _⟩⟩
instance : inhabited p := ⟨0⟩
instance [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M] :
  has_scalar S p := ⟨λ c x, ⟨c • x.1, smul_of_tower_mem _ c x.2⟩⟩

instance [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M] : is_scalar_tower S R p :=
p.to_sub_mul_action.is_scalar_tower

protected lemma nonempty : (p : set M).nonempty := ⟨0, p.zero_mem⟩

@[simp] lemma mk_eq_zero {x} (h : x ∈ p) : (⟨x, h⟩ : p) = 0 ↔ x = 0 := subtype.ext_iff_val

variables {p}
@[simp, norm_cast] lemma coe_eq_zero {x : p} : (x : M) = 0 ↔ x = 0 :=
(set_like.coe_eq_coe : (x : M) = (0 : p) ↔ x = 0)
@[simp, norm_cast] lemma coe_add (x y : p) : (↑(x + y) : M) = ↑x + ↑y := rfl
@[simp, norm_cast] lemma coe_zero : ((0 : p) : M) = 0 := rfl
@[norm_cast] lemma coe_smul (r : R) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_smul_of_tower [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M]
  (r : S) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_mk (x : M) (hx : x ∈ p) : ((⟨x, hx⟩ : p) : M) = x := rfl
@[simp] lemma coe_mem (x : p) : (x : M) ∈ p := x.2

variables (p)

instance : add_comm_monoid p :=
{ add := (+), zero := 0, .. p.to_add_submonoid.to_add_comm_monoid }

instance module' [semiring S] [has_scalar S R] [module S M] [is_scalar_tower S R M] : module S p :=
by refine {smul := (•), ..p.to_sub_mul_action.mul_action', ..};
   { intros, apply set_coe.ext, simp [smul_add, add_smul, mul_smul] }
instance : module R p := p.module'

instance no_zero_smul_divisors [no_zero_smul_divisors R M] : no_zero_smul_divisors R p :=
⟨λ c x h,
  have c = 0 ∨ (x : M) = 0,
  from eq_zero_or_eq_zero_of_smul_eq_zero (congr_arg coe h),
  this.imp_right (@subtype.ext_iff _ _ x 0).mpr⟩

/-- Embedding of a submodule `p` to the ambient space `M`. -/
protected def subtype : p →ₗ[R] M :=
by refine {to_fun := coe, ..}; simp [coe_smul]

@[simp] theorem subtype_apply (x : p) : p.subtype x = x := rfl

lemma subtype_eq_val : ((submodule.subtype p) : p → M) = subtype.val := rfl

/-- Note the `add_submonoid` version of this lemma is called `add_submonoid.coe_finset_sum`. -/
@[simp] lemma coe_sum (x : ι → p) (s : finset ι) : ↑(∑ i in s, x i) = ∑ i in s, (x i : M) :=
p.subtype.map_sum

section restrict_scalars
variables (S) [semiring S] [module S M] [module R M] [has_scalar S R] [is_scalar_tower S R M]

/--
`V.restrict_scalars S` is the `S`-submodule of the `S`-module given by restriction of scalars,
corresponding to `V`, an `R`-submodule of the original `R`-module.
-/
def restrict_scalars (V : submodule R M) : submodule S M :=
{ carrier := V,
  zero_mem' := V.zero_mem,
  smul_mem' := λ c m h, V.smul_of_tower_mem c h,
  add_mem' := λ x y hx hy, V.add_mem hx hy }

@[simp]
lemma coe_restrict_scalars (V : submodule R M) : (V.restrict_scalars S : set M) = V :=
rfl

@[simp]
lemma restrict_scalars_mem (V : submodule R M) (m : M) : m ∈ V.restrict_scalars S ↔ m ∈ V :=
iff.refl _

@[simp]
lemma restrict_scalars_self (V : submodule R M) : V.restrict_scalars R = V :=
set_like.coe_injective rfl

variables (R S M)

lemma restrict_scalars_injective :
  function.injective (restrict_scalars S : submodule R M → submodule S M) :=
λ V₁ V₂ h, ext $ set.ext_iff.1 (set_like.ext'_iff.1 h : _)

@[simp] lemma restrict_scalars_inj {V₁ V₂ : submodule R M} :
  restrict_scalars S V₁ = restrict_scalars S V₂ ↔ V₁ = V₂ :=
(restrict_scalars_injective S _ _).eq_iff

/-- Even though `p.restrict_scalars S` has type `submodule S M`, it is still an `R`-module. -/
instance restrict_scalars.orig_module (p : submodule R M) :
  module R (p.restrict_scalars S) :=
(by apply_instance : module R p)

instance (p : submodule R M) : is_scalar_tower S R (p.restrict_scalars S) :=
{ smul_assoc := λ r s x, subtype.ext $ smul_assoc r s (x : M) }

/-- `restrict_scalars S` is an embedding of the lattice of `R`-submodules into
the lattice of `S`-submodules. -/
@[simps]
def restrict_scalars_embedding : submodule R M ↪o submodule S M :=
{ to_fun := restrict_scalars S,
  inj' := restrict_scalars_injective S R M,
  map_rel_iff' := λ p q, by simp [set_like.le_def] }

/-- Turning `p : submodule R M` into an `S`-submodule gives the same module structure
as turning it into a type and adding a module structure. -/
@[simps {simp_rhs := tt}]
def restrict_scalars_equiv (p : submodule R M) : p.restrict_scalars S ≃ₗ[R] p :=
{ to_fun := id, inv_fun := id, map_smul' := λ c x, rfl, .. add_equiv.refl p }

end restrict_scalars

end add_comm_monoid

section add_comm_group

variables [ring R] [add_comm_group M]
variables {module_M : module R M}
variables (p p' : submodule R M)
variables {r : R} {x y : M}

lemma neg_mem (hx : x ∈ p) : -x ∈ p := p.to_sub_mul_action.neg_mem hx

/-- Reinterpret a submodule as an additive subgroup. -/
def to_add_subgroup : add_subgroup M :=
{ neg_mem' := λ _, p.neg_mem , .. p.to_add_submonoid }

@[simp] lemma coe_to_add_subgroup : (p.to_add_subgroup : set M) = p := rfl

@[simp] lemma mem_to_add_subgroup : x ∈ p.to_add_subgroup ↔ x ∈ p := iff.rfl

include module_M

theorem to_add_subgroup_injective : injective (to_add_subgroup : submodule R M → add_subgroup M)
| p q h := set_like.ext (set_like.ext_iff.1 h : _)

@[simp] theorem to_add_subgroup_eq : p.to_add_subgroup = p'.to_add_subgroup ↔ p = p' :=
to_add_subgroup_injective.eq_iff

@[mono] lemma to_add_subgroup_strict_mono :
  strict_mono (to_add_subgroup : submodule R M → add_subgroup M) := λ _ _, id

@[mono] lemma to_add_subgroup_mono : monotone (to_add_subgroup : submodule R M → add_subgroup M) :=
to_add_subgroup_strict_mono.monotone

omit module_M

lemma sub_mem : x ∈ p → y ∈ p → x - y ∈ p := p.to_add_subgroup.sub_mem

@[simp] lemma neg_mem_iff : -x ∈ p ↔ x ∈ p := p.to_add_subgroup.neg_mem_iff

lemma add_mem_iff_left : y ∈ p → (x + y ∈ p ↔ x ∈ p) := p.to_add_subgroup.add_mem_cancel_right

lemma add_mem_iff_right : x ∈ p → (x + y ∈ p ↔ y ∈ p) := p.to_add_subgroup.add_mem_cancel_left

instance : has_neg p := ⟨λx, ⟨-x.1, neg_mem _ x.2⟩⟩

@[simp, norm_cast] lemma coe_neg (x : p) : ((-x : p) : M) = -x := rfl

instance : add_comm_group p :=
{ add := (+), zero := 0, neg := has_neg.neg, ..p.to_add_subgroup.to_add_comm_group }

@[simp, norm_cast] lemma coe_sub (x y : p) : (↑(x - y) : M) = ↑x - ↑y := rfl

end add_comm_group

section is_domain

variables [ring R] [is_domain R]
variables [add_comm_group M] [module R M] {b : ι → M}

lemma not_mem_of_ortho {x : M} {N : submodule R M}
  (ortho : ∀ (c : R) (y ∈ N), c • x + y = (0 : M) → c = 0) :
  x ∉ N :=
by { intro hx, simpa using ortho (-1) x hx }

lemma ne_zero_of_ortho {x : M} {N : submodule R M}
  (ortho : ∀ (c : R) (y ∈ N), c • x + y = (0 : M) → c = 0) :
  x ≠ 0 :=
mt (λ h, show x ∈ N, from h.symm ▸ N.zero_mem) (not_mem_of_ortho ortho)

end is_domain

section ordered_monoid

variables [semiring R]

/-- A submodule of an `ordered_add_comm_monoid` is an `ordered_add_comm_monoid`. -/
instance to_ordered_add_comm_monoid
  {M} [ordered_add_comm_monoid M] [module R M] (S : submodule R M) :
  ordered_add_comm_monoid S :=
subtype.coe_injective.ordered_add_comm_monoid coe rfl (λ _ _, rfl)

/-- A submodule of a `linear_ordered_add_comm_monoid` is a `linear_ordered_add_comm_monoid`. -/
instance to_linear_ordered_add_comm_monoid
  {M} [linear_ordered_add_comm_monoid M] [module R M] (S : submodule R M) :
  linear_ordered_add_comm_monoid S :=
subtype.coe_injective.linear_ordered_add_comm_monoid coe rfl (λ _ _, rfl)

/-- A submodule of an `ordered_cancel_add_comm_monoid` is an `ordered_cancel_add_comm_monoid`. -/
instance to_ordered_cancel_add_comm_monoid
  {M} [ordered_cancel_add_comm_monoid M] [module R M] (S : submodule R M) :
  ordered_cancel_add_comm_monoid S :=
subtype.coe_injective.ordered_cancel_add_comm_monoid coe rfl (λ _ _, rfl)

/-- A submodule of a `linear_ordered_cancel_add_comm_monoid` is a
`linear_ordered_cancel_add_comm_monoid`. -/
instance to_linear_ordered_cancel_add_comm_monoid
  {M} [linear_ordered_cancel_add_comm_monoid M] [module R M] (S : submodule R M) :
  linear_ordered_cancel_add_comm_monoid S :=
subtype.coe_injective.linear_ordered_cancel_add_comm_monoid coe rfl (λ _ _, rfl)

end ordered_monoid

section ordered_group

variables [ring R]

/-- A submodule of an `ordered_add_comm_group` is an `ordered_add_comm_group`. -/
instance to_ordered_add_comm_group
  {M} [ordered_add_comm_group M] [module R M] (S : submodule R M) :
  ordered_add_comm_group S :=
subtype.coe_injective.ordered_add_comm_group coe rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl)

/-- A submodule of a `linear_ordered_add_comm_group` is a
`linear_ordered_add_comm_group`. -/
instance to_linear_ordered_add_comm_group
  {M} [linear_ordered_add_comm_group M] [module R M] (S : submodule R M) :
  linear_ordered_add_comm_group S :=
subtype.coe_injective.linear_ordered_add_comm_group coe rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl)

end ordered_group

end submodule

namespace submodule

variables [division_ring S] [semiring R] [add_comm_monoid M] [module R M]
variables [has_scalar S R] [module S M] [is_scalar_tower S R M]

variables (p : submodule R M) {s : S} {x y : M}

theorem smul_mem_iff (s0 : s ≠ 0) : s • x ∈ p ↔ x ∈ p :=
p.to_sub_mul_action.smul_mem_iff s0

end submodule

/-- Subspace of a vector space. Defined to equal `submodule`. -/
abbreviation subspace (R : Type u) (M : Type v)
  [field R] [add_comm_group M] [module R M] :=
submodule R M


/- ------------------------------------------------------------------------- -|
| @project: riemann_hypothesis                                                |
| @file:    mathlib/impl.lean                                                 |
| @authors: Brandon H. Gomes, Alex Kontorovich                                |
| @affil:   Rutgers University                                                |
|- ------------------------------------------------------------------------- -/

import analysis.special_functions.exp_log
import topology.metric_space.cau_seq_filter

import riemann_hypothesis

/-!
-/

open riemann_hypothesis
open_locale big_operators

noncomputable theory

/--
-/
instance : Algebra ℝ := {
    zero := has_zero.zero,
    one := has_one.one,
    neg := has_neg.neg,
    add := has_add.add,
    sub := has_sub.sub,
    mul := has_mul.mul,
    inv := has_inv.inv
}

/--
-/
instance : Algebra ℂ := {
    zero := has_zero.zero,
    one := has_one.one,
    neg := has_neg.neg,
    add := has_add.add,
    sub := has_sub.sub,
    mul := has_mul.mul,
    inv := has_inv.inv
}

/--
-/
def real_explog : ExpLog ℝ ℝ :=
{
    exp                   := real.exp,
    exp_homomorphism_zero := real.exp_zero,
    exp_homomorphism      := real.exp_add,
    exp_homomorphism_inv  := λ _ _, by rw [real.exp_sub, division_def],
    exp_injective         := λ _ _ eq, real.exp_injective eq,
    exp_monotonic         := λ _ _ lt, real.exp_lt_exp.mpr lt,
    exp_monotonic_reverse := λ _ _ lt, real.exp_lt_exp.mp lt,
    exp_positive          := real.exp_pos,
    log                   := λ a _, real.log a,
    log_domain_irrel      := λ _ _ _ _ eq, by rw eq,
    log_inverted          := λ _ apos, by rw real.exp_log apos,
}

/--
-/
def complex_witness.proofs.abs_exp_is_exp_real_part
    : Π z, complex.abs (complex.exp z) = real.exp (complex.re z) :=
begin
    intros,
    rw [← complex.exp_of_real_re,
          complex.abs_exp_eq_iff_re_eq.mpr,
        ← complex.of_real_exp,
          complex.abs_of_real,
          abs_of_pos (real.exp_pos _)],
    repeat { refine rfl },
end

/--
-/
def lim_le (f : cau_seq ℝ abs) (g : cau_seq ℝ abs)
    : (∃ j, ∀ i, j ≤ i → f i ≤ g i) → f.lim ≤ g.lim :=
begin
  intros,
  refine le_of_tendsto_of_tendsto (cau_seq.tendsto_limit _) (cau_seq.tendsto_limit _) _,
  rwa [filter.eventually_le, filter.eventually_at_top],
end

/--
-/
def unit_circle_pow_lemma
    : Π x, complex.abs x ≤ 1 → Π (n : ℕ), complex.abs (x ^ n) ≤ 1 :=
begin
    intros _ x_le_1 _,
    induction n with n hn,
        simp,
        rw pow_succ,
        refine le_trans _ hn,
        rw is_absolute_value.abv_pow complex.abs,
        rw complex.abs_mul,
        rw (_ : complex.abs x ^ n = 1 * complex.abs x ^ n),
        refine mul_le_mul
            x_le_1 (le_of_eq (is_absolute_value.abv_pow _ _ _)) (complex.abs_nonneg _) zero_le_one,
        simp,
end

/--
-/
def complex_witness.proofs.exp_linearization
    : Π x, complex.abs x ≤ 1
    → Π z, complex.abs (complex.exp (x * z) - (x * z + 1))
        ≤ (complex.abs x * complex.abs x) * real.exp (complex.abs z) :=
begin
    intros _ x_le_1 _,
    rw complex.exp,
    rw ← cau_seq.lim_const (x*z+1),
    rw sub_eq_add_neg,
    rw ← cau_seq.lim_neg,
    rw cau_seq.lim_add,
    rw ← complex.lim_abs,
    rw real.exp,
    rw complex.exp,
    rw ← complex.lim_re,
    rw ← cau_seq.lim_const ((complex.abs x) * (complex.abs x)),
    rw cau_seq.lim_mul_lim,
    rw mul_comm (cau_seq.const _ _),

    refine lim_le _ _ _,
    existsi 2,
    intros i i_ge_2,

    rw (complex.cau_seq_re _).mul_apply,

    rw (_ : (complex.cau_seq_re (complex.exp' ↑(complex.abs z))) i
          = (∑ k in finset.range i, ((complex.abs z) : ℂ) ^ k / k.fact).re),

    rw ← (finset.range _).sum_hom complex.re,
    norm_cast,

    show complex.abs (∑ k in finset.range i, (x * z) ^ k / k.fact - (x * z + 1))
            ≤ (∑ k in finset.range i, (complex.abs z) ^ k / k.fact) * (complex.abs x * complex.abs x),

    rw (_ : (x * z + 1) = ∑ k in finset.range 2, (x * z) ^ k / ↑(k.fact)),

    rw sum_range_sub_sum_range i_ge_2,

    rw (_ : complex.abs (∑ k in (finset.range i).filter (λ k, 2 ≤ k), (x * z) ^ k / ↑(k.fact))
          = complex.abs (∑ k in (finset.range i).filter (λ k, 2 ≤ k), x ^ 2 * x ^ (k - 2) * z ^ k / ↑(k.fact))),

    rw (_ : (∑ k in finset.range i, complex.abs z ^ k / ↑(k.fact))
          = (∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + (1 + complex.abs z))),

    rw (_ : (∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + (1 + complex.abs z))
          * (complex.abs x * complex.abs x)
          = complex.abs x ^ 2
          * (∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + (1 + complex.abs z))),

    have drop_extra_powers_of_x
        : complex.abs (∑ k in (finset.range i).filter (λ k, 2 ≤ k), x ^ 2 * x ^ (k - 2) * z ^ k / ↑(k.fact))
        ≤ ∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs x ^ 2 * (complex.abs z ^ k / ↑(k.fact)),
    {
        refine le_trans (abv_sum_le_sum_abv _ _) (finset.sum_le_sum (λ m _, _)),

        rw mul_div_assoc,
        rw complex.abs_mul,
        rw complex.abs_mul,
        rw is_absolute_value.abv_pow complex.abs,
        rw complex.abs_div,
        rw complex.abs_cast_nat,
        rw ← mul_div_assoc,
        rw division_def,
        rw division_def,
        rw ← is_absolute_value.abv_pow complex.abs z,
        rw ← mul_assoc,

        refine mul_le_mul_of_nonneg_right (mul_le_mul_of_nonneg_right _ (complex.abs_nonneg _)) (by simp),

        have : complex.abs x ^ 2 * complex.abs (x ^ (m - 2)) ≤ complex.abs x ^ 2 * 1,
            by refine mul_le_mul_of_nonneg_left (unit_circle_pow_lemma _ x_le_1 _) (pow_nonneg (complex.abs_nonneg _) _),

        simp at this,
        refine this,
    },

    simp [abs_mul, is_absolute_value.abv_pow complex.abs, complex.abs_div, finset.mul_sum.symm]
        at drop_extra_powers_of_x,

    refine le_trans drop_extra_powers_of_x (mul_le_mul (le_refl _) _ _ (pow_nonneg (complex.abs_nonneg _) _)),

    rw (_ : ∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact)
          = ∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + 0),

    rw (_ : ∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + 0 + (1 + complex.abs z)
          = ∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact) + (1 + complex.abs z)),

    refine add_le_add_left _ (∑ k in (finset.range i).filter (λ k, 2 ≤ k), complex.abs z ^ k / ↑(k.fact)),
    rw (_ : (0 : ℝ) = 0 + 0),
    refine add_le_add (by linarith) (complex.abs_nonneg _),
    simp,
    simp,
    simp,
    rw (_ : (0 : ℝ) = ∑ k in (finset.range i).filter (λ k, 2 ≤ k), 0),
    refine finset.sum_le_sum _,
    intros m _,
    rw (_ : complex.abs z ^ m / (m.fact : ℝ) = complex.abs z ^ m * (1 / (m.fact : ℝ))),
    refine mul_nonneg _ _,
    rw ← is_absolute_value.abv_pow complex.abs,
    refine complex.abs_nonneg _,
    refine le_of_lt (one_div_pos.mpr _),
    norm_cast,
    refine nat.fact_pos _,
    ring,
    refine (finset.sum_eq_zero _).symm,
    intros,
    refine rfl,
    ring,
    rw (_ : (1 + complex.abs z) = ∑ k in finset.range 2, (complex.abs z) ^ k / ↑(k.fact)),
    rw ← sum_range_sub_sum_range i_ge_2,
    simp,
    simp [sub_eq_add_neg, finset.sum_range_succ, add_assoc],
    ring,
    refine congr_arg complex.abs _,
    refine finset.sum_congr rfl _,
    intros,
    rw mul_pow,
    rw ← pow_add,
    rw nat.add_sub_cancel',
    simp at H,
    refine H.2,
    simp [sub_eq_add_neg, finset.sum_range_succ, add_assoc],
    refine rfl,
end

/--
-/
def complex_witness.proofs.real_log_bound
    : Π (x : ℝ) (p : 0 < 1 - x),
        complex.abs ↑x ≤ 2⁻¹ → complex.abs ↑(real.log (1 - x)) ≤ complex.abs ↑x + complex.abs ↑x :=
begin
    intros _ _ x_le_half,
    rw complex.abs_of_real at x_le_half,
    simp,

    have ineq0 : abs (∑ i in finset.range 0, x ^ (i + 1) / (↑i + 1) + real.log (1 - x))
               ≤ abs x ^ (0 + 1) / (1 - abs x),
    {
        refine real.abs_log_sub_add_sum_range_le _ _,
        rw (_ : (2 : ℝ)⁻¹ = 1 / 2) at x_le_half,
        linarith,
        simp,
    },
    simp at ineq0,

    rw (_: abs x + abs x = abs x / (1 - 2⁻¹)),

    have ineq1 : -(2⁻¹) ≤ -(abs x),
        by refine neg_le_neg x_le_half,

    have ineq2 : 1 - 2⁻¹ ≤ 1 - abs x,
        by refine sub_le_sub_left x_le_half _,

    have ineq3 : (1 - abs x)⁻¹ ≤ (1 - 2⁻¹)⁻¹,
    {
        refine inv_le_inv_of_le _ _,
        simp,
        rw (_ : (2 : ℝ)⁻¹ = 1 / 2),
        linarith,
        simp,
        refine ineq2,
    },

    have ineq4 : abs x / (1 - abs x) ≤ abs x / (1 - 2⁻¹) ,
    {
        rw (_ : abs x / (1 - abs x) = (1 - abs x)⁻¹ * abs x),
        rw (_ : abs x / (1 - 2⁻¹) = (1 - 2⁻¹)⁻¹ * abs x),
        refine mul_le_mul ineq3 _ _ _,
        refine le_refl _,
        refine abs_nonneg _,
        rw (_: (1 - (2 : ℝ)⁻¹)⁻¹ = 2),
        linarith,
        ring,
        ring,
        ring,
    },

    linarith,
    ring,
end

/--
-/
def complex_witness : Complex ℂ ℝ :=
{
    real_part                := complex.re,
    abs                      := complex.abs,
    exp                      := complex.exp,
    real_explog              := real_explog,
    abs_nonneg               := complex.abs_nonneg,
    exp_nonzero              := complex.exp_ne_zero,
    exp_homomorphism_zero    := complex.exp_zero,
    exp_homomorphism         := complex.exp_add,
    exp_homomorphism_inv     := λ _ _, by rw [complex.exp_sub, division_def],
    real_part_scaling        := λ _ _, by rw [complex.mul_re, complex.of_real_im, complex.of_real_re, zero_mul, sub_zero],
    abs_exp_is_exp_real_part := complex_witness.proofs.abs_exp_is_exp_real_part,
    exp_linearization        := complex_witness.proofs.exp_linearization,
    real_log_bound           := complex_witness.proofs.real_log_bound
}


import .add

noncomputable theory

namespace hidden

open myring
open ordered_myring
open myfield
open ordered_myfield
open ordered_integral_domain

namespace cau_seq

-- The proof is very similar to the one we use for real series in analysis.
-- We bound the absolute values of each of the series above, and
-- use the ax - by = a(x - y) + y(a - b) trick, along with the
-- triangle inequality and some appropriately chosen eventual bounds
-- on |x - y| and |a - b|
def mul : cau_seq → cau_seq → cau_seq :=
λ f g, ⟨λ n, f.val n * g.val n,
begin
  have hf := f.property,
  have hg := g.property,
  dsimp only [is_cau_seq] at *,
  intros ε hε,
  -- Use the fact that f and g have bounded absolute value
  cases cau_seq.abs_bounded_above f with uf huf,
  cases huf with hufpos huf,
  cases cau_seq.abs_bounded_above g with ug hug,
  cases hug with hugpos hug,
  -- to create some "magical" bound on the difference between
  -- term m and n
  have hεfpos : 0 < (ε / 2) / ug,
    rw [div_def, ←zero_mul (0 : myrat)],
    apply @lt_mul_comb_nonneg _ _ (0 : myrat) _ 0 _ (by refl) (by refl),
      from half_pos hε,
    from pos_impl_inv_pos hugpos,
  have hεgpos : 0 < (ε / 2) / uf,
    rw [div_def, ←zero_mul (0 : myrat)],
    apply @lt_mul_comb_nonneg _ _ (0 : myrat) _ 0 _ (by refl) (by refl),
      from half_pos hε,
    from pos_impl_inv_pos hufpos,
  cases hf ((ε / 2) / ug) hεfpos with M hM,
  cases hg ((ε / 2) / uf) hεgpos with N hN,
  clear hεfpos hεgpos hf hg, -- Tidy a bit
  -- Obvious facts are obvious
  existsi mynat.max M N,
  intros m n hm hn,
  have hMm : M ≤ m, from mynat.max_le_cancel_left hm,
  have hMn : M ≤ n, from mynat.max_le_cancel_left hn,
  have hNm : N ≤ m, from mynat.max_le_cancel_right hm,
  have hNn : N ≤ n, from mynat.max_le_cancel_right hn,
  clear hm hn,
  -- Now we need to use the "trick"
  have : f.val n * g.val n - f.val m * g.val m =
         f.val n * (g.val n - g.val m) + g.val m * (f.val n - f.val m),
  {
    repeat { rw sub_def <|> rw mul_add <|> rw mul_neg },
    have : f.val n * g.val n + -(f.val n * g.val m) + (g.val m * f.val n + -(g.val m * f.val m)) =
           f.val n * g.val n + - (f.val m * g.val m) + (f.val n * g.val m + - (f.val n * g.val m)),
      ac_refl,
    rw this, clear this,
    rw [add_neg, add_zero],
  },
  rw this, clear this,
  -- And the triangle inequality
  have : abs (f.val n * (g.val n - g.val m) + g.val m * (f.val n - f.val m)) ≤
         abs (f.val n * (g.val n - g.val m)) + abs (g.val m * (f.val n - f.val m)),
    apply triangle_ineq,
  apply le_lt_chain (abs (f.val n * (g.val n - g.val m)) +
                           abs (g.val m * (f.val n - f.val m))),
    assumption,
  clear this,
  -- Abs distrib over mul
  rw [abs_mul, abs_mul],
  -- Apply given hypotheses, after lots of rearranging so lean gets it
  -- Needed for rearranging
  have huf0 : uf ≠ 0,
    assume hufeq0,
    from lt_impl_ne hufpos hufeq0.symm,
  -- hug0 is frying right now
  have hug0 : ug ≠ 0,
    assume hugeq0,
    from lt_impl_ne hugpos hugeq0.symm,
  -- We juggle a bit so we can simply apply lt_comb
  conv {
    to_rhs,
    rw ←half_plus_half myrat.two_nzero ε,
    congr,
      rw ←div_mul_cancel (ε / 2) uf huf0,
      rw mul_comm,
      skip,
    rw ←div_mul_cancel (ε / 2) ug hug0,
    rw mul_comm,
  },
  -- This meaty line converts one goal into 8! (fortunately not 40320)
  apply lt_comb; apply lt_mul_comb_nonneg,
  -- Fortunately we can kill half of them in one go
  any_goals { apply abs_nonneg, },
  -- And the others are things we worked out earlier
  {
    from huf n,
  }, {
    from hN m n hNm hNn,
  }, {
    from hug m,
  }, {
    from hM m n hMm hMn,
  },
end⟩

instance: has_mul cau_seq := ⟨mul⟩

theorem mul_val {a b : cau_seq} {n : mynat} : (a * b).val n = a.val n * b.val n := rfl

theorem mul_comm (a b : cau_seq) : a * b = b * a :=
begin
  apply cau_seq.seq_eq_impl_eq,
  intro n,
  rw [mul_val, mul_val, myring.mul_comm],
end

open classical

local attribute [instance] classical.prop_decidable

def inv: cau_seq → cau_seq :=
λ f : cau_seq, ⟨(λ n : mynat, if f ≈ (0 : cau_seq) then 0 else (f.val n)⁻¹),
begin
  unfold is_cau_seq,
  intros ε hε,
  by_cases (f ≈ (0 : cau_seq)),
    existsi (0 : mynat),
    intros m n hm hn,
    rwa [if_pos h, if_pos h, sub_def, zero_add, abs_neg, abs_zero],
  have hf := f.property,
  unfold is_cau_seq at hf,
  cases cau_seq.nzero_impl_abs_eventually_bounded_below f h with A hA,
  cases hA with N hN,
  have h0AAε : 0 < A * (A * ε), {
    rw ←mul_zero (0 : myrat),
    apply @lt_mul_comb_nonneg _ _ 0 A 0 (A*ε) (by refl) (by refl) hN.left,
    rw ←mul_zero (0 : myrat),
    apply @lt_mul_comb_nonneg _ _ 0 A 0 ε (by refl) (by refl) hN.left,
    assumption,
  },
  cases hf (A*(A*ε)) h0AAε with M hM,
  existsi (mynat.max M N),
  intros m n hm hn,
  rw [if_neg h, if_neg h],
  have hnpos : 0 < abs (f.val n), {
    transitivity A,
      from hN.left,
    apply hN.right n,
    from mynat.max_le_cancel_right hn,
  },
  have hnzero: f.val n ≠ 0, {
    assume this,
    rw this at hnpos,
    rw abs_zero at hnpos,
    apply lt_nrefl (0 : myrat),
    assumption,
  },
  rw lt_mul_pos_left _ hnpos,
  rw [←abs_mul, sub_def, mul_add, mul_inv hnzero],
  have hmpos : 0 < abs (f.val m), {
    transitivity A,
      from hN.left,
    apply hN.right m,
    from mynat.max_le_cancel_right hm,
  },
  have hmzero: f.val m ≠ 0, {
    assume this,
    rw this at hmpos,
    rw abs_zero at hmpos,
    apply lt_nrefl (0 : myrat),
    assumption,
  },
  rw [lt_mul_pos_left _ hmpos, ←abs_mul, mul_add, mul_one, ←mul_assoc,
      myring.mul_comm, neg_mul, ←mul_assoc, inv_mul hmzero, one_mul, ←sub_def],
  suffices: A * (A * ε) ≤ abs (f.val m) * ((abs (f.val n)) * ε),
    apply lt_le_chain (A * (A * ε)),
    apply hM,
      from mynat.max_le_cancel_left hn,
      from mynat.max_le_cancel_left hm,
    assumption,
  apply le_mul_comb_nonneg, {
    from lt_impl_le hN.left,
  }, {
    rw ←zero_mul (0 : myrat),
    apply le_mul_comb_nonneg,
    refl, refl,
    from lt_impl_le hN.left,
    from lt_impl_le hε,
  }, {
    have := hN.right m (mynat.max_le_cancel_right hm),
    apply lt_impl_le,
    assumption,
  },
  apply le_mul_comb_nonneg, {
    from lt_impl_le hN.left,
  }, {
    from lt_impl_le hε,
  }, {
    have := hN.right n (mynat.max_le_cancel_right hn),
    apply lt_impl_le,
    assumption,
  },
  refl,
end⟩

instance: has_inv cau_seq := ⟨inv⟩

-- Lemma to help rewrite definitional equalities
lemma inv_val (f : cau_seq) (n : mynat) :
(f⁻¹).val n = if f ≈ (0 : cau_seq) then 0 else (f.val n)⁻¹ :=
rfl

theorem inv_equiv_zero (a : cau_seq) (h : a ≈ 0) : a⁻¹ ≈ 0 :=
begin
  apply cau_seq.seq_eq_impl_cau_seq_equiv,
  intro n,
  rw [inv_val, if_pos],
  refl,
  assumption,
end

end cau_seq

namespace real

private theorem mul_equiv (a b x : cau_seq) (hab : a ≈ b) : a * x ≈ b * x :=
begin
  rw cau_seq.setoid_equiv at *,
  intros q hq,
  cases cau_seq.abs_bounded_above x with u hu,
  cases hu with hu h,
  have hqu : 0 < q * u⁻¹,
    apply zero_lt_mul,
      assumption,
    apply pos_impl_inv_pos,
    assumption,
  cases hab (q * u⁻¹) hqu with N hN,
  existsi N,
  intros n hn,
  rw [cau_seq.mul_val, cau_seq.mul_val, ←sub_mul, abs_mul,
     lt_mul_pos_right u⁻¹ (pos_impl_inv_pos hu)],
  apply le_lt_chain (abs (a.val n - b.val n)), {
    conv {
      to_rhs,
      rw ←mul_one (abs (a.val n - b.val n)),
    },
    rw mul_assoc,
    apply le_mul_comb_nonneg, {
      exact abs_nonneg _,
    }, {
      apply zero_le_mul,
        exact abs_nonneg _,
      apply lt_impl_le,
      apply pos_impl_inv_pos,
      assumption,
    }, {
      refl,
    }, {
      have this : u ≠ 0,
        assume hu0,
        apply lt_impl_ne hu,
        symmetry, assumption,
      rw ←mul_inv this,
      have huinv : 0 ≤ u⁻¹,
        apply lt_impl_le,
        apply pos_impl_inv_pos,
        assumption,
      apply le_mul_nonneg_right _ _ u⁻¹ huinv,
      apply lt_impl_le,
      exact h n,
    },
  }, {
    exact hN n hn,
  },
end

def mul : real → real → real :=
quotient.lift₂ (λ f g, ⟦f * g⟧)
begin
  intros a x b y hab hxy,
  dsimp only [],
  rw [cau_seq.class_equiv, ←cau_seq.setoid_equiv],
  apply @setoid.trans _ _ _ (b * x),
    apply mul_equiv a b x,
    assumption,
  rw [cau_seq.mul_comm b, cau_seq.mul_comm b],
  apply mul_equiv x y b,
  assumption,
end

instance: has_mul real := ⟨mul⟩

theorem mul_eq_cls {a b : cau_seq} {x y : real} :
x = ⟦a⟧ → y = ⟦b⟧ → x * y = ⟦a * b⟧ :=
λ hax hby, by rw [hax, hby]; refl

open classical

local attribute [instance] classical.prop_decidable

def inv: real → real :=
quotient.lift (λ f, ⟦f⁻¹⟧)
begin
  intros a b hab,
  have ha := a.property,
  have hb := b.property,
  dsimp only [],
  by_cases ha0: a ≈ (0 : cau_seq), {
    have: b ≈ (0 : cau_seq),
      apply @setoid.trans cau_seq _ b a 0,
        apply @setoid.symm cau_seq _ a b,
        assumption,
      assumption,
    rw [cau_seq.class_equiv, ←cau_seq.setoid_equiv],
    apply @setoid.trans cau_seq _ a⁻¹ 0 b⁻¹,
      apply cau_seq.inv_equiv_zero,
      assumption,
    apply @setoid.symm cau_seq _ _ _,
    apply cau_seq.inv_equiv_zero,
    assumption,
  }, {
    have hb0 : ¬b ≈ (0 : cau_seq),
      assume hb0,
      apply ha0,
      apply @setoid.trans cau_seq _ a b 0,
        assumption,
      assumption,
    cases cau_seq.nzero_impl_abs_eventually_bounded_below a ha0 with A hA,
    cases cau_seq.nzero_impl_abs_eventually_bounded_below b hb0 with B hB,
    cases hA with N₁ hN₁,
    cases hB with N₂ hN₂,
    rw cau_seq.class_equiv,
    rw cau_seq.setoid_equiv at hab,
    dsimp only [cau_seq.equivalent] at hab ⊢,
    intros ε hε,
    have h0ABε : 0 < A * (B * ε), {
      rw ←zero_mul (0 : myrat),
      apply @lt_mul_comb_nonneg _ _  (0 : myrat) _ 0 _ (by refl) (by refl) hN₁.left,
      rw ←zero_mul (0 : myrat),
      apply @lt_mul_comb_nonneg _ _ (0 : myrat) _ 0 _ (by refl) (by refl) hN₂.left hε,
    },
    cases hab (A * (B * ε)) h0ABε with N₃ hN₃,
    existsi mynat.max (mynat.max N₁ N₂) N₃,
    intros n hn,
    rw [cau_seq.inv_val, cau_seq.inv_val, if_neg ha0, if_neg hb0],
    have hanpos : 0 < abs (a.val n), {
      transitivity A,
        from hN₁.left,
      apply hN₁.right n,
      apply @mynat.max_le_cancel_left _ N₂ _,
      apply @mynat.max_le_cancel_left _ N₃ _,
      assumption,
    },
    have hbnpos : 0 < abs (b.val n), {
      transitivity B,
        from hN₂.left,
      apply hN₂.right n,
      apply @mynat.max_le_cancel_right N₁ _ _,
      apply @mynat.max_le_cancel_left _ N₃ _,
      assumption,
    },
    have hanzero : a.val n ≠ 0, {
      assume this,
      rw this at hanpos,
      rw abs_zero at hanpos,
      apply lt_nrefl (0 : myrat),
      assumption,
    },
    have hbnzero : b.val n ≠ 0, {
      assume this,
      rw this at hbnpos,
      rw abs_zero at hbnpos,
      apply lt_nrefl (0 : myrat),
      assumption,
    },
    -- A small amount of rearranging...
    rw [lt_mul_pos_left _ hanpos, ←abs_mul, sub_def, mul_add,
        mul_inv hanzero, lt_mul_pos_left _ hbnpos, ←abs_mul,
        mul_add, mul_one, mul_neg, mul_neg, add_comm,
        mul_comm (a.val n), ←mul_assoc, mul_inv hbnzero,
        one_mul, ←abs_neg, neg_distr, neg_neg, ←sub_def],
    apply lt_le_chain (A * (B * ε)), {
      apply hN₃ n,
      apply mynat.max_le_cancel_right hn,
    }, {
      rw [←mul_assoc (abs (b.val n)), mul_comm (abs (b.val n)), mul_assoc (abs (a.val n))],
      apply le_mul_comb_nonneg, {
        from lt_impl_le hN₁.left,
      }, {
        rw ←zero_mul (0 : myrat),
        apply @le_mul_comb_nonneg _ _ (0 : myrat) _ _ _ (by refl) (by refl),
        from lt_impl_le hN₂.left,
        from lt_impl_le hε,
      }, {
        apply lt_impl_le,
        apply hN₁.right n,
        apply @mynat.max_le_cancel_left _ N₂ _,
        apply @mynat.max_le_cancel_left _ N₃ _,
        assumption,
      },
      apply le_mul_comb_nonneg, {
        from lt_impl_le hN₂.left,
      }, {
        from lt_impl_le hε,
      }, {
        apply lt_impl_le,
        apply hN₂.right n,
        apply @mynat.max_le_cancel_right N₁ _ _,
        apply @mynat.max_le_cancel_left _ N₃ _,
        assumption,
      },
      refl,
    },
  },
end

instance: has_inv real := ⟨inv⟩

theorem inv_eq_cls {a : cau_seq} {x : real} :
x = ⟦a⟧ → x⁻¹ = ⟦a⁻¹⟧ :=
λ hax, by rw hax; refl

variables x y z : real

-- Use a b c for corresponding sequences

private theorem mul_comm (x y : real): x * y = y * x :=
begin
  cases quotient.exists_rep x with a ha,
  cases quotient.exists_rep y with b hb,
  have hxy := mul_eq_cls ha.symm hb.symm,
  have hyx := mul_eq_cls hb.symm ha.symm,
  apply seq_eq_imp_real_eq hxy hyx,
  intro n,
  repeat { rw cau_seq.mul_val, },
  rw mul_comm,
end

private theorem mul_zero: x * 0 = 0 :=
begin
  cases quotient.exists_rep x with a ha,
  apply seq_eq_imp_real_eq (mul_eq_cls ha.symm real_zero) real_zero,
  intro n,
  rw cau_seq.mul_val,
  dsimp only [],
  rw mul_zero,
end

private theorem mul_one: x * 1 = x :=
begin
  cases quotient.exists_rep x with a ha,
  apply seq_eq_imp_real_eq (mul_eq_cls ha.symm real_one) ha.symm,
  intro n,
  rw cau_seq.mul_val,
  dsimp only [],
  rw mul_one,
end

private theorem mul_assoc: x * y * z = x * (y * z) :=
begin
  cases quotient.exists_rep x with a ha,
  cases quotient.exists_rep y with b hb,
  cases quotient.exists_rep z with c hc,
  have h₁: x * y * z = ⟦a * b * c⟧,
    rw mul_eq_cls, rw mul_eq_cls,
    repeat { symmetry, assumption, },
  have h₂: x * (y * z) = ⟦a * (b * c)⟧,
    rw mul_eq_cls,
    symmetry, assumption,
    rw mul_eq_cls,
    symmetry, assumption,
    symmetry, assumption,
  apply seq_eq_imp_real_eq h₁ h₂,
  intro n,
  repeat { rw cau_seq.mul_val, },
  rw mul_assoc,
end

private theorem mul_add: x * (y + z) = x * y + x * z :=
begin
  cases quotient.exists_rep x with a ha,
  cases quotient.exists_rep y with b hb,
  cases quotient.exists_rep z with c hc,
  have h₁: x * (y + z) = ⟦a * (b + c)⟧,
    rw mul_eq_cls,
    symmetry, assumption,
    rw add_eq_cls,
    symmetry, assumption,
    symmetry, assumption,
  have h₂: x * y + x * z = ⟦a * b + a * c⟧,
    rw add_eq_cls,
    rw mul_eq_cls,
    repeat { symmetry, assumption, },
    rw mul_eq_cls,
    repeat { symmetry, assumption, },
  apply seq_eq_imp_real_eq h₁ h₂,
  intro n,
  rw [cau_seq.add_val, cau_seq.mul_val, cau_seq.add_val, cau_seq.mul_val,
      cau_seq.mul_val, mul_add],
end

-- Reciprocal "inv"

-- We can't actually prove this in general fields so why not
@[simp]
theorem zero_inv : 0⁻¹ = (0 : real) :=
begin
  have h : (0 : real)⁻¹ = ⟦0⁻¹⟧,
    apply inv_eq_cls real_zero,
  apply seq_eq_imp_real_eq h real_zero,
  intro n,
  dsimp only [],
  rw [cau_seq.inv_val, if_pos],
  apply @setoid.refl _ _ _,
end

private theorem not_equiv_zero_impl_eventually_nzero (f : cau_seq) :
¬f ≈ 0 → ∃ N : mynat, ∀ n, N ≤ n → f.val n ≠ 0 :=
begin
  intros hnf0,
  cases cau_seq.nzero_impl_abs_eventually_bounded_below f hnf0 with δ h,
  cases h with N h,
  cases h with hδ h,
  existsi N,
  intros n hn,
  have := h n hn,
  intro hfval0,
  have habsfval : abs (f.val n) = 0,
    rw [hfval0, abs_zero],
  suffices : 0 < abs (f.val n),
    exact lt_impl_ne this habsfval.symm,
  transitivity δ; assumption,
end

private theorem mul_inv : x ≠ 0 → x * x⁻¹ = 1 :=
begin
  intro hx0,
  cases quotient.exists_rep x with f hf,
  rw [inv_eq_cls hf.symm, ←hf, @mul_eq_cls f f⁻¹ ⟦f⟧ _ (by refl) (by refl)],
  rw [real_one, coe_def, cau_seq.class_equiv],
  unfold cau_seq.equivalent,
  intros ε hε,
  have hnf0 : ¬f ≈ 0,
    assume hf0,
    apply hx0,
    rwa [←hf, real_zero, coe_def, cau_seq.class_equiv],
  cases not_equiv_zero_impl_eventually_nzero f hnf0 with N hN,
  existsi N,
  intros n hn,
  dsimp,
  have := hN n hn,
  rwa [cau_seq.mul_val, cau_seq.inv_val, if_neg hnf0, myfield.mul_inv this,
      sub_self, abs_zero],
end

local attribute [instance] prop_decidable

instance: myring real := ⟨
  by apply_instance,
  real.add_assoc,
  real.add_zero,
  real.add_neg,
  mul_assoc,
  mul_comm,
  mul_one,
  mul_add,
⟩

instance : myfield real := {
  mul_inv := mul_inv,
  nontrivial := real.nontrivial,
}

end real

end hidden


example : ∀ (l : list ℕ), list.append l (0 :: list.nil) ≠ list.nil :=
by intros; induction l; simp *


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import logic.equiv.local_equiv
import topology.sets.opens

/-!
# Local homeomorphisms

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

This file defines homeomorphisms between open subsets of topological spaces. An element `e` of
`local_homeomorph α β` is an extension of `local_equiv α β`, i.e., it is a pair of functions
`e.to_fun` and `e.inv_fun`, inverse of each other on the sets `e.source` and `e.target`.
Additionally, we require that these sets are open, and that the functions are continuous on them.
Equivalently, they are homeomorphisms there.

As in equivs, we register a coercion to functions, and we use `e x` and `e.symm x` throughout
instead of `e.to_fun x` and `e.inv_fun x`.

## Main definitions

`homeomorph.to_local_homeomorph`: associating a local homeomorphism to a homeomorphism, with
                                  source = target = univ
`local_homeomorph.symm`  : the inverse of a local homeomorphism
`local_homeomorph.trans` : the composition of two local homeomorphisms
`local_homeomorph.refl`  : the identity local homeomorphism
`local_homeomorph.of_set`: the identity on a set `s`
`eq_on_source`           : equivalence relation describing the "right" notion of equality for local
                           homeomorphisms

## Implementation notes

Most statements are copied from their local_equiv versions, although some care is required
especially when restricting to subsets, as these should be open subsets.

For design notes, see `local_equiv.lean`.

### Local coding conventions

If a lemma deals with the intersection of a set with either source or target of a `local_equiv`,
then it should use `e.source ∩ s` or `e.target ∩ t`, not `s ∩ e.source` or `t ∩ e.target`.
-/

open function set filter topological_space (second_countable_topology)
open_locale topology

variables {α : Type*} {β : Type*} {γ : Type*} {δ : Type*}
[topological_space α] [topological_space β] [topological_space γ] [topological_space δ]

/-- local homeomorphisms, defined on open subsets of the space -/
@[nolint has_nonempty_instance]
structure local_homeomorph (α : Type*) (β : Type*) [topological_space α] [topological_space β]
  extends local_equiv α β :=
(open_source        : is_open source)
(open_target        : is_open target)
(continuous_to_fun  : continuous_on to_fun source)
(continuous_inv_fun : continuous_on inv_fun target)

namespace local_homeomorph

variables (e : local_homeomorph α β) (e' : local_homeomorph β γ)

instance : has_coe_to_fun (local_homeomorph α β) (λ _, α → β) := ⟨λ e, e.to_fun⟩

/-- The inverse of a local homeomorphism -/
protected def symm : local_homeomorph β α :=
{ open_source        := e.open_target,
  open_target        := e.open_source,
  continuous_to_fun  := e.continuous_inv_fun,
  continuous_inv_fun := e.continuous_to_fun,
  ..e.to_local_equiv.symm }

/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
  because it is a composition of multiple projections. -/
def simps.apply (e : local_homeomorph α β) : α → β := e
/-- See Note [custom simps projection] -/
def simps.symm_apply (e : local_homeomorph α β) : β → α := e.symm

initialize_simps_projections local_homeomorph
  (to_local_equiv_to_fun → apply, to_local_equiv_inv_fun → symm_apply,
   to_local_equiv_source → source, to_local_equiv_target → target, -to_local_equiv)

protected lemma continuous_on : continuous_on e e.source := e.continuous_to_fun

lemma continuous_on_symm : continuous_on e.symm e.target := e.continuous_inv_fun

@[simp, mfld_simps] lemma mk_coe (e : local_equiv α β) (a b c d) :
  (local_homeomorph.mk e a b c d : α → β) = e := rfl

@[simp, mfld_simps] lemma mk_coe_symm (e : local_equiv α β) (a b c d) :
  ((local_homeomorph.mk e a b c d).symm : β → α) = e.symm := rfl

lemma to_local_equiv_injective : injective (to_local_equiv : local_homeomorph α β → local_equiv α β)
| ⟨e, h₁, h₂, h₃, h₄⟩ ⟨e', h₁', h₂', h₃', h₄'⟩ rfl := rfl

/- Register a few simp lemmas to make sure that `simp` puts the application of a local
homeomorphism in its normal form, i.e., in terms of its coercion to a function. -/

@[simp, mfld_simps] lemma to_fun_eq_coe (e : local_homeomorph α β) : e.to_fun = e := rfl

@[simp, mfld_simps] lemma inv_fun_eq_coe (e : local_homeomorph α β) : e.inv_fun = e.symm := rfl

@[simp, mfld_simps] lemma coe_coe : (e.to_local_equiv : α → β) = e := rfl

@[simp, mfld_simps] lemma coe_coe_symm : (e.to_local_equiv.symm : β → α) = e.symm := rfl

@[simp, mfld_simps] lemma map_source {x : α} (h : x ∈ e.source) : e x ∈ e.target :=
e.map_source' h

@[simp, mfld_simps] lemma map_target {x : β} (h : x ∈ e.target) : e.symm x ∈ e.source :=
e.map_target' h

@[simp, mfld_simps] lemma left_inv {x : α} (h : x ∈ e.source) : e.symm (e x) = x :=
e.left_inv' h

@[simp, mfld_simps] lemma right_inv {x : β} (h : x ∈ e.target) : e (e.symm x) = x :=
e.right_inv' h

lemma eq_symm_apply {x : α} {y : β} (hx : x ∈ e.source) (hy : y ∈ e.target) :
  x = e.symm y ↔ e x = y := e.to_local_equiv.eq_symm_apply hx hy

protected lemma maps_to : maps_to e e.source e.target := λ x, e.map_source
protected lemma symm_maps_to : maps_to e.symm e.target e.source := e.symm.maps_to
protected lemma left_inv_on : left_inv_on e.symm e e.source := λ x, e.left_inv
protected lemma right_inv_on : right_inv_on e.symm e e.target := λ x, e.right_inv
protected lemma inv_on : inv_on e.symm e e.source e.target := ⟨e.left_inv_on, e.right_inv_on⟩
protected lemma inj_on : inj_on e e.source := e.left_inv_on.inj_on
protected lemma bij_on : bij_on e e.source e.target := e.inv_on.bij_on e.maps_to e.symm_maps_to
protected lemma surj_on : surj_on e e.source e.target := e.bij_on.surj_on

/-- A homeomorphism induces a local homeomorphism on the whole space -/
@[simps {simp_rhs := tt, .. mfld_cfg}]
def _root_.homeomorph.to_local_homeomorph (e : α ≃ₜ β) :
  local_homeomorph α β :=
{ open_source        := is_open_univ,
  open_target        := is_open_univ,
  continuous_to_fun  := by { erw ← continuous_iff_continuous_on_univ, exact e.continuous_to_fun },
  continuous_inv_fun := by { erw ← continuous_iff_continuous_on_univ, exact e.continuous_inv_fun },
  ..e.to_equiv.to_local_equiv }

/-- Replace `to_local_equiv` field to provide better definitional equalities. -/
def replace_equiv (e : local_homeomorph α β) (e' : local_equiv α β) (h : e.to_local_equiv = e') :
  local_homeomorph α β :=
{ to_local_equiv := e',
  open_source := h ▸ e.open_source,
  open_target := h ▸ e.open_target,
  continuous_to_fun := h ▸ e.continuous_to_fun,
  continuous_inv_fun := h ▸ e.continuous_inv_fun }

lemma replace_equiv_eq_self (e : local_homeomorph α β) (e' : local_equiv α β)
  (h : e.to_local_equiv = e') :
  e.replace_equiv e' h = e :=
by { cases e, subst e', refl }

lemma source_preimage_target : e.source ⊆ e ⁻¹' e.target := e.maps_to

lemma eq_of_local_equiv_eq {e e' : local_homeomorph α β}
  (h : e.to_local_equiv = e'.to_local_equiv) : e = e' :=
by { cases e, cases e', cases h, refl }

lemma eventually_left_inverse (e : local_homeomorph α β) {x} (hx : x ∈ e.source) :
  ∀ᶠ y in 𝓝 x, e.symm (e y) = y :=
(e.open_source.eventually_mem hx).mono e.left_inv'

lemma eventually_left_inverse' (e : local_homeomorph α β) {x} (hx : x ∈ e.target) :
  ∀ᶠ y in 𝓝 (e.symm x), e.symm (e y) = y :=
e.eventually_left_inverse (e.map_target hx)

lemma eventually_right_inverse (e : local_homeomorph α β) {x} (hx : x ∈ e.target) :
  ∀ᶠ y in 𝓝 x, e (e.symm y) = y :=
(e.open_target.eventually_mem hx).mono e.right_inv'

lemma eventually_right_inverse' (e : local_homeomorph α β) {x} (hx : x ∈ e.source) :
  ∀ᶠ y in 𝓝 (e x), e (e.symm y) = y :=
e.eventually_right_inverse (e.map_source hx)

lemma eventually_ne_nhds_within (e : local_homeomorph α β) {x} (hx : x ∈ e.source) :
  ∀ᶠ x' in 𝓝[≠] x, e x' ≠ e x :=
eventually_nhds_within_iff.2 $ (e.eventually_left_inverse hx).mono $
  λ x' hx', mt $ λ h, by rw [mem_singleton_iff, ← e.left_inv hx, ← h, hx']

lemma nhds_within_source_inter {x} (hx : x ∈ e.source) (s : set α) :
  𝓝[e.source ∩ s] x = 𝓝[s] x :=
nhds_within_inter_of_mem (mem_nhds_within_of_mem_nhds $ is_open.mem_nhds e.open_source hx)

lemma nhds_within_target_inter {x} (hx : x ∈ e.target) (s : set β) :
  𝓝[e.target ∩ s] x = 𝓝[s] x :=
e.symm.nhds_within_source_inter hx s

lemma image_eq_target_inter_inv_preimage {s : set α} (h : s ⊆ e.source) :
  e '' s = e.target ∩ e.symm ⁻¹' s :=
e.to_local_equiv.image_eq_target_inter_inv_preimage h

lemma image_source_inter_eq' (s : set α) :
  e '' (e.source ∩ s) = e.target ∩ e.symm ⁻¹' s :=
e.to_local_equiv.image_source_inter_eq' s

lemma image_source_inter_eq (s : set α) :
  e '' (e.source ∩ s) = e.target ∩ e.symm ⁻¹' (e.source ∩ s) :=
e.to_local_equiv.image_source_inter_eq s

lemma symm_image_eq_source_inter_preimage {s : set β} (h : s ⊆ e.target) :
  e.symm '' s = e.source ∩ e ⁻¹' s :=
e.symm.image_eq_target_inter_inv_preimage h

lemma symm_image_target_inter_eq (s : set β) :
  e.symm '' (e.target ∩ s) = e.source ∩ e ⁻¹' (e.target ∩ s) :=
e.symm.image_source_inter_eq _

lemma source_inter_preimage_inv_preimage (s : set α) :
  e.source ∩ e ⁻¹' (e.symm ⁻¹' s) = e.source ∩ s :=
e.to_local_equiv.source_inter_preimage_inv_preimage s

lemma target_inter_inv_preimage_preimage (s : set β) :
  e.target ∩ e.symm ⁻¹' (e ⁻¹' s) = e.target ∩ s :=
e.symm.source_inter_preimage_inv_preimage _

lemma source_inter_preimage_target_inter (s : set β) :
  e.source ∩ (e ⁻¹' (e.target ∩ s)) = e.source ∩ (e ⁻¹' s) :=
e.to_local_equiv.source_inter_preimage_target_inter s

lemma image_source_eq_target (e : local_homeomorph α β) : e '' e.source = e.target :=
e.to_local_equiv.image_source_eq_target

lemma symm_image_target_eq_source (e : local_homeomorph α β) : e.symm '' e.target = e.source :=
e.symm.image_source_eq_target

/-- Two local homeomorphisms are equal when they have equal `to_fun`, `inv_fun` and `source`.
It is not sufficient to have equal `to_fun` and `source`, as this only determines `inv_fun` on
the target. This would only be true for a weaker notion of equality, arguably the right one,
called `eq_on_source`. -/
@[ext]
protected lemma ext (e' : local_homeomorph α β) (h : ∀x, e x = e' x)
  (hinv : ∀x, e.symm x = e'.symm x) (hs : e.source = e'.source) : e = e' :=
eq_of_local_equiv_eq (local_equiv.ext h hinv hs)

protected lemma ext_iff {e e' : local_homeomorph α β} : e = e' ↔ (∀ x, e x = e' x) ∧
  (∀ x, e.symm x = e'.symm x) ∧ e.source = e'.source :=
⟨by { rintro rfl, exact ⟨λ x, rfl, λ x, rfl, rfl⟩ }, λ h, e.ext e' h.1 h.2.1 h.2.2⟩

@[simp, mfld_simps] lemma symm_to_local_equiv : e.symm.to_local_equiv = e.to_local_equiv.symm := rfl
-- The following lemmas are already simp via local_equiv
lemma symm_source : e.symm.source = e.target := rfl
lemma symm_target : e.symm.target = e.source := rfl
@[simp, mfld_simps] lemma symm_symm : e.symm.symm = e := eq_of_local_equiv_eq $ by simp

/-- A local homeomorphism is continuous at any point of its source -/
protected

/-- A local homeomorphism inverse is continuous at any point of its target -/
lemma continuous_at_symm {x : β} (h : x ∈ e.target) : continuous_at e.symm x :=
e.symm.continuous_at h

lemma tendsto_symm {x} (hx : x ∈ e.source) :
  tendsto e.symm (𝓝 (e x)) (𝓝 x) :=
by simpa only [continuous_at, e.left_inv hx] using e.continuous_at_symm (e.map_source hx)

lemma map_nhds_eq {x} (hx : x ∈ e.source) : map e (𝓝 x) = 𝓝 (e x) :=
le_antisymm (e.continuous_at hx) $
  le_map_of_right_inverse (e.eventually_right_inverse' hx) (e.tendsto_symm hx)

lemma symm_map_nhds_eq {x} (hx : x ∈ e.source) :
  map e.symm (𝓝 (e x)) = 𝓝 x :=
(e.symm.map_nhds_eq $ e.map_source hx).trans $ by rw e.left_inv hx

lemma image_mem_nhds {x} (hx : x ∈ e.source) {s : set α} (hs : s ∈ 𝓝 x) :
  e '' s ∈ 𝓝 (e x) :=
e.map_nhds_eq hx ▸ filter.image_mem_map hs

lemma map_nhds_within_eq (e : local_homeomorph α β) {x} (hx : x ∈ e.source) (s : set α) :
  map e (𝓝[s] x) = 𝓝[e '' (e.source ∩ s)] (e x) :=
calc map e (𝓝[s] x) = map e (𝓝[e.source ∩ s] x) :
  congr_arg (map e) (e.nhds_within_source_inter hx _).symm
... = 𝓝[e '' (e.source ∩ s)] (e x) :
  (e.left_inv_on.mono $ inter_subset_left _ _).map_nhds_within_eq (e.left_inv hx)
    (e.continuous_at_symm (e.map_source hx)).continuous_within_at
    (e.continuous_at hx).continuous_within_at

lemma map_nhds_within_preimage_eq (e : local_homeomorph α β) {x} (hx : x ∈ e.source) (s : set β) :
  map e (𝓝[e ⁻¹' s] x) = 𝓝[s] (e x) :=
by rw [e.map_nhds_within_eq hx, e.image_source_inter_eq', e.target_inter_inv_preimage_preimage,
  e.nhds_within_target_inter (e.map_source hx)]

lemma eventually_nhds (e : local_homeomorph α β) {x : α} (p : β → Prop)
  (hx : x ∈ e.source) : (∀ᶠ y in 𝓝 (e x), p y) ↔ ∀ᶠ x in 𝓝 x, p (e x) :=
iff.trans (by rw [e.map_nhds_eq hx]) eventually_map

lemma eventually_nhds' (e : local_homeomorph α β) {x : α} (p : α → Prop)
  (hx : x ∈ e.source) : (∀ᶠ y in 𝓝 (e x), p (e.symm y)) ↔ ∀ᶠ x in 𝓝 x, p x :=
begin
  rw [e.eventually_nhds _ hx],
  refine eventually_congr ((e.eventually_left_inverse hx).mono $ λ y hy, _),
  rw [hy]
end

lemma eventually_nhds_within (e : local_homeomorph α β) {x : α} (p : β → Prop) {s : set α}
  (hx : x ∈ e.source) : (∀ᶠ y in 𝓝[e.symm ⁻¹' s] (e x), p y) ↔ ∀ᶠ x in 𝓝[s] x, p (e x) :=
begin
  refine iff.trans _ eventually_map,
  rw [e.map_nhds_within_eq hx, e.image_source_inter_eq', e.nhds_within_target_inter (e.maps_to hx)]
end

lemma eventually_nhds_within' (e : local_homeomorph α β) {x : α} (p : α → Prop) {s : set α}
  (hx : x ∈ e.source) : (∀ᶠ y in 𝓝[e.symm ⁻¹' s] (e x), p (e.symm y)) ↔ ∀ᶠ x in 𝓝[s] x, p x :=
begin
  rw [e.eventually_nhds_within _ hx],
  refine eventually_congr ((eventually_nhds_within_of_eventually_nhds $
    e.eventually_left_inverse hx).mono $ λ y hy, _),
  rw [hy]
end

/-- This lemma is useful in the manifold library in the case that `e` is a chart. It states that
  locally around `e x` the set `e.symm ⁻¹' s` is the same as the set intersected with the target
  of `e` and some other neighborhood of `f x` (which will be the source of a chart on `γ`).  -/
lemma preimage_eventually_eq_target_inter_preimage_inter
  {e : local_homeomorph α β} {s : set α} {t : set γ} {x : α}
  {f : α → γ} (hf : continuous_within_at f s x) (hxe : x ∈ e.source) (ht : t ∈ 𝓝 (f x)) :
  e.symm ⁻¹' s =ᶠ[𝓝 (e x)] (e.target ∩ e.symm ⁻¹' (s ∩ f ⁻¹' t) : set β) :=
begin
  rw [eventually_eq_set, e.eventually_nhds _ hxe],
  filter_upwards [(e.open_source.mem_nhds hxe),
    mem_nhds_within_iff_eventually.mp (hf.preimage_mem_nhds_within ht)],
  intros y hy hyu,
  simp_rw [mem_inter_iff, mem_preimage, mem_inter_iff, e.maps_to hy, true_and, iff_self_and,
    e.left_inv hy, iff_true_intro hyu]
end

lemma preimage_open_of_open {s : set β} (hs : is_open s) : is_open (e.source ∩ e ⁻¹' s) :=
e.continuous_on.preimage_open_of_open e.open_source hs

/-!
### `local_homeomorph.is_image` relation

We say that `t : set β` is an image of `s : set α` under a local homeomorphism `e` if any of the
following equivalent conditions hold:

* `e '' (e.source ∩ s) = e.target ∩ t`;
* `e.source ∩ e ⁻¹ t = e.source ∩ s`;
* `∀ x ∈ e.source, e x ∈ t ↔ x ∈ s` (this one is used in the definition).

This definition is a restatement of `local_equiv.is_image` for local homeomorphisms. In this section
we transfer API about `local_equiv.is_image` to local homeomorphisms and add a few
`local_homeomorph`-specific lemmas like `local_homeomorph.is_image.closure`.
-/

/-- We say that `t : set β` is an image of `s : set α` under a local homeomorphism `e` if any of the
following equivalent conditions hold:

* `e '' (e.source ∩ s) = e.target ∩ t`;
* `e.source ∩ e ⁻¹ t = e.source ∩ s`;
* `∀ x ∈ e.source, e x ∈ t ↔ x ∈ s` (this one is used in the definition).
-/
def is_image (s : set α) (t : set β) : Prop := ∀ ⦃x⦄, x ∈ e.source → (e x ∈ t ↔ x ∈ s)

namespace is_image

variables {e} {s : set α} {t : set β} {x : α} {y : β}

lemma to_local_equiv (h : e.is_image s t) : e.to_local_equiv.is_image s t := h

lemma apply_mem_iff (h : e.is_image s t) (hx : x ∈ e.source) : e x ∈ t ↔ x ∈ s := h hx

protected lemma symm (h : e.is_image s t) : e.symm.is_image t s := h.to_local_equiv.symm

lemma symm_apply_mem_iff (h : e.is_image s t) (hy : y ∈ e.target) : (e.symm y ∈ s ↔ y ∈ t) :=
h.symm hy

@[simp] lemma symm_iff : e.symm.is_image t s ↔ e.is_image s t := ⟨λ h, h.symm, λ h, h.symm⟩

protected lemma maps_to (h : e.is_image s t) : maps_to e (e.source ∩ s) (e.target ∩ t) :=
h.to_local_equiv.maps_to

lemma symm_maps_to (h : e.is_image s t) : maps_to e.symm (e.target ∩ t) (e.source ∩ s) :=
h.symm.maps_to

lemma image_eq (h : e.is_image s t) : e '' (e.source ∩ s) = e.target ∩ t :=
h.to_local_equiv.image_eq

lemma symm_image_eq (h : e.is_image s t) : e.symm '' (e.target ∩ t) = e.source ∩ s :=
h.symm.image_eq

lemma iff_preimage_eq : e.is_image s t ↔ e.source ∩ e ⁻¹' t = e.source ∩ s :=
local_equiv.is_image.iff_preimage_eq

alias iff_preimage_eq ↔ preimage_eq of_preimage_eq

lemma iff_symm_preimage_eq : e.is_image s t ↔ e.target ∩ e.symm ⁻¹' s = e.target ∩ t :=
symm_iff.symm.trans iff_preimage_eq

alias iff_symm_preimage_eq ↔ symm_preimage_eq of_symm_preimage_eq

lemma iff_symm_preimage_eq' :
  e.is_image s t ↔ e.target ∩ e.symm ⁻¹' (e.source ∩ s) = e.target ∩ t :=
by rw [iff_symm_preimage_eq, ← image_source_inter_eq, ← image_source_inter_eq']

alias iff_symm_preimage_eq' ↔ symm_preimage_eq' of_symm_preimage_eq'

lemma iff_preimage_eq' : e.is_image s t ↔ e.source ∩ e ⁻¹' (e.target ∩ t) = e.source ∩ s :=
symm_iff.symm.trans iff_symm_preimage_eq'

alias iff_preimage_eq' ↔ preimage_eq' of_preimage_eq'

lemma of_image_eq (h : e '' (e.source ∩ s) = e.target ∩ t) : e.is_image s t :=
local_equiv.is_image.of_image_eq h

lemma of_symm_image_eq (h : e.symm '' (e.target ∩ t) = e.source ∩ s) : e.is_image s t :=
local_equiv.is_image.of_symm_image_eq h

protected lemma compl (h : e.is_image s t) : e.is_image sᶜ tᶜ :=
λ x hx, not_congr (h hx)

protected lemma inter {s' t'} (h : e.is_image s t) (h' : e.is_image s' t') :
  e.is_image (s ∩ s') (t ∩ t') :=
λ x hx, and_congr (h hx) (h' hx)

protected lemma union {s' t'} (h : e.is_image s t) (h' : e.is_image s' t') :
  e.is_image (s ∪ s') (t ∪ t') :=
λ x hx, or_congr (h hx) (h' hx)

protected lemma diff {s' t'} (h : e.is_image s t) (h' : e.is_image s' t') :
  e.is_image (s \ s') (t \ t') :=
h.inter h'.compl

lemma left_inv_on_piecewise {e' : local_homeomorph α β} [∀ i, decidable (i ∈ s)]
  [∀ i, decidable (i ∈ t)] (h : e.is_image s t) (h' : e'.is_image s t) :
  left_inv_on (t.piecewise e.symm e'.symm) (s.piecewise e e') (s.ite e.source e'.source) :=
h.to_local_equiv.left_inv_on_piecewise h'

lemma inter_eq_of_inter_eq_of_eq_on {e' : local_homeomorph α β} (h : e.is_image s t)
  (h' : e'.is_image s t) (hs : e.source ∩ s = e'.source ∩ s) (Heq : eq_on e e' (e.source ∩ s)) :
  e.target ∩ t = e'.target ∩ t :=
h.to_local_equiv.inter_eq_of_inter_eq_of_eq_on h' hs Heq

lemma symm_eq_on_of_inter_eq_of_eq_on {e' : local_homeomorph α β} (h : e.is_image s t)
  (hs : e.source ∩ s = e'.source ∩ s) (Heq : eq_on e e' (e.source ∩ s)) :
  eq_on e.symm e'.symm (e.target ∩ t) :=
h.to_local_equiv.symm_eq_on_of_inter_eq_of_eq_on hs Heq

lemma map_nhds_within_eq (h : e.is_image s t) (hx : x ∈ e.source) :
  map e (𝓝[s] x) = 𝓝[t] (e x) :=
by rw [e.map_nhds_within_eq hx, h.image_eq, e.nhds_within_target_inter (e.map_source hx)]

protected lemma closure (h : e.is_image s t) : e.is_image (closure s) (closure t) :=
λ x hx, by simp only [mem_closure_iff_nhds_within_ne_bot, ← h.map_nhds_within_eq hx, map_ne_bot_iff]

protected lemma interior (h : e.is_image s t) : e.is_image (interior s) (interior t) :=
by simpa only [closure_compl, compl_compl] using h.compl.closure.compl

protected lemma frontier (h : e.is_image s t) :
  e.is_image (frontier s) (frontier t) :=
h.closure.diff h.interior

lemma is_open_iff (h : e.is_image s t) :
  is_open (e.source ∩ s) ↔ is_open (e.target ∩ t) :=
⟨λ hs, h.symm_preimage_eq' ▸ e.symm.preimage_open_of_open hs,
  λ hs, h.preimage_eq' ▸ e.preimage_open_of_open hs⟩

/-- Restrict a `local_homeomorph` to a pair of corresponding open sets. -/
@[simps to_local_equiv] def restr (h : e.is_image s t) (hs : is_open (e.source ∩ s)) :
  local_homeomorph α β :=
{ to_local_equiv := h.to_local_equiv.restr,
  open_source := hs,
  open_target := h.is_open_iff.1 hs,
  continuous_to_fun := e.continuous_on.mono (inter_subset_left _ _),
  continuous_inv_fun := e.symm.continuous_on.mono (inter_subset_left _ _) }

end is_image

lemma is_image_source_target : e.is_image e.source e.target :=
e.to_local_equiv.is_image_source_target

lemma is_image_source_target_of_disjoint (e' : local_homeomorph α β)
  (hs : disjoint e.source e'.source) (ht : disjoint e.target e'.target) :
  e.is_image e'.source e'.target :=
e.to_local_equiv.is_image_source_target_of_disjoint e'.to_local_equiv hs ht

/-- Preimage of interior or interior of preimage coincide for local homeomorphisms, when restricted
to the source. -/
lemma preimage_interior (s : set β) :
  e.source ∩ e ⁻¹' (interior s) = e.source ∩ interior (e ⁻¹' s) :=
(is_image.of_preimage_eq rfl).interior.preimage_eq

lemma preimage_closure (s : set β) :
  e.source ∩ e ⁻¹' (closure s) = e.source ∩ closure (e ⁻¹' s) :=
(is_image.of_preimage_eq rfl).closure.preimage_eq

lemma preimage_frontier (s : set β) :
  e.source ∩ e ⁻¹' (frontier s) = e.source ∩ frontier (e ⁻¹' s) :=
(is_image.of_preimage_eq rfl).frontier.preimage_eq

lemma preimage_open_of_open_symm {s : set α} (hs : is_open s) :
  is_open (e.target ∩ e.symm ⁻¹' s) :=
e.symm.continuous_on.preimage_open_of_open e.open_target hs

/-- The image of an open set in the source is open. -/
lemma image_open_of_open {s : set α} (hs : is_open s) (h : s ⊆ e.source) : is_open (e '' s) :=
begin
  have : e '' s = e.target ∩ e.symm ⁻¹' s :=
    e.to_local_equiv.image_eq_target_inter_inv_preimage h,
  rw this,
  exact e.continuous_on_symm.preimage_open_of_open e.open_target hs
end

/-- The image of the restriction of an open set to the source is open. -/
lemma image_open_of_open' {s : set α} (hs : is_open s) : is_open (e '' (e.source ∩ s)) :=
image_open_of_open _ (is_open.inter e.open_source hs) (inter_subset_left _ _)

/-- A `local_equiv` with continuous open forward map and an open source is a `local_homeomorph`. -/
def of_continuous_open_restrict (e : local_equiv α β) (hc : continuous_on e e.source)
  (ho : is_open_map (e.source.restrict e)) (hs : is_open e.source) :
  local_homeomorph α β :=
{ to_local_equiv := e,
  open_source := hs,
  open_target := by simpa only [range_restrict, e.image_source_eq_target] using ho.is_open_range,
  continuous_to_fun := hc,
  continuous_inv_fun := e.image_source_eq_target ▸
    ho.continuous_on_image_of_left_inv_on e.left_inv_on }

/-- A `local_equiv` with continuous open forward map and an open source is a `local_homeomorph`. -/
def of_continuous_open (e : local_equiv α β) (hc : continuous_on e e.source)
  (ho : is_open_map e) (hs : is_open e.source) :
  local_homeomorph α β :=
of_continuous_open_restrict e hc (ho.restrict hs) hs

/-- Restricting a local homeomorphism `e` to `e.source ∩ s` when `s` is open. This is sometimes hard
to use because of the openness assumption, but it has the advantage that when it can
be used then its local_equiv is defeq to local_equiv.restr -/
protected def restr_open (s : set α) (hs : is_open s) :
  local_homeomorph α β :=
(@is_image.of_symm_preimage_eq α β _ _ e s (e.symm ⁻¹' s) rfl).restr
  (is_open.inter e.open_source hs)

@[simp, mfld_simps] lemma restr_open_to_local_equiv (s : set α) (hs : is_open s) :
  (e.restr_open s hs).to_local_equiv = e.to_local_equiv.restr s := rfl

-- Already simp via local_equiv
lemma restr_open_source (s : set α) (hs : is_open s) :
  (e.restr_open s hs).source = e.source ∩ s := rfl

/-- Restricting a local homeomorphism `e` to `e.source ∩ interior s`. We use the interior to make
sure that the restriction is well defined whatever the set s, since local homeomorphisms are by
definition defined on open sets. In applications where `s` is open, this coincides with the
restriction of local equivalences -/
@[simps apply symm_apply (mfld_cfg), simps source target {attrs := []}]
protected def restr (s : set α) : local_homeomorph α β :=
e.restr_open (interior s) is_open_interior

@[simp, mfld_simps] lemma restr_to_local_equiv (s : set α) :
  (e.restr s).to_local_equiv = (e.to_local_equiv).restr (interior s) := rfl

lemma restr_source' (s : set α) (hs : is_open s) : (e.restr s).source = e.source ∩ s :=
by rw [e.restr_source, hs.interior_eq]

lemma restr_to_local_equiv' (s : set α) (hs : is_open s):
  (e.restr s).to_local_equiv = e.to_local_equiv.restr s :=
by rw [e.restr_to_local_equiv, hs.interior_eq]

lemma restr_eq_of_source_subset {e : local_homeomorph α β} {s : set α} (h : e.source ⊆ s) :
  e.restr s = e :=
begin
  apply eq_of_local_equiv_eq,
  rw restr_to_local_equiv,
  apply local_equiv.restr_eq_of_source_subset,
  exact interior_maximal h e.open_source
end

@[simp, mfld_simps] lemma restr_univ {e : local_homeomorph α β} : e.restr univ = e :=
restr_eq_of_source_subset (subset_univ _)

lemma restr_source_inter (s : set α) : e.restr (e.source ∩ s) = e.restr s :=
begin
  refine local_homeomorph.ext _ _ (λx, rfl) (λx, rfl) _,
  simp [e.open_source.interior_eq, ← inter_assoc]
end

/-- The identity on the whole space as a local homeomorphism. -/
@[simps apply (mfld_cfg), simps source target {attrs := []}]
protected def refl (α : Type*) [topological_space α] : local_homeomorph α α :=
(homeomorph.refl α).to_local_homeomorph

@[simp, mfld_simps] lemma refl_local_equiv :
  (local_homeomorph.refl α).to_local_equiv = local_equiv.refl α := rfl
@[simp, mfld_simps] lemma refl_symm : (local_homeomorph.refl α).symm = local_homeomorph.refl α :=
rfl

section
variables {s : set α} (hs : is_open s)

/-- The identity local equiv on a set `s` -/
@[simps apply (mfld_cfg), simps source target {attrs := []}]
def of_set (s : set α) (hs : is_open s) : local_homeomorph α α :=
{ open_source        := hs,
  open_target        := hs,
  continuous_to_fun  := continuous_id.continuous_on,
  continuous_inv_fun := continuous_id.continuous_on,
  ..local_equiv.of_set s }

@[simp, mfld_simps] lemma of_set_to_local_equiv :
  (of_set s hs).to_local_equiv = local_equiv.of_set s := rfl
@[simp, mfld_simps] lemma of_set_symm : (of_set s hs).symm = of_set s hs := rfl

@[simp, mfld_simps] lemma of_set_univ_eq_refl :
  of_set univ is_open_univ = local_homeomorph.refl α :=
by ext; simp

end

/-- Composition of two local homeomorphisms when the target of the first and the source of
the second coincide. -/
protected def trans' (h : e.target = e'.source) : local_homeomorph α γ :=
{ open_source       := e.open_source,
  open_target       := e'.open_target,
  continuous_to_fun := begin
    apply e'.continuous_to_fun.comp e.continuous_to_fun,
    rw ← h,
    exact e.to_local_equiv.source_subset_preimage_target
  end,
  continuous_inv_fun := begin
    apply e.continuous_inv_fun.comp e'.continuous_inv_fun,
    rw h,
    exact e'.to_local_equiv.target_subset_preimage_source
  end,
  ..local_equiv.trans' e.to_local_equiv e'.to_local_equiv h }

/-- Composing two local homeomorphisms, by restricting to the maximal domain where their
composition is well defined. -/
protected def trans : local_homeomorph α γ :=
  local_homeomorph.trans' (e.symm.restr_open e'.source e'.open_source).symm
    (e'.restr_open e.target e.open_target) (by simp [inter_comm])

@[simp, mfld_simps] lemma trans_to_local_equiv :
  (e.trans e').to_local_equiv = e.to_local_equiv.trans e'.to_local_equiv := rfl
@[simp, mfld_simps] lemma coe_trans : (e.trans e' : α → γ) = e' ∘ e := rfl
@[simp, mfld_simps] lemma coe_trans_symm : ((e.trans e').symm : γ → α) = e.symm ∘ e'.symm := rfl
lemma trans_apply {x : α} : (e.trans e') x = e' (e x) := rfl

lemma trans_symm_eq_symm_trans_symm : (e.trans e').symm = e'.symm.trans e.symm :=
by cases e; cases e'; refl

/- This could be considered as a simp lemma, but there are many situations where it makes something
simple into something more complicated. -/
lemma trans_source : (e.trans e').source = e.source ∩ e ⁻¹' e'.source :=
local_equiv.trans_source e.to_local_equiv e'.to_local_equiv

lemma trans_source' : (e.trans e').source = e.source ∩ e ⁻¹' (e.target ∩ e'.source) :=
local_equiv.trans_source' e.to_local_equiv e'.to_local_equiv

lemma trans_source'' : (e.trans e').source = e.symm '' (e.target ∩ e'.source) :=
local_equiv.trans_source'' e.to_local_equiv e'.to_local_equiv

lemma image_trans_source : e '' (e.trans e').source = e.target ∩ e'.source :=
local_equiv.image_trans_source e.to_local_equiv e'.to_local_equiv

lemma trans_target : (e.trans e').target = e'.target ∩ e'.symm ⁻¹' e.target := rfl

lemma trans_target' : (e.trans e').target = e'.target ∩ e'.symm ⁻¹' (e'.source ∩ e.target) :=
trans_source' e'.symm e.symm

lemma trans_target'' : (e.trans e').target = e' '' (e'.source ∩ e.target) :=
trans_source'' e'.symm e.symm

lemma inv_image_trans_target : e'.symm '' (e.trans e').target = e'.source ∩ e.target :=
image_trans_source e'.symm e.symm

lemma trans_assoc (e'' : local_homeomorph γ δ) :
  (e.trans e').trans e'' = e.trans (e'.trans e'') :=
eq_of_local_equiv_eq $ local_equiv.trans_assoc e.to_local_equiv e'.to_local_equiv e''.to_local_equiv

@[simp, mfld_simps] lemma trans_refl : e.trans (local_homeomorph.refl β) = e :=
eq_of_local_equiv_eq $ local_equiv.trans_refl e.to_local_equiv

@[simp, mfld_simps] lemma refl_trans : (local_homeomorph.refl α).trans e = e :=
eq_of_local_equiv_eq $ local_equiv.refl_trans e.to_local_equiv

lemma trans_of_set {s : set β} (hs : is_open s) :
  e.trans (of_set s hs) = e.restr (e ⁻¹' s) :=
local_homeomorph.ext _ _ (λx, rfl) (λx, rfl) $
  by simp [local_equiv.trans_source, (e.preimage_interior _).symm, hs.interior_eq]

lemma trans_of_set' {s : set β} (hs : is_open s) :
  e.trans (of_set s hs) = e.restr (e.source ∩ e ⁻¹' s) :=
by rw [trans_of_set, restr_source_inter]

lemma of_set_trans {s : set α} (hs : is_open s) :
  (of_set s hs).trans e = e.restr s :=
local_homeomorph.ext _ _ (λx, rfl) (λx, rfl) $
  by simp [local_equiv.trans_source, hs.interior_eq, inter_comm]

lemma of_set_trans' {s : set α} (hs : is_open s) :
  (of_set s hs).trans e = e.restr (e.source ∩ s) :=
by rw [of_set_trans, restr_source_inter]

@[simp, mfld_simps] lemma of_set_trans_of_set
  {s : set α} (hs : is_open s) {s' : set α} (hs' : is_open s') :
  (of_set s hs).trans (of_set s' hs') = of_set (s ∩ s') (is_open.inter hs hs')  :=
begin
  rw (of_set s hs).trans_of_set hs',
  ext; simp [hs'.interior_eq]
end

lemma restr_trans (s : set α) :
  (e.restr s).trans e' = (e.trans e').restr s :=
eq_of_local_equiv_eq $ local_equiv.restr_trans e.to_local_equiv e'.to_local_equiv (interior s)

/-- Postcompose a local homeomorphism with an homeomorphism.
We modify the source and target to have better definitional behavior. -/
@[simps {fully_applied := ff}]
def trans_homeomorph (e' : β ≃ₜ γ) : local_homeomorph α γ :=
{ to_local_equiv := e.to_local_equiv.trans_equiv e'.to_equiv,
  open_source := e.open_source,
  open_target := e.open_target.preimage e'.symm.continuous,
  continuous_to_fun := e'.continuous.comp_continuous_on e.continuous_on,
  continuous_inv_fun := e.symm.continuous_on.comp e'.symm.continuous.continuous_on (λ x h, h) }

lemma trans_equiv_eq_trans (e' : β ≃ₜ γ) : e.trans_homeomorph e' = e.trans e'.to_local_homeomorph :=
to_local_equiv_injective $ local_equiv.trans_equiv_eq_trans _ _

/-- Precompose a local homeomorphism with an homeomorphism.
We modify the source and target to have better definitional behavior. -/
@[simps {fully_applied := ff}]
def _root_.homeomorph.trans_local_homeomorph (e : α ≃ₜ β) : local_homeomorph α γ :=
{ to_local_equiv := e.to_equiv.trans_local_equiv e'.to_local_equiv,
  open_source := e'.open_source.preimage e.continuous,
  open_target := e'.open_target,
  continuous_to_fun := e'.continuous_on.comp e.continuous.continuous_on (λ x h, h),
  continuous_inv_fun := e.symm.continuous.comp_continuous_on e'.symm.continuous_on }

lemma _root_.homeomorph.trans_local_homeomorph_eq_trans (e : α ≃ₜ β) :
  e.trans_local_homeomorph e' = e.to_local_homeomorph.trans e' :=
to_local_equiv_injective $ equiv.trans_local_equiv_eq_trans _ _

/-- `eq_on_source e e'` means that `e` and `e'` have the same source, and coincide there. They
should really be considered the same local equiv. -/
def eq_on_source (e e' : local_homeomorph α β) : Prop :=
e.source = e'.source ∧ (eq_on e e' e.source)

lemma eq_on_source_iff (e e' : local_homeomorph α β) :
eq_on_source e e' ↔ local_equiv.eq_on_source e.to_local_equiv e'.to_local_equiv :=
iff.rfl

/-- `eq_on_source` is an equivalence relation -/
instance : setoid (local_homeomorph α β) :=
{ r     := eq_on_source,
  iseqv := ⟨
    λe, (@local_equiv.eq_on_source_setoid α β).iseqv.1 e.to_local_equiv,
    λe e' h, (@local_equiv.eq_on_source_setoid α β).iseqv.2.1 ((eq_on_source_iff e e').1 h),
    λe e' e'' h h', (@local_equiv.eq_on_source_setoid α β).iseqv.2.2
      ((eq_on_source_iff e e').1 h) ((eq_on_source_iff e' e'').1 h')⟩ }

lemma eq_on_source_refl : e ≈ e := setoid.refl _

/-- If two local homeomorphisms are equivalent, so are their inverses -/
lemma eq_on_source.symm' {e e' : local_homeomorph α β} (h : e ≈ e') : e.symm ≈ e'.symm :=
local_equiv.eq_on_source.symm' h

/-- Two equivalent local homeomorphisms have the same source -/
lemma eq_on_source.source_eq {e e' : local_homeomorph α β} (h : e ≈ e') : e.source = e'.source :=
h.1

/-- Two equivalent local homeomorphisms have the same target -/
lemma eq_on_source.target_eq {e e' : local_homeomorph α β} (h : e ≈ e') : e.target = e'.target :=
h.symm'.1

/-- Two equivalent local homeomorphisms have coinciding `to_fun` on the source -/
lemma eq_on_source.eq_on {e e' : local_homeomorph α β} (h : e ≈ e') :
  eq_on e e' e.source :=
h.2

/-- Two equivalent local homeomorphisms have coinciding `inv_fun` on the target -/
lemma eq_on_source.symm_eq_on_target {e e' : local_homeomorph α β} (h : e ≈ e') :
  eq_on e.symm e'.symm e.target :=
h.symm'.2

/-- Composition of local homeomorphisms respects equivalence -/
lemma eq_on_source.trans' {e e' : local_homeomorph α β} {f f' : local_homeomorph β γ}
  (he : e ≈ e') (hf : f ≈ f') : e.trans f ≈ e'.trans f' :=
local_equiv.eq_on_source.trans' he hf

/-- Restriction of local homeomorphisms respects equivalence -/
lemma eq_on_source.restr {e e' : local_homeomorph α β} (he : e ≈ e') (s : set α) :
  e.restr s ≈ e'.restr s :=
local_equiv.eq_on_source.restr he _

lemma set.eq_on.restr_eq_on_source {e e' : local_homeomorph α β}
  (h : eq_on e e' (e.source ∩ e'.source)) :
  e.restr e'.source ≈ e'.restr e.source :=
begin
  split,
  { rw e'.restr_source' _ e.open_source,
    rw e.restr_source' _ e'.open_source,
    exact set.inter_comm _ _ },
  { rw e.restr_source' _ e'.open_source,
    refine (eq_on.trans _ h).trans _;
    simp only with mfld_simps },
end

/-- Composition of a local homeomorphism and its inverse is equivalent to the restriction of the
identity to the source -/
lemma trans_self_symm :
  e.trans e.symm ≈ local_homeomorph.of_set e.source e.open_source :=
local_equiv.trans_self_symm _

lemma trans_symm_self :
  e.symm.trans e ≈ local_homeomorph.of_set e.target e.open_target :=
e.symm.trans_self_symm

lemma eq_of_eq_on_source_univ {e e' : local_homeomorph α β} (h : e ≈ e')
  (s : e.source = univ) (t : e.target = univ) : e = e' :=
eq_of_local_equiv_eq $ local_equiv.eq_of_eq_on_source_univ _ _ h s t

section prod

/-- The product of two local homeomorphisms, as a local homeomorphism on the product space. -/
@[simps to_local_equiv apply (mfld_cfg), simps source target symm_apply {attrs := []}]
def prod (e : local_homeomorph α β) (e' : local_homeomorph γ δ) :
  local_homeomorph (α × γ) (β × δ) :=
{ open_source := e.open_source.prod e'.open_source,
  open_target := e.open_target.prod e'.open_target,
  continuous_to_fun := e.continuous_on.prod_map e'.continuous_on,
  continuous_inv_fun := e.continuous_on_symm.prod_map e'.continuous_on_symm,
  to_local_equiv := e.to_local_equiv.prod e'.to_local_equiv }

@[simp, mfld_simps] lemma prod_symm (e : local_homeomorph α β) (e' : local_homeomorph γ δ) :
  (e.prod e').symm = (e.symm.prod e'.symm) :=
rfl

@[simp]
lemma refl_prod_refl {α β : Type*} [topological_space α] [topological_space β] :
  (local_homeomorph.refl α).prod (local_homeomorph.refl β) = local_homeomorph.refl (α × β) :=
by { ext1 ⟨x, y⟩, { refl }, { rintro ⟨x, y⟩, refl }, exact univ_prod_univ }

@[simp, mfld_simps] lemma prod_trans
  {η : Type*} {ε : Type*} [topological_space η] [topological_space ε]
  (e : local_homeomorph α β) (f : local_homeomorph β γ)
  (e' : local_homeomorph δ η) (f' : local_homeomorph η ε) :
  (e.prod e').trans (f.prod f') = (e.trans f).prod (e'.trans f') :=
local_homeomorph.eq_of_local_equiv_eq $
  by dsimp only [trans_to_local_equiv, prod_to_local_equiv]; apply local_equiv.prod_trans

lemma prod_eq_prod_of_nonempty {e₁ e₁' : local_homeomorph α β} {e₂ e₂' : local_homeomorph γ δ}
  (h : (e₁.prod e₂).source.nonempty) :
  e₁.prod e₂ = e₁'.prod e₂' ↔ e₁ = e₁' ∧ e₂ = e₂' :=
begin
  obtain ⟨⟨x, y⟩, -⟩ := id h,
  haveI : nonempty α := ⟨x⟩,
  haveI : nonempty β  := ⟨e₁ x⟩,
  haveI : nonempty γ := ⟨y⟩,
  haveI : nonempty δ := ⟨e₂ y⟩,
  simp_rw [local_homeomorph.ext_iff, prod_apply, prod_symm_apply, prod_source, prod.ext_iff,
    set.prod_eq_prod_iff_of_nonempty h,
    forall_and_distrib, prod.forall, forall_const, forall_forall_const, and_assoc, and.left_comm]
end

lemma prod_eq_prod_of_nonempty' {e₁ e₁' : local_homeomorph α β} {e₂ e₂' : local_homeomorph γ δ}
  (h : (e₁'.prod e₂').source.nonempty) :
  e₁.prod e₂ = e₁'.prod e₂' ↔ e₁ = e₁' ∧ e₂ = e₂' :=
by rw [eq_comm, prod_eq_prod_of_nonempty h, eq_comm, @eq_comm _ e₂']

end prod

section piecewise

/-- Combine two `local_homeomorph`s using `set.piecewise`. The source of the new `local_homeomorph`
is `s.ite e.source e'.source = e.source ∩ s ∪ e'.source \ s`, and similarly for target.  The
function sends `e.source ∩ s` to `e.target ∩ t` using `e` and `e'.source \ s` to `e'.target \ t`
using `e'`, and similarly for the inverse function. To ensure that the maps `to_fun` and `inv_fun`
are inverse of each other on the new `source` and `target`, the definition assumes that the sets `s`
and `t` are related both by `e.is_image` and `e'.is_image`. To ensure that the new maps are
continuous on `source`/`target`, it also assumes that `e.source` and `e'.source` meet `frontier s`
on the same set and `e x = e' x` on this intersection. -/
@[simps to_local_equiv apply {fully_applied := ff}]
def piecewise (e e' : local_homeomorph α β) (s : set α) (t : set β)
  [∀ x, decidable (x ∈ s)] [∀ y, decidable (y ∈ t)] (H : e.is_image s t) (H' : e'.is_image s t)
  (Hs : e.source ∩ frontier s = e'.source ∩ frontier s)
  (Heq : eq_on e e' (e.source ∩ frontier s)) :
  local_homeomorph α β :=
{ to_local_equiv := e.to_local_equiv.piecewise e'.to_local_equiv s t H H',
  open_source := e.open_source.ite e'.open_source Hs,
  open_target := e.open_target.ite e'.open_target $
    H.frontier.inter_eq_of_inter_eq_of_eq_on H'.frontier Hs Heq,
  continuous_to_fun := continuous_on_piecewise_ite e.continuous_on e'.continuous_on Hs Heq,
  continuous_inv_fun := continuous_on_piecewise_ite e.continuous_on_symm e'.continuous_on_symm
    (H.frontier.inter_eq_of_inter_eq_of_eq_on H'.frontier Hs Heq)
    (H.frontier.symm_eq_on_of_inter_eq_of_eq_on Hs Heq) }

@[simp] lemma symm_piecewise (e e' : local_homeomorph α β) {s : set α} {t : set β}
  [∀ x, decidable (x ∈ s)] [∀ y, decidable (y ∈ t)] (H : e.is_image s t) (H' : e'.is_image s t)
  (Hs : e.source ∩ frontier s = e'.source ∩ frontier s)
  (Heq : eq_on e e' (e.source ∩ frontier s)) :
  (e.piecewise e' s t H H' Hs Heq).symm =
    e.symm.piecewise e'.symm t s H.symm H'.symm
      (H.frontier.inter_eq_of_inter_eq_of_eq_on H'.frontier Hs Heq)
      (H.frontier.symm_eq_on_of_inter_eq_of_eq_on Hs Heq) :=
rfl

/-- Combine two `local_homeomorph`s with disjoint sources and disjoint targets. We reuse
`local_homeomorph.piecewise` then override `to_local_equiv` to `local_equiv.disjoint_union`.
This way we have better definitional equalities for `source` and `target`. -/
def disjoint_union (e e' : local_homeomorph α β)
  [∀ x, decidable (x ∈ e.source)] [∀ y, decidable (y ∈ e.target)]
  (Hs : disjoint e.source e'.source) (Ht : disjoint e.target e'.target) :
  local_homeomorph α β :=
(e.piecewise e' e.source e.target e.is_image_source_target
  (e'.is_image_source_target_of_disjoint e Hs.symm Ht.symm)
  (by rw [e.open_source.inter_frontier_eq, (Hs.symm.frontier_right e'.open_source).inter_eq])
  (by { rw e.open_source.inter_frontier_eq, exact eq_on_empty _ _ })).replace_equiv
    (e.to_local_equiv.disjoint_union e'.to_local_equiv Hs Ht)
    (local_equiv.disjoint_union_eq_piecewise _ _ _ _).symm

end piecewise

section pi

variables {ι : Type*} [fintype ι] {Xi Yi : ι → Type*} [Π i, topological_space (Xi i)]
  [Π i, topological_space (Yi i)] (ei : Π i, local_homeomorph (Xi i) (Yi i))

/-- The product of a finite family of `local_homeomorph`s. -/
@[simps to_local_equiv] def pi : local_homeomorph (Π i, Xi i) (Π i, Yi i) :=
{ to_local_equiv := local_equiv.pi (λ i, (ei i).to_local_equiv),
  open_source := is_open_set_pi finite_univ $ λ i hi, (ei i).open_source,
  open_target := is_open_set_pi finite_univ $ λ i hi, (ei i).open_target,
  continuous_to_fun := continuous_on_pi.2 $ λ i, (ei i).continuous_on.comp
    (continuous_apply _).continuous_on (λ f hf, hf i trivial),
  continuous_inv_fun := continuous_on_pi.2 $ λ i, (ei i).continuous_on_symm.comp
    (continuous_apply _).continuous_on (λ f hf, hf i trivial) }

end pi

section continuity

/-- Continuity within a set at a point can be read under right composition with a local
homeomorphism, if the point is in its target -/
lemma continuous_within_at_iff_continuous_within_at_comp_right
  {f : β → γ} {s : set β} {x : β} (h : x ∈ e.target) :
  continuous_within_at f s x ↔ continuous_within_at (f ∘ e) (e ⁻¹' s) (e.symm x) :=
by simp_rw [continuous_within_at, ← @tendsto_map'_iff _ _ _ _ e,
  e.map_nhds_within_preimage_eq (e.map_target h), (∘), e.right_inv h]

/-- Continuity at a point can be read under right composition with a local homeomorphism, if the
point is in its target -/
lemma continuous_at_iff_continuous_at_comp_right
  {f : β → γ} {x : β} (h : x ∈ e.target) :
  continuous_at f x ↔ continuous_at (f ∘ e) (e.symm x) :=
by rw [← continuous_within_at_univ, e.continuous_within_at_iff_continuous_within_at_comp_right h,
       preimage_univ, continuous_within_at_univ]

/-- A function is continuous on a set if and only if its composition with a local homeomorphism
on the right is continuous on the corresponding set. -/
lemma continuous_on_iff_continuous_on_comp_right {f : β → γ} {s : set β} (h : s ⊆ e.target) :
  continuous_on f s ↔ continuous_on (f ∘ e) (e.source ∩ e ⁻¹' s) :=
begin
  simp only [← e.symm_image_eq_source_inter_preimage h, continuous_on, ball_image_iff],
  refine forall₂_congr (λ x hx, _),
  rw [e.continuous_within_at_iff_continuous_within_at_comp_right (h hx),
    e.symm_image_eq_source_inter_preimage h, inter_comm, continuous_within_at_inter],
  exact is_open.mem_nhds e.open_source (e.map_target (h hx))
end

/-- Continuity within a set at a point can be read under left composition with a local
homeomorphism if a neighborhood of the initial point is sent to the source of the local
homeomorphism-/
lemma continuous_within_at_iff_continuous_within_at_comp_left
  {f : γ → α} {s : set γ} {x : γ} (hx : f x ∈ e.source) (h : f ⁻¹' e.source ∈ 𝓝[s] x) :
  continuous_within_at f s x ↔ continuous_within_at (e ∘ f) s x :=
begin
  refine ⟨(e.continuous_at hx).comp_continuous_within_at, λ fe_cont, _⟩,
  rw [← continuous_within_at_inter' h] at fe_cont ⊢,
  have : continuous_within_at (e.symm ∘ (e ∘ f)) (s ∩ f ⁻¹' e.source) x,
  { have : continuous_within_at e.symm univ (e (f x))
      := (e.continuous_at_symm (e.map_source hx)).continuous_within_at,
    exact continuous_within_at.comp this fe_cont (subset_univ _) },
  exact this.congr (λy hy, by simp [e.left_inv hy.2]) (by simp [e.left_inv hx])
end

/-- Continuity at a point can be read under left composition with a local homeomorphism if a
neighborhood of the initial point is sent to the source of the local homeomorphism-/
lemma continuous_at_iff_continuous_at_comp_left {f : γ → α} {x : γ} (h : f ⁻¹' e.source ∈ 𝓝 x) :
  continuous_at f x ↔ continuous_at (e ∘ f) x :=
begin
  have hx : f x ∈ e.source := (mem_of_mem_nhds h : _),
  have h' : f ⁻¹' e.source ∈ 𝓝[univ] x, by rwa nhds_within_univ,
  rw [← continuous_within_at_univ, ← continuous_within_at_univ,
      e.continuous_within_at_iff_continuous_within_at_comp_left hx h']
end

/-- A function is continuous on a set if and only if its composition with a local homeomorphism
on the left is continuous on the corresponding set. -/
lemma continuous_on_iff_continuous_on_comp_left {f : γ → α} {s : set γ} (h : s ⊆ f ⁻¹' e.source) :
  continuous_on f s ↔ continuous_on (e ∘ f) s :=
forall₂_congr $ λ x hx, e.continuous_within_at_iff_continuous_within_at_comp_left
  (h hx) (mem_of_superset self_mem_nhds_within h)

/-- A function is continuous if and only if its composition with a local homeomorphism
on the left is continuous and its image is contained in the source. -/
lemma continuous_iff_continuous_comp_left {f : γ → α} (h : f ⁻¹' e.source = univ) :
  continuous f ↔ continuous (e ∘ f) :=
begin
  simp only [continuous_iff_continuous_on_univ],
  exact e.continuous_on_iff_continuous_on_comp_left (eq.symm h).subset,
end

end continuity

/-- The homeomorphism obtained by restricting a `local_homeomorph` to a subset of the source. -/
@[simps] def homeomorph_of_image_subset_source
  {s : set α} {t : set β} (hs : s ⊆ e.source) (ht : e '' s = t) : s ≃ₜ t :=
{ to_fun := λ a, ⟨e a, (congr_arg ((∈) (e a)) ht).mp ⟨a, a.2, rfl⟩⟩,
  inv_fun := λ b, ⟨e.symm b, let ⟨a, ha1, ha2⟩ := (congr_arg ((∈) ↑b) ht).mpr b.2 in
    ha2 ▸ (e.left_inv (hs ha1)).symm ▸ ha1⟩,
  left_inv := λ a, subtype.ext (e.left_inv (hs a.2)),
  right_inv := λ b, let ⟨a, ha1, ha2⟩ := (congr_arg ((∈) ↑b) ht).mpr b.2 in
    subtype.ext (e.right_inv (ha2 ▸ e.map_source (hs ha1))),
  continuous_to_fun := (continuous_on_iff_continuous_restrict.mp
    (e.continuous_on.mono hs)).subtype_mk _,
  continuous_inv_fun := (continuous_on_iff_continuous_restrict.mp
    (e.continuous_on_symm.mono (λ b hb, let ⟨a, ha1, ha2⟩ := show b ∈ e '' s, from ht.symm ▸ hb in
      ha2 ▸ e.map_source (hs ha1)))).subtype_mk _ }

/-- A local homeomrphism defines a homeomorphism between its source and target. -/
def to_homeomorph_source_target : e.source ≃ₜ e.target :=
e.homeomorph_of_image_subset_source subset_rfl e.image_source_eq_target

lemma second_countable_topology_source [second_countable_topology β]
  (e : local_homeomorph α β) :
  second_countable_topology e.source :=
e.to_homeomorph_source_target.second_countable_topology

/-- If a local homeomorphism has source and target equal to univ, then it induces a homeomorphism
between the whole spaces, expressed in this definition. -/
@[simps apply symm_apply (mfld_cfg)]
def to_homeomorph_of_source_eq_univ_target_eq_univ (h : e.source = (univ : set α))
  (h' : e.target = univ) : α ≃ₜ β :=
{ to_fun := e,
  inv_fun := e.symm,
  left_inv := λx, e.left_inv $ by { rw h, exact mem_univ _ },
  right_inv := λx, e.right_inv $ by { rw h', exact mem_univ _ },
  continuous_to_fun := begin
    rw [continuous_iff_continuous_on_univ],
    convert e.continuous_to_fun,
    rw h
  end,
  continuous_inv_fun := begin
    rw [continuous_iff_continuous_on_univ],
    convert e.continuous_inv_fun,
    rw h'
  end }

/-- A local homeomorphism whose source is all of `α` defines an open embedding of `α` into `β`.  The
converse is also true; see `open_embedding.to_local_homeomorph`. -/
lemma to_open_embedding (h : e.source = set.univ) : open_embedding e :=
begin
  apply open_embedding_of_continuous_injective_open,
  { apply continuous_iff_continuous_on_univ.mpr,
    rw ← h,
    exact e.continuous_to_fun },
  { apply set.injective_iff_inj_on_univ.mpr,
    rw ← h,
    exact e.inj_on },
  { intros U hU,
    simpa only [h, subset_univ] with mfld_simps using e.image_open_of_open hU}
end

end local_homeomorph

namespace homeomorph
variables (e : α ≃ₜ β) (e' : β ≃ₜ γ)
/- Register as simp lemmas that the fields of a local homeomorphism built from a homeomorphism
correspond to the fields of the original homeomorphism. -/

@[simp, mfld_simps] lemma refl_to_local_homeomorph :
  (homeomorph.refl α).to_local_homeomorph = local_homeomorph.refl α := rfl
@[simp, mfld_simps] lemma symm_to_local_homeomorph :
  e.symm.to_local_homeomorph = e.to_local_homeomorph.symm := rfl
@[simp, mfld_simps] lemma trans_to_local_homeomorph :
  (e.trans e').to_local_homeomorph = e.to_local_homeomorph.trans e'.to_local_homeomorph :=
local_homeomorph.eq_of_local_equiv_eq $ equiv.trans_to_local_equiv _ _

end homeomorph

namespace open_embedding
variables (f : α → β) (h : open_embedding f)

/-- An open embedding of `α` into `β`, with `α` nonempty, defines a local homeomorphism whose source
is all of `α`.  The converse is also true; see `local_homeomorph.to_open_embedding`. -/
@[simps apply source target (mfld_cfg)]
noncomputable def to_local_homeomorph [nonempty α] : local_homeomorph α β :=
local_homeomorph.of_continuous_open
  ((h.to_embedding.inj.inj_on univ).to_local_equiv _ _)
  h.continuous.continuous_on h.is_open_map is_open_univ

lemma continuous_at_iff
  {f : α → β} {g : β → γ} (hf : open_embedding f) {x : α} :
  continuous_at (g ∘ f) x ↔ continuous_at g (f x) :=
begin
  haveI : nonempty α := ⟨x⟩,
  convert (((hf.to_local_homeomorph f).continuous_at_iff_continuous_at_comp_right) _).symm,
  { apply (local_homeomorph.left_inv _ _).symm,
    simp, },
  { simp, },
end

end open_embedding

namespace topological_space.opens

open topological_space
variables (s : opens α) [nonempty s]

/-- The inclusion of an open subset `s` of a space `α` into `α` is a local homeomorphism from the
subtype `s` to `α`. -/
noncomputable def local_homeomorph_subtype_coe : local_homeomorph s α :=
open_embedding.to_local_homeomorph _ s.2.open_embedding_subtype_coe

@[simp, mfld_simps] lemma local_homeomorph_subtype_coe_coe :
  (s.local_homeomorph_subtype_coe : s → α) = coe := rfl

@[simp, mfld_simps] lemma local_homeomorph_subtype_coe_source :
  s.local_homeomorph_subtype_coe.source = set.univ := rfl

@[simp, mfld_simps] lemma local_homeomorph_subtype_coe_target :
  s.local_homeomorph_subtype_coe.target = s :=
by { simp only [local_homeomorph_subtype_coe, subtype.range_coe_subtype] with mfld_simps, refl }

end topological_space.opens

namespace local_homeomorph

open topological_space
variables (e : local_homeomorph α β)
variables (s : opens α) [nonempty s]

/-- The restriction of a local homeomorphism `e` to an open subset `s` of the domain type produces a
local homeomorphism whose domain is the subtype `s`.-/
noncomputable def subtype_restr : local_homeomorph s β := s.local_homeomorph_subtype_coe.trans e

lemma subtype_restr_def : e.subtype_restr s = s.local_homeomorph_subtype_coe.trans e := rfl

@[simp, mfld_simps] lemma subtype_restr_coe : ((e.subtype_restr s : local_homeomorph s β) : s → β)
  = set.restrict ↑s (e : α → β) := rfl

@[simp, mfld_simps] lemma subtype_restr_source : (e.subtype_restr s).source = coe ⁻¹' e.source :=
by simp only [subtype_restr_def] with mfld_simps

/- This lemma characterizes the transition functions of an open subset in terms of the transition
functions of the original space. -/
lemma subtype_restr_symm_trans_subtype_restr (f f' : local_homeomorph α β) :
  (f.subtype_restr s).symm.trans (f'.subtype_restr s)
  ≈ (f.symm.trans f').restr (f.target ∩ (f.symm) ⁻¹' s) :=
begin
  simp only [subtype_restr_def, trans_symm_eq_symm_trans_symm],
  have openness₁ : is_open (f.target ∩ f.symm ⁻¹' s) := f.preimage_open_of_open_symm s.2,
  rw [← of_set_trans _ openness₁, ← trans_assoc, ← trans_assoc],
  refine eq_on_source.trans' _ (eq_on_source_refl _),
  -- f' has been eliminated !!!
  have sets_identity : f.symm.source ∩ (f.target ∩ (f.symm) ⁻¹' s) = f.symm.source ∩ f.symm ⁻¹' s,
  { mfld_set_tac },
  have openness₂ : is_open (s : set α) := s.2,
  rw [of_set_trans', sets_identity, ← trans_of_set' _ openness₂, trans_assoc],
  refine eq_on_source.trans' (eq_on_source_refl _) _,
  -- f has been eliminated !!!
  refine setoid.trans (trans_symm_self s.local_homeomorph_subtype_coe) _,
  simp only with mfld_simps,
end

end local_homeomorph


def p (x : Nat := 0) : Nat × Nat :=
(x, x)

theorem ex1 : p.1 = 0 :=
rfl

theorem ex2 : (p (x := 1) |>.2) = 1 :=
rfl

def c {α : Type} [Inhabited α] : α × α :=
(arbitrary, arbitrary)

theorem ex3 {α} [Inhabited α] : c.1 = arbitrary (α := α) :=
rfl

theorem ex4 {α} [Inhabited α] : c.2 = arbitrary (α := α) :=
rfl