/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro
-/
import topology.metric_space.basic
import topology.algebra.uniform_group
import topology.algebra.ring
import ring_theory.subring.basic
import group_theory.archimedean
import algebra.periodic

/-!
# Topological properties of ℝ
-/

noncomputable theory
open classical filter int metric set topological_space
open_locale classical topological_space filter uniformity interval

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

instance : metric_space ℚ :=
metric_space.induced coe rat.cast_injective real.metric_space

namespace rat

theorem dist_eq (x y : ℚ) : dist x y = |x - y| := rfl

@[norm_cast, simp] lemma dist_cast (x y : ℚ) : dist (x : ℝ) y = dist x y := rfl

theorem uniform_continuous_coe_real : uniform_continuous (coe : ℚ → ℝ) :=
uniform_continuous_comap

theorem uniform_embedding_coe_real : uniform_embedding (coe : ℚ → ℝ) :=
uniform_embedding_comap rat.cast_injective

theorem dense_embedding_coe_real : dense_embedding (coe : ℚ → ℝ) :=
uniform_embedding_coe_real.dense_embedding $
λ x, mem_closure_iff_nhds.2 $ λ t ht,
let ⟨ε,ε0, hε⟩ := metric.mem_nhds_iff.1 ht in
let ⟨q, h⟩ := exists_rat_near x ε0 in
⟨_, hε (mem_ball'.2 h), q, rfl⟩

theorem embedding_coe_real : embedding (coe : ℚ → ℝ) := dense_embedding_coe_real.to_embedding

theorem continuous_coe_real : continuous (coe : ℚ → ℝ) := uniform_continuous_coe_real.continuous

end rat

namespace int

instance : has_dist ℤ := ⟨λ x y, dist (x : ℝ) y⟩

theorem dist_eq (x y : ℤ) : dist x y = |x - y| := rfl

@[norm_cast, simp] theorem dist_cast_real (x y : ℤ) : dist (x : ℝ) y = dist x y := rfl

@[norm_cast, simp] theorem dist_cast_rat (x y : ℤ) : dist (x : ℚ) y = dist x y :=
by rw [← int.dist_cast_real, ← rat.dist_cast]; congr' 1; norm_cast

lemma pairwise_one_le_dist : pairwise (λ m n : ℤ, 1 ≤ dist m n) :=
begin
  intros m n hne,
  rw dist_eq, norm_cast, rwa [← zero_add (1 : ℤ), int.add_one_le_iff, abs_pos, sub_ne_zero]
end

lemma uniform_embedding_coe_rat : uniform_embedding (coe : ℤ → ℚ) :=
uniform_embedding_bot_of_pairwise_le_dist zero_lt_one $ by simpa using pairwise_one_le_dist

lemma closed_embedding_coe_rat : closed_embedding (coe : ℤ → ℚ) :=
closed_embedding_of_pairwise_le_dist zero_lt_one $ by simpa using pairwise_one_le_dist

lemma uniform_embedding_coe_real : uniform_embedding (coe : ℤ → ℝ) :=
uniform_embedding_bot_of_pairwise_le_dist zero_lt_one pairwise_one_le_dist

lemma closed_embedding_coe_real : closed_embedding (coe : ℤ → ℝ) :=
closed_embedding_of_pairwise_le_dist zero_lt_one pairwise_one_le_dist

instance : metric_space ℤ := int.uniform_embedding_coe_real.comap_metric_space _

theorem preimage_ball (x : ℤ) (r : ℝ) : coe ⁻¹' (ball (x : ℝ) r) = ball x r := rfl

theorem preimage_closed_ball (x : ℤ) (r : ℝ) :
  coe ⁻¹' (closed_ball (x : ℝ) r) = closed_ball x r := rfl

theorem ball_eq_Ioo (x : ℤ) (r : ℝ) : ball x r = Ioo ⌊↑x - r⌋ ⌈↑x + r⌉ :=
by rw [← preimage_ball, real.ball_eq_Ioo, preimage_Ioo]

theorem closed_ball_eq_Icc (x : ℤ) (r : ℝ) : closed_ball x r = Icc ⌈↑x - r⌉ ⌊↑x + r⌋ :=
by rw [← preimage_closed_ball, real.closed_ball_eq_Icc, preimage_Icc]

instance : proper_space ℤ :=
⟨ begin
    intros x r,
    rw closed_ball_eq_Icc,
    exact (set.finite_Icc _ _).is_compact,
  end ⟩

instance : noncompact_space ℤ :=
begin
  rw [← not_compact_space_iff, metric.compact_space_iff_bounded_univ],
  rintro ⟨r, hr⟩,
  refine (hr (⌊r⌋ + 1) 0 trivial trivial).not_lt _,
  simpa [dist_eq] using (lt_floor_add_one r).trans_le (le_abs_self _)
end

end int

instance : noncompact_space ℚ := int.closed_embedding_coe_rat.noncompact_space
instance : noncompact_space ℝ := int.closed_embedding_coe_real.noncompact_space

theorem real.uniform_continuous_add : uniform_continuous (λp : ℝ × ℝ, p.1 + p.2) :=
metric.uniform_continuous_iff.2 $ λ ε ε0,
let ⟨δ, δ0, Hδ⟩ := rat_add_continuous_lemma abs ε0 in
⟨δ, δ0, λ a b h, let ⟨h₁, h₂⟩ := max_lt_iff.1 h in Hδ h₁ h₂⟩

-- TODO(Mario): Find a way to use rat_add_continuous_lemma
theorem rat.uniform_continuous_add : uniform_continuous (λp : ℚ × ℚ, p.1 + p.2) :=
rat.uniform_embedding_coe_real.to_uniform_inducing.uniform_continuous_iff.2 $
  by simp only [(∘), rat.cast_add]; exact real.uniform_continuous_add.comp
    (rat.uniform_continuous_coe_real.prod_map rat.uniform_continuous_coe_real)

theorem real.uniform_continuous_neg : uniform_continuous (@has_neg.neg ℝ _) :=
metric.uniform_continuous_iff.2 $ λ ε ε0, ⟨_, ε0, λ a b h,
  by rw dist_comm at h; simpa [real.dist_eq] using h⟩

theorem rat.uniform_continuous_neg : uniform_continuous (@has_neg.neg ℚ _) :=
metric.uniform_continuous_iff.2 $ λ ε ε0, ⟨_, ε0, λ a b h,
  by rw dist_comm at h; simpa [rat.dist_eq] using h⟩

instance : uniform_add_group ℝ :=
uniform_add_group.mk' real.uniform_continuous_add real.uniform_continuous_neg

instance : uniform_add_group ℚ :=
uniform_add_group.mk' rat.uniform_continuous_add rat.uniform_continuous_neg

 -- short-circuit type class inference
instance : topological_add_group ℝ := by apply_instance
instance : topological_add_group ℚ := by apply_instance

instance : order_topology ℚ :=
induced_order_topology _ (λ x y, rat.cast_lt) (@exists_rat_btwn _ _ _)

instance : proper_space ℝ :=
{ is_compact_closed_ball := λx r, by { rw real.closed_ball_eq_Icc, apply is_compact_Icc } }

instance : second_countable_topology ℝ := second_countable_of_proper

lemma real.is_topological_basis_Ioo_rat :
  @is_topological_basis ℝ _ (⋃(a b : ℚ) (h : a < b), {Ioo a b}) :=
is_topological_basis_of_open_of_nhds
  (by simp [is_open_Ioo] {contextual:=tt})
  (assume a v hav hv,
    let ⟨l, u, ⟨hl, hu⟩, h⟩ := mem_nhds_iff_exists_Ioo_subset.mp (is_open.mem_nhds hv hav),
        ⟨q, hlq, hqa⟩ := exists_rat_btwn hl,
        ⟨p, hap, hpu⟩ := exists_rat_btwn hu in
    ⟨Ioo q p,
      by { simp only [mem_Union], exact ⟨q, p, rat.cast_lt.1 $ hqa.trans hap, rfl⟩ },
      ⟨hqa, hap⟩, assume a' ⟨hqa', ha'p⟩, h ⟨hlq.trans hqa', ha'p.trans hpu⟩⟩)

/- TODO(Mario): Prove that these are uniform isomorphisms instead of uniform embeddings
lemma uniform_embedding_add_rat {r : ℚ} : uniform_embedding (λp:ℚ, p + r) :=
_

lemma uniform_embedding_mul_rat {q : ℚ} (hq : q ≠ 0) : uniform_embedding ((*) q) :=
_ -/

lemma real.mem_closure_iff {s : set ℝ} {x : ℝ} :
  x ∈ closure s ↔ ∀ ε > 0, ∃ y ∈ s, |y - x| < ε :=
by simp [mem_closure_iff_nhds_basis nhds_basis_ball, real.dist_eq]

lemma real.uniform_continuous_inv (s : set ℝ) {r : ℝ} (r0 : 0 < r) (H : ∀ x ∈ s, r ≤ |x|) :
  uniform_continuous (λp:s, p.1⁻¹) :=
metric.uniform_continuous_iff.2 $ λ ε ε0,
let ⟨δ, δ0, Hδ⟩ := rat_inv_continuous_lemma abs ε0 r0 in
⟨δ, δ0, λ a b h, Hδ (H _ a.2) (H _ b.2) h⟩

lemma real.uniform_continuous_abs : uniform_continuous (abs : ℝ → ℝ) :=
metric.uniform_continuous_iff.2 $ λ ε ε0,
  ⟨ε, ε0, λ a b, lt_of_le_of_lt (abs_abs_sub_abs_le_abs_sub _ _)⟩

lemma rat.uniform_continuous_abs : uniform_continuous (abs : ℚ → ℚ) :=
metric.uniform_continuous_iff.2 $ λ ε ε0,
  ⟨ε, ε0, λ a b h, lt_of_le_of_lt
    (by simpa [rat.dist_eq] using abs_abs_sub_abs_le_abs_sub _ _) h⟩

lemma real.tendsto_inv {r : ℝ} (r0 : r ≠ 0) : tendsto (λq, q⁻¹) (𝓝 r) (𝓝 r⁻¹) :=
by rw ← abs_pos at r0; exact
tendsto_of_uniform_continuous_subtype
  (real.uniform_continuous_inv {x | |r| / 2 < |x|} (half_pos r0) (λ x h, le_of_lt h))
  (is_open.mem_nhds ((is_open_lt' (|r| / 2)).preimage continuous_abs) (half_lt_self r0))

lemma real.continuous_inv : continuous (λa:{r:ℝ // r ≠ 0}, a.val⁻¹) :=
continuous_iff_continuous_at.mpr $ assume ⟨r, hr⟩,
  tendsto.comp (real.tendsto_inv hr) (continuous_iff_continuous_at.mp continuous_subtype_val _)

lemma real.continuous.inv [topological_space α] {f : α → ℝ} (h : ∀a, f a ≠ 0) (hf : continuous f) :
  continuous (λa, (f a)⁻¹) :=
show continuous ((has_inv.inv ∘ @subtype.val ℝ (λr, r ≠ 0)) ∘ λa, ⟨f a, h a⟩),
  from real.continuous_inv.comp (continuous_subtype_mk _ hf)

lemma real.uniform_continuous_mul_const {x : ℝ} : uniform_continuous ((*) x) :=
metric.uniform_continuous_iff.2 $ λ ε ε0, begin
  cases no_top (|x|) with y xy,
  have y0 := lt_of_le_of_lt (abs_nonneg _) xy,
  refine ⟨_, div_pos ε0 y0, λ a b h, _⟩,
  rw [real.dist_eq, ← mul_sub, abs_mul, ← mul_div_cancel' ε (ne_of_gt y0)],
  exact mul_lt_mul' (le_of_lt xy) h (abs_nonneg _) y0
end

lemma real.uniform_continuous_mul (s : set (ℝ × ℝ))
  {r₁ r₂ : ℝ} (H : ∀ x ∈ s, |(x : ℝ × ℝ).1| < r₁ ∧ |x.2| < r₂) :
  uniform_continuous (λp:s, p.1.1 * p.1.2) :=
metric.uniform_continuous_iff.2 $ λ ε ε0,
let ⟨δ, δ0, Hδ⟩ := rat_mul_continuous_lemma abs ε0 in
⟨δ, δ0, λ a b h,
  let ⟨h₁, h₂⟩ := max_lt_iff.1 h in Hδ (H _ a.2).1 (H _ b.2).2 h₁ h₂⟩

protected lemma real.continuous_mul : continuous (λp : ℝ × ℝ, p.1 * p.2) :=
continuous_iff_continuous_at.2 $ λ ⟨a₁, a₂⟩,
tendsto_of_uniform_continuous_subtype
  (real.uniform_continuous_mul
    ({x | |x| < |a₁| + 1}.prod {x | |x| < |a₂| + 1})
    (λ x, id))
  (is_open.mem_nhds
    (((is_open_gt' (|a₁| + 1)).preimage continuous_abs).prod
      ((is_open_gt' (|a₂| + 1)).preimage continuous_abs ))
    ⟨lt_add_one (|a₁|), lt_add_one (|a₂|)⟩)

instance : topological_ring ℝ :=
{ continuous_mul := real.continuous_mul, ..real.topological_add_group }

lemma rat.continuous_mul : continuous (λp : ℚ × ℚ, p.1 * p.2) :=
rat.embedding_coe_real.continuous_iff.2 $ by simp [(∘)]; exact
real.continuous_mul.comp ((rat.continuous_coe_real.prod_map rat.continuous_coe_real))

instance : topological_ring ℚ :=
{ continuous_mul := rat.continuous_mul, ..rat.topological_add_group }

instance : complete_space ℝ :=
begin
  apply complete_of_cauchy_seq_tendsto,
  intros u hu,
  let c : cau_seq ℝ abs := ⟨u, metric.cauchy_seq_iff'.1 hu⟩,
  refine ⟨c.lim, λ s h, _⟩,
  rcases metric.mem_nhds_iff.1 h with ⟨ε, ε0, hε⟩,
  have := c.equiv_lim ε ε0,
  simp only [mem_map, mem_at_top_sets, mem_set_of_eq],
  refine this.imp (λ N hN n hn, hε (hN n hn))
end

lemma real.totally_bounded_ball (x ε : ℝ) : totally_bounded (ball x ε) :=
by rw real.ball_eq_Ioo; apply totally_bounded_Ioo

lemma rat.totally_bounded_Icc (a b : ℚ) : totally_bounded (Icc a b) :=
begin
  have := totally_bounded_preimage rat.uniform_embedding_coe_real (totally_bounded_Icc a b),
  rwa (set.ext (λ q, _) : Icc _ _ = _), simp
end

section

lemma closure_of_rat_image_lt {q : ℚ} : closure ((coe:ℚ → ℝ) '' {x | q < x}) = {r | ↑q ≤ r} :=
subset.antisymm
  ((is_closed_ge' _).closure_subset_iff.2
    (image_subset_iff.2 $ λ p h, le_of_lt $ (@rat.cast_lt ℝ _ _ _).2 h)) $
λ x hx, mem_closure_iff_nhds.2 $ λ t ht,
let ⟨ε, ε0, hε⟩ := metric.mem_nhds_iff.1 ht in
let ⟨p, h₁, h₂⟩ := exists_rat_btwn ((lt_add_iff_pos_right x).2 ε0) in
⟨_, hε (show abs _ < _,
    by rwa [abs_of_nonneg (le_of_lt $ sub_pos.2 h₁), sub_lt_iff_lt_add']),
  p, rat.cast_lt.1 (@lt_of_le_of_lt ℝ _ _ _ _ hx h₁), rfl⟩

/- TODO(Mario): Put these back only if needed later
lemma closure_of_rat_image_le_eq {q : ℚ} : closure ((coe:ℚ → ℝ) '' {x | q ≤ x}) = {r | ↑q ≤ r} :=
_

lemma closure_of_rat_image_le_le_eq {a b : ℚ} (hab : a ≤ b) :
  closure (of_rat '' {q:ℚ | a ≤ q ∧ q ≤ b}) = {r:ℝ | of_rat a ≤ r ∧ r ≤ of_rat b} :=
_-/

lemma real.bounded_iff_bdd_below_bdd_above {s : set ℝ} : bounded s ↔ bdd_below s ∧ bdd_above s :=
⟨begin
  assume bdd,
  rcases (bounded_iff_subset_ball 0).1 bdd with ⟨r, hr⟩, -- hr : s ⊆ closed_ball 0 r
  rw real.closed_ball_eq_Icc at hr, -- hr : s ⊆ Icc (0 - r) (0 + r)
  exact ⟨bdd_below_Icc.mono hr, bdd_above_Icc.mono hr⟩
end,
λ h, bounded_of_bdd_above_of_bdd_below h.2 h.1⟩

lemma real.subset_Icc_Inf_Sup_of_bounded {s : set ℝ} (h : bounded s) :
  s ⊆ Icc (Inf s) (Sup s) :=
subset_Icc_cInf_cSup (real.bounded_iff_bdd_below_bdd_above.1 h).1
  (real.bounded_iff_bdd_below_bdd_above.1 h).2

end

section periodic

namespace function

lemma periodic.compact_of_continuous' [topological_space α] {f : ℝ → α} {c : ℝ}
  (hp : periodic f c) (hc : 0 < c) (hf : continuous f) :
  is_compact (range f) :=
begin
  convert is_compact_Icc.image hf,
  ext x,
  refine ⟨_, mem_range_of_mem_image f (Icc 0 c)⟩,
  rintros ⟨y, h1⟩,
  obtain ⟨z, hz, h2⟩ := hp.exists_mem_Ico₀ hc y,
  exact ⟨z, mem_Icc_of_Ico hz, h2.symm.trans h1⟩,
end

/-- A continuous, periodic function has compact range. -/
lemma periodic.compact_of_continuous [topological_space α] {f : ℝ → α} {c : ℝ}
  (hp : periodic f c) (hc : c ≠ 0) (hf : continuous f) :
  is_compact (range f) :=
begin
  cases lt_or_gt_of_ne hc with hneg hpos,
  exacts [hp.neg.compact_of_continuous' (neg_pos.mpr hneg) hf, hp.compact_of_continuous' hpos hf],
end

/-- A continuous, periodic function is bounded. -/
lemma periodic.bounded_of_continuous [pseudo_metric_space α] {f : ℝ → α} {c : ℝ}
  (hp : periodic f c) (hc : c ≠ 0) (hf : continuous f) :
  bounded (range f) :=
(hp.compact_of_continuous hc hf).bounded

end function

end periodic

section subgroups

/-- Given a nontrivial subgroup `G ⊆ ℝ`, if `G ∩ ℝ_{>0}` has no minimum then `G` is dense. -/
lemma real.subgroup_dense_of_no_min {G : add_subgroup ℝ} {g₀ : ℝ} (g₀_in : g₀ ∈ G) (g₀_ne : g₀ ≠ 0)
  (H' : ¬ ∃ a : ℝ, is_least {g : ℝ | g ∈ G ∧ 0 < g} a) :
  dense (G : set ℝ) :=
begin
  let G_pos := {g : ℝ | g ∈ G ∧ 0 < g},
  push_neg at H',
  intros x,
  suffices : ∀ ε > (0 : ℝ), ∃ g ∈ G, |x - g| < ε,
    by simpa only [real.mem_closure_iff, abs_sub_comm],
  intros ε ε_pos,
  obtain ⟨g₁, g₁_in, g₁_pos⟩ : ∃ g₁ : ℝ, g₁ ∈ G ∧ 0 < g₁,
  { cases lt_or_gt_of_ne g₀_ne with Hg₀ Hg₀,
    { exact ⟨-g₀, G.neg_mem g₀_in, neg_pos.mpr Hg₀⟩ },
    { exact ⟨g₀, g₀_in, Hg₀⟩ } },
  obtain ⟨a, ha⟩ : ∃ a, is_glb G_pos a :=
    ⟨Inf G_pos, is_glb_cInf ⟨g₁, g₁_in, g₁_pos⟩ ⟨0, λ _ hx, le_of_lt hx.2⟩⟩,
  have a_notin : a ∉ G_pos,
  { intros H,
    exact H' a ⟨H, ha.1⟩ },
  obtain ⟨g₂, g₂_in, g₂_pos, g₂_lt⟩ : ∃ g₂ : ℝ, g₂ ∈ G ∧ 0 < g₂ ∧ g₂ < ε,
  { obtain ⟨b, hb, hb', hb''⟩ := ha.exists_between_self_add' a_notin ε_pos,
    obtain ⟨c, hc, hc', hc''⟩ := ha.exists_between_self_add' a_notin (sub_pos.2 hb'),
    refine ⟨b - c, G.sub_mem hb.1 hc.1, _, _⟩ ;
    linarith },
  refine ⟨floor (x/g₂) * g₂, _, _⟩,
  { exact add_subgroup.int_mul_mem _ g₂_in },
  { rw abs_of_nonneg (sub_floor_div_mul_nonneg x g₂_pos),
    linarith [sub_floor_div_mul_lt x g₂_pos] }
end

/-- Subgroups of `ℝ` are either dense or cyclic. See `real.subgroup_dense_of_no_min` and
`subgroup_cyclic_of_min` for more precise statements. -/
lemma real.subgroup_dense_or_cyclic (G : add_subgroup ℝ) :
  dense (G : set ℝ) ∨ ∃ a : ℝ, G = add_subgroup.closure {a} :=
begin
  cases add_subgroup.bot_or_exists_ne_zero G with H H,
  { right,
    use 0,
    rw [H, add_subgroup.closure_singleton_zero] },
  { let G_pos := {g : ℝ | g ∈ G ∧ 0 < g},
    by_cases H' : ∃ a, is_least G_pos a,
    { right,
      rcases H' with ⟨a, ha⟩,
      exact ⟨a, add_subgroup.cyclic_of_min ha⟩ },
    { left,
      rcases H with ⟨g₀, g₀_in, g₀_ne⟩,
      exact real.subgroup_dense_of_no_min g₀_in g₀_ne H' } }
end

end subgroups


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.category.Group.basic
import Mathlib.group_theory.free_abelian_group
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
The free abelian group on a type is the left adjoint of the
forgetful functor from abelian groups to types.
-/

namespace AddCommGroup

/--
The free functor `Type u ⥤ AddCommGroup` sending a type `X` to the
free abelian group with generators `x : X`.
-/
def free : Type u ⥤ AddCommGroup :=
  category_theory.functor.mk (fun (α : Type u) => of (free_abelian_group α))
    fun (X Y : Type u) => free_abelian_group.map

@[simp] theorem free_obj_coe {α : Type u} :
    ↥(category_theory.functor.obj free α) = free_abelian_group α :=
  rfl

@[simp] theorem free_map_coe {α : Type u} {β : Type u} {f : α → β} (x : free_abelian_group α) :
    coe_fn (category_theory.functor.map free f) x = f <$> x :=
  rfl

/--
The free-forgetful adjunction for abelian groups.
-/
def adj : free ⊣ category_theory.forget AddCommGroup :=
  category_theory.adjunction.mk_of_hom_equiv
    (category_theory.adjunction.core_hom_equiv.mk
      fun (X : Type u) (G : AddCommGroup) => free_abelian_group.hom_equiv X ↥G)

end Mathlib


import data.polynomial.algebra_map
import data.nat.choose.cast
import number_theory.bernoulli
-- where do these files come from?

open_locale big_operators -- so that we can use `∑` instead of `finset.sum`
open_locale nat polynomial -- so that we can use `n!`
open nat finset
open power_series
open polynomial

-- taking A to be a commutative ring and a ℚ-algebra
variables (A : Type*) [comm_ring A] [algebra ℚ A]

-- Go check the definition of `bernoulli'`!
/-- The Bernoulli numbers are defined to be `bernoulli'` with a parity sign. -/
def bernoulli_neg (n : ℕ) : ℚ := (-1)^n * bernoulli' n

-- What does the `simp` tag do?
@[simp] lemma bernoulli_neg_zero : bernoulli_neg 0 = 1 := sorry

-- Try to solve this with and without `norm_num`
@[simp] lemma bernoulli_neg_one : bernoulli_neg 1 = -1/2 := sorry

-- What does the statement say? Write a docstring
theorem bernoulli_neg_eq_bernoulli'_of_ne_one {n : ℕ} (hn : n ≠ 1) : bernoulli_neg n = bernoulli' n :=
begin
  -- Step 1. Might be better to look at this by breaking into cases `n = 0` and `n ≠ 0`
  -- Try using `by_cases` for this.
  -- Step 2. Now it might help to further split n into odd and even, how can you do that using `cases`?
  sorry,
end

-- Challenge!
-- What does the statement say? Write a docstring ; how do you get the `∑` symbol?
@[simp] theorem sum_bernoulli_neg (n : ℕ) :
  ∑ k in range n, (n.choose k : ℚ) * bernoulli_neg k = if n = 1 then 1 else 0 :=
begin
  -- Step 1. Break this into cases when n is 0 or nonzero. How is this different from using `by_cases`?
  -- Step 2. You will be needing cases again.
  -- Step 3. Find a way to make this a sum upto n.
  -- Hint : It might help to prove that ∑_{i = 0}^{n - 1} ((n + 2) choose (i + 2)) * B_{i + 2} = n/2
  -- Can you rewrite this sum in valid Lean code?
  -- Step 4. Use a property of sum of bernoulli'
  -- Step 5. ring : New tactic unlocked!
  sorry,
end


import tactic -- tactic mode
import data.real.basic -- the real numbers

example (a b : ℝ) : (a+b)^3=a^3+3*a^2*b+3*a*b^2+b^3 :=
begin
  ring,
end


/-
 if R is a ring and if the multiplicative subset S consists completely of nonzerodivisors, then R→S−1R is injective

[proof omitted]
-/

import ring_theory.localization group_theory.submonoid

universe u

variables {α : Type u} [comm_ring α] (S : set α) [is_submonoid S]

theorem localization.inj_of_subset_non_zero_divisors (H : S ⊆ localization.non_zero_divisors α) :
  function.injective (localization.of_comm_ring α S) :=
λ x y h, let ⟨w, hws, hw⟩ := quotient.exact h in
eq.symm $ by simpa [add_neg_eq_zero] using H hws _ hw


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov

! This file was ported from Lean 3 source module topology.metric_space.metrizable
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.SpecificLimits.Basic
import Mathbin.Topology.UrysohnsLemma
import Mathbin.Topology.ContinuousFunction.Bounded
import Mathbin.Topology.UniformSpace.Cauchy

/-!
# Metrizability of a T₃ topological space with second countable topology

In this file we define metrizable topological spaces, i.e., topological spaces for which there
exists a metric space structure that generates the same topology.

We also show that a T₃ topological space with second countable topology `X` is metrizable.

First we prove that `X` can be embedded into `l^∞`, then use this embedding to pull back the metric
space structure.
-/

open Set Filter Metric

open BoundedContinuousFunction Filter Topology

namespace TopologicalSpace

variable {ι X Y : Type _} {π : ι → Type _} [TopologicalSpace X] [TopologicalSpace Y] [Finite ι]
  [∀ i, TopologicalSpace (π i)]

/-- A topological space is *pseudo metrizable* if there exists a pseudo metric space structure
compatible with the topology. To endow such a space with a compatible distance, use
`letI : pseudo_metric_space X := topological_space.pseudo_metrizable_space_pseudo_metric X`. -/
class PseudoMetrizableSpace (X : Type _) [t : TopologicalSpace X] : Prop where
  exists_pseudo_metric : ∃ m : PseudoMetricSpace X, m.toUniformSpace.toTopologicalSpace = t
#align topological_space.pseudo_metrizable_space TopologicalSpace.PseudoMetrizableSpace

instance (priority := 100) PseudoMetricSpace.to_pseudoMetrizableSpace {X : Type _}
    [m : PseudoMetricSpace X] : PseudoMetrizableSpace X :=
  ⟨⟨m, rfl⟩⟩
#align pseudo_metric_space.to_pseudo_metrizable_space PseudoMetricSpace.to_pseudoMetrizableSpace

/-- Construct on a metrizable space a metric compatible with the topology. -/
noncomputable def pseudoMetrizableSpacePseudoMetric (X : Type _) [TopologicalSpace X]
    [h : PseudoMetrizableSpace X] : PseudoMetricSpace X :=
  h.exists_pseudo_metric.some.replaceTopology h.exists_pseudo_metric.choose_spec.symm
#align topological_space.pseudo_metrizable_space_pseudo_metric TopologicalSpace.pseudoMetrizableSpacePseudoMetric

instance pseudoMetrizableSpace_prod [PseudoMetrizableSpace X] [PseudoMetrizableSpace Y] :
    PseudoMetrizableSpace (X × Y) :=
  by
  letI : PseudoMetricSpace X := pseudo_metrizable_space_pseudo_metric X
  letI : PseudoMetricSpace Y := pseudo_metrizable_space_pseudo_metric Y
  infer_instance
#align topological_space.pseudo_metrizable_space_prod TopologicalSpace.pseudoMetrizableSpace_prod

/-- Given an inducing map of a topological space into a pseudo metrizable space, the source space
is also pseudo metrizable. -/
theorem Inducing.pseudoMetrizableSpace [PseudoMetrizableSpace Y] {f : X → Y} (hf : Inducing f) :
    PseudoMetrizableSpace X :=
  letI : PseudoMetricSpace Y := pseudo_metrizable_space_pseudo_metric Y
  ⟨⟨hf.comap_pseudo_metric_space, rfl⟩⟩
#align inducing.pseudo_metrizable_space Inducing.pseudoMetrizableSpace

/-- Every pseudo-metrizable space is first countable. -/
instance (priority := 100) PseudoMetrizableSpace.firstCountableTopology
    [h : PseudoMetrizableSpace X] : TopologicalSpace.FirstCountableTopology X :=
  by
  rcases h with ⟨_, hm⟩
  rw [← hm]
  exact
    @UniformSpace.firstCountableTopology X PseudoMetricSpace.toUniformSpace
      Emetric.Uniformity.Filter.isCountablyGenerated
#align topological_space.pseudo_metrizable_space.first_countable_topology TopologicalSpace.PseudoMetrizableSpace.firstCountableTopology

instance PseudoMetrizableSpace.subtype [PseudoMetrizableSpace X] (s : Set X) :
    PseudoMetrizableSpace s :=
  inducing_subtype_val.PseudoMetrizableSpace
#align topological_space.pseudo_metrizable_space.subtype TopologicalSpace.PseudoMetrizableSpace.subtype

instance pseudoMetrizableSpace_pi [∀ i, PseudoMetrizableSpace (π i)] :
    PseudoMetrizableSpace (∀ i, π i) :=
  by
  cases nonempty_fintype ι
  letI := fun i => pseudo_metrizable_space_pseudo_metric (π i)
  infer_instance
#align topological_space.pseudo_metrizable_space_pi TopologicalSpace.pseudoMetrizableSpace_pi

/-- A topological space is metrizable if there exists a metric space structure compatible with the
topology. To endow such a space with a compatible distance, use
`letI : metric_space X := topological_space.metrizable_space_metric X` -/
class MetrizableSpace (X : Type _) [t : TopologicalSpace X] : Prop where
  exists_metric : ∃ m : MetricSpace X, m.toUniformSpace.toTopologicalSpace = t
#align topological_space.metrizable_space TopologicalSpace.MetrizableSpace

instance (priority := 100) MetricSpace.to_metrizableSpace {X : Type _} [m : MetricSpace X] :
    MetrizableSpace X :=
  ⟨⟨m, rfl⟩⟩
#align metric_space.to_metrizable_space MetricSpace.to_metrizableSpace

instance (priority := 100) MetrizableSpace.to_pseudoMetrizableSpace [h : MetrizableSpace X] :
    PseudoMetrizableSpace X :=
  ⟨let ⟨m, hm⟩ := h.1
    ⟨m.toPseudoMetricSpace, hm⟩⟩
#align topological_space.metrizable_space.to_pseudo_metrizable_space TopologicalSpace.MetrizableSpace.to_pseudoMetrizableSpace

/-- Construct on a metrizable space a metric compatible with the topology. -/
noncomputable def metrizableSpaceMetric (X : Type _) [TopologicalSpace X] [h : MetrizableSpace X] :
    MetricSpace X :=
  h.exists_metric.some.replaceTopology h.exists_metric.choose_spec.symm
#align topological_space.metrizable_space_metric TopologicalSpace.metrizableSpaceMetric

instance (priority := 100) t2Space_of_metrizableSpace [MetrizableSpace X] : T2Space X :=
  by
  letI : MetricSpace X := metrizable_space_metric X
  infer_instance
#align topological_space.t2_space_of_metrizable_space TopologicalSpace.t2Space_of_metrizableSpace

instance metrizableSpace_prod [MetrizableSpace X] [MetrizableSpace Y] : MetrizableSpace (X × Y) :=
  by
  letI : MetricSpace X := metrizable_space_metric X
  letI : MetricSpace Y := metrizable_space_metric Y
  infer_instance
#align topological_space.metrizable_space_prod TopologicalSpace.metrizableSpace_prod

/-- Given an embedding of a topological space into a metrizable space, the source space is also
metrizable. -/
theorem Embedding.metrizableSpace [MetrizableSpace Y] {f : X → Y} (hf : Embedding f) :
    MetrizableSpace X :=
  letI : MetricSpace Y := metrizable_space_metric Y
  ⟨⟨hf.comap_metric_space f, rfl⟩⟩
#align embedding.metrizable_space Embedding.metrizableSpace

instance MetrizableSpace.subtype [MetrizableSpace X] (s : Set X) : MetrizableSpace s :=
  embedding_subtype_val.MetrizableSpace
#align topological_space.metrizable_space.subtype TopologicalSpace.MetrizableSpace.subtype

instance metrizableSpace_pi [∀ i, MetrizableSpace (π i)] : MetrizableSpace (∀ i, π i) :=
  by
  cases nonempty_fintype ι
  letI := fun i => metrizable_space_metric (π i)
  infer_instance
#align topological_space.metrizable_space_pi TopologicalSpace.metrizableSpace_pi

variable (X) [T3Space X] [SecondCountableTopology X]

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- A T₃ topological space with second countable topology can be embedded into `l^∞ = ℕ →ᵇ ℝ`.
-/
theorem exists_embedding_l_infty : ∃ f : X → ℕ →ᵇ ℝ, Embedding f :=
  by
  haveI : NormalSpace X := normalSpaceOfT3SecondCountable X
  -- Choose a countable basis, and consider the set `s` of pairs of set `(U, V)` such that `U ∈ B`,
  -- `V ∈ B`, and `closure U ⊆ V`.
  rcases exists_countable_basis X with ⟨B, hBc, -, hB⟩
  set s : Set (Set X × Set X) := { UV ∈ B ×ˢ B | closure UV.1 ⊆ UV.2 }
  -- `s` is a countable set.
  haveI : Encodable s := ((hBc.prod hBc).mono (inter_subset_left _ _)).toEncodable
  -- We don't have the space of bounded (possibly discontinuous) functions, so we equip `s`
  -- with the discrete topology and deal with `s →ᵇ ℝ` instead.
  letI : TopologicalSpace s := ⊥
  haveI : DiscreteTopology s := ⟨rfl⟩
  rsuffices ⟨f, hf⟩ : ∃ f : X → s →ᵇ ℝ, Embedding f
  ·
    exact
      ⟨fun x => (f x).extend (Encodable.encode' s) 0,
        (BoundedContinuousFunction.isometry_extend (Encodable.encode' s)
                (0 : ℕ →ᵇ ℝ)).Embedding.comp
          hf⟩
  have hd : ∀ UV : s, Disjoint (closure UV.1.1) (UV.1.2ᶜ) := fun UV =>
    disjoint_compl_right.mono_right (compl_subset_compl.2 UV.2.2)
  -- Choose a sequence of `εₙ > 0`, `n : s`, that is bounded above by `1` and tends to zero
  -- along the `cofinite` filter.
  obtain ⟨ε, ε01, hε⟩ : ∃ ε : s → ℝ, (∀ UV, ε UV ∈ Ioc (0 : ℝ) 1) ∧ tendsto ε cofinite (𝓝 0) :=
    by
    rcases posSumOfEncodable zero_lt_one s with ⟨ε, ε0, c, hεc, hc1⟩
    refine' ⟨ε, fun UV => ⟨ε0 UV, _⟩, hεc.summable.tendsto_cofinite_zero⟩
    exact (le_hasSum hεc UV fun _ _ => (ε0 _).le).trans hc1
  /- For each `UV = (U, V) ∈ s` we use Urysohn's lemma to choose a function `f UV` that is equal to
    zero on `U` and is equal to `ε UV` on the complement to `V`. -/
  have :
    ∀ UV : s,
      ∃ f : C(X, ℝ),
        eq_on f 0 UV.1.1 ∧ eq_on f (fun _ => ε UV) (UV.1.2ᶜ) ∧ ∀ x, f x ∈ Icc 0 (ε UV) :=
    by
    intro UV
    rcases exists_continuous_zero_one_of_closed isClosed_closure
        (hB.is_open UV.2.1.2).isClosed_compl (hd UV) with
      ⟨f, hf₀, hf₁, hf01⟩
    exact
      ⟨ε UV • f, fun x hx => by simp [hf₀ (subset_closure hx)], fun x hx => by simp [hf₁ hx],
        fun x =>
        ⟨mul_nonneg (ε01 _).1.le (hf01 _).1, mul_le_of_le_one_right (ε01 _).1.le (hf01 _).2⟩⟩
  choose f hf0 hfε hf0ε
  have hf01 : ∀ UV x, f UV x ∈ Icc (0 : ℝ) 1 := fun UV x =>
    Icc_subset_Icc_right (ε01 _).2 (hf0ε _ _)
  -- The embedding is given by `F x UV = f UV x`.
  set F : X → s →ᵇ ℝ := fun x =>
    ⟨⟨fun UV => f UV x, continuous_of_discreteTopology⟩, 1, fun UV₁ UV₂ =>
      Real.dist_le_of_mem_Icc_01 (hf01 _ _) (hf01 _ _)⟩
  have hF : ∀ x UV, F x UV = f UV x := fun _ _ => rfl
  refine' ⟨F, Embedding.mk' _ (fun x y hxy => _) fun x => le_antisymm _ _⟩
  · /- First we prove that `F` is injective. Indeed, if `F x = F y` and `x ≠ y`, then we can find
        `(U, V) ∈ s` such that `x ∈ U` and `y ∉ V`, hence `F x UV = 0 ≠ ε UV = F y UV`. -/
    refine' Classical.not_not.1 fun Hne => _
    -- `by_contra Hne` timeouts
    rcases hB.mem_nhds_iff.1 (is_open_ne.mem_nhds Hne) with ⟨V, hVB, hxV, hVy⟩
    rcases hB.exists_closure_subset (hB.mem_nhds hVB hxV) with ⟨U, hUB, hxU, hUV⟩
    set UV : ↥s := ⟨(U, V), ⟨hUB, hVB⟩, hUV⟩
    apply (ε01 UV).1.Ne
    calc
      (0 : ℝ) = F x UV := (hf0 UV hxU).symm
      _ = F y UV := by rw [hxy]
      _ = ε UV := hfε UV fun h : y ∈ V => hVy h rfl

  · /- Now we prove that each neighborhood `V` of `x : X` include a preimage of a neighborhood of
        `F x` under `F`. Without loss of generality, `V` belongs to `B`. Choose `U ∈ B` such that
        `x ∈ V` and `closure V ⊆ U`. Then the preimage of the `(ε (U, V))`-neighborhood of `F x`
        is included by `V`. -/
    refine' ((nhds_basis_ball.comap _).le_basis_iffₓ hB.nhds_has_basis).2 _
    rintro V ⟨hVB, hxV⟩
    rcases hB.exists_closure_subset (hB.mem_nhds hVB hxV) with ⟨U, hUB, hxU, hUV⟩
    set UV : ↥s := ⟨(U, V), ⟨hUB, hVB⟩, hUV⟩
    refine' ⟨ε UV, (ε01 UV).1, fun y (hy : dist (F y) (F x) < ε UV) => _⟩
    replace hy : dist (F y UV) (F x UV) < ε UV
    exact (BoundedContinuousFunction.dist_coe_le_dist _).trans_lt hy
    contrapose! hy
    rw [hF, hF, hfε UV hy, hf0 UV hxU, Pi.zero_apply, dist_zero_right]
    exact le_abs_self _
  · /- Finally, we prove that `F` is continuous. Given `δ > 0`, consider the set `T` of `(U, V) ∈ s`
        such that `ε (U, V) ≥ δ`. Since `ε` tends to zero, `T` is finite. Since each `f` is continuous,
        we can choose a neighborhood such that `dist (F y (U, V)) (F x (U, V)) ≤ δ` for any
        `(U, V) ∈ T`. For `(U, V) ∉ T`, the same inequality is true because both `F y (U, V)` and
        `F x (U, V)` belong to the interval `[0, ε (U, V)]`. -/
    refine' (nhds_basis_closed_ball.comap _).ge_iff.2 fun δ δ0 => _
    have h_fin : { UV : s | δ ≤ ε UV }.Finite := by simpa only [← not_lt] using hε (gt_mem_nhds δ0)
    have : ∀ᶠ y in 𝓝 x, ∀ UV, δ ≤ ε UV → dist (F y UV) (F x UV) ≤ δ :=
      by
      refine' (eventually_all_finite h_fin).2 fun UV hUV => _
      exact (f UV).Continuous.Tendsto x (closed_ball_mem_nhds _ δ0)
    refine' this.mono fun y hy => (BoundedContinuousFunction.dist_le δ0.le).2 fun UV => _
    cases' le_total δ (ε UV) with hle hle
    exacts[hy _ hle, (Real.dist_le_of_mem_Icc (hf0ε _ _) (hf0ε _ _)).trans (by rwa [sub_zero])]
#align topological_space.exists_embedding_l_infty TopologicalSpace.exists_embedding_l_infty

/-- *Urysohn's metrization theorem* (Tychonoff's version): a T₃ topological space with second
countable topology `X` is metrizable, i.e., there exists a metric space structure that generates the
same topology. -/
theorem metrizableSpace_of_t3_second_countable : MetrizableSpace X :=
  let ⟨f, hf⟩ := exists_embedding_l_infty X
  hf.MetrizableSpace
#align topological_space.metrizable_space_of_t3_second_countable TopologicalSpace.metrizableSpace_of_t3_second_countable

instance : MetrizableSpace ENNReal :=
  metrizableSpace_of_t3_second_countable ENNReal

end TopologicalSpace


import data.int.basic

example (p : ℤ) (q : 2 ∣ int.nat_abs (p - 1)) :
int.nat_abs ((p - 1) / 2) = int.nat_abs (p - 1) / 2 :=
begin
  cases q with d hd,
  have H : ∃ e, (p - 1) = 2 * e,
  cases (int.nat_abs_eq (p - 1)),
    existsi (d : ℤ),
    rw [h,hd],
    refl,
  existsi (- (d : ℤ)),
    rw [h,hd],
  rw int.coe_nat_mul,
  rw mul_neg_eq_neg_mul_symm,refl,

  cases H with e He,
  rw He,
  rw int.nat_abs_mul,
  rw int.mul_div_cancel_left,
  show _ = 2 * _ / _,
  rw nat.mul_div_cancel_left,
  exact dec_trivial,
  exact dec_trivial,
end


/-
Copyright (c) 2021 Alexander Griffin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Alexander Griffin.
-/
import group_theory.specific_groups.dihedral

/-!
# Dihedral Group Centers

In this file we prove which elements can and cannot
be members of the center of a (non-degenerate) dihedral group.

## Main results

- `sr_not_mem_center`: No reflection is central (if the group is non-degenerate).
- `r_not_mem_center`: Rotations with order greater than 2 are not central.
- `r_mem_center_r`: Rotations with order at most 2 commute with all rotations.
- `r_mem_center_sr`: Rotations with order at most 2 commute with all reflections.

## Notation

- `r i`: A rotation element of a dihedral group.
- `sr i`: A reflection element of a dihedral group.

## References

See the file for dihedral groups in mathlib.
-/

open dihedral_group

variables {n : ℕ}
variables {x : dihedral_group n}

namespace dihedral_github

/--
No reflection is central (if the group is non-degenerate).
`i` is any `zmod n`.
-/
lemma sr_not_mem_center [fact (n > 2)] (i : zmod n): ∃ x, sr i * x ≠ x * r i :=
  begin
    use (r 1),
  end

/--
Rotations with order greater than 2 are not central.
`i` is any `zmod n` term satisfying `2 * i ≠ 0`.
-/
theorem r_not_mem_center (i : zmod n): 2 * i ≠ 0 → ∃ x, r i * x ≠ x * r i :=
  begin
    intro h,
    use (sr 1),
    have : r i * sr 1 = sr (1 - i), by refl,
    rw this,
    have : sr 1 * r i = sr (1 + i), by refl,
    rw this,
    ring_nf,
    simp,
    rwa [eq_comm, ← sub_eq_zero, sub_neg_eq_add, ← two_mul],
  end

/--
Rotations with order at most 2 commute with all rotations.
`i` is any `zmod n` term satisfying `2 * i = 0`.
-/
theorem r_mem_center_r (i : zmod n): 2 * i = 0 → ∀ (j : zmod n), r i * r j = r j * r i :=
  begin
    intros h j,
    have : r i * r j = r (i + j), by refl,
    rw this,
    have : r j * r i = r (j + i), by refl,
    rw this,
    ring_nf,
    ring,
  end

/--
Rotations with order at most 2 commute with all reflections.
`i` is any `zmod n` term satisfying `2 * i = 0`.
-/
theorem r_mem_center_sr (i : zmod n): 2 * i = 0 → ∀ (j : zmod n), r i * sr j = sr j * r i :=
  begin
    intros h j,
    have : r i * sr j = sr (j - i), by refl,
    rw this,
    have : sr j * r i = sr (j + i), by refl,
    rw this,
    ring_nf,
    rw sub_eq_add_neg, congr,
    rwa [eq_comm, ← sub_eq_zero, sub_neg_eq_add, ← two_mul],
  end

end dihedral_github


-- vim: ts=2 sw=0 sts=-1 et ai tw=70

import ..logic
import .basic

namespace hidden
namespace myset

universes u v w

open myset

variables {α : Type u} {β : Type v} {γ : Type w}

section definitions
-- A function between two sets is well-defined so long as each element
-- of the first gets sent to an element of the latter
def well_defined (r : myset α) (s : myset β) (f : α → β) : Prop :=
∀ a : α, a ∈ r → f a ∈ s

variables {r : myset α} {s : myset β} {f : α → β}
variable (h : well_defined r s f)

include h

-- These only need to be fed a proof of well-definedness now, which is
-- cleaner
def injective: Prop :=
∀ a₁ a₂ : α, a₁ ∈ r → a₂ ∈ r → f a₁ = f a₂ → a₁ = a₂
def surjective: Prop :=
∀ b : β, b ∈ s → ∃ a : α, a ∈ r ∧ f a = b

def bijective: Prop := injective h ∧ surjective h

omit h

end definitions

section theorems

-- All the following theorems need to be fed proofs of
-- well-definedness, so they are all capable of guessing which types,
-- sets and functions you are talking about. Hence all these arguments
-- are implicit
variables {r : myset α} {s : myset β} {t : myset γ}
variables {f : α → β} {g : β → γ}

section -- This is a section for classical logic
open classical
local attribute [instance] prop_decidable

-- Cannot have a well-defined function from an empty set to a
-- non-empty set
theorem no_wdefined_func_nemp_to_emp
(hnemp : ¬empty r) (hemp : empty s) :
¬well_defined r s f :=
begin
  assume h,
  unfold myset.empty at hnemp, -- necessary for some reason
  -- CLASSICAL USED HERE
  rw not_forall at hnemp,
  cases hnemp with x hx,
  simp at hx,
  have hfxin := h x hx,
  have hfxnin := hemp (f x),
  contradiction,
end
end -- section for classical logic

theorem composition_well_defined:
well_defined r s f → well_defined s t g → well_defined r t (g ∘ f) :=
begin
  assume hwf hwg,
  intro a,
  assume har,
  apply hwg,
  apply hwf,
  assumption,
end

theorem inj_inj (hwf: well_defined r s f) (hwg: well_defined s t g):
injective hwf → injective hwg
  → injective (composition_well_defined hwf hwg) :=
begin
  assume hif hig,
  intros a b,
  assume har hbr hgfagfb,
  apply hif _ _ har hbr,
  apply hig _ _ (hwf a har) (hwf b hbr),
  assumption,
end

-- not the prettiest
theorem surj_surj (hwf: well_defined r s f) (hwg: well_defined s t g):
surjective hwf → surjective hwg
  → surjective (composition_well_defined hwf hwg) :=
begin
  assume hsf hsg,
  intro a,
  assume hat,
  cases hsg a hat with a_g ha_g,
  cases hsf a_g ha_g.left with a_f ha_f,
  existsi a_f,
  split, {
    from ha_f.left,
  }, {
    rw [←ha_g.right, ←ha_f.right],
  },
end

theorem bij_bij (hwf: well_defined r s f) (hwg: well_defined s t g):
bijective hwf → bijective hwg
  → bijective (composition_well_defined hwf hwg) :=
begin
  assume hbf hbg,
  cases hbf with hif hsf,
  cases hbg with hig hsg,
  split, {
    from inj_inj _ _ hif hig,
  }, {
    from surj_surj _ _ hsf hsg,
  },
end

-- hmmm. better to define exclusion map separately?
theorem wf_by_inclusion (r: myset α) (s t: myset β)
(hwf: well_defined r s f) (hst: s ⊆ t):
well_defined r t f :=
begin
  intro a,
  assume har,
  apply hst,
  apply hwf,
  from har,
end

-- -- hmmmmmmmm
-- theorem inj_by_inclusion (r: myset α) (s t: myset β)
-- (hwf: well_defined r s f)
-- (hif: injective hwf) (hst: s ⊆ t):
-- injective (wf_by_inclusion r s t hwf hst) :=
-- hif

end theorems

end myset
end hidden


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

/-!
# Null measurable sets and complete measures

## Main definitions

### Null measurable sets and functions

A set `s : set α` is called *null measurable* (`measure_theory.null_measurable_set`) if it satisfies
any of the following equivalent conditions:

* there exists a measurable set `t` such that `s =ᵐ[μ] t` (this is used as a definition);
* `measure_theory.to_measurable μ s =ᵐ[μ] s`;
* there exists a measurable subset `t ⊆ s` such that `t =ᵐ[μ] s` (in this case the latter equality
  means that `μ (s \ t) = 0`);
* `s` can be represented as a union of a measurable set and a set of measure zero;
* `s` can be represented as a difference of a measurable set and a set of measure zero.

Null measurable sets form a σ-algebra that is registered as a `measurable_space` instance on
`measure_theory.null_measurable_space α μ`. We also say that `f : α → β` is
`measure_theory.null_measurable` if the preimage of a measurable set is a null measurable set.
In other words, `f : α → β` is null measurable if it is measurable as a function
`measure_theory.null_measurable_space α μ → β`.

### Complete measures

We say that a measure `μ` is complete w.r.t. the `measurable_space α` σ-algebra (or the σ-algebra is
complete w.r.t measure `μ`) if every set of measure zero is measurable. In this case all null
measurable sets and functions are measurable.

For each measure `μ`, we define `measure_theory.measure.completion μ` to be the same measure
interpreted as a measure on `measure_theory.null_measurable_space α μ` and prove that this is a
complete measure.

## Implementation notes

We define `measure_theory.null_measurable_set` as `@measurable_set (null_measurable_space α μ) _` so
that theorems about `measurable_set`s like `measurable_set.union` can be applied to
`null_measurable_set`s. However, these lemmas output terms of the same form
`@measurable_set (null_measurable_space α μ) _ _`. While this is definitionally equal to the
expected output `null_measurable_set s μ`, it looks different and may be misleading. So we copy all
standard lemmas about measurable sets to the `measure_theory.null_measurable_set` namespace and fix
the output type.

## Tags

measurable, measure, null measurable, completion
-/

open filter set encodable

variables {ι α β γ : Type*}

namespace measure_theory

/-- A type tag for `α` with `measurable_set` given by `null_measurable_set`. -/
@[nolint unused_arguments]
def null_measurable_space (α : Type*) [measurable_space α] (μ : measure α . volume_tac) : Type* := α

section

variables {m0 : measurable_space α} {μ : measure α} {s t : set α}

instance [h : inhabited α] : inhabited (null_measurable_space α μ) := h
instance [h : subsingleton α] : subsingleton (null_measurable_space α μ) := h

instance : measurable_space (null_measurable_space α μ) :=
{ measurable_set' := λ s, ∃ t, measurable_set t ∧ s =ᵐ[μ] t,
  measurable_set_empty := ⟨∅, measurable_set.empty, ae_eq_refl _⟩,
  measurable_set_compl := λ s ⟨t, htm, hts⟩, ⟨tᶜ, htm.compl, hts.compl⟩,
  measurable_set_Union := λ s hs, by { choose t htm hts using hs,
    exact ⟨⋃ i, t i, measurable_set.Union htm, eventually_eq.countable_Union hts⟩ } }

/-- A set is called `null_measurable_set` if it can be approximated by a measurable set up to
a set of null measure. -/
def null_measurable_set [measurable_space α] (s : set α) (μ : measure α . volume_tac) : Prop :=
@measurable_set (null_measurable_space α μ) _ s

@[simp] lemma _root_.measurable_set.null_measurable_set (h : measurable_set s) :
  null_measurable_set s μ :=
⟨s, h, ae_eq_refl _⟩

@[simp] lemma null_measurable_set_empty : null_measurable_set ∅ μ := measurable_set.empty

@[simp] lemma null_measurable_set_univ : null_measurable_set univ μ := measurable_set.univ

namespace null_measurable_set

lemma of_null (h : μ s = 0) : null_measurable_set s μ :=
⟨∅, measurable_set.empty, ae_eq_empty.2 h⟩

lemma compl (h : null_measurable_set s μ) : null_measurable_set sᶜ μ := h.compl

lemma of_compl (h : null_measurable_set sᶜ μ) : null_measurable_set s μ := h.of_compl

@[simp] lemma compl_iff : null_measurable_set sᶜ μ ↔ null_measurable_set s μ :=
measurable_set.compl_iff

@[nontriviality]
lemma of_subsingleton [subsingleton α] : null_measurable_set s μ := subsingleton.measurable_set

protected lemma congr (hs : null_measurable_set s μ) (h : s =ᵐ[μ] t) :
  null_measurable_set t μ :=
let ⟨s', hm, hs'⟩ := hs in ⟨s', hm, h.symm.trans hs'⟩

protected

protected lemma bUnion_decode₂ [encodable ι] ⦃f : ι → set α⦄ (h : ∀ i, null_measurable_set (f i) μ)
  (n : ℕ) : null_measurable_set (⋃ b ∈ encodable.decode₂ ι n, f b) μ :=
measurable_set.bUnion_decode₂ h n

protected lemma bUnion {f : ι → set α} {s : set ι} (hs : s.countable)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) : null_measurable_set (⋃ b ∈ s, f b) μ :=
measurable_set.bUnion hs h

protected lemma sUnion {s : set (set α)} (hs : s.countable) (h : ∀ t ∈ s, null_measurable_set t μ) :
  null_measurable_set (⋃₀ s) μ :=
by { rw sUnion_eq_bUnion, exact measurable_set.bUnion hs h }

protected lemma Inter {ι : Sort*} [countable ι] {f : ι → set α}
  (h : ∀ i, null_measurable_set (f i) μ) :
  null_measurable_set (⋂ i, f i) μ :=
measurable_set.Inter h

protected lemma bInter {f : β → set α} {s : set β} (hs : s.countable)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) : null_measurable_set (⋂ b ∈ s, f b) μ :=
measurable_set.bInter hs h

protected lemma sInter {s : set (set α)} (hs : s.countable) (h : ∀ t ∈ s, null_measurable_set t μ) :
  null_measurable_set (⋂₀ s) μ :=
measurable_set.sInter hs h

@[simp] protected lemma union (hs : null_measurable_set s μ) (ht : null_measurable_set t μ) :
  null_measurable_set (s ∪ t) μ :=
hs.union ht

protected lemma union_null (hs : null_measurable_set s μ) (ht : μ t = 0) :
  null_measurable_set (s ∪ t) μ :=
hs.union (of_null ht)

@[simp] protected lemma inter (hs : null_measurable_set s μ) (ht : null_measurable_set t μ) :
  null_measurable_set (s ∩ t) μ :=
hs.inter ht

@[simp] protected lemma diff (hs : null_measurable_set s μ) (ht : null_measurable_set t μ) :
  null_measurable_set (s \ t) μ :=
hs.diff ht

@[simp] protected lemma disjointed {f : ℕ → set α} (h : ∀ i, null_measurable_set (f i) μ) (n) :
  null_measurable_set (disjointed f n) μ :=
measurable_set.disjointed h n

@[simp] protected lemma const (p : Prop) : null_measurable_set {a : α | p} μ :=
measurable_set.const p

instance [measurable_singleton_class α] : measurable_singleton_class (null_measurable_space α μ) :=
⟨λ x, (@measurable_set_singleton α _ _ x).null_measurable_set⟩

protected lemma insert [measurable_singleton_class (null_measurable_space α μ)]
  (hs : null_measurable_set s μ) (a : α) :
  null_measurable_set (insert a s) μ :=
hs.insert a

lemma exists_measurable_superset_ae_eq (h : null_measurable_set s μ) :
  ∃ t ⊇ s, measurable_set t ∧ t =ᵐ[μ] s :=
begin
  rcases h with ⟨t, htm, hst⟩,
  refine ⟨t ∪ to_measurable μ (s \ t), _, htm.union (measurable_set_to_measurable _ _), _⟩,
  { exact diff_subset_iff.1 (subset_to_measurable _ _) },
  { have : to_measurable μ (s \ t) =ᵐ[μ] (∅ : set α), by simp [ae_le_set.1 hst.le],
    simpa only [union_empty] using hst.symm.union this }
end

lemma to_measurable_ae_eq (h : null_measurable_set s μ) :
  to_measurable μ s =ᵐ[μ] s :=
begin
  rw [to_measurable, dif_pos],
  exact h.exists_measurable_superset_ae_eq.some_spec.snd.2
end

lemma compl_to_measurable_compl_ae_eq (h : null_measurable_set s μ) :
  (to_measurable μ sᶜ)ᶜ =ᵐ[μ] s :=
by simpa only [compl_compl] using h.compl.to_measurable_ae_eq.compl

lemma exists_measurable_subset_ae_eq (h : null_measurable_set s μ) :
  ∃ t ⊆ s, measurable_set t ∧ t =ᵐ[μ] s :=
⟨(to_measurable μ sᶜ)ᶜ, compl_subset_comm.2 $ subset_to_measurable _ _,
  (measurable_set_to_measurable _ _).compl, h.compl_to_measurable_compl_ae_eq⟩

end null_measurable_set

/-- If `sᵢ` is a countable family of (null) measurable pairwise `μ`-a.e. disjoint sets, then there
exists a subordinate family `tᵢ ⊆ sᵢ` of measurable pairwise disjoint sets such that
`tᵢ =ᵐ[μ] sᵢ`. -/
lemma exists_subordinate_pairwise_disjoint [countable ι] {s : ι → set α}
  (h : ∀ i, null_measurable_set (s i) μ) (hd : pairwise (ae_disjoint μ on s)) :
  ∃ t : ι → set α, (∀ i, t i ⊆ s i) ∧ (∀ i, s i =ᵐ[μ] t i) ∧ (∀ i, measurable_set (t i)) ∧
    pairwise (disjoint on t) :=
begin
  choose t ht_sub htm ht_eq using λ i, (h i).exists_measurable_subset_ae_eq,
  rcases exists_null_pairwise_disjoint_diff hd with ⟨u, hum, hu₀, hud⟩,
  exact ⟨λ i, t i \ u i, λ i, (diff_subset _ _).trans (ht_sub _),
    λ i, (ht_eq _).symm.trans (diff_null_ae_eq_self (hu₀ i)).symm,
    λ i, (htm i).diff (hum i), hud.mono $
      λ i j h, h.mono (diff_subset_diff_left (ht_sub i)) (diff_subset_diff_left (ht_sub j))⟩
end

lemma measure_Union {m0 : measurable_space α} {μ : measure α} [countable ι] {f : ι → set α}
  (hn : pairwise (disjoint on f)) (h : ∀ i, measurable_set (f i)) :
  μ (⋃ i, f i) = ∑' i, μ (f i) :=
begin
  rw [measure_eq_extend (measurable_set.Union h),
    extend_Union measurable_set.empty _ measurable_set.Union _ hn h],
  { simp [measure_eq_extend, h] },
  { exact μ.empty },
  { exact μ.m_Union }
end

lemma measure_Union₀ [countable ι] {f : ι → set α} (hd : pairwise (ae_disjoint μ on f))
  (h : ∀ i, null_measurable_set (f i) μ) :
  μ (⋃ i, f i) = ∑' i, μ (f i) :=
begin
  rcases exists_subordinate_pairwise_disjoint h hd with ⟨t, ht_sub, ht_eq, htm, htd⟩,
  calc μ (⋃ i, f i) = μ (⋃ i, t i) : measure_congr (eventually_eq.countable_Union ht_eq)
  ... = ∑' i, μ (t i) : measure_Union htd htm
  ... = ∑' i, μ (f i) : tsum_congr (λ i, measure_congr (ht_eq _).symm)
end

lemma measure_union₀_aux (hs : null_measurable_set s μ) (ht : null_measurable_set t μ)
  (hd : ae_disjoint μ s t) :
  μ (s ∪ t) = μ s + μ t :=
begin
  rw [union_eq_Union, measure_Union₀, tsum_fintype, fintype.sum_bool, cond, cond],
  exacts [(pairwise_on_bool ae_disjoint.symmetric).2 hd, λ b, bool.cases_on b ht hs]
end

/-- A null measurable set `t` is Carathéodory measurable: for any `s`, we have
`μ (s ∩ t) + μ (s \ t) = μ s`. -/
lemma measure_inter_add_diff₀ (s : set α) (ht : null_measurable_set t μ) :
  μ (s ∩ t) + μ (s \ t) = μ s :=
begin
  refine le_antisymm _ _,
  { rcases exists_measurable_superset μ s with ⟨s', hsub, hs'm, hs'⟩,
    replace hs'm : null_measurable_set s' μ := hs'm.null_measurable_set,
    calc μ (s ∩ t) + μ (s \ t) ≤ μ (s' ∩ t) + μ (s' \ t) :
      add_le_add (measure_mono $ inter_subset_inter_left _ hsub)
        (measure_mono $ diff_subset_diff_left hsub)
    ... = μ (s' ∩ t ∪ s' \ t) :
      (measure_union₀_aux (hs'm.inter ht) (hs'm.diff ht) $
        (@disjoint_inf_sdiff _ s' t _).ae_disjoint).symm
    ... = μ s' : congr_arg μ (inter_union_diff _ _)
    ... = μ s : hs' },
  { calc μ s = μ (s ∩ t ∪ s \ t) : by rw inter_union_diff
    ... ≤ μ (s ∩ t) + μ (s \ t) : measure_union_le _ _ }
end

lemma measure_union_add_inter₀ (s : set α) (ht : null_measurable_set t μ) :
  μ (s ∪ t) + μ (s ∩ t) = μ s + μ t :=
by rw [← measure_inter_add_diff₀ (s ∪ t) ht, union_inter_cancel_right, union_diff_right,
  ← measure_inter_add_diff₀ s ht, add_comm, ← add_assoc, add_right_comm]

lemma measure_union_add_inter₀' (hs : null_measurable_set s μ) (t : set α) :
  μ (s ∪ t) + μ (s ∩ t) = μ s + μ t :=
by rw [union_comm, inter_comm, measure_union_add_inter₀ t hs, add_comm]

lemma measure_union₀ (ht : null_measurable_set t μ) (hd : ae_disjoint μ s t) :
  μ (s ∪ t) = μ s + μ t :=
by rw [← measure_union_add_inter₀ s ht, hd.eq, add_zero]

lemma measure_union₀' (hs : null_measurable_set s μ) (hd : ae_disjoint μ s t) :
  μ (s ∪ t) = μ s + μ t :=
by rw [union_comm, measure_union₀ hs hd.symm, add_comm]

lemma measure_add_measure_compl₀ {s : set α} (hs : null_measurable_set s μ) :
  μ s + μ sᶜ = μ univ :=
by rw [← measure_union₀' hs ae_disjoint_compl_right, union_compl_self]

section measurable_singleton_class

variable [measurable_singleton_class (null_measurable_space α μ)]

lemma null_measurable_set_singleton (x : α) : null_measurable_set {x} μ :=
measurable_set_singleton x

@[simp] lemma null_measurable_set_insert {a : α} {s : set α} :
  null_measurable_set (insert a s) μ ↔ null_measurable_set s μ :=
measurable_set_insert

lemma null_measurable_set_eq {a : α} : null_measurable_set {x | x = a} μ :=
null_measurable_set_singleton a

protected lemma _root_.set.finite.null_measurable_set (hs : s.finite) : null_measurable_set s μ :=
finite.measurable_set hs

protected lemma _root_.finset.null_measurable_set (s : finset α) : null_measurable_set ↑s μ :=
finset.measurable_set s

end measurable_singleton_class

lemma _root_.set.finite.null_measurable_set_bUnion {f : ι → set α} {s : set ι} (hs : s.finite)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) :
  null_measurable_set (⋃ b ∈ s, f b) μ :=
finite.measurable_set_bUnion hs h

lemma _root_.finset.null_measurable_set_bUnion {f : ι → set α} (s : finset ι)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) :
  null_measurable_set (⋃ b ∈ s, f b) μ :=
finset.measurable_set_bUnion s h

lemma _root_.set.finite.null_measurable_set_sUnion {s : set (set α)} (hs : s.finite)
  (h : ∀ t ∈ s, null_measurable_set t μ) :
  null_measurable_set (⋃₀ s) μ :=
finite.measurable_set_sUnion hs h

lemma _root_.set.finite.null_measurable_set_bInter {f : ι → set α} {s : set ι} (hs : s.finite)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) : null_measurable_set (⋂ b ∈ s, f b) μ :=
finite.measurable_set_bInter hs h

lemma _root_.finset.null_measurable_set_bInter {f : ι → set α} (s : finset ι)
  (h : ∀ b ∈ s, null_measurable_set (f b) μ) : null_measurable_set (⋂ b ∈ s, f b) μ :=
s.finite_to_set.null_measurable_set_bInter h

lemma _root_.set.finite.null_measurable_set_sInter {s : set (set α)} (hs : s.finite)
  (h : ∀ t ∈ s, null_measurable_set t μ) : null_measurable_set (⋂₀ s) μ :=
null_measurable_set.sInter hs.countable h

lemma null_measurable_set_to_measurable : null_measurable_set (to_measurable μ s) μ :=
(measurable_set_to_measurable _ _).null_measurable_set

end

section null_measurable

variables [measurable_space α] [measurable_space β] [measurable_space γ]
  {f : α → β} {μ : measure α}

/-- A function `f : α → β` is null measurable if the preimage of a measurable set is a null
measurable set. -/
def null_measurable (f : α → β) (μ : measure α . volume_tac) : Prop :=
∀ ⦃s : set β⦄, measurable_set s → null_measurable_set (f ⁻¹' s) μ

protected lemma _root_.measurable.null_measurable (h : measurable f) : null_measurable f μ :=
λ s hs, (h hs).null_measurable_set

protected lemma null_measurable.measurable' (h : null_measurable f μ) :
  @measurable (null_measurable_space α μ) β _ _ f := h

lemma measurable.comp_null_measurable {g : β → γ} (hg : measurable g)
  (hf : null_measurable f μ) : null_measurable (g ∘ f) μ :=
hg.comp hf

lemma null_measurable.congr {g : α → β} (hf : null_measurable f μ) (hg : f =ᵐ[μ] g) :
  null_measurable g μ :=
λ s hs, (hf hs).congr $ eventually_eq_set.2 $ hg.mono $
  λ x hx, by rw [mem_preimage, mem_preimage, hx]

end null_measurable

section is_complete

/-- A measure is complete if every null set is also measurable.
  A null set is a subset of a measurable set with measure `0`.
  Since every measure is defined as a special case of an outer measure, we can more simply state
  that a set `s` is null if `μ s = 0`. -/
class measure.is_complete {_ : measurable_space α} (μ : measure α) : Prop :=
(out' : ∀ s, μ s = 0 → measurable_set s)

variables {m0 : measurable_space α} {μ : measure α} {s t : set α}

theorem measure.is_complete_iff :
  μ.is_complete ↔ ∀ s, μ s = 0 → measurable_set s := ⟨λ h, h.1, λ h, ⟨h⟩⟩
theorem measure.is_complete.out (h : μ.is_complete) :
  ∀ s, μ s = 0 → measurable_set s := h.1

theorem measurable_set_of_null [μ.is_complete] (hs : μ s = 0) : measurable_set s :=
measure_theory.measure.is_complete.out' s hs

theorem null_measurable_set.measurable_of_complete (hs : null_measurable_set s μ) [μ.is_complete] :
  measurable_set s :=
diff_diff_cancel_left (subset_to_measurable μ s) ▸ (measurable_set_to_measurable _ _).diff
  (measurable_set_of_null (ae_le_set.1 hs.to_measurable_ae_eq.le))

theorem null_measurable.measurable_of_complete [μ.is_complete] {m1 : measurable_space β} {f : α → β}
  (hf : null_measurable f μ) : measurable f :=
λ s hs, (hf hs).measurable_of_complete

lemma _root_.measurable.congr_ae {α β} [measurable_space α] [measurable_space β] {μ : measure α}
  [hμ : μ.is_complete] {f g : α → β} (hf : measurable f) (hfg : f =ᵐ[μ] g) :
  measurable g :=
(hf.null_measurable.congr hfg).measurable_of_complete

namespace measure

/-- Given a measure we can complete it to a (complete) measure on all null measurable sets. -/
def completion {_ : measurable_space α} (μ : measure α) :
  @measure_theory.measure (null_measurable_space α μ) _ :=
{ to_outer_measure := μ.to_outer_measure,
  m_Union := λ s hs hd, measure_Union₀ (hd.mono $ λ i j h, h.ae_disjoint) hs,
  trimmed := begin
    refine le_antisymm (λ s, _) (outer_measure.le_trim _),
    rw outer_measure.trim_eq_infi, simp only [to_outer_measure_apply],
    refine (infi₂_mono _).trans_eq (measure_eq_infi _).symm,
    exact λ t ht, infi_mono' (λ h, ⟨h.null_measurable_set, le_rfl⟩)
  end }

instance completion.is_complete {m : measurable_space α} (μ : measure α) :
  μ.completion.is_complete :=
⟨λ z hz, null_measurable_set.of_null hz⟩

@[simp] lemma coe_completion {_ : measurable_space α} (μ : measure α) :
  ⇑μ.completion = μ := rfl

lemma completion_apply {_ : measurable_space α} (μ : measure α) (s : set α) :
  μ.completion s = μ s := rfl

end measure

end is_complete

end measure_theory