/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Johannes Hölzl, Yury Kudryashov
-/
import algebra.category.Group.basic
import category_theory.concrete_category.reflects_isomorphisms
import category_theory.elementwise
import algebra.ring.equiv

/-!
# Category instances for semiring, ring, comm_semiring, and comm_ring.

We introduce the bundled categories:
* `SemiRing`
* `Ring`
* `CommSemiRing`
* `CommRing`
along with the relevant forgetful functors between them.
-/

universes u v

open category_theory

/-- The category of semirings. -/
def SemiRing : Type (u+1) := bundled semiring

namespace SemiRing

/-- `ring_hom` doesn't actually assume associativity. This alias is needed to make the category
theory machinery work. We use the same trick in `category_theory.Mon.assoc_monoid_hom`. -/
abbreviation assoc_ring_hom (M N : Type*) [semiring M] [semiring N] := ring_hom M N

instance bundled_hom : bundled_hom assoc_ring_hom :=
⟨λ M N [semiring M] [semiring N], by exactI @ring_hom.to_fun M N _ _,
 λ M [semiring M], by exactI @ring_hom.id M _,
 λ M N P [semiring M] [semiring N] [semiring P], by exactI @ring_hom.comp M N P _ _ _,
 λ M N [semiring M] [semiring N], by exactI @ring_hom.coe_inj M N _ _⟩

attribute [derive [large_category, concrete_category]] SemiRing

instance : has_coe_to_sort SemiRing Type* := bundled.has_coe_to_sort

/-- Construct a bundled SemiRing from the underlying type and typeclass. -/
def of (R : Type u) [semiring R] : SemiRing := bundled.of R

/-- Typecheck a `ring_hom` as a morphism in `SemiRing`. -/
def of_hom {R S : Type u} [semiring R] [semiring S] (f : R →+* S) : of R ⟶ of S := f

@[simp]

instance : inhabited SemiRing := ⟨of punit⟩

instance (R : SemiRing) : semiring R := R.str

@[simp] lemma coe_of (R : Type u) [semiring R] : (SemiRing.of R : Type u) = R := rfl

instance has_forget_to_Mon : has_forget₂ SemiRing Mon :=
bundled_hom.mk_has_forget₂
  (λ R hR, @monoid_with_zero.to_monoid R (@semiring.to_monoid_with_zero R hR))
  (λ R₁ R₂, ring_hom.to_monoid_hom) (λ _ _ _, rfl)

instance has_forget_to_AddCommMon : has_forget₂ SemiRing AddCommMon :=
-- can't use bundled_hom.mk_has_forget₂, since AddCommMon is an induced category
{ forget₂ :=
  { obj := λ R, AddCommMon.of R,
    map := λ R₁ R₂ f, ring_hom.to_add_monoid_hom f } }

end SemiRing

/-- The category of rings. -/
def Ring : Type (u+1) := bundled ring

namespace Ring

instance : bundled_hom.parent_projection @ring.to_semiring := ⟨⟩

attribute [derive [(λ Ring, has_coe_to_sort Ring Type*), large_category, concrete_category]] Ring

/-- Construct a bundled Ring from the underlying type and typeclass. -/
def of (R : Type u) [ring R] : Ring := bundled.of R

/-- Typecheck a `ring_hom` as a morphism in `Ring`. -/
def of_hom {R S : Type u} [ring R] [ring S] (f : R →+* S) : of R ⟶ of S := f

@[simp] lemma of_hom_apply {R S : Type u} [ring R] [ring S] (f : R →+* S) (x : R) :
  of_hom f x = f x := rfl

instance : inhabited Ring := ⟨of punit⟩

instance (R : Ring) : ring R := R.str

@[simp] lemma coe_of (R : Type u) [ring R] : (Ring.of R : Type u) = R := rfl

instance has_forget_to_SemiRing : has_forget₂ Ring SemiRing := bundled_hom.forget₂ _ _
instance has_forget_to_AddCommGroup : has_forget₂ Ring AddCommGroup :=
-- can't use bundled_hom.mk_has_forget₂, since AddCommGroup is an induced category
{ forget₂ :=
  { obj := λ R, AddCommGroup.of R,
    map := λ R₁ R₂ f, ring_hom.to_add_monoid_hom f } }

end Ring

/-- The category of commutative semirings. -/
def CommSemiRing : Type (u+1) := bundled comm_semiring

namespace CommSemiRing

instance : bundled_hom.parent_projection @comm_semiring.to_semiring := ⟨⟩

attribute [derive [large_category, concrete_category]] CommSemiRing

instance : has_coe_to_sort CommSemiRing Type* := bundled.has_coe_to_sort

/-- Construct a bundled CommSemiRing from the underlying type and typeclass. -/
def of (R : Type u) [comm_semiring R] : CommSemiRing := bundled.of R

/-- Typecheck a `ring_hom` as a morphism in `CommSemiRing`. -/
def of_hom {R S : Type u} [comm_semiring R] [comm_semiring S] (f : R →+* S) : of R ⟶ of S := f

@[simp]
lemma of_hom_apply {R S : Type u} [comm_semiring R] [comm_semiring S] (f : R →+* S) (x : R) :
  of_hom f x = f x := rfl

instance : inhabited CommSemiRing := ⟨of punit⟩

instance (R : CommSemiRing) : comm_semiring R := R.str

@[simp] lemma coe_of (R : Type u) [comm_semiring R] : (CommSemiRing.of R : Type u) = R := rfl

instance has_forget_to_SemiRing : has_forget₂ CommSemiRing SemiRing := bundled_hom.forget₂ _ _

/-- The forgetful functor from commutative rings to (multiplicative) commutative monoids. -/
instance has_forget_to_CommMon : has_forget₂ CommSemiRing CommMon :=
has_forget₂.mk'
  (λ R : CommSemiRing, CommMon.of R) (λ R, rfl)
  (λ R₁ R₂ f, f.to_monoid_hom) (by tidy)

end CommSemiRing

/-- The category of commutative rings. -/
def CommRing : Type (u+1) := bundled comm_ring

namespace CommRing

instance : bundled_hom.parent_projection @comm_ring.to_ring := ⟨⟩

attribute [derive [large_category, concrete_category]] CommRing

instance : has_coe_to_sort CommRing Type* := bundled.has_coe_to_sort

/-- Construct a bundled CommRing from the underlying type and typeclass. -/
def of (R : Type u) [comm_ring R] : CommRing := bundled.of R

/-- Typecheck a `ring_hom` as a morphism in `CommRing`. -/
def of_hom {R S : Type u} [comm_ring R] [comm_ring S] (f : R →+* S) : of R ⟶ of S := f

@[simp] lemma of_hom_apply {R S : Type u} [comm_ring R] [comm_ring S] (f : R →+* S) (x : R) :
  of_hom f x = f x := rfl

instance : inhabited CommRing := ⟨of punit⟩

instance (R : CommRing) : comm_ring R := R.str

@[simp] lemma coe_of (R : Type u) [comm_ring R] : (CommRing.of R : Type u) = R := rfl

instance has_forget_to_Ring : has_forget₂ CommRing Ring := bundled_hom.forget₂ _ _

/-- The forgetful functor from commutative rings to (multiplicative) commutative monoids. -/
instance has_forget_to_CommSemiRing : has_forget₂ CommRing CommSemiRing :=
has_forget₂.mk' (λ R : CommRing, CommSemiRing.of R) (λ R, rfl) (λ R₁ R₂ f, f) (by tidy)

instance : full (forget₂ CommRing CommSemiRing) :=
{ preimage := λ X Y f, f, }

end CommRing

-- This example verifies an improvement possible in Lean 3.8.
-- Before that, to have `add_ring_hom.map_zero` usable by `simp` here,
-- we had to mark all the concrete category `has_coe_to_sort` instances reducible.
-- Now, it just works.
example {R S : CommRing} (i : R ⟶ S) (r : R) (h : r = 0) : i r = 0 :=
by simp [h]

namespace ring_equiv

variables {X Y : Type u}

/-- Build an isomorphism in the category `Ring` from a `ring_equiv` between `ring`s. -/
@[simps] def to_Ring_iso [ring X] [ring Y] (e : X ≃+* Y) : Ring.of X ≅ Ring.of Y :=
{ hom := e.to_ring_hom,
  inv := e.symm.to_ring_hom }

/-- Build an isomorphism in the category `CommRing` from a `ring_equiv` between `comm_ring`s. -/
@[simps] def to_CommRing_iso [comm_ring X] [comm_ring Y] (e : X ≃+* Y) :
  CommRing.of X ≅ CommRing.of Y :=
{ hom := e.to_ring_hom,
  inv := e.symm.to_ring_hom }

end ring_equiv

namespace category_theory.iso

/-- Build a `ring_equiv` from an isomorphism in the category `Ring`. -/
def Ring_iso_to_ring_equiv {X Y : Ring} (i : X ≅ Y) : X ≃+* Y :=
{ to_fun    := i.hom,
  inv_fun   := i.inv,
  left_inv  := by tidy,
  right_inv := by tidy,
  map_add'  := by tidy,
  map_mul'  := by tidy }.

/-- Build a `ring_equiv` from an isomorphism in the category `CommRing`. -/
def CommRing_iso_to_ring_equiv {X Y : CommRing} (i : X ≅ Y) : X ≃+* Y :=
{ to_fun    := i.hom,
  inv_fun   := i.inv,
  left_inv  := by tidy,
  right_inv := by tidy,
  map_add'  := by tidy,
  map_mul'  := by tidy }.

@[simp]
lemma CommRing_iso_to_ring_equiv_to_ring_hom {X Y : CommRing} (i : X ≅ Y) :
  i.CommRing_iso_to_ring_equiv.to_ring_hom = i.hom := by { ext, refl }

@[simp]
lemma CommRing_iso_to_ring_equiv_symm_to_ring_hom {X Y : CommRing} (i : X ≅ Y) :
  i.CommRing_iso_to_ring_equiv.symm.to_ring_hom = i.inv := by { ext, refl }

end category_theory.iso

/-- Ring equivalences between `ring`s are the same as (isomorphic to) isomorphisms in `Ring`. -/
def ring_equiv_iso_Ring_iso {X Y : Type u} [ring X] [ring Y] :
  (X ≃+* Y) ≅ (Ring.of X ≅ Ring.of Y) :=
{ hom := λ e, e.to_Ring_iso,
  inv := λ i, i.Ring_iso_to_ring_equiv, }

/-- Ring equivalences between `comm_ring`s are the same as (isomorphic to) isomorphisms
in `CommRing`. -/
def ring_equiv_iso_CommRing_iso {X Y : Type u} [comm_ring X] [comm_ring Y] :
  (X ≃+* Y) ≅ (CommRing.of X ≅ CommRing.of Y) :=
{ hom := λ e, e.to_CommRing_iso,
  inv := λ i, i.CommRing_iso_to_ring_equiv, }

instance Ring.forget_reflects_isos : reflects_isomorphisms (forget Ring.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget Ring).map f),
    let e : X ≃+* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_Ring_iso).1⟩,
  end }

instance CommRing.forget_reflects_isos : reflects_isomorphisms (forget CommRing.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget CommRing).map f),
    let e : X ≃+* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_CommRing_iso).1⟩,
  end }

lemma CommRing.comp_eq_ring_hom_comp {R S T : CommRing} (f : R ⟶ S) (g : S ⟶ T) :
  f ≫ g = g.comp f := rfl

lemma CommRing.ring_hom_comp_eq_comp {R S T : Type*} [comm_ring R] [comm_ring S]
  [comm_ring T] (f : R →+* S) (g : S →+* T) :
  g.comp f = CommRing.of_hom f ≫ CommRing.of_hom g := rfl

-- It would be nice if we could have the following,
-- but it requires making `reflects_isomorphisms_forget₂` an instance,
-- which can cause typeclass loops:

local attribute [priority 50,instance] reflects_isomorphisms_forget₂
example : reflects_isomorphisms (forget₂ Ring AddCommGroup) := by apply_instance


/-
Copyright (c) 2019 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.constructions
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Neighborhoods and continuity relative to a subset

This file defines relative versions

* `nhds_within`           of `nhds`
* `continuous_on`         of `continuous`
* `continuous_within_at`  of `continuous_at`

and proves their basic properties, including the relationships between
these restricted notions and the corresponding notions for the subtype
equipped with the subspace topology.

## Notation

* `𝓝 x`: the filter of neighborhoods of a point `x`;
* `𝓟 s`: the principal filter of a set `s`;
* `𝓝[s] x`: the filter `nhds_within x s` of neighborhoods of a point `x` within a set `s`.

-/

/-- The "neighborhood within" filter. Elements of `𝓝[s] a` are sets containing the
intersection of `s` and a neighborhood of `a`. -/
def nhds_within {α : Type u_1} [topological_space α] (a : α) (s : set α) : filter α :=
  nhds a ⊓ filter.principal s

@[simp] theorem nhds_bind_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} :
    (filter.bind (nhds a) fun (x : α) => nhds_within x s) = nhds_within a s :=
  Eq.trans filter.bind_inf_principal (congr_arg2 has_inf.inf nhds_bind_nhds rfl)

@[simp] theorem eventually_nhds_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α}
    {p : α → Prop} :
    filter.eventually (fun (y : α) => filter.eventually (fun (x : α) => p x) (nhds_within y s))
          (nhds a) ↔
        filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  iff.mp filter.ext_iff nhds_bind_nhds_within (set_of fun (x : α) => p x)

theorem eventually_nhds_within_iff {α : Type u_1} [topological_space α] {a : α} {s : set α}
    {p : α → Prop} :
    filter.eventually (fun (x : α) => p x) (nhds_within a s) ↔
        filter.eventually (fun (x : α) => x ∈ s → p x) (nhds a) :=
  filter.eventually_inf_principal

@[simp] theorem eventually_nhds_within_nhds_within {α : Type u_1} [topological_space α] {a : α}
    {s : set α} {p : α → Prop} :
    filter.eventually (fun (y : α) => filter.eventually (fun (x : α) => p x) (nhds_within y s))
          (nhds_within a s) ↔
        filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  sorry

theorem nhds_within_eq {α : Type u_1} [topological_space α] (a : α) (s : set α) :
    nhds_within a s =
        infi
          fun (t : set α) =>
            infi
              fun (H : t ∈ set_of fun (t : set α) => a ∈ t ∧ is_open t) =>
                filter.principal (t ∩ s) :=
  filter.has_basis.eq_binfi (filter.has_basis.inf_principal (nhds_basis_opens a) s)

theorem nhds_within_univ {α : Type u_1} [topological_space α] (a : α) :
    nhds_within a set.univ = nhds a :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (nhds_within a set.univ = nhds a))
        (nhds_within.equations._eqn_1 a set.univ)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (nhds a ⊓ filter.principal set.univ = nhds a)) filter.principal_univ))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ ⊤ = nhds a)) inf_top_eq)) (Eq.refl (nhds a))))

theorem nhds_within_has_basis {α : Type u_1} {β : Type u_2} [topological_space α] {p : β → Prop}
    {s : β → set α} {a : α} (h : filter.has_basis (nhds a) p s) (t : set α) :
    filter.has_basis (nhds_within a t) p fun (i : β) => s i ∩ t :=
  filter.has_basis.inf_principal h t

theorem nhds_within_basis_open {α : Type u_1} [topological_space α] (a : α) (t : set α) :
    filter.has_basis (nhds_within a t) (fun (u : set α) => a ∈ u ∧ is_open u)
        fun (u : set α) => u ∩ t :=
  nhds_within_has_basis (nhds_basis_opens a) t

theorem mem_nhds_within {α : Type u_1} [topological_space α] {t : set α} {a : α} {s : set α} :
    t ∈ nhds_within a s ↔ ∃ (u : set α), is_open u ∧ a ∈ u ∧ u ∩ s ⊆ t :=
  sorry

theorem mem_nhds_within_iff_exists_mem_nhds_inter {α : Type u_1} [topological_space α] {t : set α}
    {a : α} {s : set α} : t ∈ nhds_within a s ↔ ∃ (u : set α), ∃ (H : u ∈ nhds a), u ∩ s ⊆ t :=
  filter.has_basis.mem_iff (nhds_within_has_basis (filter.basis_sets (nhds a)) s)

theorem diff_mem_nhds_within_compl {X : Type u_1} [topological_space X] {x : X} {s : set X}
    (hs : s ∈ nhds x) (t : set X) : s \ t ∈ nhds_within x (tᶜ) :=
  filter.diff_mem_inf_principal_compl hs t

theorem nhds_of_nhds_within_of_nhds {α : Type u_1} [topological_space α] {s : set α} {t : set α}
    {a : α} (h1 : s ∈ nhds a) (h2 : t ∈ nhds_within a s) : t ∈ nhds a :=
  sorry

theorem mem_nhds_within_of_mem_nhds {α : Type u_1} [topological_space α] {s : set α} {t : set α}
    {a : α} (h : s ∈ nhds a) : s ∈ nhds_within a t :=
  filter.mem_inf_sets_of_left h

theorem self_mem_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} :
    s ∈ nhds_within a s :=
  filter.mem_inf_sets_of_right (filter.mem_principal_self s)

theorem inter_mem_nhds_within {α : Type u_1} [topological_space α] (s : set α) {t : set α} {a : α}
    (h : t ∈ nhds a) : s ∩ t ∈ nhds_within a s :=
  filter.inter_mem_sets (filter.mem_inf_sets_of_right (filter.mem_principal_self s))
    (filter.mem_inf_sets_of_left h)

theorem nhds_within_mono {α : Type u_1} [topological_space α] (a : α) {s : set α} {t : set α}
    (h : s ⊆ t) : nhds_within a s ≤ nhds_within a t :=
  inf_le_inf_left (nhds a) (iff.mpr filter.principal_mono h)

theorem pure_le_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} (ha : a ∈ s) :
    pure a ≤ nhds_within a s :=
  le_inf (pure_le_nhds a) (iff.mpr filter.le_principal_iff ha)

theorem mem_of_mem_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α}
    (ha : a ∈ s) (ht : t ∈ nhds_within a s) : a ∈ t :=
  pure_le_nhds_within ha ht

theorem filter.eventually.self_of_nhds_within {α : Type u_1} [topological_space α] {p : α → Prop}
    {s : set α} {x : α} (h : filter.eventually (fun (y : α) => p y) (nhds_within x s))
    (hx : x ∈ s) : p x :=
  mem_of_mem_nhds_within hx h

theorem tendsto_const_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {l : filter β}
    {s : set α} {a : α} (ha : a ∈ s) : filter.tendsto (fun (x : β) => a) l (nhds_within a s) :=
  filter.tendsto.mono_right filter.tendsto_const_pure (pure_le_nhds_within ha)

theorem nhds_within_restrict'' {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α}
    (h : t ∈ nhds_within a s) : nhds_within a s = nhds_within a (s ∩ t) :=
  le_antisymm
    (le_inf inf_le_left
      (iff.mpr filter.le_principal_iff (filter.inter_mem_sets self_mem_nhds_within h)))
    (inf_le_inf_left (nhds a) (iff.mpr filter.principal_mono (set.inter_subset_left s t)))

theorem nhds_within_restrict' {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α}
    (h : t ∈ nhds a) : nhds_within a s = nhds_within a (s ∩ t) :=
  nhds_within_restrict'' s (filter.mem_inf_sets_of_left h)

theorem nhds_within_restrict {α : Type u_1} [topological_space α] {a : α} (s : set α) {t : set α}
    (h₀ : a ∈ t) (h₁ : is_open t) : nhds_within a s = nhds_within a (s ∩ t) :=
  nhds_within_restrict' s (mem_nhds_sets h₁ h₀)

theorem nhds_within_le_of_mem {α : Type u_1} [topological_space α] {a : α} {s : set α} {t : set α}
    (h : s ∈ nhds_within a t) : nhds_within a t ≤ nhds_within a s :=
  sorry

theorem nhds_within_eq_nhds_within {α : Type u_1} [topological_space α] {a : α} {s : set α}
    {t : set α} {u : set α} (h₀ : a ∈ s) (h₁ : is_open s) (h₂ : t ∩ s = u ∩ s) :
    nhds_within a t = nhds_within a u :=
  sorry

theorem nhds_within_eq_of_open {α : Type u_1} [topological_space α] {a : α} {s : set α} (h₀ : a ∈ s)
    (h₁ : is_open s) : nhds_within a s = nhds a :=
  iff.mpr inf_eq_left (iff.mpr filter.le_principal_iff (mem_nhds_sets h₁ h₀))

@[simp] theorem nhds_within_empty {α : Type u_1} [topological_space α] (a : α) :
    nhds_within a ∅ = ⊥ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nhds_within a ∅ = ⊥)) (nhds_within.equations._eqn_1 a ∅)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ filter.principal ∅ = ⊥)) filter.principal_empty))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nhds a ⊓ ⊥ = ⊥)) inf_bot_eq)) (Eq.refl ⊥)))

theorem nhds_within_union {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) :
    nhds_within a (s ∪ t) = nhds_within a s ⊔ nhds_within a t :=
  sorry

theorem nhds_within_inter {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) :
    nhds_within a (s ∩ t) = nhds_within a s ⊓ nhds_within a t :=
  sorry

theorem nhds_within_inter' {α : Type u_1} [topological_space α] (a : α) (s : set α) (t : set α) :
    nhds_within a (s ∩ t) = nhds_within a s ⊓ filter.principal t :=
  sorry

@[simp] theorem nhds_within_singleton {α : Type u_1} [topological_space α] (a : α) :
    nhds_within a (singleton a) = pure a :=
  sorry

@[simp] theorem nhds_within_insert {α : Type u_1} [topological_space α] (a : α) (s : set α) :
    nhds_within a (insert a s) = pure a ⊔ nhds_within a s :=
  sorry

theorem mem_nhds_within_insert {α : Type u_1} [topological_space α] {a : α} {s : set α}
    {t : set α} : t ∈ nhds_within a (insert a s) ↔ a ∈ t ∧ t ∈ nhds_within a s :=
  sorry

theorem insert_mem_nhds_within_insert {α : Type u_1} [topological_space α] {a : α} {s : set α}
    {t : set α} (h : t ∈ nhds_within a s) : insert a t ∈ nhds_within a (insert a s) :=
  sorry

theorem nhds_within_prod_eq {α : Type u_1} [topological_space α] {β : Type u_2}
    [topological_space β] (a : α) (b : β) (s : set α) (t : set β) :
    nhds_within (a, b) (set.prod s t) = filter.prod (nhds_within a s) (nhds_within b t) :=
  sorry

theorem nhds_within_prod {α : Type u_1} [topological_space α] {β : Type u_2} [topological_space β]
    {s : set α} {u : set α} {t : set β} {v : set β} {a : α} {b : β} (hu : u ∈ nhds_within a s)
    (hv : v ∈ nhds_within b t) : set.prod u v ∈ nhds_within (a, b) (set.prod s t) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (set.prod u v ∈ nhds_within (a, b) (set.prod s t)))
        (nhds_within_prod_eq a b s t)))
    (filter.prod_mem_prod hu hv)

theorem tendsto_if_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β}
    {g : α → β} {p : α → Prop} [decidable_pred p] {a : α} {s : set α} {l : filter β}
    (h₀ : filter.tendsto f (nhds_within a (s ∩ p)) l)
    (h₁ : filter.tendsto g (nhds_within a (s ∩ set_of fun (x : α) => ¬p x)) l) :
    filter.tendsto (fun (x : α) => ite (p x) (f x) (g x)) (nhds_within a s) l :=
  sorry

theorem map_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α] (f : α → β) (a : α)
    (s : set α) :
    filter.map f (nhds_within a s) =
        infi
          fun (t : set α) =>
            infi
              fun (H : t ∈ set_of fun (t : set α) => a ∈ t ∧ is_open t) =>
                filter.principal (f '' (t ∩ s)) :=
  filter.has_basis.eq_binfi (filter.has_basis.map f (nhds_within_basis_open a s))

theorem tendsto_nhds_within_mono_left {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : α → β} {a : α} {s : set α} {t : set α} {l : filter β} (hst : s ⊆ t)
    (h : filter.tendsto f (nhds_within a t) l) : filter.tendsto f (nhds_within a s) l :=
  filter.tendsto.mono_left h (nhds_within_mono a hst)

theorem tendsto_nhds_within_mono_right {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : β → α} {l : filter β} {a : α} {s : set α} {t : set α} (hst : s ⊆ t)
    (h : filter.tendsto f l (nhds_within a s)) : filter.tendsto f l (nhds_within a t) :=
  filter.tendsto.mono_right h (nhds_within_mono a hst)

theorem tendsto_nhds_within_of_tendsto_nhds {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : α → β} {a : α} {s : set α} {l : filter β} (h : filter.tendsto f (nhds a) l) :
    filter.tendsto f (nhds_within a s) l :=
  filter.tendsto.mono_left h inf_le_left

theorem principal_subtype {α : Type u_1} (s : set α) (t : set (Subtype fun (x : α) => x ∈ s)) :
    filter.principal t = filter.comap coe (filter.principal (coe '' t)) :=
  sorry

theorem mem_closure_iff_nhds_within_ne_bot {α : Type u_1} [topological_space α] {s : set α}
    {x : α} : x ∈ closure s ↔ filter.ne_bot (nhds_within x s) :=
  mem_closure_iff_cluster_pt

theorem nhds_within_ne_bot_of_mem {α : Type u_1} [topological_space α] {s : set α} {x : α}
    (hx : x ∈ s) : filter.ne_bot (nhds_within x s) :=
  iff.mp mem_closure_iff_nhds_within_ne_bot (subset_closure hx)

theorem is_closed.mem_of_nhds_within_ne_bot {α : Type u_1} [topological_space α] {s : set α}
    (hs : is_closed s) {x : α} (hx : filter.ne_bot (nhds_within x s)) : x ∈ s :=
  sorry

theorem dense_range.nhds_within_ne_bot {α : Type u_1} [topological_space α] {ι : Type u_2}
    {f : ι → α} (h : dense_range f) (x : α) : filter.ne_bot (nhds_within x (set.range f)) :=
  iff.mp mem_closure_iff_cluster_pt (h x)

theorem eventually_eq_nhds_within_iff {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : α → β} {g : α → β} {s : set α} {a : α} :
    filter.eventually_eq (nhds_within a s) f g ↔
        filter.eventually (fun (x : α) => x ∈ s → f x = g x) (nhds a) :=
  filter.mem_inf_principal

theorem eventually_eq_nhds_within_of_eq_on {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : α → β} {g : α → β} {s : set α} {a : α} (h : set.eq_on f g s) :
    filter.eventually_eq (nhds_within a s) f g :=
  filter.mem_inf_sets_of_right h

theorem set.eq_on.eventually_eq_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α]
    {f : α → β} {g : α → β} {s : set α} {a : α} (h : set.eq_on f g s) :
    filter.eventually_eq (nhds_within a s) f g :=
  eventually_eq_nhds_within_of_eq_on h

theorem tendsto_nhds_within_congr {α : Type u_1} {β : Type u_2} [topological_space α] {f : α → β}
    {g : α → β} {s : set α} {a : α} {l : filter β} (hfg : ∀ (x : α), x ∈ s → f x = g x)
    (hf : filter.tendsto f (nhds_within a s) l) : filter.tendsto g (nhds_within a s) l :=
  iff.mp (filter.tendsto_congr' (eventually_eq_nhds_within_of_eq_on hfg)) hf

theorem eventually_nhds_with_of_forall {α : Type u_1} [topological_space α] {s : set α} {a : α}
    {p : α → Prop} (h : ∀ (x : α), x ∈ s → p x) :
    filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  filter.mem_inf_sets_of_right h

theorem tendsto_nhds_within_of_tendsto_nhds_of_eventually_within {α : Type u_1}
    [topological_space α] {β : Type u_2} {a : α} {l : filter β} {s : set α} (f : β → α)
    (h1 : filter.tendsto f l (nhds a)) (h2 : filter.eventually (fun (x : β) => f x ∈ s) l) :
    filter.tendsto f l (nhds_within a s) :=
  iff.mpr filter.tendsto_inf { left := h1, right := iff.mpr filter.tendsto_principal h2 }

theorem filter.eventually_eq.eq_of_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α]
    {s : set α} {f : α → β} {g : α → β} {a : α} (h : filter.eventually_eq (nhds_within a s) f g)
    (hmem : a ∈ s) : f a = g a :=
  filter.eventually.self_of_nhds_within h hmem

theorem eventually_nhds_within_of_eventually_nhds {α : Type u_1} [topological_space α] {s : set α}
    {a : α} {p : α → Prop} (h : filter.eventually (fun (x : α) => p x) (nhds a)) :
    filter.eventually (fun (x : α) => p x) (nhds_within a s) :=
  mem_nhds_within_of_mem_nhds h

/-!
### `nhds_within` and subtypes
-/

theorem mem_nhds_within_subtype {α : Type u_1} [topological_space α] {s : set α}
    {a : Subtype fun (x : α) => x ∈ s} {t : set (Subtype fun (x : α) => x ∈ s)}
    {u : set (Subtype fun (x : α) => x ∈ s)} :
    t ∈ nhds_within a u ↔ t ∈ filter.comap coe (nhds_within (↑a) (coe '' u)) :=
  sorry

theorem nhds_within_subtype {α : Type u_1} [topological_space α] (s : set α)
    (a : Subtype fun (x : α) => x ∈ s) (t : set (Subtype fun (x : α) => x ∈ s)) :
    nhds_within a t = filter.comap coe (nhds_within (↑a) (coe '' t)) :=
  filter.ext fun (u : set (Subtype fun (x : α) => x ∈ s)) => mem_nhds_within_subtype

theorem nhds_within_eq_map_subtype_coe {α : Type u_1} [topological_space α] {s : set α} {a : α}
    (h : a ∈ s) : nhds_within a s = filter.map coe (nhds { val := a, property := h }) :=
  sorry

theorem tendsto_nhds_within_iff_subtype {α : Type u_1} {β : Type u_2} [topological_space α]
    {s : set α} {a : α} (h : a ∈ s) (f : α → β) (l : filter β) :
    filter.tendsto f (nhds_within a s) l ↔
        filter.tendsto (set.restrict f s) (nhds { val := a, property := h }) l :=
  sorry

/-- A function between topological spaces is continuous at a point `x₀` within a subset `s`
if `f x` tends to `f x₀` when `x` tends to `x₀` while staying within `s`. -/
def continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    (f : α → β) (s : set α) (x : α) :=
  filter.tendsto f (nhds_within x s) (nhds (f x))

/-- If a function is continuous within `s` at `x`, then it tends to `f x` within `s` by definition.
We register this fact for use with the dot notation, especially to use `tendsto.comp` as
`continuous_within_at.comp` will have a different meaning. -/
theorem continuous_within_at.tendsto {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x) :
    filter.tendsto f (nhds_within x s) (nhds (f x)) :=
  h

/-- A function between topological spaces is continuous on a subset `s`
when it's continuous at every point of `s` within `s`. -/
def continuous_on {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    (f : α → β) (s : set α) :=
  ∀ (x : α), x ∈ s → continuous_within_at f s x

theorem continuous_on.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (hf : continuous_on f s) (hx : x ∈ s) :
    continuous_within_at f s x :=
  hf x hx

theorem continuous_within_at_univ {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] (f : α → β) (x : α) :
    continuous_within_at f set.univ x ↔ continuous_at f x :=
  sorry

theorem continuous_within_at_iff_continuous_at_restrict {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] (f : α → β) {x : α} {s : set α} (h : x ∈ s) :
    continuous_within_at f s x ↔ continuous_at (set.restrict f s) { val := x, property := h } :=
  tendsto_nhds_within_iff_subtype h f (nhds (f x))

theorem continuous_within_at.tendsto_nhds_within {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β}
    (h : continuous_within_at f s x) (ht : set.maps_to f s t) :
    filter.tendsto f (nhds_within x s) (nhds_within (f x) t) :=
  iff.mpr filter.tendsto_inf
    { left := h,
      right :=
        iff.mpr filter.tendsto_principal
          (filter.mem_inf_sets_of_right (iff.mpr filter.mem_principal_sets ht)) }

theorem continuous_within_at.tendsto_nhds_within_image {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α}
    (h : continuous_within_at f s x) :
    filter.tendsto f (nhds_within x s) (nhds_within (f x) (f '' s)) :=
  continuous_within_at.tendsto_nhds_within h (set.maps_to_image f s)

theorem continuous_within_at.prod_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4}
    [topological_space α] [topological_space β] [topological_space γ] [topological_space δ]
    {f : α → γ} {g : β → δ} {s : set α} {t : set β} {x : α} {y : β}
    (hf : continuous_within_at f s x) (hg : continuous_within_at g t y) :
    continuous_within_at (prod.map f g) (set.prod s t) (x, y) :=
  sorry

theorem continuous_on_iff {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : α → β} {s : set α} :
    continuous_on f s ↔
        ∀ (x : α),
          x ∈ s →
            ∀ (t : set β),
              is_open t → f x ∈ t → ∃ (u : set α), is_open u ∧ x ∈ u ∧ u ∩ s ⊆ f ⁻¹' t :=
  sorry

theorem continuous_on_iff_continuous_restrict {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} :
    continuous_on f s ↔ continuous (set.restrict f s) :=
  sorry

theorem continuous_on_iff' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : α → β} {s : set α} :
    continuous_on f s ↔ ∀ (t : set β), is_open t → ∃ (u : set α), is_open u ∧ f ⁻¹' t ∩ s = u ∩ s :=
  sorry

theorem continuous_on_iff_is_closed {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} :
    continuous_on f s ↔
        ∀ (t : set β), is_closed t → ∃ (u : set α), is_closed u ∧ f ⁻¹' t ∩ s = u ∩ s :=
  sorry

theorem continuous_on.prod_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4}
    [topological_space α] [topological_space β] [topological_space γ] [topological_space δ]
    {f : α → γ} {g : β → δ} {s : set α} {t : set β} (hf : continuous_on f s)
    (hg : continuous_on g t) : continuous_on (prod.map f g) (set.prod s t) :=
  sorry

theorem continuous_on_empty {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] (f : α → β) : continuous_on f ∅ :=
  fun (x : α) => false.elim

theorem nhds_within_le_comap {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {x : α} {s : set α} {f : α → β} (ctsf : continuous_within_at f s x) :
    nhds_within x s ≤ filter.comap f (nhds_within (f x) (f '' s)) :=
  iff.mp filter.map_le_iff_le_comap (continuous_within_at.tendsto_nhds_within_image ctsf)

theorem continuous_within_at_iff_ptendsto_res {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] (f : α → β) {x : α} {s : set α} :
    continuous_within_at f s x ↔ filter.ptendsto (pfun.res f s) (nhds x) (nhds (f x)) :=
  filter.tendsto_iff_ptendsto (nhds x) (nhds (f x)) s f

theorem continuous_iff_continuous_on_univ {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} : continuous f ↔ continuous_on f set.univ :=
  sorry

theorem continuous_within_at.mono {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α}
    (h : continuous_within_at f t x) (hs : s ⊆ t) : continuous_within_at f s x :=
  filter.tendsto.mono_left h (nhds_within_mono x hs)

theorem continuous_within_at.mono_of_mem {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α}
    (h : continuous_within_at f t x) (hs : t ∈ nhds_within x s) : continuous_within_at f s x :=
  filter.tendsto.mono_left h (nhds_within_le_of_mem hs)

theorem continuous_within_at_inter' {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : t ∈ nhds_within x s) :
    continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x :=
  sorry

theorem continuous_within_at_inter {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} (h : t ∈ nhds x) :
    continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x :=
  sorry

theorem continuous_within_at_union {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α} :
    continuous_within_at f (s ∪ t) x ↔ continuous_within_at f s x ∧ continuous_within_at f t x :=
  sorry

theorem continuous_within_at.union {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α}
    (hs : continuous_within_at f s x) (ht : continuous_within_at f t x) :
    continuous_within_at f (s ∪ t) x :=
  iff.mpr continuous_within_at_union { left := hs, right := ht }

theorem continuous_within_at.mem_closure_image {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x)
    (hx : x ∈ closure s) : f x ∈ closure (f '' s) :=
  mem_closure_of_tendsto h
    (filter.mem_sets_of_superset self_mem_nhds_within (set.subset_preimage_image f s))

theorem continuous_within_at.mem_closure {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} {A : set β}
    (h : continuous_within_at f s x) (hx : x ∈ closure s) (hA : s ⊆ f ⁻¹' A) : f x ∈ closure A :=
  closure_mono (iff.mpr set.image_subset_iff hA) (continuous_within_at.mem_closure_image h hx)

theorem continuous_within_at.image_closure {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α}
    (hf : ∀ (x : α), x ∈ closure s → continuous_within_at f s x) :
    f '' closure s ⊆ closure (f '' s) :=
  sorry

@[simp] theorem continuous_within_at_singleton {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {x : α} : continuous_within_at f (singleton x) x :=
  sorry

@[simp] theorem continuous_within_at_insert_self {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {x : α} {s : set α} :
    continuous_within_at f (insert x s) x ↔ continuous_within_at f s x :=
  sorry

theorem continuous_within_at.insert_self {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {x : α} {s : set α} :
    continuous_within_at f s x → continuous_within_at f (insert x s) x :=
  iff.mpr continuous_within_at_insert_self

theorem continuous_within_at.diff_iff {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set α} {x : α}
    (ht : continuous_within_at f t x) :
    continuous_within_at f (s \ t) x ↔ continuous_within_at f s x :=
  sorry

@[simp] theorem continuous_within_at_diff_self {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} :
    continuous_within_at f (s \ singleton x) x ↔ continuous_within_at f s x :=
  continuous_within_at.diff_iff continuous_within_at_singleton

theorem is_open_map.continuous_on_image_of_left_inv_on {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {s : set α}
    (h : is_open_map (set.restrict f s)) {finv : β → α} (hleft : set.left_inv_on finv f s) :
    continuous_on finv (f '' s) :=
  sorry

theorem is_open_map.continuous_on_range_of_left_inverse {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} (hf : is_open_map f) {finv : β → α}
    (hleft : function.left_inverse finv f) : continuous_on finv (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (continuous_on finv (set.range f))) (Eq.symm set.image_univ)))
    (is_open_map.continuous_on_image_of_left_inv_on (is_open_map.restrict hf is_open_univ)
      fun (x : α) (_x : x ∈ set.univ) => hleft x)

theorem continuous_on.congr_mono {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {g : α → β} {s : set α} {s₁ : set α} (h : continuous_on f s)
    (h' : set.eq_on g f s₁) (h₁ : s₁ ⊆ s) : continuous_on g s₁ :=
  sorry

theorem continuous_on.congr {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {g : α → β} {s : set α} (h : continuous_on f s)
    (h' : set.eq_on g f s) : continuous_on g s :=
  continuous_on.congr_mono h h' (set.subset.refl s)

theorem continuous_on_congr {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {g : α → β} {s : set α} (h' : set.eq_on g f s) :
    continuous_on g s ↔ continuous_on f s :=
  { mp := fun (h : continuous_on g s) => continuous_on.congr h (set.eq_on.symm h'),
    mpr := fun (h : continuous_on f s) => continuous_on.congr h h' }

theorem continuous_at.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_at f x) :
    continuous_within_at f s x :=
  continuous_within_at.mono (iff.mpr (continuous_within_at_univ f x) h) (set.subset_univ s)

theorem continuous_within_at.continuous_at {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_within_at f s x)
    (hs : s ∈ nhds x) : continuous_at f x :=
  sorry

theorem continuous_on.continuous_at {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous_on f s)
    (hx : s ∈ nhds x) : continuous_at f x :=
  continuous_within_at.continuous_at (h x (mem_of_nhds hx)) hx

theorem continuous_within_at.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α]
    [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β}
    {x : α} (hg : continuous_within_at g t (f x)) (hf : continuous_within_at f s x)
    (h : s ⊆ f ⁻¹' t) : continuous_within_at (g ∘ f) s x :=
  sorry

theorem continuous_within_at.comp' {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β}
    {s : set α} {t : set β} {x : α} (hg : continuous_within_at g t (f x))
    (hf : continuous_within_at f s x) : continuous_within_at (g ∘ f) (s ∩ f ⁻¹' t) x :=
  continuous_within_at.comp hg (continuous_within_at.mono hf (set.inter_subset_left s (f ⁻¹' t)))
    (set.inter_subset_right s (f ⁻¹' t))

theorem continuous_on.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α]
    [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β}
    (hg : continuous_on g t) (hf : continuous_on f s) (h : s ⊆ f ⁻¹' t) : continuous_on (g ∘ f) s :=
  fun (x : α) (hx : x ∈ s) => continuous_within_at.comp (hg (f x) (h hx)) (hf x hx) h

theorem continuous_on.mono {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {f : α → β} {s : set α} {t : set α} (hf : continuous_on f s) (h : t ⊆ s) : continuous_on f t :=
  fun (x : α) (hx : x ∈ t) => filter.tendsto.mono_left (hf x (h hx)) (nhds_within_mono x h)

theorem continuous_on.comp' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α]
    [topological_space β] [topological_space γ] {g : β → γ} {f : α → β} {s : set α} {t : set β}
    (hg : continuous_on g t) (hf : continuous_on f s) : continuous_on (g ∘ f) (s ∩ f ⁻¹' t) :=
  continuous_on.comp hg (continuous_on.mono hf (set.inter_subset_left s (f ⁻¹' t)))
    (set.inter_subset_right s (f ⁻¹' t))

theorem continuous.continuous_on {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} (h : continuous f) : continuous_on f s :=
  continuous_on.mono
    (eq.mp (Eq._oldrec (Eq.refl (continuous f)) (propext continuous_iff_continuous_on_univ)) h)
    (set.subset_univ s)

theorem continuous.continuous_within_at {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} (h : continuous f) :
    continuous_within_at f s x :=
  continuous_at.continuous_within_at (continuous.continuous_at h)

theorem continuous.comp_continuous_on {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β}
    {s : set α} (hg : continuous g) (hf : continuous_on f s) : continuous_on (g ∘ f) s :=
  continuous_on.comp (continuous.continuous_on hg) hf set.subset_preimage_univ

theorem continuous_on.comp_continuous {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [topological_space α] [topological_space β] [topological_space γ] {g : β → γ} {f : α → β}
    {s : set β} (hg : continuous_on g s) (hf : continuous f) (hs : ∀ (x : α), f x ∈ s) :
    continuous (g ∘ f) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (continuous (g ∘ f))) (propext continuous_iff_continuous_on_univ)))
    (continuous_on.comp hg
      (eq.mp (Eq._oldrec (Eq.refl (continuous f)) (propext continuous_iff_continuous_on_univ)) hf)
      fun (x : α) (_x : x ∈ set.univ) => hs x)

theorem continuous_within_at.preimage_mem_nhds_within {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β}
    (h : continuous_within_at f s x) (ht : t ∈ nhds (f x)) : f ⁻¹' t ∈ nhds_within x s :=
  h ht

theorem continuous_within_at.preimage_mem_nhds_within' {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {x : α} {s : set α} {t : set β}
    (h : continuous_within_at f s x) (ht : t ∈ nhds_within (f x) (f '' s)) :
    f ⁻¹' t ∈ nhds_within x s :=
  sorry

theorem continuous_within_at.congr_of_eventually_eq {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {f₁ : α → β} {s : set α} {x : α}
    (h : continuous_within_at f s x) (h₁ : filter.eventually_eq (nhds_within x s) f₁ f)
    (hx : f₁ x = f x) : continuous_within_at f₁ s x :=
  sorry

theorem continuous_within_at.congr {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {f₁ : α → β} {s : set α} {x : α}
    (h : continuous_within_at f s x) (h₁ : ∀ (y : α), y ∈ s → f₁ y = f y) (hx : f₁ x = f x) :
    continuous_within_at f₁ s x :=
  continuous_within_at.congr_of_eventually_eq h
    (filter.mem_sets_of_superset self_mem_nhds_within h₁) hx

theorem continuous_within_at.congr_mono {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {g : α → β} {s : set α} {s₁ : set α} {x : α}
    (h : continuous_within_at f s x) (h' : set.eq_on g f s₁) (h₁ : s₁ ⊆ s) (hx : g x = f x) :
    continuous_within_at g s₁ x :=
  continuous_within_at.congr (continuous_within_at.mono h h₁) h' hx

theorem continuous_on_const {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {s : set α} {c : β} : continuous_on (fun (x : α) => c) s :=
  continuous.continuous_on continuous_const

theorem continuous_within_at_const {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {b : β} {s : set α} {x : α} :
    continuous_within_at (fun (_x : α) => b) s x :=
  continuous.continuous_within_at continuous_const

theorem continuous_on_id {α : Type u_1} [topological_space α] {s : set α} : continuous_on id s :=
  continuous.continuous_on continuous_id

theorem continuous_within_at_id {α : Type u_1} [topological_space α] {s : set α} {x : α} :
    continuous_within_at id s x :=
  continuous.continuous_within_at continuous_id

theorem continuous_on_open_iff {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} (hs : is_open s) :
    continuous_on f s ↔ ∀ (t : set β), is_open t → is_open (s ∩ f ⁻¹' t) :=
  sorry

theorem continuous_on.preimage_open_of_open {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s)
    (hs : is_open s) (ht : is_open t) : is_open (s ∩ f ⁻¹' t) :=
  iff.mp (continuous_on_open_iff hs) hf t ht

theorem continuous_on.preimage_closed_of_closed {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s)
    (hs : is_closed s) (ht : is_closed t) : is_closed (s ∩ f ⁻¹' t) :=
  sorry

theorem continuous_on.preimage_interior_subset_interior_preimage {α : Type u_1} {β : Type u_2}
    [topological_space α] [topological_space β] {f : α → β} {s : set α} {t : set β}
    (hf : continuous_on f s) (hs : is_open s) : s ∩ f ⁻¹' interior t ⊆ s ∩ interior (f ⁻¹' t) :=
  sorry

theorem continuous_on_of_locally_continuous_on {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α}
    (h : ∀ (x : α), x ∈ s → ∃ (t : set α), is_open t ∧ x ∈ t ∧ continuous_on f (s ∩ t)) :
    continuous_on f s :=
  sorry

theorem continuous_on_open_of_generate_from {α : Type u_1} [topological_space α] {β : Type u_2}
    {s : set α} {T : set (set β)} {f : α → β} (hs : is_open s)
    (h : ∀ (t : set β), t ∈ T → is_open (s ∩ f ⁻¹' t)) : continuous_on f s :=
  sorry

theorem continuous_within_at.prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α]
    [topological_space β] [topological_space γ] {f : α → β} {g : α → γ} {s : set α} {x : α}
    (hf : continuous_within_at f s x) (hg : continuous_within_at g s x) :
    continuous_within_at (fun (x : α) => (f x, g x)) s x :=
  filter.tendsto.prod_mk_nhds hf hg

theorem continuous_on.prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [topological_space α]
    [topological_space β] [topological_space γ] {f : α → β} {g : α → γ} {s : set α}
    (hf : continuous_on f s) (hg : continuous_on g s) :
    continuous_on (fun (x : α) => (f x, g x)) s :=
  fun (x : α) (hx : x ∈ s) => continuous_within_at.prod (hf x hx) (hg x hx)

theorem inducing.continuous_on_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : β → γ}
    (hg : inducing g) {s : set α} : continuous_on f s ↔ continuous_on (g ∘ f) s :=
  sorry

theorem embedding.continuous_on_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [topological_space α] [topological_space β] [topological_space γ] {f : α → β} {g : β → γ}
    (hg : embedding g) {s : set α} : continuous_on f s ↔ continuous_on (g ∘ f) s :=
  inducing.continuous_on_iff (embedding.to_inducing hg)

theorem continuous_within_at_of_not_mem_closure {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {f : α → β} {s : set α} {x : α} :
    ¬x ∈ closure s → continuous_within_at f s x :=
  sorry

theorem continuous_on_if' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {s : set α} {p : α → Prop} {f : α → β} {g : α → β} {h : (a : α) → Decidable (p a)}
    (hpf :
      ∀ (a : α),
        a ∈ s ∩ frontier (set_of fun (a : α) => p a) →
          filter.tendsto f (nhds_within a (s ∩ set_of fun (a : α) => p a))
            (nhds (ite (p a) (f a) (g a))))
    (hpg :
      ∀ (a : α),
        a ∈ s ∩ frontier (set_of fun (a : α) => p a) →
          filter.tendsto g (nhds_within a (s ∩ set_of fun (a : α) => ¬p a))
            (nhds (ite (p a) (f a) (g a))))
    (hf : continuous_on f (s ∩ set_of fun (a : α) => p a))
    (hg : continuous_on g (s ∩ set_of fun (a : α) => ¬p a)) :
    continuous_on (fun (a : α) => ite (p a) (f a) (g a)) s :=
  sorry

theorem continuous_on_if {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {p : α → Prop} {h : (a : α) → Decidable (p a)} {s : set α} {f : α → β} {g : α → β}
    (hp : ∀ (a : α), a ∈ s ∩ frontier (set_of fun (a : α) => p a) → f a = g a)
    (hf : continuous_on f (s ∩ closure (set_of fun (a : α) => p a)))
    (hg : continuous_on g (s ∩ closure (set_of fun (a : α) => ¬p a))) :
    continuous_on (fun (a : α) => ite (p a) (f a) (g a)) s :=
  sorry

theorem continuous_if' {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {p : α → Prop} {f : α → β} {g : α → β} {h : (a : α) → Decidable (p a)}
    (hpf :
      ∀ (a : α),
        a ∈ frontier (set_of fun (x : α) => p x) →
          filter.tendsto f (nhds_within a (set_of fun (x : α) => p x))
            (nhds (ite (p a) (f a) (g a))))
    (hpg :
      ∀ (a : α),
        a ∈ frontier (set_of fun (x : α) => p x) →
          filter.tendsto g (nhds_within a (set_of fun (x : α) => ¬p x))
            (nhds (ite (p a) (f a) (g a))))
    (hf : continuous_on f (set_of fun (x : α) => p x))
    (hg : continuous_on g (set_of fun (x : α) => ¬p x)) :
    continuous fun (a : α) => ite (p a) (f a) (g a) :=
  sorry

theorem continuous_on_fst {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {s : set (α × β)} : continuous_on prod.fst s :=
  continuous.continuous_on continuous_fst

theorem continuous_within_at_fst {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {s : set (α × β)} {p : α × β} : continuous_within_at prod.fst s p :=
  continuous.continuous_within_at continuous_fst

theorem continuous_on_snd {α : Type u_1} {β : Type u_2} [topological_space α] [topological_space β]
    {s : set (α × β)} : continuous_on prod.snd s :=
  continuous.continuous_on continuous_snd

theorem continuous_within_at_snd {α : Type u_1} {β : Type u_2} [topological_space α]
    [topological_space β] {s : set (α × β)} {p : α × β} : continuous_within_at prod.snd s p :=
  continuous.continuous_within_at continuous_snd

end Mathlib


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import algebra.big_operators.nat_antidiagonal
import topology.algebra.infinite_sum.basic
import topology.algebra.ring.basic

/-!
# Infinite sum in a ring

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

This file provides lemmas about the interaction between infinite sums and multiplication.

## Main results

* `tsum_mul_tsum_eq_tsum_sum_antidiagonal`: Cauchy product formula
-/

open filter finset function
open_locale big_operators classical

variables {ι κ R α : Type*}

section non_unital_non_assoc_semiring
variables [non_unital_non_assoc_semiring α] [topological_space α] [topological_semiring α]
  {f g : ι → α} {a a₁ a₂ : α}

lemma has_sum.mul_left (a₂) (h : has_sum f a₁) : has_sum (λ i, a₂ * f i) (a₂ * a₁) :=
by simpa only using h.map (add_monoid_hom.mul_left a₂) (continuous_const.mul continuous_id)

lemma has_sum.mul_right (a₂) (hf : has_sum f a₁) : has_sum (λ i, f i * a₂) (a₁ * a₂) :=
by simpa only using hf.map (add_monoid_hom.mul_right a₂) (continuous_id.mul continuous_const)

lemma summable.mul_left (a) (hf : summable f) : summable (λ i, a * f i) :=
(hf.has_sum.mul_left _).summable

lemma summable.mul_right (a) (hf : summable f) : summable (λ i, f i * a) :=
(hf.has_sum.mul_right _).summable

section tsum
variables [t2_space α]

lemma summable.tsum_mul_left (a) (hf : summable f) : ∑' i, a * f i = a * ∑' i, f i :=
(hf.has_sum.mul_left _).tsum_eq

lemma summable.tsum_mul_right (a) (hf : summable f) : ∑' i, f i * a = (∑' i, f i) * a :=
(hf.has_sum.mul_right _).tsum_eq

lemma commute.tsum_right (a) (h : ∀ i, commute a (f i)) : commute a (∑' i, f i) :=
if hf : summable f then
  (hf.tsum_mul_left a).symm.trans ((congr_arg _ $ funext h).trans (hf.tsum_mul_right a))
else
  (tsum_eq_zero_of_not_summable hf).symm ▸ commute.zero_right _

lemma commute.tsum_left (a) (h : ∀ i, commute (f i) a) : commute (∑' i, f i) a :=
(commute.tsum_right _ $ λ i, (h i).symm).symm

end tsum
end non_unital_non_assoc_semiring

section division_semiring
variables [division_semiring α] [topological_space α] [topological_semiring α] {f g : ι → α}
  {a a₁ a₂ : α}

lemma has_sum.div_const (h : has_sum f a) (b : α) : has_sum (λ i, f i / b) (a / b) :=
by simp only [div_eq_mul_inv, h.mul_right b⁻¹]

lemma summable.div_const (h : summable f) (b : α) : summable (λ i, f i / b) :=
(h.has_sum.div_const _).summable

lemma has_sum_mul_left_iff (h : a₂ ≠ 0) : has_sum (λ i, a₂ * f i) (a₂ * a₁) ↔ has_sum f a₁ :=
⟨λ H, by simpa only [inv_mul_cancel_left₀ h] using H.mul_left a₂⁻¹, has_sum.mul_left _⟩

lemma has_sum_mul_right_iff (h : a₂ ≠ 0) : has_sum (λ i, f i * a₂) (a₁ * a₂) ↔ has_sum f a₁ :=
⟨λ H, by simpa only [mul_inv_cancel_right₀ h] using H.mul_right a₂⁻¹, has_sum.mul_right _⟩

lemma has_sum_div_const_iff (h : a₂ ≠ 0) : has_sum (λ i, f i / a₂) (a₁ / a₂) ↔ has_sum f a₁ :=
by simpa only [div_eq_mul_inv] using has_sum_mul_right_iff (inv_ne_zero h)

lemma summable_mul_left_iff (h : a ≠ 0) : summable (λ i, a * f i) ↔ summable f :=
⟨λ H, by simpa only [inv_mul_cancel_left₀ h] using H.mul_left a⁻¹, λ H, H.mul_left _⟩

lemma summable_mul_right_iff (h : a ≠ 0) : summable (λ i, f i * a) ↔ summable f :=
⟨λ H, by simpa only [mul_inv_cancel_right₀ h] using H.mul_right a⁻¹, λ H, H.mul_right _⟩

lemma summable_div_const_iff (h : a ≠ 0) : summable (λ i, f i / a) ↔ summable f :=
by simpa only [div_eq_mul_inv] using summable_mul_right_iff (inv_ne_zero h)

lemma tsum_mul_left [t2_space α] : (∑' x, a * f x) = a * ∑' x, f x :=
if hf : summable f then hf.tsum_mul_left a
else if ha : a = 0 then by simp [ha]
else by rw [tsum_eq_zero_of_not_summable hf,
  tsum_eq_zero_of_not_summable (mt (summable_mul_left_iff ha).mp hf), mul_zero]

lemma tsum_mul_right [t2_space α] : (∑' x, f x * a) = (∑' x, f x) * a :=
if hf : summable f then hf.tsum_mul_right a
else if ha : a = 0 then by simp [ha]
else by rw [tsum_eq_zero_of_not_summable hf,
  tsum_eq_zero_of_not_summable (mt (summable_mul_right_iff ha).mp hf), zero_mul]

lemma tsum_div_const [t2_space α] : (∑' x, f x / a) = (∑' x, f x) / a :=
by simpa only [div_eq_mul_inv] using tsum_mul_right

end division_semiring

/-!
### Multipliying two infinite sums

In this section, we prove various results about `(∑' x : ι, f x) * (∑' y : κ, g y)`. Note that we
always assume that the family `λ x : ι × κ, f x.1 * g x.2` is summable, since there is no way to
deduce this from the summmabilities of `f` and `g` in general, but if you are working in a normed
space, you may want to use the analogous lemmas in `analysis/normed_space/basic`
(e.g `tsum_mul_tsum_of_summable_norm`).

We first establish results about arbitrary index types, `ι` and `κ`, and then we specialize to
`ι = κ = ℕ` to prove the Cauchy product formula (see `tsum_mul_tsum_eq_tsum_sum_antidiagonal`).

#### Arbitrary index types
-/

section tsum_mul_tsum
variables [topological_space α] [t3_space α] [non_unital_non_assoc_semiring α]
  [topological_semiring α] {f : ι → α} {g : κ → α} {s t u : α}

lemma has_sum.mul_eq (hf : has_sum f s) (hg : has_sum g t)
  (hfg : has_sum (λ (x : ι × κ), f x.1 * g x.2) u) :
  s * t = u :=
have key₁ : has_sum (λ i, f i * t) (s * t),
  from hf.mul_right t,
have this : ∀ i : ι, has_sum (λ c : κ, f i * g c) (f i * t),
  from λ i, hg.mul_left (f i),
have key₂ : has_sum (λ i, f i * t) u,
  from has_sum.prod_fiberwise hfg this,
key₁.unique key₂

lemma has_sum.mul (hf : has_sum f s) (hg : has_sum g t)
  (hfg : summable (λ (x : ι × κ), f x.1 * g x.2)) :
  has_sum (λ (x : ι × κ), f x.1 * g x.2) (s * t) :=
let ⟨u, hu⟩ := hfg in
(hf.mul_eq hg hu).symm ▸ hu

/-- Product of two infinites sums indexed by arbitrary types.
    See also `tsum_mul_tsum_of_summable_norm` if `f` and `g` are abolutely summable. -/
lemma tsum_mul_tsum (hf : summable f) (hg : summable g)
  (hfg : summable (λ (x : ι × κ), f x.1 * g x.2)) :
  (∑' x, f x) * (∑' y, g y) = (∑' z : ι × κ, f z.1 * g z.2) :=
hf.has_sum.mul_eq hg.has_sum hfg.has_sum

end tsum_mul_tsum

/-!
#### `ℕ`-indexed families (Cauchy product)

We prove two versions of the Cauchy product formula. The first one is
`tsum_mul_tsum_eq_tsum_sum_range`, where the `n`-th term is a sum over `finset.range (n+1)`
involving `nat` subtraction.
In order to avoid `nat` subtraction, we also provide `tsum_mul_tsum_eq_tsum_sum_antidiagonal`,
where the `n`-th term is a sum over all pairs `(k, l)` such that `k+l=n`, which corresponds to the
`finset` `finset.nat.antidiagonal n`
-/

section cauchy_product
variables [topological_space α] [non_unital_non_assoc_semiring α] {f g : ℕ → α}

/- The family `(k, l) : ℕ × ℕ ↦ f k * g l` is summable if and only if the family
`(n, k, l) : Σ (n : ℕ), nat.antidiagonal n ↦ f k * g l` is summable. -/
lemma summable_mul_prod_iff_summable_mul_sigma_antidiagonal :
  summable (λ x : ℕ × ℕ, f x.1 * g x.2) ↔
    summable (λ x : (Σ (n : ℕ), nat.antidiagonal n), f (x.2 : ℕ × ℕ).1 * g (x.2 : ℕ × ℕ).2) :=
nat.sigma_antidiagonal_equiv_prod.summable_iff.symm

variables [t3_space α] [topological_semiring α]

lemma summable_sum_mul_antidiagonal_of_summable_mul (h : summable (λ x : ℕ × ℕ, f x.1 * g x.2)) :
  summable (λ n, ∑ kl in nat.antidiagonal n, f kl.1 * g kl.2) :=
begin
  rw summable_mul_prod_iff_summable_mul_sigma_antidiagonal at h,
  conv {congr, funext, rw [← finset.sum_finset_coe, ← tsum_fintype]},
  exact h.sigma' (λ n, (has_sum_fintype _).summable),
end

/-- The **Cauchy product formula** for the product of two infinites sums indexed by `ℕ`, expressed
by summing on `finset.nat.antidiagonal`.

See also `tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm` if `f` and `g` are absolutely
summable. -/
lemma tsum_mul_tsum_eq_tsum_sum_antidiagonal (hf : summable f) (hg : summable g)
  (hfg : summable (λ (x : ℕ × ℕ), f x.1 * g x.2)) :
  (∑' n, f n) * (∑' n, g n) = (∑' n, ∑ kl in nat.antidiagonal n, f kl.1 * g kl.2) :=
begin
  conv_rhs {congr, funext, rw [← finset.sum_finset_coe, ← tsum_fintype]},
  rw [tsum_mul_tsum hf hg hfg, ← nat.sigma_antidiagonal_equiv_prod.tsum_eq (_ : ℕ × ℕ → α)],
  exact tsum_sigma' (λ n, (has_sum_fintype _).summable)
    (summable_mul_prod_iff_summable_mul_sigma_antidiagonal.mp hfg)
end

lemma summable_sum_mul_range_of_summable_mul (h : summable (λ x : ℕ × ℕ, f x.1 * g x.2)) :
  summable (λ n, ∑ k in range (n+1), f k * g (n - k)) :=
begin
  simp_rw ← nat.sum_antidiagonal_eq_sum_range_succ (λ k l, f k * g l),
  exact summable_sum_mul_antidiagonal_of_summable_mul h
end

/-- The **Cauchy product formula** for the product of two infinites sums indexed by `ℕ`, expressed
by summing on `finset.range`.

See also `tsum_mul_tsum_eq_tsum_sum_range_of_summable_norm` if `f` and `g` are absolutely summable.
-/
lemma tsum_mul_tsum_eq_tsum_sum_range (hf : summable f) (hg : summable g)
  (hfg : summable (λ (x : ℕ × ℕ), f x.1 * g x.2)) :
  (∑' n, f n) * (∑' n, g n) = ∑' n, ∑ k in range (n + 1), f k * g (n - k) :=
begin
  simp_rw ← nat.sum_antidiagonal_eq_sum_range_succ (λ k l, f k * g l),
  exact tsum_mul_tsum_eq_tsum_sum_antidiagonal hf hg hfg
end

end cauchy_product


/-
Copyright (c) 2020 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.num.lemmas
import Mathlib.data.nat.prime
import Mathlib.tactic.ring
import Mathlib.PostPort

namespace Mathlib

/-!
# Primality for binary natural numbers

This file defines versions of `nat.min_fac` and `nat.prime` for `num` and `pos_num`. As with other
`num` definitions, they are not intended for general use (`nat` should be used instead of `num` in
most cases) but they can be used in contexts where kernel computation is required, such as proofs
by `rfl` and `dec_trivial`, as well as in `#reduce`.

The default decidable instance for `nat.prime` is optimized for VM evaluation, so it should be
preferred within `#eval` or in tactic execution, while for proofs the `norm_num` tactic can be used
to construct primality and non-primality proofs more efficiently than kernel computation.

Nevertheless, sometimes proof by computational reflection requires natural number computations, and
`num` implements algorithms directly on binary natural numbers for this purpose.
-/

namespace pos_num

/-- Auxiliary function for computing the smallest prime factor of a `pos_num`. Unlike
`nat.min_fac_aux`, we use a natural number `fuel` variable that is set to an upper bound on the
number of iterations. It is initialized to the number `n` we are determining primality for. Even
though this is exponential in the input (since it is a `nat`, not a `num`), it will get lazily
evaluated during kernel reduction, so we will only require about `sqrt n` unfoldings, for the
`sqrt n` iterations of the loop. -/
def min_fac_aux (n : pos_num) : ℕ → pos_num → pos_num :=
  sorry

theorem min_fac_aux_to_nat {fuel : ℕ} {n : pos_num} {k : pos_num} (h : nat.sqrt ↑n < fuel + ↑(bit1 k)) : ↑(min_fac_aux n fuel k) = nat.min_fac_aux ↑n ↑(bit1 k) := sorry

/-- Returns the smallest prime factor of `n ≠ 1`. -/
def min_fac : pos_num → pos_num :=
  sorry

@[simp] theorem min_fac_to_nat (n : pos_num) : ↑(min_fac n) = nat.min_fac ↑n := sorry

/-- Primality predicate for a `pos_num`. -/
@[simp] def prime (n : pos_num) :=
  nat.prime ↑n

protected instance decidable_prime : decidable_pred prime :=
  sorry

end pos_num

namespace num

/-- Returns the smallest prime factor of `n ≠ 1`. -/
def min_fac : num → pos_num :=
  sorry

@[simp] theorem min_fac_to_nat (n : num) : ↑(min_fac n) = nat.min_fac ↑n :=
  num.cases_on n (idRhs (↑(min_fac 0) = ↑(min_fac 0)) rfl)
    fun (n : pos_num) => idRhs (↑(pos_num.min_fac n) = nat.min_fac ↑n) (pos_num.min_fac_to_nat n)

/-- Primality predicate for a `num`. -/
@[simp] def prime (n : num) :=
  nat.prime ↑n

protected instance decidable_prime : decidable_pred prime :=
  sorry


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.category.Top.basic
import Mathlib.measure_theory.giry_monad
import Mathlib.category_theory.monad.algebra
import Mathlib.PostPort

universes u u_1

namespace Mathlib

/-
* Meas, the category of measurable spaces

Measurable spaces and measurable functions form a (concrete) category Meas.

Measure : Meas ⥤ Meas is the functor which sends a measurable space X
to the space of measures on X; it is a monad (the "Giry monad").

Borel : Top ⥤ Meas sends a topological space X to X equipped with the
σ-algebra of Borel sets (the σ-algebra generated by the open subsets of X).

## Tags

measurable space, giry monad, borel

-/

/-- The category of measurable spaces and measurable functions. -/
def Meas := category_theory.bundled measurable_space

namespace Meas

protected instance measurable_space (X : Meas) : measurable_space ↥X :=
  category_theory.bundled.str X

/-- Construct a bundled `Meas` from the underlying type and the typeclass. -/
def of (α : Type u) [measurable_space α] : Meas := category_theory.bundled.mk α

@[simp] theorem coe_of (X : Type u) [measurable_space X] : ↥(of X) = X := rfl

protected instance unbundled_hom : category_theory.unbundled_hom measurable :=
  category_theory.unbundled_hom.mk measurable_id measurable.comp

protected instance large_category : category_theory.large_category Meas :=
  category_theory.bundled_hom.category
    fun (α β : Type u_1) (Iα : measurable_space α) (Iβ : measurable_space β) => Subtype measurable

protected instance inhabited : Inhabited Meas := { default := of empty }

/-- `Measure X` is the measurable space of measures over the measurable space `X`. It is the
weakest measurable space, s.t. λμ, μ s is measurable for all measurable sets `s` in `X`. An
important purpose is to assign a monadic structure on it, the Giry monad. In the Giry monad,
the pure values are the Dirac measure, and the bind operation maps to the integral:
`(μ >>= ν) s = ∫ x. (ν x) s dμ`.

In probability theory, the `Meas`-morphisms `X → Prob X` are (sub-)Markov kernels (here `Prob` is
the restriction of `Measure` to (sub-)probability space.)
-/
def Measure : Meas ⥤ Meas :=
  category_theory.functor.mk
    (fun (X : Meas) =>
      category_theory.bundled.mk (measure_theory.measure (category_theory.bundled.α X)))
    fun (X Y : Meas) (f : X ⟶ Y) => { val := ⇑(measure_theory.measure.map ↑f), property := sorry }

/-- The Giry monad, i.e. the monadic structure associated with `Measure`. -/
protected instance Measure.category_theory.monad : category_theory.monad Measure :=
  category_theory.monad.mk
    (category_theory.nat_trans.mk
      fun (X : Meas) => { val := measure_theory.measure.dirac, property := sorry })
    (category_theory.nat_trans.mk
      fun (X : Meas) => { val := measure_theory.measure.join, property := sorry })

/-- An example for an algebra on `Measure`: the nonnegative Lebesgue integral is a hom, behaving
nicely under the monad operations. -/
def Integral : category_theory.monad.algebra Measure :=
  category_theory.monad.algebra.mk (of ennreal)
    { val :=
        fun (m : measure_theory.measure ennreal) =>
          measure_theory.lintegral m fun (x : ennreal) => x,
      property := sorry }

end Meas

protected instance Top.has_forget_to_Meas : category_theory.has_forget₂ Top Meas :=
  category_theory.bundled_hom.mk_has_forget₂ borel
    (fun (X Y : category_theory.bundled topological_space) (f : X ⟶ Y) =>
      { val := continuous_map.to_fun f, property := sorry })
    sorry

/-- The Borel functor, the canonical embedding of topological spaces into measurable spaces. -/
def Borel : Top ⥤ Meas := category_theory.forget₂ Top Meas

end Mathlib


/-
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Authors: Moritz Firsching, Nikolas Kuhn
-/
import tactic
import data.zmod.basic
import data.finset.card
import data.polynomial.basic
import order.locally_finite

open zmod finset
open polynomial (X)
open_locale polynomial
open_locale big_operators

/-!
# The law of quadratic reciprocity

## Outline
  - Legendre symbol
  - Euler's criterion
  - First proof
    - Lemma of Gauss
    - proof
  - Second proof
    - A.
    - B.
    - First expression -- TO DO
    - Second expression -- TO DO
    - The multiplicative group of a finite field is cyclic
    - proof
-/

namespace book
namespace quadratic_reciprocity

section
/- Throughout this section, `p` is an odd prime. -/
variables (p : ℕ) (h_p : p ≠ 2) [fact (nat.prime p)]

def legendre_sym (a : ℤ) : ℤ :=
  ite ( (a : zmod p) = 0) 0 $
    ite (∃ b : zmod p, a = b ^ 2) 1 (-1)

/--
Fermat's little theorem: If `a` is nonzero modulo the odd prime `p`, then `a ^ (p - 1) = -1`
modulo `p`.
-/
lemma fermat_little (a : ℤ): (a ≠ (0 : zmod p)) → a ^ (p - 1) = (-1 : zmod p) :=
begin
  let units_finset := (finset.univ : finset (zmod p)).erase 0,
  suffices : (units_finset).image (λ x : zmod p, (a : zmod p) * x) = units_finset, by
    sorry,
  sorry,
end

theorem euler_criterion (a : ℤ) :
  a ≠ (0 : zmod p) → (legendre_sym p a : zmod p) = a ^ ((p - 1) / 2) :=
begin
  sorry,
end

lemma product_rule (a b : ℤ) :
  legendre_sym p (a * b) = (legendre_sym p a) * (legendre_sym p b) :=
begin
  sorry,
end

end

/-!
### First proof
For the statement, see `theorem quadratic_reciprocity_1`.
-/

-- Maybe define the function `r i` = "reduce p i" explicitly ?
--TODO : This should probably be broken down a little bit first
lemma lemma_of_Gauss (p : ℕ) [fact (nat.prime p)] (a : ℤ) (h_a : a ≠ (0 : zmod p))
  ( r : ℤ → ℤ ) (h_r : (∀ i, (- (p: ℤ) - 1)/2 ≤ r i ∧ r i ≤ ((p : ℤ) - 1)/2))
  ( H : ∀ i, (r i : ℤ) = (a * i : zmod p) ) :
   legendre_sym p a =
   card ((Icc (1 : ℤ) (((p : ℤ)-1)/2)).image r ∩ (Icc (-((p: ℤ) - 1)/2) (-1))):=
begin
  sorry
end

theorem quadratic_reciprocity_1 (p q : ℕ) (hp : p ≠ 2) (hq : q ≠ 2)
  [fact (nat.prime p)] [fact (nat.prime q)] :
  (legendre_sym p q) * (legendre_sym q p) = -1 ^ ((p-1) / 2 * (q - 1) / 2 ) :=
  sorry

/-!
### Second Proof
TODO:
    - A.
    - B.
    - First expression
    - Second expression
    - The multiplicative group of a finite field is cyclic
-/

/- The group of units of a finite field is cyclic, i.e. has a multiplicative generator-/
lemma mult_cyclic (K : Type*) [field K] [fintype K] : ∃ ζ : Kˣ, ∀ α : Kˣ, ∃ k : ℤ, α = ζ ^ k :=
begin
  sorry,
end

lemma fact_A (p q : ℕ) (hp : p ≠ 2) (hq : q ≠ 2) [fact (nat.prime p)] [fact (nat.prime q)]
  (h_pq : p ≠ q) (K : Type*) [field K] [fintype K] (H : fintype.card K = q ^ (p - 1)) :
  ∀ a b : K, (a + b) ^ q = a ^ q + b ^ q :=
begin
  sorry,
end

/-
For any element `ζ` of multiplicative order `p` in a field `K`, we have a polynomial
decomposition`X^p - 1 = (X - ζ) * (X - ζ ^ 2) * ... * (X - ζ ^ p)`.
-/
lemma fact_B (p : ℕ) [fact (prime p)] (K : Type*) [field K] (ζ : Kˣ) (h_1 : ζ ^ p = 1)
  (h_2 : ζ ≠ 1) :
  X  ^ (p - 1) - (1 : K[X])  = ∏ i in Icc 1 p, (X - (polynomial.C (ζ : K)) ^ i) :=
begin
  sorry,
end

theorem quadratic_reciprocity_2 (p q : ℕ) (hp : p ≠ 2) (hq : q ≠ 2)
  [fact (nat.prime p)] [fact (nat.prime q)] :
  (legendre_sym p q) * (legendre_sym q p) = -1 ^ ((p-1) / 2 * (q - 1) / 2 ) :=
begin
  sorry
end

end quadratic_reciprocity
end book