import tactic.ring
import tactic.rewrite_search

namespace tactic.rewrite_search.vs_ring

suggestion arithmetic

constants a b c d e : ℚ

lemma test3 : (a * (b + c)) * d = a * (b * d) + a * (c * d) :=
by rewrite_search {explain := tt, trace_summary := tt, no visualiser, metric.edit_distance {}}

lemma test4_ring : (a * (b + c + 1)) * d = a * (b * d) + a * (1 * d) + a * (c * d) :=
by ring

lemma test4 : (a * (b + c + 1)) * d = a * (b * d) + a * (1 * d) + a * (c * d) :=
by rewrite_search {explain := tt, trace_summary := tt, no visualiser, metric.edit_distance {refresh_freq := 3} cm, strategy := pexplore, max_iterations := 100}

lemma test5_ring : (a * (b + c + 1) / e) * d = a * (b / e * d) + a * (1 / e * d) + a * (c / e * d) :=
by ring

-- lemma test5 : (a * (b + c + 1) / e) * d = a * (b / e * d) + a * (1 / e * d) + a * (c / e * d) :=
-- by rewrite_search {explain := tt, trace_summary := tt, no visualiser}

-- lemma test5_2 : (a * (b + c + 1) / e) * d = a * (b / e * d) + a * (1 / e * d) + a * (c / e * d) :=
-- by rewrite_search [add_comm, add_assoc, mul_one, mul_assoc, /-mul_comm,-/ left_distrib, right_distrib] {explain := tt, trace_summary := tt, no visualiser, metric.edit_distance {refresh_freq := 10} cm, strategy.pexplore {pop_size := 5}, max_iterations := 500}

-- lemma test5_bfs : (a * (b + c + 1) / e) * d = a * (b / e * d) + a * (1 / e * d) + a * (c / e * d) :=
-- by rewrite_search [add_comm, add_assoc, mul_one, mul_assoc, /-mul_comm,-/ left_distrib, right_distrib] {explain := tt, trace_summary := tt, no visualiser, metric.edit_distance {refresh_freq := 3} svm, strategy := bfs, max_iterations := 2000}

end tactic.rewrite_search.vs_ring


import MLIRSemantics.Util.Arith
import Init.Data.List

namespace List

-- General theorems

theorem all_cons {α} (P: α → Bool) head tail:
    all (head::tail) P ↔ P head ∧ all tail P := by
  simp [all, foldr]

@[simp]
theorem all_nil {α} (P: α → Bool):
    all [] P = true := by
  simp [all, foldr]

-- getF

def getF {α} (l: List α) (n: Nat) (h: n < l.length): α :=
  match l, n, h with
  | a::as, 0, h => a
  | a::as, n+1, h => getF as n (Nat.lt_of_succ_lt_succ h)

def extF {α} (l₁ l₂: List α) (h_len: l₁.length = l₂.length):
    (∀ (n: Nat) (h: n < l₁.length),
      l₁.getF n h = l₂.getF n (by simp [←h_len, h])) →
    l₁ = l₂ := by
  revert l₂
  induction l₁ with
  | nil =>
      intros l₂ h_len; simp at h_len;
      cases l₂; simp; simp at h_len
  | cons x₁ t₁ ih =>
      intros l₂ h_len; simp at h_len
      cases l₂; simp at h_len
      case cons x₂ t₂ =>
        intros h_ext
        have h_x: x₁ = x₂ := by
          specialize (h_ext 0 (by simp; simp_arith));
          simp [getF] at h_ext; apply h_ext
        rw [h_x, ih t₂]
        . simp at h_len; apply h_len
        . intros n h; specialize (h_ext (n+1) (
            by simp; apply Nat.succ_lt_succ; apply h))
          simp [getF] at h_ext; apply h_ext

@[simp]
def getF_map {α β} (l: List α) (f: α → β) n h:
    getF (l.map f) n h = f (getF l n (by simp at h; apply h)) := by
  revert n
  induction l with
  | nil =>
      intros n h
      cases (Nat.not_lt_zero n h)
  | cons a as ih =>
      intros n h
      exact match n with
      | 0 => by simp [getF]
      | m+1 => by simp [getF, ih]

/- theorem getF_eq_get {α} (l: List α) n:
    get? l n.val = some (getF l n) := by -/

-- uniform

def uniform {α} (v: α): Nat → List α
  | 0 => []
  | n+1 => v :: uniform v n

theorem uniform_length {α} (v: α) n: (uniform v n).length = n := by
  induction n <;> simp [uniform]; assumption

-- rangeF

def rangeF (n: Nat): List (Fin n) :=
  match n with
  | 0 => []
  | n+1 => ⟨0,Nat.zero_lt_succ n⟩ ::
      (rangeF n).map (fun i => ⟨i.val+1, Nat.succ_lt_succ i.isLt⟩)

@[simp]
theorem length_rangeF (n: Nat):
    length (rangeF n) = n := by
  induction n with
  | zero => simp
  | succ m ih => simp [rangeF, ih]

@[simp]
theorem getF_rangeF (n: Nat): ∀ i h,
    getF (rangeF n) i h = ⟨i, by simp at h; apply h⟩ := by
  induction n with
  | zero =>
      intro i h;
      cases (Nat.not_lt_zero _ (by simp at h; apply h))
  | succ m ih =>
      intro i
      exact match i with
      | 0 => by simp [getF]
      | j+1 => by simp [rangeF, getF, ih]

--

def remap {α} (l: List α) (f: Nat→Nat) (h: ∀n, n < l.length → f n < l.length) :=
  (rangeF l.length).map (fun i => l.getF (f i.val) (h i.val i.isLt))

@[simp]
theorem length_remap {α} (l: List α) f h:
    (l.remap f h).length = l.length := by
  simp [remap]

@[simp]
theorem getF_remap {α} (l: List α) f h_f n h_n:
    getF (l.remap f h_f) n h_n
    = getF l (f n) (by apply h_f; simp at h_n; apply h_n) := by
  simp [remap]

theorem remap_remap {α} (l: List α) f h_f g h_g:
    remap (remap l f h_f) g h_g
    = remap l (f ∘ g) (fun n h =>
        by simp [Function.comp] at *; apply h_f; apply h_g; apply h) := by
  apply extF <;> simp

end List


/-
Copyright (c) 2019 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 Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.rat.basic
import Mathlib.PostPort

namespace Mathlib

/-!
# Order for Rational Numbers

## Summary

We define the order on `ℚ`, prove that `ℚ` is a discrete, linearly ordered field, and define
functions such as `abs` and `sqrt` that depend on this order.

## Notations

- `/.` is infix notation for `rat.mk`.

## Tags

rat, rationals, field, ℚ, numerator, denominator, num, denom, order, ordering, sqrt, abs
-/

namespace rat

protected def nonneg : ℚ → Prop := sorry

@[simp] theorem mk_nonneg (a : ℤ) {b : ℤ} (h : 0 < b) : rat.nonneg (mk a b) ↔ 0 ≤ a := sorry

protected theorem nonneg_add {a : ℚ} {b : ℚ} : rat.nonneg a → rat.nonneg b → rat.nonneg (a + b) :=
  sorry

protected theorem nonneg_mul {a : ℚ} {b : ℚ} : rat.nonneg a → rat.nonneg b → rat.nonneg (a * b) :=
  sorry

protected theorem nonneg_antisymm {a : ℚ} : rat.nonneg a → rat.nonneg (-a) → a = 0 := sorry

protected theorem nonneg_total (a : ℚ) : rat.nonneg a ∨ rat.nonneg (-a) :=
  cases_on a
    fun (n : ℤ) (a_denom : ℕ) (a_pos : 0 < a_denom) (a_cop : nat.coprime (int.nat_abs n) a_denom) =>
      or.imp_right neg_nonneg_of_nonpos (le_total 0 n)

protected instance decidable_nonneg (a : ℚ) : Decidable (rat.nonneg a) :=
  cases_on a
    fun (a_num : ℤ) (a_denom : ℕ) (a_pos : 0 < a_denom)
      (a_cop : nat.coprime (int.nat_abs a_num) a_denom) => eq.mpr sorry (int.decidable_le 0 a_num)

protected def le (a : ℚ) (b : ℚ) := rat.nonneg (b - a)

protected instance has_le : HasLessEq ℚ := { LessEq := rat.le }

protected instance decidable_le : DecidableRel LessEq := sorry

protected theorem le_def {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (b0 : 0 < b) (d0 : 0 < d) :
    mk a b ≤ mk c d ↔ a * d ≤ c * b :=
  sorry

protected theorem le_refl (a : ℚ) : a ≤ a :=
  (fun (this : rat.nonneg (a - a)) => this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (rat.nonneg (a - a))) (sub_self a))) (le_refl 0))

protected theorem le_total (a : ℚ) (b : ℚ) : a ≤ b ∨ b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (rat.nonneg (b - a) ∨ rat.nonneg (-(b - a)))) (neg_sub b a))
    (rat.nonneg_total (b - a))

protected theorem le_antisymm {a : ℚ} {b : ℚ} (hab : a ≤ b) (hba : b ≤ a) : a = b := sorry

protected theorem le_trans {a : ℚ} {b : ℚ} {c : ℚ} (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c := sorry

protected instance linear_order : linear_order ℚ :=
  linear_order.mk rat.le (partial_order.lt._default rat.le) rat.le_refl rat.le_trans rat.le_antisymm
    rat.le_total (fun (a b : ℚ) => rat.decidable_nonneg (b - a))
    (fun (a b : ℚ) => rat.decidable_eq a b) Mathlib.decidable_lt_of_decidable_le

/- Extra instances to short-circuit type class resolution -/

protected instance has_lt : HasLess ℚ := preorder.to_has_lt ℚ

protected instance distrib_lattice : distrib_lattice ℚ := Mathlib.distrib_lattice_of_linear_order

protected instance lattice : lattice ℚ := Mathlib.lattice_of_linear_order

protected instance semilattice_inf : semilattice_inf ℚ := lattice.to_semilattice_inf ℚ

protected instance semilattice_sup : semilattice_sup ℚ := lattice.to_semilattice_sup ℚ

protected instance has_inf : has_inf ℚ := semilattice_inf.to_has_inf ℚ

protected instance has_sup : has_sup ℚ := semilattice_sup.to_has_sup ℚ

protected instance partial_order : partial_order ℚ := semilattice_inf.to_partial_order ℚ

protected instance preorder : preorder ℚ := directed_order.to_preorder

protected theorem le_def' {p : ℚ} {q : ℚ} : p ≤ q ↔ num p * ↑(denom q) ≤ num q * ↑(denom p) := sorry

protected theorem lt_def {p : ℚ} {q : ℚ} : p < q ↔ num p * ↑(denom q) < num q * ↑(denom p) := sorry

theorem nonneg_iff_zero_le {a : ℚ} : rat.nonneg a ↔ 0 ≤ a := sorry

theorem num_nonneg_iff_zero_le {a : ℚ} : 0 ≤ num a ↔ 0 ≤ a :=
  cases_on a
    fun (a_num : ℤ) (a_denom : ℕ) (a_pos : 0 < a_denom)
      (a_cop : nat.coprime (int.nat_abs a_num) a_denom) =>
      idRhs (rat.nonneg (mk' a_num a_denom a_pos a_cop) ↔ 0 ≤ mk' a_num a_denom a_pos a_cop)
        nonneg_iff_zero_le

protected theorem add_le_add_left {a : ℚ} {b : ℚ} {c : ℚ} : c + a ≤ c + b ↔ a ≤ b := sorry

protected theorem mul_nonneg {a : ℚ} {b : ℚ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a * b)) (Eq.symm (propext nonneg_iff_zero_le))))
    (rat.nonneg_mul (eq.mp (Eq._oldrec (Eq.refl (0 ≤ a)) (Eq.symm (propext nonneg_iff_zero_le))) ha)
      (eq.mp (Eq._oldrec (Eq.refl (0 ≤ b)) (Eq.symm (propext nonneg_iff_zero_le))) hb))

protected instance linear_ordered_field : linear_ordered_field ℚ :=
  linear_ordered_field.mk field.add field.add_assoc field.zero field.zero_add field.add_zero
    field.neg field.sub field.add_left_neg field.add_comm field.mul field.mul_assoc field.one
    field.one_mul field.mul_one field.left_distrib field.right_distrib linear_order.le
    linear_order.lt linear_order.le_refl linear_order.le_trans linear_order.le_antisymm sorry sorry
    sorry linear_order.le_total linear_order.decidable_le linear_order.decidable_eq
    linear_order.decidable_lt field.exists_pair_ne field.mul_comm field.inv field.mul_inv_cancel
    field.inv_zero

/- Extra instances to short-circuit type class resolution -/

protected instance linear_ordered_comm_ring : linear_ordered_comm_ring ℚ :=
  linear_ordered_field.to_linear_ordered_comm_ring ℚ

protected instance linear_ordered_ring : linear_ordered_ring ℚ :=
  linear_ordered_comm_ring.to_linear_ordered_ring ℚ

protected instance ordered_ring : ordered_ring ℚ := linear_ordered_ring.to_ordered_ring ℚ

protected instance linear_ordered_semiring : linear_ordered_semiring ℚ :=
  linear_ordered_comm_ring.to_linear_ordered_semiring

protected instance ordered_semiring : ordered_semiring ℚ := ordered_ring.to_ordered_semiring

protected instance linear_ordered_add_comm_group : linear_ordered_add_comm_group ℚ :=
  linear_ordered_ring.to_linear_ordered_add_comm_group

protected instance ordered_add_comm_group : ordered_add_comm_group ℚ :=
  ordered_ring.to_ordered_add_comm_group ℚ

protected instance ordered_cancel_add_comm_monoid : ordered_cancel_add_comm_monoid ℚ :=
  ordered_semiring.to_ordered_cancel_add_comm_monoid ℚ

protected instance ordered_add_comm_monoid : ordered_add_comm_monoid ℚ :=
  ordered_cancel_add_comm_monoid.to_ordered_add_comm_monoid

theorem num_pos_iff_pos {a : ℚ} : 0 < num a ↔ 0 < a := sorry

theorem div_lt_div_iff_mul_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (b_pos : 0 < b) (d_pos : 0 < d) :
    ↑a / ↑b < ↑c / ↑d ↔ a * d < c * b :=
  sorry

theorem lt_one_iff_num_lt_denom {q : ℚ} : q < 1 ↔ num q < ↑(denom q) := sorry

theorem abs_def (q : ℚ) : abs q = mk ↑(int.nat_abs (num q)) ↑(denom q) := sorry

end Mathlib


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Jeremy Avigad, Andrew Zipperer, Haitao Zhang, Minchao Wu, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.basic
import Mathlib.logic.function.conjugate
import Mathlib.PostPort

universes u v u_1 w y u_2 u_3

namespace Mathlib

/-!
# Functions over sets

## Main definitions

### Predicate

* `eq_on f₁ f₂ s` : functions `f₁` and `f₂` are equal at every point of `s`;
* `maps_to f s t` : `f` sends every point of `s` to a point of `t`;
* `inj_on f s` : restriction of `f` to `s` is injective;
* `surj_on f s t` : every point in `s` has a preimage in `s`;
* `bij_on f s t` : `f` is a bijection between `s` and `t`;
* `left_inv_on f' f s` : for every `x ∈ s` we have `f' (f x) = x`;
* `right_inv_on f' f t` : for every `y ∈ t` we have `f (f' y) = y`;
* `inv_on f' f s t` : `f'` is a two-side inverse of `f` on `s` and `t`, i.e.
  we have `left_inv_on f' f s` and `right_inv_on f' f t`.

### Functions

* `restrict f s` : restrict the domain of `f` to the set `s`;
* `cod_restrict f s h` : given `h : ∀ x, f x ∈ s`, restrict the codomain of `f` to the set `s`;
* `maps_to.restrict f s t h`: given `h : maps_to f s t`, restrict the domain of `f` to `s`
  and the codomain to `t`.
-/

namespace set

/-! ### Restrict -/

/-- Restrict domain of a function `f` to a set `s`. Same as `subtype.restrict` but this version
takes an argument `↥s` instead of `subtype s`. -/
def restrict {α : Type u} {β : Type v} (f : α → β) (s : set α) : ↥s → β :=
  fun (x : ↥s) => f ↑x

theorem restrict_eq {α : Type u} {β : Type v} (f : α → β) (s : set α) : restrict f s = f ∘ coe :=
  rfl

@[simp] theorem restrict_apply {α : Type u} {β : Type v} (f : α → β) (s : set α) (x : ↥s) : restrict f s x = f ↑x :=
  rfl

@[simp] theorem range_restrict {α : Type u} {β : Type v} (f : α → β) (s : set α) : range (restrict f s) = f '' s :=
  Eq.trans (range_comp f fun (x : ↥s) => ↑x) (congr_arg (image f) subtype.range_coe)

/-- Restrict codomain of a function `f` to a set `s`. Same as `subtype.coind` but this version
has codomain `↥s` instead of `subtype s`. -/
def cod_restrict {α : Type u} {β : Type v} (f : α → β) (s : set β) (h : ∀ (x : α), f x ∈ s) : α → ↥s :=
  fun (x : α) => { val := f x, property := h x }

@[simp] theorem coe_cod_restrict_apply {α : Type u} {β : Type v} (f : α → β) (s : set β) (h : ∀ (x : α), f x ∈ s) (x : α) : ↑(cod_restrict f s h x) = f x :=
  rfl

/-! ### Equality on a set -/

/-- Two functions `f₁ f₂ : α → β` are equal on `s`
  if `f₁ x = f₂ x` for all `x ∈ a`. -/
def eq_on {α : Type u} {β : Type v} (f₁ : α → β) (f₂ : α → β) (s : set α) :=
  ∀ {x : α}, x ∈ s → f₁ x = f₂ x

theorem eq_on.symm {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} (h : eq_on f₁ f₂ s) : eq_on f₂ f₁ s :=
  fun (x : α) (hx : x ∈ s) => Eq.symm (h hx)

theorem eq_on_comm {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} : eq_on f₁ f₂ s ↔ eq_on f₂ f₁ s :=
  { mp := eq_on.symm, mpr := eq_on.symm }

theorem eq_on_refl {α : Type u} {β : Type v} (f : α → β) (s : set α) : eq_on f f s :=
  fun (_x : α) (_x_1 : _x ∈ s) => rfl

theorem eq_on.trans {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} {f₃ : α → β} (h₁ : eq_on f₁ f₂ s) (h₂ : eq_on f₂ f₃ s) : eq_on f₁ f₃ s :=
  fun (x : α) (hx : x ∈ s) => Eq.trans (h₁ hx) (h₂ hx)

theorem eq_on.image_eq {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} (heq : eq_on f₁ f₂ s) : f₁ '' s = f₂ '' s :=
  image_congr heq

theorem eq_on.mono {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {f₁ : α → β} {f₂ : α → β} (hs : s₁ ⊆ s₂) (hf : eq_on f₁ f₂ s₂) : eq_on f₁ f₂ s₁ :=
  fun (x : α) (hx : x ∈ s₁) => hf (hs hx)

theorem comp_eq_of_eq_on_range {α : Type u} {β : Type v} {ι : Sort u_1} {f : ι → α} {g₁ : α → β} {g₂ : α → β} (h : eq_on g₁ g₂ (range f)) : g₁ ∘ f = g₂ ∘ f :=
  funext fun (x : ι) => h (mem_range_self x)

/-! ### maps to -/

/-- `maps_to f a b` means that the image of `a` is contained in `b`. -/
def maps_to {α : Type u} {β : Type v} (f : α → β) (s : set α) (t : set β) :=
  ∀ {x : α}, x ∈ s → f x ∈ t

/-- Given a map `f` sending `s : set α` into `t : set β`, restrict domain of `f` to `s`
and the codomain to `t`. Same as `subtype.map`. -/
def maps_to.restrict {α : Type u} {β : Type v} (f : α → β) (s : set α) (t : set β) (h : maps_to f s t) : ↥s → ↥t :=
  subtype.map f h

@[simp] theorem maps_to.coe_restrict_apply {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : maps_to f s t) (x : ↥s) : ↑(maps_to.restrict f s t h x) = f ↑x :=
  rfl

theorem maps_to_iff_exists_map_subtype {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} : maps_to f s t ↔ ∃ (g : ↥s → ↥t), ∀ (x : ↥s), f ↑x = ↑(g x) := sorry

theorem maps_to' {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} : maps_to f s t ↔ f '' s ⊆ t :=
  iff.symm image_subset_iff

theorem maps_to_empty {α : Type u} {β : Type v} (f : α → β) (t : set β) : maps_to f ∅ t :=
  empty_subset fun (x : α) => t (f x)

theorem maps_to.image_subset {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : maps_to f s t) : f '' s ⊆ t :=
  iff.mp maps_to' h

theorem maps_to.congr {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (h₁ : maps_to f₁ s t) (h : eq_on f₁ f₂ s) : maps_to f₂ s t :=
  fun (x : α) (hx : x ∈ s) => h hx ▸ h₁ hx

theorem eq_on.maps_to_iff {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (H : eq_on f₁ f₂ s) : maps_to f₁ s t ↔ maps_to f₂ s t :=
  { mp := fun (h : maps_to f₁ s t) => maps_to.congr h H,
    mpr := fun (h : maps_to f₂ s t) => maps_to.congr h (eq_on.symm H) }

theorem maps_to.comp {α : Type u} {β : Type v} {γ : Type w} {s : set α} {t : set β} {p : set γ} {f : α → β} {g : β → γ} (h₁ : maps_to g t p) (h₂ : maps_to f s t) : maps_to (g ∘ f) s p :=
  fun (x : α) (h : x ∈ s) => h₁ (h₂ h)

theorem maps_to_id {α : Type u} (s : set α) : maps_to id s s :=
  fun (x : α) => id

theorem maps_to.iterate {α : Type u} {f : α → α} {s : set α} (h : maps_to f s s) (n : ℕ) : maps_to (nat.iterate f n) s s := sorry

theorem maps_to.iterate_restrict {α : Type u} {f : α → α} {s : set α} (h : maps_to f s s) (n : ℕ) : nat.iterate (maps_to.restrict f s s h) n = maps_to.restrict (nat.iterate f n) s s (maps_to.iterate h n) := sorry

theorem maps_to.mono {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (hs : s₂ ⊆ s₁) (ht : t₁ ⊆ t₂) (hf : maps_to f s₁ t₁) : maps_to f s₂ t₂ :=
  fun (x : α) (hx : x ∈ s₂) => ht (hf (hs hx))

theorem maps_to.union_union {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : maps_to f s₁ t₁) (h₂ : maps_to f s₂ t₂) : maps_to f (s₁ ∪ s₂) (t₁ ∪ t₂) :=
  fun (x : α) (hx : x ∈ s₁ ∪ s₂) => or.elim hx (fun (hx : x ∈ s₁) => Or.inl (h₁ hx)) fun (hx : x ∈ s₂) => Or.inr (h₂ hx)

theorem maps_to.union {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t : set β} {f : α → β} (h₁ : maps_to f s₁ t) (h₂ : maps_to f s₂ t) : maps_to f (s₁ ∪ s₂) t :=
  union_self t ▸ maps_to.union_union h₁ h₂

@[simp] theorem maps_to_union {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t : set β} {f : α → β} : maps_to f (s₁ ∪ s₂) t ↔ maps_to f s₁ t ∧ maps_to f s₂ t := sorry

theorem maps_to.inter {α : Type u} {β : Type v} {s : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : maps_to f s t₁) (h₂ : maps_to f s t₂) : maps_to f s (t₁ ∩ t₂) :=
  fun (x : α) (hx : x ∈ s) => { left := h₁ hx, right := h₂ hx }

theorem maps_to.inter_inter {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : maps_to f s₁ t₁) (h₂ : maps_to f s₂ t₂) : maps_to f (s₁ ∩ s₂) (t₁ ∩ t₂) :=
  fun (x : α) (hx : x ∈ s₁ ∩ s₂) => { left := h₁ (and.left hx), right := h₂ (and.right hx) }

@[simp] theorem maps_to_inter {α : Type u} {β : Type v} {s : set α} {t₁ : set β} {t₂ : set β} {f : α → β} : maps_to f s (t₁ ∩ t₂) ↔ maps_to f s t₁ ∧ maps_to f s t₂ := sorry

theorem maps_to_univ {α : Type u} {β : Type v} (f : α → β) (s : set α) : maps_to f s univ :=
  fun (x : α) (h : x ∈ s) => trivial

theorem maps_to_image {α : Type u} {β : Type v} (f : α → β) (s : set α) : maps_to f s (f '' s) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (maps_to f s (f '' s))) (propext maps_to'))) (subset.refl (f '' s))

theorem maps_to_preimage {α : Type u} {β : Type v} (f : α → β) (t : set β) : maps_to f (f ⁻¹' t) t :=
  subset.refl (f ⁻¹' t)

theorem maps_to_range {α : Type u} {β : Type v} (f : α → β) (s : set α) : maps_to f s (range f) :=
  maps_to.mono (subset.refl s) (image_subset_range f s) (maps_to_image f s)

theorem surjective_maps_to_image_restrict {α : Type u} {β : Type v} (f : α → β) (s : set α) : function.surjective (maps_to.restrict f s (f '' s) (maps_to_image f s)) := sorry

theorem maps_to.mem_iff {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : maps_to f s t) (hc : maps_to f (sᶜ) (tᶜ)) {x : α} : f x ∈ t ↔ x ∈ s :=
  { mp := fun (ht : f x ∈ t) => by_contra fun (hs : ¬x ∈ s) => hc hs ht, mpr := fun (hx : x ∈ s) => h hx }

/-! ### Injectivity on a set -/

/-- `f` is injective on `a` if the restriction of `f` to `a` is injective. -/
def inj_on {α : Type u} {β : Type v} (f : α → β) (s : set α) :=
  ∀ {x₁ : α}, x₁ ∈ s → ∀ {x₂ : α}, x₂ ∈ s → f x₁ = f x₂ → x₁ = x₂

theorem inj_on_empty {α : Type u} {β : Type v} (f : α → β) : inj_on f ∅ :=
  fun (_x : α) (h₁ : _x ∈ ∅) => false.elim h₁

theorem inj_on.eq_iff {α : Type u} {β : Type v} {s : set α} {f : α → β} {x : α} {y : α} (h : inj_on f s) (hx : x ∈ s) (hy : y ∈ s) : f x = f y ↔ x = y :=
  { mp := h hx hy, mpr := fun (h : x = y) => h ▸ rfl }

theorem inj_on.congr {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} (h₁ : inj_on f₁ s) (h : eq_on f₁ f₂ s) : inj_on f₂ s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) => h hx ▸ h hy ▸ h₁ hx hy

theorem eq_on.inj_on_iff {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} (H : eq_on f₁ f₂ s) : inj_on f₁ s ↔ inj_on f₂ s :=
  { mp := fun (h : inj_on f₁ s) => inj_on.congr h H, mpr := fun (h : inj_on f₂ s) => inj_on.congr h (eq_on.symm H) }

theorem inj_on.mono {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {f : α → β} (h : s₁ ⊆ s₂) (ht : inj_on f s₂) : inj_on f s₁ :=
  fun (x : α) (hx : x ∈ s₁) (y : α) (hy : y ∈ s₁) (H : f x = f y) => ht (h hx) (h hy) H

theorem inj_on_insert {α : Type u} {β : Type v} {f : α → β} {s : set α} {a : α} (has : ¬a ∈ s) : inj_on f (insert a s) ↔ inj_on f s ∧ ¬f a ∈ f '' s := sorry

theorem injective_iff_inj_on_univ {α : Type u} {β : Type v} {f : α → β} : function.injective f ↔ inj_on f univ :=
  { mp := fun (h : function.injective f) (x : α) (hx : x ∈ univ) (y : α) (hy : y ∈ univ) (hxy : f x = f y) => h hxy,
    mpr := fun (h : inj_on f univ) (_x _x_1 : α) (heq : f _x = f _x_1) => h trivial trivial heq }

theorem inj_on_of_injective {α : Type u} {β : Type v} {f : α → β} (h : function.injective f) (s : set α) : inj_on f s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) (hxy : f x = f y) => h hxy

theorem Mathlib.function.injective.inj_on {α : Type u} {β : Type v} {f : α → β} (h : function.injective f) (s : set α) : inj_on f s :=
  inj_on_of_injective

theorem inj_on.comp {α : Type u} {β : Type v} {γ : Type w} {s : set α} {t : set β} {f : α → β} {g : β → γ} (hg : inj_on g t) (hf : inj_on f s) (h : maps_to f s t) : inj_on (g ∘ f) s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) (heq : function.comp g f x = function.comp g f y) =>
    hf hx hy (hg (h hx) (h hy) heq)

theorem inj_on_iff_injective {α : Type u} {β : Type v} {s : set α} {f : α → β} : inj_on f s ↔ function.injective (restrict f s) := sorry

theorem inj_on.inv_fun_on_image {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {f : α → β} [Nonempty α] (h : inj_on f s₂) (ht : s₁ ⊆ s₂) : function.inv_fun_on f s₂ '' (f '' s₁) = s₁ := sorry

theorem inj_on_preimage {α : Type u} {β : Type v} {f : α → β} {B : set (set β)} (hB : B ⊆ 𝒫 range f) : inj_on (preimage f) B :=
  fun (s : set β) (hs : s ∈ B) (t : set β) (ht : t ∈ B) (hst : f ⁻¹' s = f ⁻¹' t) =>
    iff.mp (preimage_eq_preimage' (hB hs) (hB ht)) hst

/-! ### Surjectivity on a set -/

/-- `f` is surjective from `a` to `b` if `b` is contained in the image of `a`. -/
def surj_on {α : Type u} {β : Type v} (f : α → β) (s : set α) (t : set β) :=
  t ⊆ f '' s

theorem surj_on.subset_range {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : surj_on f s t) : t ⊆ range f :=
  subset.trans h (image_subset_range f s)

theorem surj_on_iff_exists_map_subtype {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} : surj_on f s t ↔ ∃ (t' : set β), ∃ (g : ↥s → ↥t'), t ⊆ t' ∧ function.surjective g ∧ ∀ (x : ↥s), f ↑x = ↑(g x) := sorry

theorem surj_on_empty {α : Type u} {β : Type v} (f : α → β) (s : set α) : surj_on f s ∅ :=
  empty_subset (f '' s)

theorem surj_on.comap_nonempty {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : surj_on f s t) (ht : set.nonempty t) : set.nonempty s :=
  nonempty.of_image (nonempty.mono h ht)

theorem surj_on.congr {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (h : surj_on f₁ s t) (H : eq_on f₁ f₂ s) : surj_on f₂ s t :=
  eq.mpr (id (Eq._oldrec (Eq.refl (surj_on f₂ s t)) (surj_on.equations._eqn_1 f₂ s t)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (t ⊆ f₂ '' s)) (Eq.symm (eq_on.image_eq H)))) h)

theorem eq_on.surj_on_iff {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (h : eq_on f₁ f₂ s) : surj_on f₁ s t ↔ surj_on f₂ s t :=
  { mp := fun (H : surj_on f₁ s t) => surj_on.congr H h,
    mpr := fun (H : surj_on f₂ s t) => surj_on.congr H (eq_on.symm h) }

theorem surj_on.mono {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (hs : s₁ ⊆ s₂) (ht : t₁ ⊆ t₂) (hf : surj_on f s₁ t₂) : surj_on f s₂ t₁ :=
  subset.trans ht (subset.trans hf (image_subset f hs))

theorem surj_on.union {α : Type u} {β : Type v} {s : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : surj_on f s t₁) (h₂ : surj_on f s t₂) : surj_on f s (t₁ ∪ t₂) :=
  fun (x : β) (hx : x ∈ t₁ ∪ t₂) => or.elim hx (fun (hx : x ∈ t₁) => h₁ hx) fun (hx : x ∈ t₂) => h₂ hx

theorem surj_on.union_union {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : surj_on f s₁ t₁) (h₂ : surj_on f s₂ t₂) : surj_on f (s₁ ∪ s₂) (t₁ ∪ t₂) :=
  surj_on.union (surj_on.mono (subset_union_left s₁ s₂) (subset.refl t₁) h₁)
    (surj_on.mono (subset_union_right s₁ s₂) (subset.refl t₂) h₂)

theorem surj_on.inter_inter {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : surj_on f s₁ t₁) (h₂ : surj_on f s₂ t₂) (h : inj_on f (s₁ ∪ s₂)) : surj_on f (s₁ ∩ s₂) (t₁ ∩ t₂) := sorry

theorem surj_on.inter {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t : set β} {f : α → β} (h₁ : surj_on f s₁ t) (h₂ : surj_on f s₂ t) (h : inj_on f (s₁ ∪ s₂)) : surj_on f (s₁ ∩ s₂) t :=
  inter_self t ▸ surj_on.inter_inter h₁ h₂ h

theorem surj_on.comp {α : Type u} {β : Type v} {γ : Type w} {s : set α} {t : set β} {p : set γ} {f : α → β} {g : β → γ} (hg : surj_on g t p) (hf : surj_on f s t) : surj_on (g ∘ f) s p :=
  subset.trans hg (subset.trans (image_subset g hf) (image_comp g f s ▸ subset.refl (g ∘ f '' s)))

theorem surjective_iff_surj_on_univ {α : Type u} {β : Type v} {f : α → β} : function.surjective f ↔ surj_on f univ univ := sorry

theorem surj_on_iff_surjective {α : Type u} {β : Type v} {s : set α} {f : α → β} : surj_on f s univ ↔ function.surjective (restrict f s) := sorry

theorem surj_on.image_eq_of_maps_to {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h₁ : surj_on f s t) (h₂ : maps_to f s t) : f '' s = t :=
  eq_of_subset_of_subset (maps_to.image_subset h₂) h₁

theorem surj_on.maps_to_compl {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : surj_on f s t) (h' : function.injective f) : maps_to f (sᶜ) (tᶜ) := sorry

theorem maps_to.surj_on_compl {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : maps_to f s t) (h' : function.surjective f) : surj_on f (sᶜ) (tᶜ) :=
  iff.mpr (function.surjective.forall h')
    fun (x : α) (ht : f x ∈ (tᶜ)) => mem_image_of_mem f fun (hs : x ∈ s) => ht (h hs)

/-! ### Bijectivity -/

/-- `f` is bijective from `s` to `t` if `f` is injective on `s` and `f '' s = t`. -/
def bij_on {α : Type u} {β : Type v} (f : α → β) (s : set α) (t : set β) :=
  maps_to f s t ∧ inj_on f s ∧ surj_on f s t

theorem bij_on.maps_to {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : maps_to f s t :=
  and.left h

theorem bij_on.inj_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : inj_on f s :=
  and.left (and.right h)

theorem bij_on.surj_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : surj_on f s t :=
  and.right (and.right h)

theorem bij_on.mk {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h₁ : maps_to f s t) (h₂ : inj_on f s) (h₃ : surj_on f s t) : bij_on f s t :=
  { left := h₁, right := { left := h₂, right := h₃ } }

theorem bij_on_empty {α : Type u} {β : Type v} (f : α → β) : bij_on f ∅ ∅ :=
  { left := maps_to_empty f ∅, right := { left := inj_on_empty f, right := surj_on_empty f ∅ } }

theorem bij_on.inter {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : bij_on f s₁ t₁) (h₂ : bij_on f s₂ t₂) (h : inj_on f (s₁ ∪ s₂)) : bij_on f (s₁ ∩ s₂) (t₁ ∩ t₂) := sorry

theorem bij_on.union {α : Type u} {β : Type v} {s₁ : set α} {s₂ : set α} {t₁ : set β} {t₂ : set β} {f : α → β} (h₁ : bij_on f s₁ t₁) (h₂ : bij_on f s₂ t₂) (h : inj_on f (s₁ ∪ s₂)) : bij_on f (s₁ ∪ s₂) (t₁ ∪ t₂) :=
  { left := maps_to.union_union (bij_on.maps_to h₁) (bij_on.maps_to h₂),
    right := { left := h, right := surj_on.union_union (bij_on.surj_on h₁) (bij_on.surj_on h₂) } }

theorem bij_on.subset_range {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : t ⊆ range f :=
  surj_on.subset_range (bij_on.surj_on h)

theorem inj_on.bij_on_image {α : Type u} {β : Type v} {s : set α} {f : α → β} (h : inj_on f s) : bij_on f s (f '' s) :=
  bij_on.mk (maps_to_image f s) h (subset.refl (f '' s))

theorem bij_on.congr {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (h₁ : bij_on f₁ s t) (h : eq_on f₁ f₂ s) : bij_on f₂ s t :=
  bij_on.mk (maps_to.congr (bij_on.maps_to h₁) h) (inj_on.congr (bij_on.inj_on h₁) h)
    (surj_on.congr (bij_on.surj_on h₁) h)

theorem eq_on.bij_on_iff {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} (H : eq_on f₁ f₂ s) : bij_on f₁ s t ↔ bij_on f₂ s t :=
  { mp := fun (h : bij_on f₁ s t) => bij_on.congr h H, mpr := fun (h : bij_on f₂ s t) => bij_on.congr h (eq_on.symm H) }

theorem bij_on.image_eq {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : f '' s = t :=
  surj_on.image_eq_of_maps_to (bij_on.surj_on h) (bij_on.maps_to h)

theorem bij_on.comp {α : Type u} {β : Type v} {γ : Type w} {s : set α} {t : set β} {p : set γ} {f : α → β} {g : β → γ} (hg : bij_on g t p) (hf : bij_on f s t) : bij_on (g ∘ f) s p :=
  bij_on.mk (maps_to.comp (bij_on.maps_to hg) (bij_on.maps_to hf))
    (inj_on.comp (bij_on.inj_on hg) (bij_on.inj_on hf) (bij_on.maps_to hf))
    (surj_on.comp (bij_on.surj_on hg) (bij_on.surj_on hf))

theorem bij_on.bijective {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (h : bij_on f s t) : function.bijective (cod_restrict (restrict f s) t fun (x : ↥s) => bij_on.maps_to h (subtype.val_prop x)) := sorry

theorem bijective_iff_bij_on_univ {α : Type u} {β : Type v} {f : α → β} : function.bijective f ↔ bij_on f univ univ := sorry

theorem bij_on.compl {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (hst : bij_on f s t) (hf : function.bijective f) : bij_on f (sᶜ) (tᶜ) := sorry

/-! ### left inverse -/

/-- `g` is a left inverse to `f` on `a` means that `g (f x) = x` for all `x ∈ a`. -/
def left_inv_on {α : Type u} {β : Type v} (f' : β → α) (f : α → β) (s : set α) :=
  ∀ {x : α}, x ∈ s → f' (f x) = x

theorem left_inv_on.eq_on {α : Type u} {β : Type v} {s : set α} {f : α → β} {f' : β → α} (h : left_inv_on f' f s) : eq_on (f' ∘ f) id s :=
  h

theorem left_inv_on.eq {α : Type u} {β : Type v} {s : set α} {f : α → β} {f' : β → α} (h : left_inv_on f' f s) {x : α} (hx : x ∈ s) : f' (f x) = x :=
  h hx

theorem left_inv_on.congr_left {α : Type u} {β : Type v} {s : set α} {f : α → β} {f₁' : β → α} {f₂' : β → α} (h₁ : left_inv_on f₁' f s) {t : set β} (h₁' : maps_to f s t) (heq : eq_on f₁' f₂' t) : left_inv_on f₂' f s :=
  fun (x : α) (hx : x ∈ s) => heq (h₁' hx) ▸ h₁ hx

theorem left_inv_on.congr_right {α : Type u} {β : Type v} {s : set α} {f₁ : α → β} {f₂ : α → β} {f₁' : β → α} (h₁ : left_inv_on f₁' f₁ s) (heq : eq_on f₁ f₂ s) : left_inv_on f₁' f₂ s :=
  fun (x : α) (hx : x ∈ s) => heq hx ▸ h₁ hx

theorem left_inv_on.inj_on {α : Type u} {β : Type v} {s : set α} {f : α → β} {f₁' : β → α} (h : left_inv_on f₁' f s) : inj_on f s :=
  fun (x₁ : α) (h₁ : x₁ ∈ s) (x₂ : α) (h₂ : x₂ ∈ s) (heq : f x₁ = f x₂) =>
    Eq.trans (Eq.trans (Eq.symm (h h₁)) (congr_arg f₁' heq)) (h h₂)

theorem left_inv_on.surj_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (h : left_inv_on f' f s) (hf : maps_to f s t) : surj_on f' t s :=
  fun (x : α) (hx : x ∈ s) => Exists.intro (f x) { left := hf hx, right := h hx }

theorem left_inv_on.maps_to {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (h : left_inv_on f' f s) (hf : surj_on f s t) : maps_to f' t s := sorry

theorem left_inv_on.comp {α : Type u} {β : Type v} {γ : Type w} {s : set α} {t : set β} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β} (hf' : left_inv_on f' f s) (hg' : left_inv_on g' g t) (hf : maps_to f s t) : left_inv_on (f' ∘ g') (g ∘ f) s :=
  fun (x : α) (h : x ∈ s) => Eq.trans (congr_arg f' (hg' (hf h))) (hf' h)

theorem left_inv_on.mono {α : Type u} {β : Type v} {s : set α} {s₁ : set α} {f : α → β} {f' : β → α} (hf : left_inv_on f' f s) (ht : s₁ ⊆ s) : left_inv_on f' f s₁ :=
  fun (x : α) (hx : x ∈ s₁) => hf (ht hx)

/-! ### Right inverse -/

/-- `g` is a right inverse to `f` on `b` if `f (g x) = x` for all `x ∈ b`. -/
def right_inv_on {α : Type u} {β : Type v} (f' : β → α) (f : α → β) (t : set β) :=
  left_inv_on f f' t

theorem right_inv_on.eq_on {α : Type u} {β : Type v} {t : set β} {f : α → β} {f' : β → α} (h : right_inv_on f' f t) : eq_on (f ∘ f') id t :=
  h

theorem right_inv_on.eq {α : Type u} {β : Type v} {t : set β} {f : α → β} {f' : β → α} (h : right_inv_on f' f t) {y : β} (hy : y ∈ t) : f (f' y) = y :=
  h hy

theorem right_inv_on.congr_left {α : Type u} {β : Type v} {t : set β} {f : α → β} {f₁' : β → α} {f₂' : β → α} (h₁ : right_inv_on f₁' f t) (heq : eq_on f₁' f₂' t) : right_inv_on f₂' f t :=
  left_inv_on.congr_right h₁ heq

theorem right_inv_on.congr_right {α : Type u} {β : Type v} {s : set α} {t : set β} {f₁ : α → β} {f₂ : α → β} {f' : β → α} (h₁ : right_inv_on f' f₁ t) (hg : maps_to f' t s) (heq : eq_on f₁ f₂ s) : right_inv_on f' f₂ t :=
  left_inv_on.congr_left h₁ hg heq

theorem right_inv_on.surj_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (hf : right_inv_on f' f t) (hf' : maps_to f' t s) : surj_on f s t :=
  left_inv_on.surj_on hf hf'

theorem right_inv_on.maps_to {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (h : right_inv_on f' f t) (hf : surj_on f' t s) : maps_to f s t :=
  left_inv_on.maps_to h hf

theorem right_inv_on.comp {α : Type u} {β : Type v} {γ : Type w} {t : set β} {p : set γ} {f : α → β} {g : β → γ} {f' : β → α} {g' : γ → β} (hf : right_inv_on f' f t) (hg : right_inv_on g' g p) (g'pt : maps_to g' p t) : right_inv_on (f' ∘ g') (g ∘ f) p :=
  left_inv_on.comp hg hf g'pt

theorem right_inv_on.mono {α : Type u} {β : Type v} {t : set β} {t₁ : set β} {f : α → β} {f' : β → α} (hf : right_inv_on f' f t) (ht : t₁ ⊆ t) : right_inv_on f' f t₁ :=
  left_inv_on.mono hf ht

theorem inj_on.right_inv_on_of_left_inv_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (hf : inj_on f s) (hf' : left_inv_on f f' t) (h₁ : maps_to f s t) (h₂ : maps_to f' t s) : right_inv_on f f' s :=
  fun (x : α) (h : x ∈ s) => hf (h₂ (h₁ h)) h (hf' (h₁ h))

theorem eq_on_of_left_inv_on_of_right_inv_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f₁' : β → α} {f₂' : β → α} (h₁ : left_inv_on f₁' f s) (h₂ : right_inv_on f₂' f t) (h : maps_to f₂' t s) : eq_on f₁' f₂' t :=
  fun (y : β) (hy : y ∈ t) => Eq.trans (congr_arg f₁' (Eq.symm (h₂ hy))) (h₁ (h hy))

theorem surj_on.left_inv_on_of_right_inv_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (hf : surj_on f s t) (hf' : right_inv_on f f' s) : left_inv_on f f' t := sorry

/-! ### Two-side inverses -/

/-- `g` is an inverse to `f` viewed as a map from `a` to `b` -/
def inv_on {α : Type u} {β : Type v} (g : β → α) (f : α → β) (s : set α) (t : set β) :=
  left_inv_on g f s ∧ right_inv_on g f t

theorem inv_on.symm {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (h : inv_on f' f s t) : inv_on f f' t s :=
  { left := and.right h, right := and.left h }

theorem inv_on.mono {α : Type u} {β : Type v} {s : set α} {s₁ : set α} {t : set β} {t₁ : set β} {f : α → β} {f' : β → α} (h : inv_on f' f s t) (hs : s₁ ⊆ s) (ht : t₁ ⊆ t) : inv_on f' f s₁ t₁ :=
  { left := left_inv_on.mono (and.left h) hs, right := right_inv_on.mono (and.right h) ht }

/-- If functions `f'` and `f` are inverse on `s` and `t`, `f` maps `s` into `t`, and `f'` maps `t`
into `s`, then `f` is a bijection between `s` and `t`. The `maps_to` arguments can be deduced from
`surj_on` statements using `left_inv_on.maps_to` and `right_inv_on.maps_to`. -/
theorem inv_on.bij_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} {f' : β → α} (h : inv_on f' f s t) (hf : maps_to f s t) (hf' : maps_to f' t s) : bij_on f s t :=
  { left := hf, right := { left := left_inv_on.inj_on (and.left h), right := right_inv_on.surj_on (and.right h) hf' } }

/-! ### `inv_fun_on` is a left/right inverse -/

theorem inj_on.left_inv_on_inv_fun_on {α : Type u} {β : Type v} {s : set α} {f : α → β} [Nonempty α] (h : inj_on f s) : left_inv_on (function.inv_fun_on f s) f s :=
  fun (x : α) (hx : x ∈ s) => function.inv_fun_on_eq' h hx

theorem surj_on.right_inv_on_inv_fun_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} [Nonempty α] (h : surj_on f s t) : right_inv_on (function.inv_fun_on f s) f t :=
  fun (y : β) (hy : y ∈ t) => function.inv_fun_on_eq (iff.mp mem_image_iff_bex (h hy))

theorem bij_on.inv_on_inv_fun_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} [Nonempty α] (h : bij_on f s t) : inv_on (function.inv_fun_on f s) f s t :=
  { left := inj_on.left_inv_on_inv_fun_on (bij_on.inj_on h), right := surj_on.right_inv_on_inv_fun_on (bij_on.surj_on h) }

theorem surj_on.inv_on_inv_fun_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} [Nonempty α] (h : surj_on f s t) : inv_on (function.inv_fun_on f s) f (function.inv_fun_on f s '' t) t := sorry

theorem surj_on.maps_to_inv_fun_on {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} [Nonempty α] (h : surj_on f s t) : maps_to (function.inv_fun_on f s) t s :=
  fun (y : β) (hy : y ∈ t) => iff.mpr mem_preimage (function.inv_fun_on_mem (iff.mp mem_image_iff_bex (h hy)))

theorem surj_on.bij_on_subset {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} [Nonempty α] (h : surj_on f s t) : bij_on f (function.inv_fun_on f s '' t) t := sorry

theorem surj_on_iff_exists_bij_on_subset {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} : surj_on f s t ↔ ∃ (s' : set α), ∃ (H : s' ⊆ s), bij_on f s' t := sorry

theorem preimage_inv_fun_of_mem {α : Type u} {β : Type v} [n : Nonempty α] {f : α → β} (hf : function.injective f) {s : set α} (h : Classical.choice n ∈ s) : function.inv_fun f ⁻¹' s = f '' s ∪ (range fᶜ) := sorry

theorem preimage_inv_fun_of_not_mem {α : Type u} {β : Type v} [n : Nonempty α] {f : α → β} (hf : function.injective f) {s : set α} (h : ¬Classical.choice n ∈ s) : function.inv_fun f ⁻¹' s = f '' s := sorry

end set

/-! ### Piecewise defined function -/

namespace set

@[simp] theorem piecewise_empty {α : Type u} {δ : α → Sort y} (f : (i : α) → δ i) (g : (i : α) → δ i) [(i : α) → Decidable (i ∈ ∅)] : piecewise ∅ f g = g := sorry

@[simp] theorem piecewise_univ {α : Type u} {δ : α → Sort y} (f : (i : α) → δ i) (g : (i : α) → δ i) [(i : α) → Decidable (i ∈ univ)] : piecewise univ f g = f := sorry

@[simp] theorem piecewise_insert_self {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) {j : α} [(i : α) → Decidable (i ∈ insert j s)] : piecewise (insert j s) f g j = f j := sorry

protected instance compl.decidable_mem {α : Type u} (s : set α) [(j : α) → Decidable (j ∈ s)] (j : α) : Decidable (j ∈ (sᶜ)) :=
  not.decidable

theorem piecewise_insert {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] [DecidableEq α] (j : α) [(i : α) → Decidable (i ∈ insert j s)] : piecewise (insert j s) f g = function.update (piecewise s f g) j (f j) := sorry

@[simp] theorem piecewise_eq_of_mem {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] {i : α} (hi : i ∈ s) : piecewise s f g i = f i := sorry

@[simp] theorem piecewise_eq_of_not_mem {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] {i : α} (hi : ¬i ∈ s) : piecewise s f g i = g i := sorry

theorem piecewise_singleton {α : Type u} {β : Type v} (x : α) [(y : α) → Decidable (y ∈ singleton x)] [DecidableEq α] (f : α → β) (g : α → β) : piecewise (singleton x) f g = function.update g x (f x) := sorry

theorem piecewise_eq_on {α : Type u} {β : Type v} (s : set α) [(j : α) → Decidable (j ∈ s)] (f : α → β) (g : α → β) : eq_on (piecewise s f g) f s :=
  fun (_x : α) => piecewise_eq_of_mem s f g

theorem piecewise_eq_on_compl {α : Type u} {β : Type v} (s : set α) [(j : α) → Decidable (j ∈ s)] (f : α → β) (g : α → β) : eq_on (piecewise s f g) g (sᶜ) :=
  fun (_x : α) => piecewise_eq_of_not_mem s f g

@[simp] theorem piecewise_insert_of_ne {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] {i : α} {j : α} (h : i ≠ j) [(i : α) → Decidable (i ∈ insert j s)] : piecewise (insert j s) f g i = piecewise s f g i := sorry

@[simp] theorem piecewise_compl {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) (g : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] [(i : α) → Decidable (i ∈ (sᶜ))] : piecewise (sᶜ) f g = piecewise s g f := sorry

@[simp] theorem piecewise_range_comp {α : Type u} {β : Type v} {ι : Sort u_1} (f : ι → α) [(j : α) → Decidable (j ∈ range f)] (g₁ : α → β) (g₂ : α → β) : piecewise (range f) g₁ g₂ ∘ f = g₁ ∘ f :=
  comp_eq_of_eq_on_range (piecewise_eq_on (range f) g₁ g₂)

theorem piecewise_preimage {α : Type u} {β : Type v} (s : set α) [(j : α) → Decidable (j ∈ s)] (f : α → β) (g : α → β) (t : set β) : piecewise s f g ⁻¹' t = s ∩ f ⁻¹' t ∪ sᶜ ∩ g ⁻¹' t := sorry

theorem comp_piecewise {α : Type u} {β : Type v} {γ : Type w} (s : set α) [(j : α) → Decidable (j ∈ s)] (h : β → γ) {f : α → β} {g : α → β} {x : α} : h (piecewise s f g x) = piecewise s (h ∘ f) (h ∘ g) x := sorry

@[simp] theorem piecewise_same {α : Type u} {δ : α → Sort y} (s : set α) (f : (i : α) → δ i) [(j : α) → Decidable (j ∈ s)] : piecewise s f f = f := sorry

theorem range_piecewise {α : Type u} {β : Type v} (s : set α) [(j : α) → Decidable (j ∈ s)] (f : α → β) (g : α → β) : range (piecewise s f g) = f '' s ∪ g '' (sᶜ) := sorry

theorem piecewise_mem_pi {α : Type u} (s : set α) [(j : α) → Decidable (j ∈ s)] {δ : α → Type u_1} {t : set α} {t' : (i : α) → set (δ i)} {f : (i : α) → δ i} {g : (i : α) → δ i} (hf : f ∈ pi t t') (hg : g ∈ pi t t') : piecewise s f g ∈ pi t t' := sorry

end set

theorem strict_mono_incr_on.inj_on {α : Type u} {β : Type v} [linear_order α] [preorder β] {f : α → β} {s : set α} (H : strict_mono_incr_on f s) : set.inj_on f s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) (hxy : f x = f y) =>
    (fun (this : ordering.compares ordering.eq x y) => this) (iff.mp (strict_mono_incr_on.compares H hx hy) hxy)

theorem strict_mono_decr_on.inj_on {α : Type u} {β : Type v} [linear_order α] [preorder β] {f : α → β} {s : set α} (H : strict_mono_decr_on f s) : set.inj_on f s :=
  strict_mono_incr_on.inj_on H

theorem strict_mono_incr_on.comp {α : Type u} {β : Type v} {γ : Type w} [preorder α] [preorder β] [preorder γ] {g : β → γ} {f : α → β} {s : set α} {t : set β} (hg : strict_mono_incr_on g t) (hf : strict_mono_incr_on f s) (hs : set.maps_to f s t) : strict_mono_incr_on (g ∘ f) s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) (hxy : x < y) => hg (hs hx) (hs hy) (hf hx hy hxy)

theorem strict_mono.comp_strict_mono_incr_on {α : Type u} {β : Type v} {γ : Type w} [preorder α] [preorder β] [preorder γ] {g : β → γ} {f : α → β} {s : set α} (hg : strict_mono g) (hf : strict_mono_incr_on f s) : strict_mono_incr_on (g ∘ f) s :=
  fun (x : α) (hx : x ∈ s) (y : α) (hy : y ∈ s) (hxy : x < y) => hg (hf hx hy hxy)

theorem strict_mono.cod_restrict {α : Type u} {β : Type v} [preorder α] [preorder β] {f : α → β} (hf : strict_mono f) {s : set β} (hs : ∀ (x : α), f x ∈ s) : strict_mono (set.cod_restrict f s hs) :=
  hf

namespace function

theorem injective.comp_inj_on {α : Type u} {β : Type v} {γ : Type w} {f : α → β} {g : β → γ} {s : set α} (hg : injective g) (hf : set.inj_on f s) : set.inj_on (g ∘ f) s :=
  set.inj_on.comp (injective.inj_on hg set.univ) hf (set.maps_to_univ f s)

theorem surjective.surj_on {α : Type u} {β : Type v} {f : α → β} (hf : surjective f) (s : set β) : set.surj_on f set.univ s :=
  set.surj_on.mono (set.subset.refl set.univ) (set.subset_univ s) (iff.mp set.surjective_iff_surj_on_univ hf)

namespace semiconj

theorem maps_to_image {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} {s : set α} {t : set α} (h : semiconj f fa fb) (ha : set.maps_to fa s t) : set.maps_to fb (f '' s) (f '' t) := sorry

theorem maps_to_range {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) : set.maps_to fb (set.range f) (set.range f) := sorry

theorem surj_on_image {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} {s : set α} {t : set α} (h : semiconj f fa fb) (ha : set.surj_on fa s t) : set.surj_on fb (f '' s) (f '' t) := sorry

theorem surj_on_range {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) (ha : surjective fa) : set.surj_on fb (set.range f) (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (set.surj_on fb (set.range f) (set.range f))) (Eq.symm set.image_univ)))
    (surj_on_image h (surjective.surj_on ha set.univ))

theorem inj_on_image {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} {s : set α} (h : semiconj f fa fb) (ha : set.inj_on fa s) (hf : set.inj_on f (fa '' s)) : set.inj_on fb (f '' s) := sorry

theorem inj_on_range {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) (ha : injective fa) (hf : set.inj_on f (set.range fa)) : set.inj_on fb (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (set.inj_on fb (set.range f))) (Eq.symm set.image_univ)))
    (inj_on_image h (injective.inj_on ha set.univ)
      (eq.mp (Eq._oldrec (Eq.refl (set.inj_on f (set.range fa))) (Eq.symm set.image_univ)) hf))

theorem bij_on_image {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} {s : set α} {t : set α} (h : semiconj f fa fb) (ha : set.bij_on fa s t) (hf : set.inj_on f t) : set.bij_on fb (f '' s) (f '' t) := sorry

theorem bij_on_range {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) (ha : bijective fa) (hf : injective f) : set.bij_on fb (set.range f) (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (set.bij_on fb (set.range f) (set.range f))) (Eq.symm set.image_univ)))
    (bij_on_image h (iff.mp set.bijective_iff_bij_on_univ ha) (injective.inj_on hf set.univ))

theorem maps_to_preimage {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) {s : set β} {t : set β} (hb : set.maps_to fb s t) : set.maps_to fa (f ⁻¹' s) (f ⁻¹' t) := sorry

theorem inj_on_preimage {α : Type u} {β : Type v} {fa : α → α} {fb : β → β} {f : α → β} (h : semiconj f fa fb) {s : set β} (hb : set.inj_on fb s) (hf : set.inj_on f (f ⁻¹' s)) : set.inj_on fa (f ⁻¹' s) := sorry

end semiconj

theorem update_comp_eq_of_not_mem_range' {α : Sort u_1} {β : Type u_2} {γ : β → Sort u_3} [DecidableEq β] (g : (b : β) → γ b) {f : α → β} {i : β} (a : γ i) (h : ¬i ∈ set.range f) : (fun (j : α) => update g i a (f j)) = fun (j : α) => g (f j) :=
  update_comp_eq_of_forall_ne' g a fun (x : α) (hx : f x = i) => h (Exists.intro x hx)

/-- Non-dependent version of `function.update_comp_eq_of_not_mem_range'` -/
theorem update_comp_eq_of_not_mem_range {α : Sort u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq β] (g : β → γ) {f : α → β} {i : β} (a : γ) (h : ¬i ∈ set.range f) : update g i a ∘ f = g ∘ f :=
  update_comp_eq_of_not_mem_range' g a h


/-
Copyright (c) 2014 Floris van Doorn (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro

! This file was ported from Lean 3 source module data.nat.basic
! leanprover-community/mathlib commit bd835ef554f37ef9b804f0903089211f89cb370b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.Basic
import Mathlib.Algebra.GroupWithZero.Basic
import Mathlib.Algebra.Ring.Defs
import Mathlib.Tactic.Convert
import Mathlib.Tactic.PushNeg
import Mathlib.Tactic.Use

/-!
# Basic operations on the natural numbers

This file contains:
- instances on the natural numbers
- some basic lemmas about natural numbers
- extra recursors:
  * `le_rec_on`, `le_induction`: recursion and induction principles starting at non-zero numbers
  * `decreasing_induction`: recursion growing downwards
  * `le_rec_on'`, `decreasing_induction'`: versions with slightly weaker assumptions
  * `strong_rec'`: recursion based on strong inequalities
- decidability instances on predicates about the natural numbers

Many theorems that used to live in this file have been moved to `Data.Nat.Order`,
so that this file requires fewer imports.
For each section here there is a corresponding section in that file with additional results.
It may be possible to move some of these results here, by tweaking their proofs.

-/

universe u v

namespace Nat

/-! ### instances -/

instance nontrivial : Nontrivial ℕ :=
  ⟨⟨0, 1, Nat.zero_ne_one⟩⟩

instance commSemiring : CommSemiring ℕ where
  add := Nat.add
  add_assoc := Nat.add_assoc
  zero := Nat.zero
  zero_add := Nat.zero_add
  add_zero := Nat.add_zero
  add_comm := Nat.add_comm
  mul := Nat.mul
  mul_assoc := Nat.mul_assoc
  one := Nat.succ Nat.zero
  one_mul := Nat.one_mul
  mul_one := Nat.mul_one
  left_distrib := Nat.left_distrib
  right_distrib := Nat.right_distrib
  zero_mul := Nat.zero_mul
  mul_zero := Nat.mul_zero
  mul_comm := Nat.mul_comm
  natCast n := n
  natCast_zero := rfl
  natCast_succ n := rfl
  nsmul m n := m * n
  nsmul_zero := Nat.zero_mul
  nsmul_succ _ _ := by dsimp only; rw [Nat.add_comm, Nat.right_distrib, Nat.one_mul]
  npow m n := n ^ m
  npow_zero := Nat.pow_zero
  npow_succ _ _ := Nat.pow_succ'

/-! Extra instances to short-circuit type class resolution and ensure computability -/

instance addCommMonoid : AddCommMonoid ℕ :=
  inferInstance

instance addMonoid : AddMonoid ℕ :=
  inferInstance

instance monoid : Monoid ℕ :=
  inferInstance

instance commMonoid : CommMonoid ℕ :=
  inferInstance

instance commSemigroup : CommSemigroup ℕ :=
  inferInstance

instance semigroup : Semigroup ℕ :=
  inferInstance

instance addCommSemigroup : AddCommSemigroup ℕ :=
  inferInstance

instance addSemigroup : AddSemigroup ℕ :=
  inferInstance

instance distrib : Distrib ℕ :=
  inferInstance

instance semiring : Semiring ℕ :=
  inferInstance

protected theorem nsmul_eq_mul (m n : ℕ) : m • n = m * n :=
  rfl
#align nat.nsmul_eq_mul Nat.nsmul_eq_mul

-- Moved to core
#align nat.eq_of_mul_eq_mul_right Nat.eq_of_mul_eq_mul_right

instance cancelCommMonoidWithZero : CancelCommMonoidWithZero ℕ :=
  { (inferInstance : CommMonoidWithZero ℕ) with
    mul_left_cancel_of_ne_zero :=
      fun h1 h2 => Nat.eq_of_mul_eq_mul_left (Nat.pos_of_ne_zero h1) h2 }
#align nat.cancel_comm_monoid_with_zero Nat.cancelCommMonoidWithZero

attribute [simp]
  Nat.not_lt_zero Nat.succ_ne_zero Nat.succ_ne_self Nat.zero_ne_one Nat.one_ne_zero
  -- Nat.zero_ne_bit1 Nat.bit1_ne_zero Nat.bit0_ne_one Nat.one_ne_bit0 Nat.bit0_ne_bit1
  -- Nat.bit1_ne_bit0

variable {m n k : ℕ}

/-!
### Recursion and `forall`/`exists`
-/

-- Porting note:
-- this doesn't work as a simp lemma in Lean 4
-- @[simp]
theorem and_forall_succ {p : ℕ → Prop} : (p 0 ∧ ∀ n, p (n + 1)) ↔ ∀ n, p n :=
  ⟨fun h n => Nat.casesOn n h.1 h.2, fun h => ⟨h _, fun _ => h _⟩⟩
#align nat.and_forall_succ Nat.and_forall_succ

-- Porting note:
-- this doesn't work as a simp lemma in Lean 4
-- @[simp]
theorem or_exists_succ {p : ℕ → Prop} : (p 0 ∨ ∃ n, p (n + 1)) ↔ ∃ n, p n :=
  ⟨fun h => h.elim (fun h0 => ⟨0, h0⟩) fun ⟨n, hn⟩ => ⟨n + 1, hn⟩, by
    rintro ⟨_ | n, hn⟩
    exacts[Or.inl hn, Or.inr ⟨n, hn⟩]⟩
#align nat.or_exists_succ Nat.or_exists_succ

/-! ### `succ` -/

theorem _root_.LT.lt.nat_succ_le {n m : ℕ} (h : n < m) : succ n ≤ m :=
  succ_le_of_lt h
#align has_lt.lt.nat_succ_le LT.lt.nat_succ_le

-- Moved to Std
#align nat.succ_eq_one_add Nat.succ_eq_one_add

theorem eq_of_lt_succ_of_not_lt {a b : ℕ} (h1 : a < b + 1) (h2 : ¬a < b) : a = b :=
  have h3 : a ≤ b := le_of_lt_succ h1
  Or.elim (eq_or_lt_of_not_lt h2) (fun h => h) fun h => absurd h (not_lt_of_ge h3)
#align nat.eq_of_lt_succ_of_not_lt Nat.eq_of_lt_succ_of_not_lt

theorem eq_of_le_of_lt_succ {n m : ℕ} (h₁ : n ≤ m) (h₂ : m < n + 1) : m = n :=
  Nat.le_antisymm (le_of_succ_le_succ h₂) h₁
#align nat.eq_of_le_of_lt_succ Nat.eq_of_le_of_lt_succ

-- Moved to Std
#align nat.one_add Nat.one_add

@[simp]
theorem succ_pos' {n : ℕ} : 0 < succ n :=
  succ_pos n
#align nat.succ_pos' Nat.succ_pos'

-- Moved to Std
#align nat.succ_inj' Nat.succ_inj'

theorem succ_injective : Function.Injective Nat.succ := fun _ _ => succ.inj
#align nat.succ_injective Nat.succ_injective

theorem succ_ne_succ {n m : ℕ} : succ n ≠ succ m ↔ n ≠ m :=
  succ_injective.ne_iff
#align nat.succ_ne_succ Nat.succ_ne_succ

-- Porting note: no longer a simp lemma, as simp can prove this
theorem succ_succ_ne_one (n : ℕ) : n.succ.succ ≠ 1 :=
  succ_ne_succ.mpr n.succ_ne_zero
#align nat.succ_succ_ne_one Nat.succ_succ_ne_one

@[simp]
theorem one_lt_succ_succ (n : ℕ) : 1 < n.succ.succ :=
  succ_lt_succ <| succ_pos n
#align nat.one_lt_succ_succ Nat.one_lt_succ_succ

-- Porting note: Nat.succ_le_succ_iff is in Std

theorem max_succ_succ {m n : ℕ} : max (succ m) (succ n) = succ (max m n) := by
  by_cases h1 : m ≤ n
  rw [max_eq_right h1, max_eq_right (succ_le_succ h1)]
  · rw [not_le] at h1
    have h2 := le_of_lt h1
    rw [max_eq_left h2, max_eq_left (succ_le_succ h2)]

#align nat.max_succ_succ Nat.max_succ_succ

theorem not_succ_lt_self {n : ℕ} : ¬succ n < n :=
  not_lt_of_ge (Nat.le_succ _)
#align nat.not_succ_lt_self Nat.not_succ_lt_self

theorem lt_succ_iff {m n : ℕ} : m < succ n ↔ m ≤ n :=
  ⟨le_of_lt_succ, lt_succ_of_le⟩
#align nat.lt_succ_iff Nat.lt_succ_iff

theorem succ_le_iff {m n : ℕ} : succ m ≤ n ↔ m < n :=
  ⟨lt_of_succ_le, succ_le_of_lt⟩
#align nat.succ_le_iff Nat.succ_le_iff

theorem lt_iff_add_one_le {m n : ℕ} : m < n ↔ m + 1 ≤ n := by rw [succ_le_iff]
#align nat.lt_iff_add_one_le Nat.lt_iff_add_one_le

-- Just a restatement of `Nat.lt_succ_iff` using `+1`.
theorem lt_add_one_iff {a b : ℕ} : a < b + 1 ↔ a ≤ b :=
  lt_succ_iff
#align nat.lt_add_one_iff Nat.lt_add_one_iff

-- A flipped version of `lt_add_one_iff`.
theorem lt_one_add_iff {a b : ℕ} : a < 1 + b ↔ a ≤ b := by simp only [add_comm, lt_succ_iff]
#align nat.lt_one_add_iff Nat.lt_one_add_iff

-- This is true reflexively, by the definition of `≤` on ℕ,
-- but it's still useful to have, to convince Lean to change the syntactic type.
theorem add_one_le_iff {a b : ℕ} : a + 1 ≤ b ↔ a < b :=
  Iff.refl _
#align nat.add_one_le_iff Nat.add_one_le_iff

theorem one_add_le_iff {a b : ℕ} : 1 + a ≤ b ↔ a < b := by simp only [add_comm, add_one_le_iff]
#align nat.one_add_le_iff Nat.one_add_le_iff

theorem of_le_succ {n m : ℕ} (H : n ≤ m.succ) : n ≤ m ∨ n = m.succ :=
  H.lt_or_eq_dec.imp le_of_lt_succ id
#align nat.of_le_succ Nat.of_le_succ

theorem succ_lt_succ_iff {m n : ℕ} : succ m < succ n ↔ m < n :=
  ⟨lt_of_succ_lt_succ, succ_lt_succ⟩
#align nat.succ_lt_succ_iff Nat.succ_lt_succ_iff

theorem div_le_iff_le_mul_add_pred {m n k : ℕ} (n0 : 0 < n) : m / n ≤ k ↔ m ≤ n * k + (n - 1) := by
  rw [← lt_succ_iff, div_lt_iff_lt_mul n0, succ_mul, mul_comm]
  cases n
  · cases n0

  exact lt_succ_iff
#align nat.div_le_iff_le_mul_add_pred Nat.div_le_iff_le_mul_add_pred

theorem two_lt_of_ne : ∀ {n}, n ≠ 0 → n ≠ 1 → n ≠ 2 → 2 < n
  | 0, h, _, _ => (h rfl).elim
  | 1, _, h, _ => (h rfl).elim
  | 2, _, _, h => (h rfl).elim
  -- Porting note: was `by decide`
  | n + 3, _, _, _ => by rw [Nat.lt_iff_add_one_le]; convert Nat.le_add_left 3 n
#align nat.two_lt_of_ne Nat.two_lt_of_ne

theorem forall_lt_succ {P : ℕ → Prop} {n : ℕ} : (∀ m < n + 1, P m) ↔ (∀ m < n, P m) ∧ P n := by
  simp only [lt_succ_iff, Decidable.le_iff_eq_or_lt, forall_eq_or_imp, and_comm]
#align nat.forall_lt_succ Nat.forall_lt_succ

theorem exists_lt_succ {P : ℕ → Prop} {n : ℕ} : (∃ m < n + 1, P m) ↔ (∃ m < n, P m) ∨ P n := by
  rw [← not_iff_not]
  push_neg
  exact forall_lt_succ
#align nat.exists_lt_succ Nat.exists_lt_succ

/-! ### `add` -/

-- Sometimes a bare `Nat.add` or similar appears as a consequence of unfolding
-- during pattern matching. These lemmas package them back up as typeclass
-- mediated operations.
@[simp]
theorem add_def {a b : ℕ} : Nat.add a b = a + b :=
  rfl
#align nat.add_def Nat.add_def

@[simp]
theorem mul_def {a b : ℕ} : Nat.mul a b = a * b :=
  rfl
#align nat.mul_def Nat.mul_def

theorem exists_eq_add_of_le (h : m ≤ n) : ∃ k : ℕ, n = m + k :=
  ⟨n - m, (add_sub_of_le h).symm⟩
#align nat.exists_eq_add_of_le Nat.exists_eq_add_of_le

theorem exists_eq_add_of_le' (h : m ≤ n) : ∃ k : ℕ, n = k + m :=
  ⟨n - m, (Nat.sub_add_cancel h).symm⟩
#align nat.exists_eq_add_of_le' Nat.exists_eq_add_of_le'

theorem exists_eq_add_of_lt (h : m < n) : ∃ k : ℕ, n = m + k + 1 :=
  ⟨n - (m + 1), by rw [add_right_comm, add_sub_of_le h]⟩
#align nat.exists_eq_add_of_lt Nat.exists_eq_add_of_lt

/-! ### `pred` -/

@[simp]
theorem add_succ_sub_one (n m : ℕ) : n + succ m - 1 = n + m := by rw [add_succ, succ_sub_one]
#align nat.add_succ_sub_one Nat.add_succ_sub_one

@[simp]
theorem succ_add_sub_one (n m : ℕ) : succ n + m - 1 = n + m := by rw [succ_add, succ_sub_one]
#align nat.succ_add_sub_one Nat.succ_add_sub_one

theorem pred_eq_sub_one (n : ℕ) : pred n = n - 1 :=
  rfl
#align nat.pred_eq_sub_one Nat.pred_eq_sub_one

theorem pred_eq_of_eq_succ {m n : ℕ} (H : m = n.succ) : m.pred = n := by simp [H]
#align nat.pred_eq_of_eq_succ Nat.pred_eq_of_eq_succ

@[simp]
theorem pred_eq_succ_iff {n m : ℕ} : pred n = succ m ↔ n = m + 2 := by
  cases n <;> constructor <;> rintro ⟨⟩ <;> rfl
#align nat.pred_eq_succ_iff Nat.pred_eq_succ_iff

theorem pred_sub (n m : ℕ) : pred n - m = pred (n - m) := by
  rw [← Nat.sub_one, Nat.sub_sub, one_add, sub_succ]
#align nat.pred_sub Nat.pred_sub

-- Moved to Std
#align nat.le_pred_of_lt Nat.le_pred_of_lt

theorem le_of_pred_lt {m n : ℕ} : pred m < n → m ≤ n :=
  match m with
  | 0 => le_of_lt
  | _ + 1 => id
#align nat.le_of_pred_lt Nat.le_of_pred_lt

/-- This ensures that `simp` succeeds on `pred (n + 1) = n`. -/
@[simp]
theorem pred_one_add (n : ℕ) : pred (1 + n) = n := by rw [add_comm, add_one, Nat.pred_succ]
#align nat.pred_one_add Nat.pred_one_add

/-! ### `mul` -/

theorem two_mul_ne_two_mul_add_one {n m} : 2 * n ≠ 2 * m + 1 :=
  mt (congr_arg (· % 2))
    (by rw [add_comm, add_mul_mod_self_left, mul_mod_right, mod_eq_of_lt] <;> simp)
#align nat.two_mul_ne_two_mul_add_one Nat.two_mul_ne_two_mul_add_one

theorem mul_ne_mul_right {a b c : ℕ} (ha : 0 < a) : a * b ≠ a * c ↔ b ≠ c :=
  (mul_right_injective₀ ha.ne').ne_iff
#align nat.mul_ne_mul_right Nat.mul_ne_mul_right

theorem mul_right_eq_self_iff {a b : ℕ} (ha : 0 < a) : a * b = a ↔ b = 1 :=
  suffices a * b = a * 1 ↔ b = 1 by rwa [mul_one] at this
  mul_right_inj' ha.ne'
#align nat.mul_right_eq_self_iff Nat.mul_right_eq_self_iff

theorem mul_left_eq_self_iff {a b : ℕ} (hb : 0 < b) : a * b = b ↔ a = 1 := by
  rw [mul_comm, Nat.mul_right_eq_self_iff hb]
#align nat.mul_left_eq_self_iff Nat.mul_left_eq_self_iff

theorem lt_succ_iff_lt_or_eq {n i : ℕ} : n < i.succ ↔ n < i ∨ n = i :=
  lt_succ_iff.trans Decidable.le_iff_lt_or_eq
#align nat.lt_succ_iff_lt_or_eq Nat.lt_succ_iff_lt_or_eq

/-!
### Recursion and induction principles

This section is here due to dependencies -- the lemmas here require some of the lemmas
proved above, and some of the results in later sections depend on the definitions in this section.
-/

@[simp]
theorem rec_zero {C : ℕ → Sort u} (h0 : C 0) (h : ∀ n, C n → C (n + 1)) :
    (Nat.rec h0 h : ∀ n, C n) 0 = h0 :=
  rfl
#align nat.rec_zero Nat.rec_zero

@[simp]
theorem rec_add_one {C : ℕ → Sort u} (h0 : C 0) (h : ∀ n, C n → C (n + 1)) (n : ℕ) :
    (Nat.rec h0 h : ∀ n, C n) (n + 1) = h n ((Nat.rec h0 h : ∀ n, C n) n) :=
  rfl
#align nat.rec_add_one Nat.rec_add_one

/-- Recursion starting at a non-zero number: given a map `C k → C (k+1)` for each `k`,
there is a map from `C n` to each `C m`, `n ≤ m`. For a version where the assumption is only made
when `k ≥ n`, see `leRecOn`. -/
@[elab_as_elim]
def leRecOn {C : ℕ → Sort u} {n : ℕ} : ∀ {m : ℕ}, n ≤ m → (∀ {k}, C k → C (k + 1)) → C n → C m
  | 0, H, _, x => Eq.recOn (Nat.eq_zero_of_le_zero H) x
  | m + 1, H, next, x =>
    Or.by_cases (of_le_succ H) (fun h : n ≤ m => next <| leRecOn h (@next) x)
      fun h : n = m + 1 => Eq.recOn h x
#align nat.le_rec_on Nat.leRecOn

theorem leRecOn_self {C : ℕ → Sort u} {n} {h : n ≤ n} {next : ∀ {k}, C k → C (k + 1)} (x : C n) :
    (leRecOn h next x : C n) = x := by
  cases n <;> unfold leRecOn Eq.recOn
  · simp
  · unfold Or.by_cases
    rw [dif_neg (Nat.not_succ_le_self _)]
#align nat.le_rec_on_self Nat.leRecOn_self

theorem leRecOn_succ {C : ℕ → Sort u} {n m} (h1 : n ≤ m) {h2 : n ≤ m + 1} {next} (x : C n) :
    (leRecOn h2 (@next) x : C (m + 1)) = next (leRecOn h1 (@next) x : C m) := by
  conv =>
    lhs
    rw [leRecOn, Or.by_cases, dif_pos h1]
#align nat.le_rec_on_succ Nat.leRecOn_succ

theorem leRecOn_succ' {C : ℕ → Sort u} {n} {h : n ≤ n + 1} {next : ∀ {k}, C k → C (k + 1)}
    (x : C n) :
    (leRecOn h next x : C (n + 1)) = next x := by rw [leRecOn_succ (le_refl n), leRecOn_self]
#align nat.le_rec_on_succ' Nat.leRecOn_succ'

theorem leRecOn_trans {C : ℕ → Sort u} {n m k} (hnm : n ≤ m) (hmk : m ≤ k) {next} (x : C n) :
    (leRecOn (le_trans hnm hmk) (@next) x : C k) = leRecOn hmk (@next) (leRecOn hnm (@next) x) := by
  induction' hmk with k hmk ih
  · rw [leRecOn_self]

  rw [leRecOn_succ (le_trans hnm hmk), ih, leRecOn_succ]
#align nat.le_rec_on_trans Nat.leRecOn_trans

theorem leRecOn_succ_left {C : ℕ → Sort u} {n m} (h1 : n ≤ m) (h2 : n + 1 ≤ m)
    {next : ∀ {k}, C k → C (k + 1)} (x : C n) :
    (leRecOn h2 next (next x) : C m) = (leRecOn h1 next x : C m) := by
  rw [Subsingleton.elim h1 (le_trans (le_succ n) h2), leRecOn_trans (le_succ n) h2, leRecOn_succ']
#align nat.le_rec_on_succ_left Nat.leRecOn_succ_left

theorem leRecOn_injective {C : ℕ → Sort u} {n m} (hnm : n ≤ m) (next : ∀ {k}, C k → C (k + 1))
    (Hnext : ∀ n, Function.Injective (@next n)) :
    Function.Injective (@leRecOn C n m hnm next) := by
  induction' hnm with m hnm ih
  · intro x y H
    rwa [leRecOn_self, leRecOn_self] at H

  intro x y H
  rw [leRecOn_succ hnm, leRecOn_succ hnm] at H
  exact ih (Hnext _ H)
#align nat.le_rec_on_injective Nat.leRecOn_injective

theorem leRecOn_surjective {C : ℕ → Sort u} {n m} (hnm : n ≤ m) (next : ∀ {k}, C k → C (k + 1))
    (Hnext : ∀ n, Function.Surjective (@next n)) :
    Function.Surjective (@leRecOn C n m hnm next) := by
  induction' hnm with m hnm ih
  · intro x
    use x
    rw [leRecOn_self]

  intro x
  rcases Hnext _ x with ⟨w, rfl⟩
  rcases ih w with ⟨x, rfl⟩
  use x
  rw [leRecOn_succ]
#align nat.le_rec_on_surjective Nat.leRecOn_surjective

/-- Recursion principle based on `<`. -/
@[elab_as_elim]
protected def strongRec' {p : ℕ → Sort u} (H : ∀ n, (∀ m, m < n → p m) → p n) : ∀ n : ℕ, p n
  | n => H n fun m _ => Nat.strongRec' H m
#align nat.strong_rec' Nat.strongRec'

/-- Recursion principle based on `<` applied to some natural number. -/
@[elab_as_elim]
def strongRecOn' {P : ℕ → Sort _} (n : ℕ) (h : ∀ n, (∀ m, m < n → P m) → P n) : P n :=
  Nat.strongRec' h n
#align nat.strong_rec_on' Nat.strongRecOn'

theorem strongRecOn'_beta {P : ℕ → Sort _} {h} {n : ℕ} :
    (strongRecOn' n h : P n) = h n fun m _ => (strongRecOn' m h : P m) := by
  simp only [strongRecOn']
  rw [Nat.strongRec']
#align nat.strong_rec_on_beta' Nat.strongRecOn'_beta

/-- Induction principle starting at a non-zero number. For maps to a `Sort*` see `le_rec_on`.
To use in an induction proof, the syntax is `induction n, hn using Nat.le_induction` (or the same
for `induction'`). -/
@[elab_as_elim]
theorem le_induction {m} {P : ∀ (n : Nat) (_ : m ≤ n), Prop} (base : P m le_rfl)
    (succ : ∀ (n : Nat) (hn : m ≤ n), P n hn → P (n + 1) (hn.trans <| Nat.le_succ _)) :
    ∀ (n : Nat) (hn : m ≤ n), P n hn := by
  apply Nat.le.rec
  · exact base
  · intros n hn
    apply succ n hn
#align nat.le_induction Nat.le_induction

/-- Decreasing induction: if `P (k+1)` implies `P k`, then `P n` implies `P m` for all `m ≤ n`.
Also works for functions to `Sort*`. For a version assuming only the assumption for `k < n`, see
`decreasing_induction'`. -/
@[elab_as_elim]
def decreasingInduction {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n)
    (hP : P n) : P m :=
  leRecOn mn (fun {k} ih hsk => ih <| h k hsk) (fun h => h) hP
#align nat.decreasing_induction Nat.decreasingInduction

@[simp]
theorem decreasingInduction_self {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {n : ℕ} (nn : n ≤ n)
    (hP : P n) :
    (decreasingInduction h nn hP : P n) = hP := by
  dsimp only [decreasingInduction]
  rw [leRecOn_self]
#align nat.decreasing_induction_self Nat.decreasingInduction_self

theorem decreasingInduction_succ {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n)
    (msn : m ≤ n + 1) (hP : P (n + 1)) :
    (decreasingInduction h msn hP : P m) = decreasingInduction h mn (h n hP) := by
  dsimp only [decreasingInduction]
  rw [leRecOn_succ]
#align nat.decreasing_induction_succ Nat.decreasingInduction_succ

@[simp]
theorem decreasingInduction_succ' {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m : ℕ}
    (msm : m ≤ m + 1) (hP : P (m + 1)) : (decreasingInduction h msm hP : P m) = h m hP := by
  dsimp only [decreasingInduction]
  rw [leRecOn_succ']
#align nat.decreasing_induction_succ' Nat.decreasingInduction_succ'

theorem decreasingInduction_trans {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n k : ℕ}
    (mn : m ≤ n) (nk : n ≤ k) (hP : P k) :
    (decreasingInduction h (le_trans mn nk) hP : P m) =
    decreasingInduction h mn (decreasingInduction h nk hP) := by
  induction' nk with k nk ih
  · rw [decreasingInduction_self]
  · rw [decreasingInduction_succ h (le_trans mn nk), ih, decreasingInduction_succ]
#align nat.decreasing_induction_trans Nat.decreasingInduction_trans

theorem decreasingInduction_succ_left {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ}
    (smn : m + 1 ≤ n) (mn : m ≤ n) (hP : P n) :
    (decreasingInduction h mn hP : P m) = h m (decreasingInduction h smn hP) := by
  rw [Subsingleton.elim mn (le_trans (le_succ m) smn), decreasingInduction_trans,
    decreasingInduction_succ']
  apply Nat.le_succ
#align nat.decreasing_induction_succ_left Nat.decreasingInduction_succ_left

/-- Given `P : ℕ → ℕ → Sort*`, if for all `a b : ℕ` we can extend `P` from the rectangle
strictly below `(a,b)` to `P a b`, then we have `P n m` for all `n m : ℕ`.
Note that for non-`Prop` output it is preferable to use the equation compiler directly if possible,
since this produces equation lemmas. -/
def strongSubRecursion {P : ℕ → ℕ → Sort _} (H : ∀ a b, (∀ x y, x < a → y < b → P x y) → P a b) :
    ∀ n m : ℕ, P n m
  | n, m => H n m fun x y _ _ => strongSubRecursion H x y
#align nat.strong_sub_recursion Nat.strongSubRecursion

-- Porting note:
-- we can't put this on the definition itself because of
-- https://github.com/leanprover/lean4/issues/1900
attribute [elab_as_elim] strongSubRecursion

/-- Given `P : ℕ → ℕ → Sort*`, if we have `P i 0` and `P 0 i` for all `i : ℕ`,
and for any `x y : ℕ` we can extend `P` from `(x,y+1)` and `(x+1,y)` to `(x+1,y+1)`
then we have `P n m` for all `n m : ℕ`.
Note that for non-`Prop` output it is preferable to use the equation compiler directly if possible,
since this produces equation lemmas. -/
def pincerRecursion {P : ℕ → ℕ → Sort _} (Ha0 : ∀ a : ℕ, P a 0) (H0b : ∀ b : ℕ, P 0 b)
    (H : ∀ x y : ℕ, P x y.succ → P x.succ y → P x.succ y.succ) : ∀ n m : ℕ, P n m
  | a, 0 => Ha0 a
  | 0, b => H0b b
  | Nat.succ a, Nat.succ b => H _ _ (pincerRecursion Ha0 H0b H _ _) (pincerRecursion Ha0 H0b H _ _)
termination_by pincerRecursion Ha0 Hab H n m => n + m
#align nat.pincer_recursion Nat.pincerRecursion

-- Porting note:
-- we can't put this on the definition itself because of
-- https://github.com/leanprover/lean4/issues/1900
attribute [elab_as_elim] pincerRecursion

/-- Recursion starting at a non-zero number: given a map `C k → C (k+1)` for each `k ≥ n`,
there is a map from `C n` to each `C m`, `n ≤ m`. -/
@[elab_as_elim]
def leRecOn' {C : ℕ → Sort _} {n : ℕ} :
    ∀ {m : ℕ}, n ≤ m → (∀ ⦃k⦄, n ≤ k → C k → C (k + 1)) → C n → C m
  | 0, H, _, x => Eq.recOn (Nat.eq_zero_of_le_zero H) x
  | m + 1, H, next, x =>
    Or.by_cases (of_le_succ H) (fun h : n ≤ m => next h <| leRecOn' h next x)
      fun h : n = m + 1 => Eq.recOn h x
#align nat.le_rec_on' Nat.leRecOn'

/-- Decreasing induction: if `P (k+1)` implies `P k` for all `m ≤ k < n`, then `P n` implies `P m`.
Also works for functions to `Sort*`. Weakens the assumptions of `decreasing_induction`. -/
@[elab_as_elim]
def decreasingInduction' {P : ℕ → Sort _} {m n : ℕ} (h : ∀ k < n, m ≤ k → P (k + 1) → P k)
    (mn : m ≤ n) (hP : P n) :
    P m := by
  revert h hP
  refine' leRecOn' mn _ _
  · intro n mn ih h hP
    apply ih
    · exact fun k hk => h k (Nat.lt.step hk)
    · exact h n (lt_succ_self n) mn hP
  · intro _ hP
    exact hP

#align nat.decreasing_induction' Nat.decreasingInduction'

/-! ### `div` -/

attribute [simp] Nat.div_self

/-- A version of `Nat.div_lt_self` using successors, rather than additional hypotheses. -/
theorem div_lt_self' (n b : ℕ) : (n + 1) / (b + 2) < n + 1 :=
  Nat.div_lt_self (Nat.succ_pos n) (Nat.succ_lt_succ (Nat.succ_pos _))
#align nat.div_lt_self' Nat.div_lt_self'

theorem le_div_iff_mul_le' {x y : ℕ} {k : ℕ} (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y :=
  le_div_iff_mul_le k0
#align nat.le_div_iff_mul_le' Nat.le_div_iff_mul_le'

theorem div_lt_iff_lt_mul' {x y : ℕ} {k : ℕ} (k0 : 0 < k) : x / k < y ↔ x < y * k :=
  lt_iff_lt_of_le_iff_le <| le_div_iff_mul_le' k0
#align nat.div_lt_iff_lt_mul' Nat.div_lt_iff_lt_mul'

theorem one_le_div_iff {a b : ℕ} (hb : 0 < b) : 1 ≤ a / b ↔ b ≤ a := by
  rw [le_div_iff_mul_le hb, one_mul]
#align nat.one_le_div_iff Nat.one_le_div_iff

theorem div_lt_one_iff {a b : ℕ} (hb : 0 < b) : a / b < 1 ↔ a < b :=
  lt_iff_lt_of_le_iff_le <| one_le_div_iff hb
#align nat.div_lt_one_iff Nat.div_lt_one_iff

protected theorem div_le_div_right {n m : ℕ} (h : n ≤ m) {k : ℕ} : n / k ≤ m / k :=
  ((Nat.eq_zero_or_pos k).elim fun k0 => by simp [k0]) fun hk =>
    (le_div_iff_mul_le' hk).2 <| le_trans (Nat.div_mul_le_self _ _) h
#align nat.div_le_div_right Nat.div_le_div_right

theorem lt_of_div_lt_div {m n k : ℕ} : m / k < n / k → m < n :=
  lt_imp_lt_of_le_imp_le fun h => Nat.div_le_div_right h
#align nat.lt_of_div_lt_div Nat.lt_of_div_lt_div

protected theorem div_pos {a b : ℕ} (hba : b ≤ a) (hb : 0 < b) : 0 < a / b :=
  Nat.pos_of_ne_zero fun h =>
    lt_irrefl a
      (calc
        a = a % b := by simpa [h] using (mod_add_div a b).symm
        _ < b := Nat.mod_lt a hb
        _ ≤ a := hba
        )
#align nat.div_pos Nat.div_pos

theorem lt_mul_of_div_lt {a b c : ℕ} (h : a / c < b) (w : 0 < c) : a < b * c :=
  lt_of_not_ge <| not_le_of_gt h ∘ (Nat.le_div_iff_mul_le w).2
#align nat.lt_mul_of_div_lt Nat.lt_mul_of_div_lt

theorem mul_div_le_mul_div_assoc (a b c : ℕ) : a * (b / c) ≤ a * b / c :=
  if hc0 : c = 0 then by simp [hc0]
  else
    (Nat.le_div_iff_mul_le (Nat.pos_of_ne_zero hc0)).2
      (by rw [mul_assoc]; exact Nat.mul_le_mul_left _ (Nat.div_mul_le_self _ _))
#align nat.mul_div_le_mul_div_assoc Nat.mul_div_le_mul_div_assoc

protected theorem eq_mul_of_div_eq_right {a b c : ℕ} (H1 : b ∣ a) (H2 : a / b = c) : a = b * c := by
  rw [← H2, Nat.mul_div_cancel' H1]
#align nat.eq_mul_of_div_eq_right Nat.eq_mul_of_div_eq_right

protected theorem div_eq_iff_eq_mul_right {a b c : ℕ} (H : 0 < b) (H' : b ∣ a) :
    a / b = c ↔ a = b * c :=
  ⟨Nat.eq_mul_of_div_eq_right H', Nat.div_eq_of_eq_mul_right H⟩
#align nat.div_eq_iff_eq_mul_right Nat.div_eq_iff_eq_mul_right

protected theorem div_eq_iff_eq_mul_left {a b c : ℕ} (H : 0 < b) (H' : b ∣ a) :
    a / b = c ↔ a = c * b := by
  rw [mul_comm]
  exact Nat.div_eq_iff_eq_mul_right H H'
#align nat.div_eq_iff_eq_mul_left Nat.div_eq_iff_eq_mul_left

protected theorem eq_mul_of_div_eq_left {a b c : ℕ} (H1 : b ∣ a) (H2 : a / b = c) : a = c * b := by
  rw [mul_comm, Nat.eq_mul_of_div_eq_right H1 H2]
#align nat.eq_mul_of_div_eq_left Nat.eq_mul_of_div_eq_left

protected theorem mul_div_cancel_left' {a b : ℕ} (Hd : a ∣ b) : a * (b / a) = b := by
  rw [mul_comm, Nat.div_mul_cancel Hd]
#align nat.mul_div_cancel_left' Nat.mul_div_cancel_left'

#align nat.mul_div_mul_left Nat.mul_div_mul_left
#align nat.mul_div_mul_right Nat.mul_div_mul_right

theorem lt_div_mul_add {a b : ℕ} (hb : 0 < b) : a < a / b * b + b := by
  rw [← Nat.succ_mul, ← Nat.div_lt_iff_lt_mul hb]
  exact Nat.lt_succ_self _
#align nat.lt_div_mul_add Nat.lt_div_mul_add

@[simp]
protected theorem div_left_inj {a b d : ℕ} (hda : d ∣ a) (hdb : d ∣ b) : a / d = b / d ↔ a = b := by
  refine ⟨fun h => ?_, congr_arg fun n => n / d⟩
  rw [← Nat.mul_div_cancel' hda, ← Nat.mul_div_cancel' hdb, h]
#align nat.div_left_inj Nat.div_left_inj

/-! ### `mod`, `dvd` -/

theorem mod_eq_iff_lt {a b : ℕ} (h : b ≠ 0) : a % b = a ↔ a < b := by
  cases b
  contradiction
  exact ⟨fun h => h.ge.trans_lt (mod_lt _ (succ_pos _)), mod_eq_of_lt⟩
#align nat.mod_eq_iff_lt Nat.mod_eq_iff_lt

@[simp]
theorem mod_succ_eq_iff_lt {a b : ℕ} : a % b.succ = a ↔ a < b.succ :=
  mod_eq_iff_lt (succ_ne_zero _)
#align nat.mod_succ_eq_iff_lt Nat.mod_succ_eq_iff_lt

-- Porting note `Nat.div_add_mod` is now in core.

theorem mod_add_div' (m k : ℕ) : m % k + m / k * k = m := by
  rw [mul_comm]
  exact mod_add_div _ _
#align nat.mod_add_div' Nat.mod_add_div'

theorem div_add_mod' (m k : ℕ) : m / k * k + m % k = m := by
  rw [mul_comm]
  exact div_add_mod _ _
#align nat.div_add_mod' Nat.div_add_mod'

/-- See also `Nat.divModEquiv` for a similar statement as an `Equiv`. -/
protected theorem div_mod_unique {n k m d : ℕ} (h : 0 < k) :
    n / k = d ∧ n % k = m ↔ m + k * d = n ∧ m < k :=
  ⟨fun ⟨e₁, e₂⟩ => e₁ ▸ e₂ ▸ ⟨mod_add_div _ _, mod_lt _ h⟩, fun ⟨h₁, h₂⟩ =>
    h₁ ▸ by
      rw [add_mul_div_left _ _ h, add_mul_mod_self_left]
      simp [div_eq_of_lt, mod_eq_of_lt, h₂]⟩
#align nat.div_mod_unique Nat.div_mod_unique

protected theorem dvd_add_left {k m n : ℕ} (h : k ∣ n) : k ∣ m + n ↔ k ∣ m :=
  (Nat.dvd_add_iff_left h).symm
#align nat.dvd_add_left Nat.dvd_add_left

protected theorem dvd_add_right {k m n : ℕ} (h : k ∣ m) : k ∣ m + n ↔ k ∣ n :=
  (Nat.dvd_add_iff_right h).symm
#align nat.dvd_add_right Nat.dvd_add_right

protected theorem mul_dvd_mul_iff_left {a b c : ℕ} (ha : 0 < a) : a * b ∣ a * c ↔ b ∣ c :=
  exists_congr fun d => by rw [mul_assoc, mul_right_inj' ha.ne']
#align nat.mul_dvd_mul_iff_left Nat.mul_dvd_mul_iff_left

protected theorem mul_dvd_mul_iff_right {a b c : ℕ} (hc : 0 < c) : a * c ∣ b * c ↔ a ∣ b :=
  exists_congr fun d => by rw [mul_right_comm, mul_left_inj' hc.ne']
#align nat.mul_dvd_mul_iff_right Nat.mul_dvd_mul_iff_right

@[simp]
theorem mod_mod_of_dvd (n : Nat) {m k : Nat} (h : m ∣ k) : n % k % m = n % m := by
  conv =>
  rhs
  rw [← mod_add_div n k]
  rcases h with ⟨t, rfl⟩
  rw [mul_assoc, add_mul_mod_self_left]
#align nat.mod_mod_of_dvd Nat.mod_mod_of_dvd

-- Moved to Std
#align nat.mod_mod Nat.mod_mod
#align nat.mod_add_mod Nat.mod_add_mod
#align nat.add_mod_mod Nat.add_mod_mod
#align nat.add_mod Nat.add_mod

theorem add_mod_eq_add_mod_right {m n k : ℕ} (i : ℕ) (H : m % n = k % n) :
    (m + i) % n = (k + i) % n := by
  rw [← mod_add_mod, ← mod_add_mod k, H]
#align nat.add_mod_eq_add_mod_right Nat.add_mod_eq_add_mod_right

theorem add_mod_eq_add_mod_left {m n k : ℕ} (i : ℕ) (H : m % n = k % n) :
    (i + m) % n = (i + k) % n := by
  rw [add_comm, add_mod_eq_add_mod_right _ H, add_comm]
#align nat.add_mod_eq_add_mod_left Nat.add_mod_eq_add_mod_left

-- Moved to Std
#align nat.mul_mod Nat.mul_mod

theorem mul_dvd_of_dvd_div {a b c : ℕ} (hab : c ∣ b) (h : a ∣ b / c) : c * a ∣ b :=
  have h1 : ∃ d, b / c = a * d := h
  let ⟨d, hd⟩ := h1
  have h3 : b = a * d * c := Nat.eq_mul_of_div_eq_left hab hd
  -- Porting note: was `cc`
  show ∃ d, b = c * a * d from ⟨d, by rwa [mul_comm, ←mul_assoc] at h3⟩
#align nat.mul_dvd_of_dvd_div Nat.mul_dvd_of_dvd_div

theorem eq_of_dvd_of_div_eq_one {a b : ℕ} (w : a ∣ b) (h : b / a = 1) : a = b := by
  rw [← Nat.div_mul_cancel w, h, one_mul]
#align nat.eq_of_dvd_of_div_eq_one Nat.eq_of_dvd_of_div_eq_one

theorem eq_zero_of_dvd_of_div_eq_zero {a b : ℕ} (w : a ∣ b) (h : b / a = 0) : b = 0 := by
  rw [← Nat.div_mul_cancel w, h, zero_mul]
#align nat.eq_zero_of_dvd_of_div_eq_zero Nat.eq_zero_of_dvd_of_div_eq_zero

theorem div_le_div_left {a b c : ℕ} (h₁ : c ≤ b) (h₂ : 0 < c) : a / b ≤ a / c :=
  (Nat.le_div_iff_mul_le h₂).2 <| le_trans (Nat.mul_le_mul_left _ h₁) (div_mul_le_self _ _)
#align nat.div_le_div_left Nat.div_le_div_left

theorem lt_iff_le_pred : ∀ {m n : ℕ}, 0 < n → (m < n ↔ m ≤ n - 1)
  | _, _ + 1, _ => lt_succ_iff
#align nat.lt_iff_le_pred Nat.lt_iff_le_pred

-- Moved to Std
#align nat.mul_div_le Nat.mul_div_le

theorem lt_mul_div_succ (m : ℕ) {n : ℕ} (n0 : 0 < n) : m < n * (m / n + 1) := by
  rw [mul_comm, ← Nat.div_lt_iff_lt_mul' n0]
  exact lt_succ_self _
#align nat.lt_mul_div_succ Nat.lt_mul_div_succ

theorem mul_add_mod (a b c : ℕ) : (a * b + c) % b = c % b := by simp [Nat.add_mod]
#align nat.mul_add_mod Nat.mul_add_mod

theorem mul_add_mod_of_lt {a b c : ℕ} (h : c < b) : (a * b + c) % b = c := by
  rw [Nat.mul_add_mod, Nat.mod_eq_of_lt h]
#align nat.mul_add_mod_of_lt Nat.mul_add_mod_of_lt

theorem pred_eq_self_iff {n : ℕ} : n.pred = n ↔ n = 0 := by
  cases n <;> simp [(Nat.succ_ne_self _).symm]
#align nat.pred_eq_self_iff Nat.pred_eq_self_iff

/-! ### `find` -/

section Find

variable {p q : ℕ → Prop} [DecidablePred p] [DecidablePred q]

theorem find_eq_iff (h : ∃ n : ℕ, p n) : Nat.find h = m ↔ p m ∧ ∀ n < m, ¬p n := by
  constructor
  · rintro rfl
    exact ⟨Nat.find_spec h, fun _ => Nat.find_min h⟩

  · rintro ⟨hm, hlt⟩
    exact le_antisymm (Nat.find_min' h hm) (not_lt.1 <| imp_not_comm.1 (hlt _) <| Nat.find_spec h)

#align nat.find_eq_iff Nat.find_eq_iff

@[simp]
theorem find_lt_iff (h : ∃ n : ℕ, p n) (n : ℕ) : Nat.find h < n ↔ ∃ m < n, p m :=
  ⟨fun h2 => ⟨Nat.find h, h2, Nat.find_spec h⟩,
   fun ⟨_, hmn, hm⟩ => (Nat.find_min' h hm).trans_lt hmn⟩
#align nat.find_lt_iff Nat.find_lt_iff

@[simp]
theorem find_le_iff (h : ∃ n : ℕ, p n) (n : ℕ) : Nat.find h ≤ n ↔ ∃ m ≤ n, p m := by
  simp only [exists_prop, ← lt_succ_iff, find_lt_iff]
#align nat.find_le_iff Nat.find_le_iff

@[simp]
theorem le_find_iff (h : ∃ n : ℕ, p n) (n : ℕ) : n ≤ Nat.find h ↔ ∀ m < n, ¬p m := by
  simp_rw [← not_lt, find_lt_iff, not_exists, not_and]
#align nat.le_find_iff Nat.le_find_iff

@[simp]
theorem lt_find_iff (h : ∃ n : ℕ, p n) (n : ℕ) : n < Nat.find h ↔ ∀ m ≤ n, ¬p m := by
  simp only [← succ_le_iff, le_find_iff, succ_le_succ_iff]
#align nat.lt_find_iff Nat.lt_find_iff

@[simp]
theorem find_eq_zero (h : ∃ n : ℕ, p n) : Nat.find h = 0 ↔ p 0 := by simp [find_eq_iff]
#align nat.find_eq_zero Nat.find_eq_zero

theorem find_mono (h : ∀ n, q n → p n) {hp : ∃ n, p n} {hq : ∃ n, q n} :
    Nat.find hp ≤ Nat.find hq :=
  Nat.find_min' _ (h _ (Nat.find_spec hq))
#align nat.find_mono Nat.find_mono

theorem find_le {h : ∃ n, p n} (hn : p n) : Nat.find h ≤ n :=
  (Nat.find_le_iff _ _).2 ⟨n, le_rfl, hn⟩
#align nat.find_le Nat.find_le

theorem find_comp_succ (h₁ : ∃ n, p n) (h₂ : ∃ n, p (n + 1)) (h0 : ¬p 0) :
    Nat.find h₁ = Nat.find h₂ + 1 := by
  refine' (find_eq_iff _).2 ⟨Nat.find_spec h₂, fun n hn => _⟩
  cases' n with n
  exacts[h0, @Nat.find_min (fun n => p (n + 1)) _ h₂ _ (succ_lt_succ_iff.1 hn)]
#align nat.find_comp_succ Nat.find_comp_succ

end Find

/-! ### `find_greatest` -/

section FindGreatest

/-- `find_greatest P b` is the largest `i ≤ bound` such that `P i` holds, or `0` if no such `i`
exists -/
protected def findGreatest (P : ℕ → Prop) [DecidablePred P] : ℕ → ℕ
  | 0 => 0
  | n + 1 => if P (n + 1) then n + 1 else Nat.findGreatest P n
#align nat.find_greatest Nat.findGreatest

variable {P Q : ℕ → Prop} [DecidablePred P] {b : ℕ}

@[simp]
theorem findGreatest_zero : Nat.findGreatest P 0 = 0 :=
  rfl
#align nat.find_greatest_zero Nat.findGreatest_zero

theorem findGreatest_succ (n : ℕ) :
    Nat.findGreatest P (n + 1) = if P (n + 1) then n + 1 else Nat.findGreatest P n :=
  rfl
#align nat.find_greatest_succ Nat.findGreatest_succ

@[simp]
theorem findGreatest_eq : ∀ {b}, P b → Nat.findGreatest P b = b
  | 0, _ => rfl
  | n + 1, h => by simp [Nat.findGreatest, h]
#align nat.find_greatest_eq Nat.findGreatest_eq

@[simp]
theorem findGreatest_of_not (h : ¬P (b + 1)) :
    Nat.findGreatest P (b + 1) = Nat.findGreatest P b := by
  simp [Nat.findGreatest, h]
#align nat.find_greatest_of_not Nat.findGreatest_of_not

end FindGreatest

/-! ### decidability of predicates -/

instance decidableBallLT :
    ∀ (n : Nat) (P : ∀ k < n, Prop) [∀ n h, Decidable (P n h)], Decidable (∀ n h, P n h)
| 0, P, _ => isTrue fun n h => by cases h
| (n+1), P, H => by
  cases' decidableBallLT n fun k h => P k (lt_succ_of_lt h) with h h
  · refine' isFalse (mt _ h)
    intro hn k h
    apply hn
  by_cases p : P n (lt_succ_self n)
  · exact
      isTrue fun k h' =>
        (le_of_lt_succ h').lt_or_eq_dec.elim (h _) fun e =>
          match k, e, h' with
          | _, rfl, _ => p
  · exact isFalse (mt (fun hn => hn _ _) p)

#align nat.decidable_ball_lt Nat.decidableBallLT

instance decidableForallFin {n : ℕ} (P : Fin n → Prop) [DecidablePred P] :
    Decidable (∀ i, P i) :=
  decidable_of_iff (∀ k h, P ⟨k, h⟩) ⟨fun a ⟨k, h⟩ => a k h, fun a k h => a ⟨k, h⟩⟩
#align nat.decidable_forall_fin Nat.decidableForallFin

instance decidableBallLe (n : ℕ) (P : ∀ k ≤ n, Prop) [∀ n h, Decidable (P n h)] :
    Decidable (∀ n h, P n h) :=
  decidable_of_iff (∀ (k) (h : k < succ n), P k (le_of_lt_succ h))
    ⟨fun a k h => a k (lt_succ_of_le h), fun a k _ => a k _⟩
#align nat.decidable_ball_le Nat.decidableBallLe

instance decidableExistsLT {P : ℕ → Prop} [h : DecidablePred P] :
    DecidablePred fun n => ∃ m : ℕ, m < n ∧ P m
  | 0 => isFalse (by simp)
  | n + 1 =>
    @decidable_of_decidable_of_iff _ _ (@instDecidableOr _ _ (decidableExistsLT n) (h n))
      (by simp only [lt_succ_iff_lt_or_eq, or_and_right, exists_or, exists_eq_left]; apply Iff.refl)
#align nat.decidable_exists_lt Nat.decidableExistsLT

instance decidableExistsLe {P : ℕ → Prop} [DecidablePred P] :
    DecidablePred fun n => ∃ m : ℕ, m ≤ n ∧ P m :=
  fun n => decidable_of_iff (∃ m, m < n + 1 ∧ P m)
    (exists_congr fun _ => and_congr_left' lt_succ_iff)
#align nat.decidable_exists_le Nat.decidableExistsLe

end Nat


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import analysis.specific_limits.basic
import order.filter.countable_Inter
import topology.G_delta

/-!
# Baire theorem

In a complete metric space, a countable intersection of dense open subsets is dense.

The good concept underlying the theorem is that of a Gδ set, i.e., a countable intersection
of open sets. Then Baire theorem can also be formulated as the fact that a countable
intersection of dense Gδ sets is a dense Gδ set. We prove Baire theorem, giving several different
formulations that can be handy. We also prove the important consequence that, if the space is
covered by a countable union of closed sets, then the union of their interiors is dense.

The names of the theorems do not contain the string "Baire", but are instead built from the form of
the statement. "Baire" is however in the docstring of all the theorems, to facilitate grep searches.

We also define the filter `residual α` generated by dense `Gδ` sets and prove that this filter
has the countable intersection property.
-/

noncomputable theory
open_locale classical topological_space filter ennreal

open filter encodable set

variables {α : Type*} {β : Type*} {γ : Type*} {ι : Type*}

section Baire_theorem
open emetric ennreal
variables [pseudo_emetric_space α] [complete_space α]

/-- Baire theorem: a countable intersection of dense open sets is dense. Formulated here when
the source space is ℕ (and subsumed below by `dense_Inter_of_open` working with any
encodable source space). -/
theorem dense_Inter_of_open_nat {f : ℕ → set α} (ho : ∀n, is_open (f n))
  (hd : ∀n, dense (f n)) : dense (⋂n, f n) :=
begin
  let B : ℕ → ℝ≥0∞ := λn, 1/2^n,
  have Bpos : ∀n, 0 < B n,
  { intro n,
    simp only [B, one_div, one_mul, ennreal.inv_pos],
    exact pow_ne_top two_ne_top },
  /- Translate the density assumption into two functions `center` and `radius` associating
  to any n, x, δ, δpos a center and a positive radius such that
  `closed_ball center radius` is included both in `f n` and in `closed_ball x δ`.
  We can also require `radius ≤ (1/2)^(n+1)`, to ensure we get a Cauchy sequence later. -/
  have : ∀n x δ, δ ≠ 0 → ∃y r, 0 < r ∧ r ≤ B (n+1) ∧ closed_ball y r ⊆ (closed_ball x δ) ∩ f n,
  { assume n x δ δpos,
    have : x ∈ closure (f n) := hd n x,
    rcases emetric.mem_closure_iff.1 this (δ/2) (ennreal.half_pos δpos) with ⟨y, ys, xy⟩,
    rw edist_comm at xy,
    obtain ⟨r, rpos, hr⟩ : ∃ r > 0, closed_ball y r ⊆ f n :=
      nhds_basis_closed_eball.mem_iff.1 (is_open_iff_mem_nhds.1 (ho n) y ys),
    refine ⟨y, min (min (δ/2) r) (B (n+1)), _, _, λz hz, ⟨_, _⟩⟩,
    show 0 < min (min (δ / 2) r) (B (n+1)),
      from lt_min (lt_min (ennreal.half_pos δpos) rpos) (Bpos (n+1)),
    show min (min (δ / 2) r) (B (n+1)) ≤ B (n+1), from min_le_right _ _,
    show z ∈ closed_ball x δ, from calc
      edist z x ≤ edist z y + edist y x : edist_triangle _ _ _
      ... ≤ (min (min (δ / 2) r) (B (n+1))) + (δ/2) : add_le_add hz (le_of_lt xy)
      ... ≤ δ/2 + δ/2 : add_le_add (le_trans (min_le_left _ _) (min_le_left _ _)) le_rfl
      ... = δ : ennreal.add_halves δ,
    show z ∈ f n, from hr (calc
      edist z y ≤ min (min (δ / 2) r) (B (n+1)) : hz
      ... ≤ r : le_trans (min_le_left _ _) (min_le_right _ _)) },
  choose! center radius Hpos HB Hball using this,
  refine λ x, (mem_closure_iff_nhds_basis nhds_basis_closed_eball).2 (λ ε εpos, _),
  /- `ε` is positive. We have to find a point in the ball of radius `ε` around `x` belonging to all
  `f n`. For this, we construct inductively a sequence `F n = (c n, r n)` such that the closed ball
  `closed_ball (c n) (r n)` is included in the previous ball and in `f n`, and such that
  `r n` is small enough to ensure that `c n` is a Cauchy sequence. Then `c n` converges to a
  limit which belongs to all the `f n`. -/
  let F : ℕ → (α × ℝ≥0∞) := λn, nat.rec_on n (prod.mk x (min ε (B 0)))
                              (λn p, prod.mk (center n p.1 p.2) (radius n p.1 p.2)),
  let c : ℕ → α := λn, (F n).1,
  let r : ℕ → ℝ≥0∞ := λn, (F n).2,
  have rpos : ∀ n, 0 < r n,
  { assume n,
    induction n with n hn,
    exact lt_min εpos (Bpos 0),
    exact Hpos n (c n) (r n) hn.ne' },
  have r0 : ∀ n, r n ≠ 0 := λ n, (rpos n).ne',
  have rB : ∀n, r n ≤ B n,
  { assume n,
    induction n with n hn,
    exact min_le_right _ _,
    exact HB n (c n) (r n) (r0 n) },
  have incl : ∀n, closed_ball (c (n+1)) (r (n+1)) ⊆ (closed_ball (c n) (r n)) ∩ (f n) :=
    λ n, Hball n (c n) (r n) (r0 n),
  have cdist : ∀n, edist (c n) (c (n+1)) ≤ B n,
  { assume n,
    rw edist_comm,
    have A : c (n+1) ∈ closed_ball (c (n+1)) (r (n+1)) := mem_closed_ball_self,
    have I := calc
      closed_ball (c (n+1)) (r (n+1)) ⊆ closed_ball (c n) (r n) :
        subset.trans (incl n) (inter_subset_left _ _)
      ... ⊆ closed_ball (c n) (B n) : closed_ball_subset_closed_ball (rB n),
    exact I A },
  have : cauchy_seq c :=
    cauchy_seq_of_edist_le_geometric_two _ one_ne_top cdist,
  -- as the sequence `c n` is Cauchy in a complete space, it converges to a limit `y`.
  rcases cauchy_seq_tendsto_of_complete this with ⟨y, ylim⟩,
  -- this point `y` will be the desired point. We will check that it belongs to all
  -- `f n` and to `ball x ε`.
  use y,
  simp only [exists_prop, set.mem_Inter],
  have I : ∀n, ∀m ≥ n, closed_ball (c m) (r m) ⊆ closed_ball (c n) (r n),
  { assume n,
    refine nat.le_induction _ (λm hnm h, _),
    { exact subset.refl _ },
    { exact subset.trans (incl m) (subset.trans (inter_subset_left _ _) h) }},
  have yball : ∀n, y ∈ closed_ball (c n) (r n),
  { assume n,
    refine is_closed_ball.mem_of_tendsto ylim _,
    refine (filter.eventually_ge_at_top n).mono (λ m hm, _),
    exact I n m hm mem_closed_ball_self },
  split,
  show ∀n, y ∈ f n,
  { assume n,
    have : closed_ball (c (n+1)) (r (n+1)) ⊆ f n := subset.trans (incl n) (inter_subset_right _ _),
    exact this (yball (n+1)) },
  show edist y x ≤ ε, from le_trans (yball 0) (min_le_left _ _),
end

/-- Baire theorem: a countable intersection of dense open sets is dense. Formulated here with ⋂₀. -/
theorem dense_sInter_of_open {S : set (set α)} (ho : ∀s∈S, is_open s) (hS : countable S)
  (hd : ∀s∈S, dense s) : dense (⋂₀S) :=
begin
  cases S.eq_empty_or_nonempty with h h,
  { simp [h] },
  { rcases hS.exists_surjective h with ⟨f, hf⟩,
    have F : ∀n, f n ∈ S := λn, by rw hf; exact mem_range_self _,
    rw [hf, sInter_range],
    exact dense_Inter_of_open_nat (λn, ho _ (F n)) (λn, hd _ (F n)) }
end

/-- Baire theorem: a countable intersection of dense open sets is dense. Formulated here with
an index set which is a countable set in any type. -/
theorem dense_bInter_of_open {S : set β} {f : β → set α} (ho : ∀s∈S, is_open (f s))
  (hS : countable S) (hd : ∀s∈S, dense (f s)) : dense (⋂s∈S, f s) :=
begin
  rw ← sInter_image,
  apply dense_sInter_of_open,
  { rwa ball_image_iff },
  { exact hS.image _ },
  { rwa ball_image_iff }
end

/-- Baire theorem: a countable intersection of dense open sets is dense. Formulated here with
an index set which is an encodable type. -/
theorem dense_Inter_of_open [encodable β] {f : β → set α} (ho : ∀s, is_open (f s))
  (hd : ∀s, dense (f s)) : dense (⋂s, f s) :=
begin
  rw ← sInter_range,
  apply dense_sInter_of_open,
  { rwa forall_range_iff },
  { exact countable_range _ },
  { rwa forall_range_iff }
end

/-- Baire theorem: a countable intersection of dense Gδ sets is dense. Formulated here with ⋂₀. -/
theorem dense_sInter_of_Gδ {S : set (set α)} (ho : ∀s∈S, is_Gδ s) (hS : countable S)
  (hd : ∀s∈S, dense s) : dense (⋂₀S) :=
begin
  -- the result follows from the result for a countable intersection of dense open sets,
  -- by rewriting each set as a countable intersection of open sets, which are of course dense.
  choose T hTo hTc hsT using ho,
  have : ⋂₀ S = ⋂₀ (⋃ s ∈ S, T s ‹_›), -- := (sInter_bUnion (λs hs, (hT s hs).2.2)).symm,
    by simp only [sInter_Union, (hsT _ _).symm, ← sInter_eq_bInter],
  rw this,
  refine dense_sInter_of_open _ (hS.bUnion hTc) _;
    simp only [mem_Union]; rintro t ⟨s, hs, tTs⟩,
  show is_open t, from hTo s hs t tTs,
  show dense t,
  { intro x,
    have := hd s hs x,
    rw hsT s hs at this,
    exact closure_mono (sInter_subset_of_mem tTs) this }
end

/-- Baire theorem: a countable intersection of dense Gδ sets is dense. Formulated here with
an index set which is an encodable type. -/
theorem dense_Inter_of_Gδ [encodable β] {f : β → set α} (ho : ∀s, is_Gδ (f s))
  (hd : ∀s, dense (f s)) : dense (⋂s, f s) :=
begin
  rw ← sInter_range,
  exact dense_sInter_of_Gδ (forall_range_iff.2 ‹_›) (countable_range _) (forall_range_iff.2 ‹_›)
end

/-- Baire theorem: a countable intersection of dense Gδ sets is dense. Formulated here with
an index set which is a countable set in any type. -/
theorem dense_bInter_of_Gδ {S : set β} {f : Π x ∈ S, set α} (ho : ∀s∈S, is_Gδ (f s ‹_›))
  (hS : countable S) (hd : ∀s∈S, dense (f s ‹_›)) : dense (⋂s∈S, f s ‹_›) :=
begin
  rw bInter_eq_Inter,
  haveI := hS.to_encodable,
  exact dense_Inter_of_Gδ (λ s, ho s s.2) (λ s, hd s s.2)
end

/-- Baire theorem: the intersection of two dense Gδ sets is dense. -/
theorem dense.inter_of_Gδ {s t : set α} (hs : is_Gδ s) (ht : is_Gδ t) (hsc : dense s)
  (htc : dense t) :
  dense (s ∩ t) :=
begin
  rw [inter_eq_Inter],
  apply dense_Inter_of_Gδ; simp [bool.forall_bool, *]
end

/-- A property holds on a residual (comeagre) set if and only if it holds on some dense `Gδ` set. -/
lemma eventually_residual {p : α → Prop} :
  (∀ᶠ x in residual α, p x) ↔ ∃ (t : set α), is_Gδ t ∧ dense t ∧ ∀ x ∈ t, p x :=
calc (∀ᶠ x in residual α, p x) ↔
  ∀ᶠ x in ⨅ (t : set α) (ht : is_Gδ t ∧ dense t), 𝓟 t, p x :
    by simp only [residual, infi_and]
... ↔ ∃ (t : set α) (ht : is_Gδ t ∧ dense t), ∀ᶠ x in 𝓟 t, p x : mem_binfi_of_directed
    (λ t₁ h₁ t₂ h₂, ⟨t₁ ∩ t₂, ⟨h₁.1.inter h₂.1, dense.inter_of_Gδ h₁.1 h₂.1 h₁.2 h₂.2⟩, by simp⟩)
    ⟨univ, is_Gδ_univ, dense_univ⟩
... ↔ _ : by simp [and_assoc]

/-- A set is residual (comeagre) if and only if it includes a dense `Gδ` set. -/
lemma mem_residual {s : set α} : s ∈ residual α ↔ ∃ t ⊆ s, is_Gδ t ∧ dense t :=
(@eventually_residual α _ _ (λ x, x ∈ s)).trans $ exists_congr $
λ t, by rw [exists_prop, and_comm (t ⊆ s), subset_def, and_assoc]

lemma dense_of_mem_residual {s : set α} (hs : s ∈ residual α) : dense s :=
let ⟨t, hts, _, hd⟩ := mem_residual.1 hs in hd.mono hts

instance : countable_Inter_filter (residual α) :=
⟨begin
  intros S hSc hS,
  simp only [mem_residual] at *,
  choose T hTs hT using hS,
  refine ⟨⋂ s ∈ S, T s ‹_›, _, _, _⟩,
  { rw [sInter_eq_bInter],
    exact Inter₂_mono hTs },
  { exact is_Gδ_bInter hSc (λ s hs, (hT s hs).1) },
  { exact dense_bInter_of_Gδ (λ s hs, (hT s hs).1) hSc (λ s hs, (hT s hs).2) }
end⟩

/-- Baire theorem: if countably many closed sets cover the whole space, then their interiors
are dense. Formulated here with an index set which is a countable set in any type. -/
theorem dense_bUnion_interior_of_closed {S : set β} {f : β → set α} (hc : ∀s∈S, is_closed (f s))
  (hS : countable S) (hU : (⋃s∈S, f s) = univ) : dense (⋃s∈S, interior (f s)) :=
begin
  let g := λs, (frontier (f s))ᶜ,
  have : dense (⋂s∈S, g s),
  { refine dense_bInter_of_open (λs hs, _) hS (λs hs, _),
    show is_open (g s), from is_open_compl_iff.2 is_closed_frontier,
    show dense (g s),
    { intro x,
      simp [interior_frontier (hc s hs)] }},
  refine this.mono _,
  show (⋂s∈S, g s) ⊆ (⋃s∈S, interior (f s)),
  assume x hx,
  have : x ∈ ⋃s∈S, f s, { have := mem_univ x, rwa ← hU at this },
  rcases mem_Union₂.1 this with ⟨s, hs, xs⟩,
  have : x ∈ g s := mem_Inter₂.1 hx s hs,
  have : x ∈ interior (f s),
  { have : x ∈ f s \ (frontier (f s)) := mem_inter xs this,
    simpa [frontier, xs, (hc s hs).closure_eq] using this },
  exact mem_Union₂.2 ⟨s, ⟨hs, this⟩⟩
end

/-- Baire theorem: if countably many closed sets cover the whole space, then their interiors
are dense. Formulated here with `⋃₀`. -/
theorem dense_sUnion_interior_of_closed {S : set (set α)} (hc : ∀s∈S, is_closed s)
  (hS : countable S) (hU : (⋃₀ S) = univ) : dense (⋃s∈S, interior s) :=
by rw sUnion_eq_bUnion at hU; exact dense_bUnion_interior_of_closed hc hS hU

/-- Baire theorem: if countably many closed sets cover the whole space, then their interiors
are dense. Formulated here with an index set which is an encodable type. -/
theorem dense_Union_interior_of_closed [encodable β] {f : β → set α} (hc : ∀s, is_closed (f s))
  (hU : (⋃s, f s) = univ) : dense (⋃s, interior (f s)) :=
begin
  rw ← bUnion_univ,
  apply dense_bUnion_interior_of_closed,
  { simp [hc] },
  { apply countable_encodable },
  { rwa ← bUnion_univ at hU }
end

/-- One of the most useful consequences of Baire theorem: if a countable union of closed sets
covers the space, then one of the sets has nonempty interior. -/
theorem nonempty_interior_of_Union_of_closed [nonempty α] [encodable β] {f : β → set α}
  (hc : ∀s, is_closed (f s)) (hU : (⋃s, f s) = univ) :
  ∃s, (interior $ f s).nonempty :=
begin
  by_contradiction h,
  simp only [not_exists, not_nonempty_iff_eq_empty] at h,
  have := calc ∅ = closure (⋃s, interior (f s)) : by simp [h]
             ... = univ : (dense_Union_interior_of_closed hc hU).closure_eq,
  exact univ_nonempty.ne_empty this.symm
end

end Baire_theorem


/-
Copyright (c) 2020 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import group_theory.group_action.opposite
import data.equiv.module

/-!
# Module operations on `Mᵐᵒᵖ`

This file contains definitions that build on top of the group action definitions in
`group_theory.group_action.opposite`.
-/

namespace mul_opposite
universes u v

variables (R : Type u) {M : Type v} [semiring R] [add_comm_monoid M] [module R M]

/-- `mul_opposite.distrib_mul_action` extends to a `module` -/
instance : module R (mul_opposite M) :=
{ add_smul := λ r₁ r₂ x, unop_injective $ add_smul r₁ r₂ (unop x),
  zero_smul := λ x, unop_injective $ zero_smul _ (unop x),
  ..mul_opposite.distrib_mul_action M R }

/-- The function `op` is a linear equivalence. -/
def op_linear_equiv : M ≃ₗ[R] Mᵐᵒᵖ :=
{ map_smul' := mul_opposite.op_smul, .. op_add_equiv }

@[simp] lemma coe_op_linear_equiv :
  (op_linear_equiv R : M → Mᵐᵒᵖ) = op := rfl
@[simp] lemma coe_op_linear_equiv_symm :
  ((op_linear_equiv R).symm : Mᵐᵒᵖ → M) = unop := rfl

@[simp]

@[simp] lemma op_linear_equiv_to_add_equiv :
  (op_linear_equiv R : M ≃ₗ[R] Mᵐᵒᵖ).to_add_equiv = op_add_equiv := rfl
@[simp] lemma op_linear_equiv_symm_to_add_equiv :
  (op_linear_equiv R : M ≃ₗ[R] Mᵐᵒᵖ).symm.to_add_equiv = op_add_equiv.symm := rfl

end mul_opposite