/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Patrick Massot, Yury Kudryashov, Rémy Degenne
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.ordered_group
import Mathlib.data.set.basic
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
# Intervals

In any preorder `α`, we define intervals (which on each side can be either infinite, open, or
closed) using the following naming conventions:
- `i`: infinite
- `o`: open
- `c`: closed

Each interval has the name `I` + letter for left side + letter for right side. For instance,
`Ioc a b` denotes the inverval `(a, b]`.

This file contains these definitions, and basic facts on inclusion, intersection, difference of
intervals (where the precise statements may depend on the properties of the order, in particular
for some statements it should be `linear_order` or `densely_ordered`).

TODO: This is just the beginning; a lot of rules are missing
-/

namespace set

/-- Left-open right-open interval -/
def Ioo {α : Type u} [preorder α] (a : α) (b : α) : set α := set_of fun (x : α) => a < x ∧ x < b

/-- Left-closed right-open interval -/
def Ico {α : Type u} [preorder α] (a : α) (b : α) : set α := set_of fun (x : α) => a ≤ x ∧ x < b

/-- Left-infinite right-open interval -/
def Iio {α : Type u} [preorder α] (a : α) : set α := set_of fun (x : α) => x < a

/-- Left-closed right-closed interval -/
def Icc {α : Type u} [preorder α] (a : α) (b : α) : set α := set_of fun (x : α) => a ≤ x ∧ x ≤ b

/-- Left-infinite right-closed interval -/
def Iic {α : Type u} [preorder α] (b : α) : set α := set_of fun (x : α) => x ≤ b

/-- Left-open right-closed interval -/
def Ioc {α : Type u} [preorder α] (a : α) (b : α) : set α := set_of fun (x : α) => a < x ∧ x ≤ b

/-- Left-closed right-infinite interval -/
def Ici {α : Type u} [preorder α] (a : α) : set α := set_of fun (x : α) => a ≤ x

/-- Left-open right-infinite interval -/
def Ioi {α : Type u} [preorder α] (a : α) : set α := set_of fun (x : α) => a < x

theorem Ioo_def {α : Type u} [preorder α] (a : α) (b : α) :
    (set_of fun (x : α) => a < x ∧ x < b) = Ioo a b :=
  rfl

theorem Ico_def {α : Type u} [preorder α] (a : α) (b : α) :
    (set_of fun (x : α) => a ≤ x ∧ x < b) = Ico a b :=
  rfl

theorem Iio_def {α : Type u} [preorder α] (a : α) : (set_of fun (x : α) => x < a) = Iio a := rfl

theorem Icc_def {α : Type u} [preorder α] (a : α) (b : α) :
    (set_of fun (x : α) => a ≤ x ∧ x ≤ b) = Icc a b :=
  rfl

theorem Iic_def {α : Type u} [preorder α] (b : α) : (set_of fun (x : α) => x ≤ b) = Iic b := rfl

theorem Ioc_def {α : Type u} [preorder α] (a : α) (b : α) :
    (set_of fun (x : α) => a < x ∧ x ≤ b) = Ioc a b :=
  rfl

theorem Ici_def {α : Type u} [preorder α] (a : α) : (set_of fun (x : α) => a ≤ x) = Ici a := rfl

theorem Ioi_def {α : Type u} [preorder α] (a : α) : (set_of fun (x : α) => a < x) = Ioi a := rfl

@[simp] theorem mem_Ioo {α : Type u} [preorder α] {a : α} {b : α} {x : α} :
    x ∈ Ioo a b ↔ a < x ∧ x < b :=
  iff.rfl

@[simp] theorem mem_Ico {α : Type u} [preorder α] {a : α} {b : α} {x : α} :
    x ∈ Ico a b ↔ a ≤ x ∧ x < b :=
  iff.rfl

@[simp] theorem mem_Iio {α : Type u} [preorder α] {b : α} {x : α} : x ∈ Iio b ↔ x < b := iff.rfl

@[simp] theorem mem_Icc {α : Type u} [preorder α] {a : α} {b : α} {x : α} :
    x ∈ Icc a b ↔ a ≤ x ∧ x ≤ b :=
  iff.rfl

@[simp] theorem mem_Iic {α : Type u} [preorder α] {b : α} {x : α} : x ∈ Iic b ↔ x ≤ b := iff.rfl

@[simp] theorem mem_Ioc {α : Type u} [preorder α] {a : α} {b : α} {x : α} :
    x ∈ Ioc a b ↔ a < x ∧ x ≤ b :=
  iff.rfl

@[simp] theorem mem_Ici {α : Type u} [preorder α] {a : α} {x : α} : x ∈ Ici a ↔ a ≤ x := iff.rfl

@[simp] theorem mem_Ioi {α : Type u} [preorder α] {a : α} {x : α} : x ∈ Ioi a ↔ a < x := iff.rfl

@[simp] theorem left_mem_Ioo {α : Type u} [preorder α] {a : α} {b : α} : a ∈ Ioo a b ↔ False :=
  sorry

@[simp] theorem left_mem_Ico {α : Type u} [preorder α] {a : α} {b : α} : a ∈ Ico a b ↔ a < b :=
  sorry

@[simp] theorem left_mem_Icc {α : Type u} [preorder α] {a : α} {b : α} : a ∈ Icc a b ↔ a ≤ b :=
  sorry

@[simp] theorem left_mem_Ioc {α : Type u} [preorder α] {a : α} {b : α} : a ∈ Ioc a b ↔ False :=
  sorry

theorem left_mem_Ici {α : Type u} [preorder α] {a : α} : a ∈ Ici a :=
  eq.mpr (id (propext mem_Ici)) (le_refl a)

@[simp] theorem right_mem_Ioo {α : Type u} [preorder α] {a : α} {b : α} : b ∈ Ioo a b ↔ False :=
  sorry

@[simp] theorem right_mem_Ico {α : Type u} [preorder α] {a : α} {b : α} : b ∈ Ico a b ↔ False :=
  sorry

@[simp] theorem right_mem_Icc {α : Type u} [preorder α] {a : α} {b : α} : b ∈ Icc a b ↔ a ≤ b :=
  sorry

@[simp] theorem right_mem_Ioc {α : Type u} [preorder α] {a : α} {b : α} : b ∈ Ioc a b ↔ a < b :=
  sorry

theorem right_mem_Iic {α : Type u} [preorder α] {a : α} : a ∈ Iic a :=
  eq.mpr (id (propext mem_Iic)) (le_refl a)

@[simp] theorem dual_Ici {α : Type u} [preorder α] {a : α} : Ici a = Iic a := rfl

@[simp] theorem dual_Iic {α : Type u} [preorder α] {a : α} : Iic a = Ici a := rfl

@[simp] theorem dual_Ioi {α : Type u} [preorder α] {a : α} : Ioi a = Iio a := rfl

@[simp] theorem dual_Iio {α : Type u} [preorder α] {a : α} : Iio a = Ioi a := rfl

@[simp] theorem dual_Icc {α : Type u} [preorder α] {a : α} {b : α} : Icc a b = Icc b a :=
  ext fun (x : order_dual α) => and_comm (a ≤ x) (x ≤ b)

@[simp] theorem dual_Ioc {α : Type u} [preorder α] {a : α} {b : α} : Ioc a b = Ico b a :=
  ext fun (x : order_dual α) => and_comm (a < x) (x ≤ b)

@[simp] theorem dual_Ico {α : Type u} [preorder α] {a : α} {b : α} : Ico a b = Ioc b a :=
  ext fun (x : order_dual α) => and_comm (a ≤ x) (x < b)

@[simp] theorem dual_Ioo {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b = Ioo b a :=
  ext fun (x : order_dual α) => and_comm (a < x) (x < b)

@[simp] theorem nonempty_Icc {α : Type u} [preorder α] {a : α} {b : α} :
    set.nonempty (Icc a b) ↔ a ≤ b :=
  sorry

@[simp] theorem nonempty_Ico {α : Type u} [preorder α] {a : α} {b : α} :
    set.nonempty (Ico a b) ↔ a < b :=
  sorry

@[simp] theorem nonempty_Ioc {α : Type u} [preorder α] {a : α} {b : α} :
    set.nonempty (Ioc a b) ↔ a < b :=
  sorry

@[simp] theorem nonempty_Ici {α : Type u} [preorder α] {a : α} : set.nonempty (Ici a) :=
  Exists.intro a left_mem_Ici

@[simp] theorem nonempty_Iic {α : Type u} [preorder α] {a : α} : set.nonempty (Iic a) :=
  Exists.intro a right_mem_Iic

@[simp] theorem nonempty_Ioo {α : Type u} [preorder α] {a : α} {b : α} [densely_ordered α] :
    set.nonempty (Ioo a b) ↔ a < b :=
  sorry

@[simp] theorem nonempty_Ioi {α : Type u} [preorder α] {a : α} [no_top_order α] :
    set.nonempty (Ioi a) :=
  no_top a

@[simp] theorem nonempty_Iio {α : Type u} [preorder α] {a : α} [no_bot_order α] :
    set.nonempty (Iio a) :=
  no_bot a

theorem nonempty_Icc_subtype {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) :
    Nonempty ↥(Icc a b) :=
  nonempty.to_subtype (iff.mpr nonempty_Icc h)

theorem nonempty_Ico_subtype {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) :
    Nonempty ↥(Ico a b) :=
  nonempty.to_subtype (iff.mpr nonempty_Ico h)

theorem nonempty_Ioc_subtype {α : Type u} [preorder α] {a : α} {b : α} (h : a < b) :
    Nonempty ↥(Ioc a b) :=
  nonempty.to_subtype (iff.mpr nonempty_Ioc h)

/-- An interval `Ici a` is nonempty. -/
protected instance nonempty_Ici_subtype {α : Type u} [preorder α] {a : α} : Nonempty ↥(Ici a) :=
  nonempty.to_subtype nonempty_Ici

/-- An interval `Iic a` is nonempty. -/
protected instance nonempty_Iic_subtype {α : Type u} [preorder α] {a : α} : Nonempty ↥(Iic a) :=
  nonempty.to_subtype nonempty_Iic

theorem nonempty_Ioo_subtype {α : Type u} [preorder α] {a : α} {b : α} [densely_ordered α]
    (h : a < b) : Nonempty ↥(Ioo a b) :=
  nonempty.to_subtype (iff.mpr nonempty_Ioo h)

/-- In a `no_top_order`, the intervals `Ioi` are nonempty. -/
protected instance nonempty_Ioi_subtype {α : Type u} [preorder α] {a : α} [no_top_order α] :
    Nonempty ↥(Ioi a) :=
  nonempty.to_subtype nonempty_Ioi

/-- In a `no_bot_order`, the intervals `Iio` are nonempty. -/
protected instance nonempty_Iio_subtype {α : Type u} [preorder α] {a : α} [no_bot_order α] :
    Nonempty ↥(Iio a) :=
  nonempty.to_subtype nonempty_Iio

@[simp] theorem Ioo_eq_empty {α : Type u} [preorder α] {a : α} {b : α} (h : b ≤ a) : Ioo a b = ∅ :=
  sorry

@[simp] theorem Ico_eq_empty {α : Type u} [preorder α] {a : α} {b : α} (h : b ≤ a) : Ico a b = ∅ :=
  sorry

@[simp] theorem Icc_eq_empty {α : Type u} [preorder α] {a : α} {b : α} (h : b < a) : Icc a b = ∅ :=
  sorry

@[simp] theorem Ioc_eq_empty {α : Type u} [preorder α] {a : α} {b : α} (h : b ≤ a) : Ioc a b = ∅ :=
  sorry

@[simp] theorem Ioo_self {α : Type u} [preorder α] (a : α) : Ioo a a = ∅ := Ioo_eq_empty (le_refl a)

@[simp] theorem Ico_self {α : Type u} [preorder α] (a : α) : Ico a a = ∅ := Ico_eq_empty (le_refl a)

@[simp] theorem Ioc_self {α : Type u} [preorder α] (a : α) : Ioc a a = ∅ := Ioc_eq_empty (le_refl a)

theorem Ici_subset_Ici {α : Type u} [preorder α] {a : α} {b : α} : Ici a ⊆ Ici b ↔ b ≤ a :=
  { mp := fun (h : Ici a ⊆ Ici b) => h left_mem_Ici,
    mpr := fun (h : b ≤ a) (x : α) (hx : x ∈ Ici a) => le_trans h hx }

theorem Iic_subset_Iic {α : Type u} [preorder α] {a : α} {b : α} : Iic a ⊆ Iic b ↔ a ≤ b :=
  Ici_subset_Ici

theorem Ici_subset_Ioi {α : Type u} [preorder α] {a : α} {b : α} : Ici a ⊆ Ioi b ↔ b < a :=
  { mp := fun (h : Ici a ⊆ Ioi b) => h left_mem_Ici,
    mpr := fun (h : b < a) (x : α) (hx : x ∈ Ici a) => lt_of_lt_of_le h hx }

theorem Iic_subset_Iio {α : Type u} [preorder α] {a : α} {b : α} : Iic a ⊆ Iio b ↔ a < b :=
  { mp := fun (h : Iic a ⊆ Iio b) => h right_mem_Iic,
    mpr := fun (h : a < b) (x : α) (hx : x ∈ Iic a) => lt_of_le_of_lt hx h }

theorem Ioo_subset_Ioo {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : b₁ ≤ b₂) : Ioo a₁ b₁ ⊆ Ioo a₂ b₂ :=
  sorry

theorem Ioo_subset_Ioo_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b : α} (h : a₁ ≤ a₂) :
    Ioo a₂ b ⊆ Ioo a₁ b :=
  Ioo_subset_Ioo h (le_refl b)

theorem Ioo_subset_Ioo_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h : b₁ ≤ b₂) :
    Ioo a b₁ ⊆ Ioo a b₂ :=
  Ioo_subset_Ioo (le_refl a) h

theorem Ico_subset_Ico {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : b₁ ≤ b₂) : Ico a₁ b₁ ⊆ Ico a₂ b₂ :=
  sorry

theorem Ico_subset_Ico_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b : α} (h : a₁ ≤ a₂) :
    Ico a₂ b ⊆ Ico a₁ b :=
  Ico_subset_Ico h (le_refl b)

theorem Ico_subset_Ico_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h : b₁ ≤ b₂) :
    Ico a b₁ ⊆ Ico a b₂ :=
  Ico_subset_Ico (le_refl a) h

theorem Icc_subset_Icc {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : b₁ ≤ b₂) : Icc a₁ b₁ ⊆ Icc a₂ b₂ :=
  sorry

theorem Icc_subset_Icc_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b : α} (h : a₁ ≤ a₂) :
    Icc a₂ b ⊆ Icc a₁ b :=
  Icc_subset_Icc h (le_refl b)

theorem Icc_subset_Icc_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h : b₁ ≤ b₂) :
    Icc a b₁ ⊆ Icc a b₂ :=
  Icc_subset_Icc (le_refl a) h

theorem Icc_subset_Ioo {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (ha : a₂ < a₁)
    (hb : b₁ < b₂) : Icc a₁ b₁ ⊆ Ioo a₂ b₂ :=
  fun (x : α) (hx : x ∈ Icc a₁ b₁) =>
    { left := lt_of_lt_of_le ha (and.left hx), right := lt_of_le_of_lt (and.right hx) hb }

theorem Icc_subset_Ici_self {α : Type u} [preorder α] {a : α} {b : α} : Icc a b ⊆ Ici a :=
  fun (x : α) => and.left

theorem Icc_subset_Iic_self {α : Type u} [preorder α] {a : α} {b : α} : Icc a b ⊆ Iic b :=
  fun (x : α) => and.right

theorem Ioc_subset_Iic_self {α : Type u} [preorder α] {a : α} {b : α} : Ioc a b ⊆ Iic b :=
  fun (x : α) => and.right

theorem Ioc_subset_Ioc {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : b₁ ≤ b₂) : Ioc a₁ b₁ ⊆ Ioc a₂ b₂ :=
  sorry

theorem Ioc_subset_Ioc_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b : α} (h : a₁ ≤ a₂) :
    Ioc a₂ b ⊆ Ioc a₁ b :=
  Ioc_subset_Ioc h (le_refl b)

theorem Ioc_subset_Ioc_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h : b₁ ≤ b₂) :
    Ioc a b₁ ⊆ Ioc a b₂ :=
  Ioc_subset_Ioc (le_refl a) h

theorem Ico_subset_Ioo_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b : α} (h₁ : a₁ < a₂) :
    Ico a₂ b ⊆ Ioo a₁ b :=
  fun (x : α) => and.imp_left (lt_of_lt_of_le h₁)

theorem Ioc_subset_Ioo_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h : b₁ < b₂) :
    Ioc a b₁ ⊆ Ioo a b₂ :=
  fun (x : α) => and.imp_right fun (h' : x ≤ b₁) => lt_of_le_of_lt h' h

theorem Icc_subset_Ico_right {α : Type u} [preorder α] {a : α} {b₁ : α} {b₂ : α} (h₁ : b₁ < b₂) :
    Icc a b₁ ⊆ Ico a b₂ :=
  fun (x : α) => and.imp_right fun (h₂ : x ≤ b₁) => lt_of_le_of_lt h₂ h₁

theorem Ioo_subset_Ico_self {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b ⊆ Ico a b :=
  fun (x : α) => and.imp_left le_of_lt

theorem Ioo_subset_Ioc_self {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b ⊆ Ioc a b :=
  fun (x : α) => and.imp_right le_of_lt

theorem Ico_subset_Icc_self {α : Type u} [preorder α] {a : α} {b : α} : Ico a b ⊆ Icc a b :=
  fun (x : α) => and.imp_right le_of_lt

theorem Ioc_subset_Icc_self {α : Type u} [preorder α] {a : α} {b : α} : Ioc a b ⊆ Icc a b :=
  fun (x : α) => and.imp_left le_of_lt

theorem Ioo_subset_Icc_self {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b ⊆ Icc a b :=
  subset.trans Ioo_subset_Ico_self Ico_subset_Icc_self

theorem Ico_subset_Iio_self {α : Type u} [preorder α] {a : α} {b : α} : Ico a b ⊆ Iio b :=
  fun (x : α) => and.right

theorem Ioo_subset_Iio_self {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b ⊆ Iio b :=
  fun (x : α) => and.right

theorem Ioc_subset_Ioi_self {α : Type u} [preorder α] {a : α} {b : α} : Ioc a b ⊆ Ioi a :=
  fun (x : α) => and.left

theorem Ioo_subset_Ioi_self {α : Type u} [preorder α] {a : α} {b : α} : Ioo a b ⊆ Ioi a :=
  fun (x : α) => and.left

theorem Ioi_subset_Ici_self {α : Type u} [preorder α] {a : α} : Ioi a ⊆ Ici a :=
  fun (x : α) (hx : x ∈ Ioi a) => le_of_lt hx

theorem Iio_subset_Iic_self {α : Type u} [preorder α] {a : α} : Iio a ⊆ Iic a :=
  fun (x : α) (hx : x ∈ Iio a) => le_of_lt hx

theorem Ico_subset_Ici_self {α : Type u} [preorder α] {a : α} {b : α} : Ico a b ⊆ Ici a :=
  fun (x : α) => and.left

theorem Icc_subset_Icc_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h₁ : a₁ ≤ b₁) : Icc a₁ b₁ ⊆ Icc a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ ≤ b₂ :=
  sorry

theorem Icc_subset_Ioo_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h₁ : a₁ ≤ b₁) : Icc a₁ b₁ ⊆ Ioo a₂ b₂ ↔ a₂ < a₁ ∧ b₁ < b₂ :=
  sorry

theorem Icc_subset_Ico_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h₁ : a₁ ≤ b₁) : Icc a₁ b₁ ⊆ Ico a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ < b₂ :=
  sorry

theorem Icc_subset_Ioc_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h₁ : a₁ ≤ b₁) : Icc a₁ b₁ ⊆ Ioc a₂ b₂ ↔ a₂ < a₁ ∧ b₁ ≤ b₂ :=
  sorry

theorem Icc_subset_Iio_iff {α : Type u} [preorder α] {a₁ : α} {b₁ : α} {b₂ : α} (h₁ : a₁ ≤ b₁) :
    Icc a₁ b₁ ⊆ Iio b₂ ↔ b₁ < b₂ :=
  sorry

theorem Icc_subset_Ioi_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} (h₁ : a₁ ≤ b₁) :
    Icc a₁ b₁ ⊆ Ioi a₂ ↔ a₂ < a₁ :=
  sorry

theorem Icc_subset_Iic_iff {α : Type u} [preorder α] {a₁ : α} {b₁ : α} {b₂ : α} (h₁ : a₁ ≤ b₁) :
    Icc a₁ b₁ ⊆ Iic b₂ ↔ b₁ ≤ b₂ :=
  sorry

theorem Icc_subset_Ici_iff {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} (h₁ : a₁ ≤ b₁) :
    Icc a₁ b₁ ⊆ Ici a₂ ↔ a₂ ≤ a₁ :=
  sorry

theorem Icc_ssubset_Icc_left {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (hI : a₂ ≤ b₂) (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Icc a₁ b₁ ⊂ Icc a₂ b₂ :=
  sorry

theorem Icc_ssubset_Icc_right {α : Type u} [preorder α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (hI : a₂ ≤ b₂) (ha : a₂ ≤ a₁) (hb : b₁ < b₂) : Icc a₁ b₁ ⊂ Icc a₂ b₂ :=
  sorry

/-- If `a ≤ b`, then `(b, +∞) ⊆ (a, +∞)`. In preorders, this is just an implication. If you need
the equivalence in linear orders, use `Ioi_subset_Ioi_iff`. -/
theorem Ioi_subset_Ioi {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : Ioi b ⊆ Ioi a :=
  fun (x : α) (hx : x ∈ Ioi b) => lt_of_le_of_lt h hx

/-- If `a ≤ b`, then `(b, +∞) ⊆ [a, +∞)`. In preorders, this is just an implication. If you need
the equivalence in dense linear orders, use `Ioi_subset_Ici_iff`. -/
theorem Ioi_subset_Ici {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : Ioi b ⊆ Ici a :=
  subset.trans (Ioi_subset_Ioi h) Ioi_subset_Ici_self

/-- If `a ≤ b`, then `(-∞, a) ⊆ (-∞, b)`. In preorders, this is just an implication. If you need
the equivalence in linear orders, use `Iio_subset_Iio_iff`. -/
theorem Iio_subset_Iio {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : Iio a ⊆ Iio b :=
  fun (x : α) (hx : x ∈ Iio a) => lt_of_lt_of_le hx h

/-- If `a ≤ b`, then `(-∞, a) ⊆ (-∞, b]`. In preorders, this is just an implication. If you need
the equivalence in dense linear orders, use `Iio_subset_Iic_iff`. -/
theorem Iio_subset_Iic {α : Type u} [preorder α] {a : α} {b : α} (h : a ≤ b) : Iio a ⊆ Iic b :=
  subset.trans (Iio_subset_Iio h) Iio_subset_Iic_self

theorem Ici_inter_Iic {α : Type u} [preorder α] {a : α} {b : α} : Ici a ∩ Iic b = Icc a b := rfl

theorem Ici_inter_Iio {α : Type u} [preorder α] {a : α} {b : α} : Ici a ∩ Iio b = Ico a b := rfl

theorem Ioi_inter_Iic {α : Type u} [preorder α] {a : α} {b : α} : Ioi a ∩ Iic b = Ioc a b := rfl

theorem Ioi_inter_Iio {α : Type u} [preorder α] {a : α} {b : α} : Ioi a ∩ Iio b = Ioo a b := rfl

theorem mem_Icc_of_Ioo {α : Type u} [preorder α] {a : α} {b : α} {x : α} (h : x ∈ Ioo a b) :
    x ∈ Icc a b :=
  Ioo_subset_Icc_self h

theorem mem_Ico_of_Ioo {α : Type u} [preorder α] {a : α} {b : α} {x : α} (h : x ∈ Ioo a b) :
    x ∈ Ico a b :=
  Ioo_subset_Ico_self h

theorem mem_Ioc_of_Ioo {α : Type u} [preorder α] {a : α} {b : α} {x : α} (h : x ∈ Ioo a b) :
    x ∈ Ioc a b :=
  Ioo_subset_Ioc_self h

theorem mem_Icc_of_Ico {α : Type u} [preorder α] {a : α} {b : α} {x : α} (h : x ∈ Ico a b) :
    x ∈ Icc a b :=
  Ico_subset_Icc_self h

theorem mem_Icc_of_Ioc {α : Type u} [preorder α] {a : α} {b : α} {x : α} (h : x ∈ Ioc a b) :
    x ∈ Icc a b :=
  Ioc_subset_Icc_self h

theorem mem_Ici_of_Ioi {α : Type u} [preorder α] {a : α} {x : α} (h : x ∈ Ioi a) : x ∈ Ici a :=
  Ioi_subset_Ici_self h

theorem mem_Iic_of_Iio {α : Type u} [preorder α] {a : α} {x : α} (h : x ∈ Iio a) : x ∈ Iic a :=
  Iio_subset_Iic_self h

@[simp] theorem Icc_self {α : Type u} [partial_order α] (a : α) : Icc a a = singleton a := sorry

@[simp] theorem Icc_diff_left {α : Type u} [partial_order α] {a : α} {b : α} :
    Icc a b \ singleton a = Ioc a b :=
  sorry

@[simp] theorem Icc_diff_right {α : Type u} [partial_order α] {a : α} {b : α} :
    Icc a b \ singleton b = Ico a b :=
  sorry

@[simp] theorem Ico_diff_left {α : Type u} [partial_order α] {a : α} {b : α} :
    Ico a b \ singleton a = Ioo a b :=
  sorry

@[simp] theorem Ioc_diff_right {α : Type u} [partial_order α] {a : α} {b : α} :
    Ioc a b \ singleton b = Ioo a b :=
  sorry

@[simp] theorem Icc_diff_both {α : Type u} [partial_order α] {a : α} {b : α} :
    Icc a b \ insert a (singleton b) = Ioo a b :=
  sorry

@[simp] theorem Ici_diff_left {α : Type u} [partial_order α] {a : α} :
    Ici a \ singleton a = Ioi a :=
  sorry

@[simp] theorem Iic_diff_right {α : Type u} [partial_order α] {a : α} :
    Iic a \ singleton a = Iio a :=
  sorry

@[simp] theorem Ico_diff_Ioo_same {α : Type u} [partial_order α] {a : α} {b : α} (h : a < b) :
    Ico a b \ Ioo a b = singleton a :=
  sorry

@[simp] theorem Ioc_diff_Ioo_same {α : Type u} [partial_order α] {a : α} {b : α} (h : a < b) :
    Ioc a b \ Ioo a b = singleton b :=
  sorry

@[simp] theorem Icc_diff_Ico_same {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≤ b) :
    Icc a b \ Ico a b = singleton b :=
  sorry

@[simp] theorem Icc_diff_Ioc_same {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≤ b) :
    Icc a b \ Ioc a b = singleton a :=
  sorry

@[simp] theorem Icc_diff_Ioo_same {α : Type u} [partial_order α] {a : α} {b : α} (h : a ≤ b) :
    Icc a b \ Ioo a b = insert a (singleton b) :=
  sorry

@[simp] theorem Ici_diff_Ioi_same {α : Type u} [partial_order α] {a : α} :
    Ici a \ Ioi a = singleton a :=
  sorry

@[simp] theorem Iic_diff_Iio_same {α : Type u} [partial_order α] {a : α} :
    Iic a \ Iio a = singleton a :=
  sorry

@[simp] theorem Ioi_union_left {α : Type u} [partial_order α] {a : α} :
    Ioi a ∪ singleton a = Ici a :=
  sorry

@[simp] theorem Iio_union_right {α : Type u} [partial_order α] {a : α} :
    Iio a ∪ singleton a = Iic a :=
  ext fun (x : α) => iff.symm le_iff_lt_or_eq

theorem Ioo_union_left {α : Type u} [partial_order α] {a : α} {b : α} (hab : a < b) :
    Ioo a b ∪ singleton a = Ico a b :=
  sorry

theorem Ioo_union_right {α : Type u} [partial_order α] {a : α} {b : α} (hab : a < b) :
    Ioo a b ∪ singleton b = Ioc a b :=
  sorry

theorem Ioc_union_left {α : Type u} [partial_order α] {a : α} {b : α} (hab : a ≤ b) :
    Ioc a b ∪ singleton a = Icc a b :=
  sorry

theorem Ico_union_right {α : Type u} [partial_order α] {a : α} {b : α} (hab : a ≤ b) :
    Ico a b ∪ singleton b = Icc a b :=
  sorry

theorem mem_Ici_Ioi_of_subset_of_subset {α : Type u} [partial_order α] {a : α} {s : set α}
    (ho : Ioi a ⊆ s) (hc : s ⊆ Ici a) : s ∈ insert (Ici a) (singleton (Ioi a)) :=
  sorry

theorem mem_Iic_Iio_of_subset_of_subset {α : Type u} [partial_order α] {a : α} {s : set α}
    (ho : Iio a ⊆ s) (hc : s ⊆ Iic a) : s ∈ insert (Iic a) (singleton (Iio a)) :=
  mem_Ici_Ioi_of_subset_of_subset ho hc

theorem mem_Icc_Ico_Ioc_Ioo_of_subset_of_subset {α : Type u} [partial_order α] {a : α} {b : α}
    {s : set α} (ho : Ioo a b ⊆ s) (hc : s ⊆ Icc a b) :
    s ∈ insert (Icc a b) (insert (Ico a b) (insert (Ioc a b) (singleton (Ioo a b)))) :=
  sorry

theorem mem_Ioo_or_eq_endpoints_of_mem_Icc {α : Type u} [partial_order α] {a : α} {b : α} {x : α}
    (hmem : x ∈ Icc a b) : x = a ∨ x = b ∨ x ∈ Ioo a b :=
  sorry

theorem mem_Ioo_or_eq_left_of_mem_Ico {α : Type u} [partial_order α] {a : α} {b : α} {x : α}
    (hmem : x ∈ Ico a b) : x = a ∨ x ∈ Ioo a b :=
  sorry

theorem mem_Ioo_or_eq_right_of_mem_Ioc {α : Type u} [partial_order α] {a : α} {b : α} {x : α}
    (hmem : x ∈ Ioc a b) : x = b ∨ x ∈ Ioo a b :=
  eq.mp (Eq._oldrec (Eq.refl (x ∈ Ico b a → x = b ∨ x ∈ Ioo a b)) dual_Ico)
    (eq.mp (Eq._oldrec (Eq.refl (x ∈ Ico b a → x = b ∨ x ∈ Ioo b a)) dual_Ioo)
      mem_Ioo_or_eq_left_of_mem_Ico)
    hmem

theorem Ici_singleton_of_top {α : Type u} [partial_order α] {a : α} (h_top : ∀ (x : α), x ≤ a) :
    Ici a = singleton a :=
  ext
    fun (x : α) =>
      { mp := fun (h : x ∈ Ici a) => le_antisymm (h_top x) h,
        mpr := fun (h : x ∈ singleton a) => le_of_eq (Eq.symm h) }

theorem Iic_singleton_of_bot {α : Type u} [partial_order α] {a : α} (h_bot : ∀ (x : α), a ≤ x) :
    Iic a = singleton a :=
  Ici_singleton_of_top h_bot

@[simp] theorem Ici_top {α : Type u} [order_top α] : Ici ⊤ = singleton ⊤ :=
  Ici_singleton_of_top fun (_x : α) => le_top

@[simp] theorem Iic_top {α : Type u} [order_top α] : Iic ⊤ = univ :=
  eq_univ_of_forall fun (x : α) => le_top

@[simp] theorem Icc_top {α : Type u} [order_top α] {a : α} : Icc a ⊤ = Ici a := sorry

@[simp] theorem Ioc_top {α : Type u} [order_top α] {a : α} : Ioc a ⊤ = Ioi a := sorry

@[simp] theorem Iic_bot {α : Type u} [order_bot α] : Iic ⊥ = singleton ⊥ :=
  Iic_singleton_of_bot fun (_x : α) => bot_le

@[simp] theorem Ici_bot {α : Type u} [order_bot α] : Ici ⊥ = univ := Iic_top

@[simp] theorem Icc_bot {α : Type u} [order_bot α] {a : α} : Icc ⊥ a = Iic a := sorry

@[simp] theorem Ico_bot {α : Type u} [order_bot α] {a : α} : Ico ⊥ a = Iio a := sorry

@[simp] theorem compl_Iic {α : Type u} [linear_order α] {a : α} : Iic aᶜ = Ioi a :=
  ext fun (_x : α) => not_le

@[simp] theorem compl_Ici {α : Type u} [linear_order α] {a : α} : Ici aᶜ = Iio a :=
  ext fun (_x : α) => not_le

@[simp] theorem compl_Iio {α : Type u} [linear_order α] {a : α} : Iio aᶜ = Ici a :=
  ext fun (_x : α) => not_lt

@[simp] theorem compl_Ioi {α : Type u} [linear_order α] {a : α} : Ioi aᶜ = Iic a :=
  ext fun (_x : α) => not_lt

@[simp] theorem Ici_diff_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ici a \ Ici b = Ico a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Ici a \ Ici b = Ico a b)) (diff_eq (Ici a) (Ici b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Ici a ∩ (Ici bᶜ) = Ico a b)) compl_Ici))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Ici a ∩ Iio b = Ico a b)) Ici_inter_Iio))
        (Eq.refl (Ico a b))))

@[simp] theorem Ici_diff_Ioi {α : Type u} [linear_order α] {a : α} {b : α} :
    Ici a \ Ioi b = Icc a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Ici a \ Ioi b = Icc a b)) (diff_eq (Ici a) (Ioi b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Ici a ∩ (Ioi bᶜ) = Icc a b)) compl_Ioi))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Ici a ∩ Iic b = Icc a b)) Ici_inter_Iic))
        (Eq.refl (Icc a b))))

@[simp] theorem Ioi_diff_Ioi {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi a \ Ioi b = Ioc a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a \ Ioi b = Ioc a b)) (diff_eq (Ioi a) (Ioi b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a ∩ (Ioi bᶜ) = Ioc a b)) compl_Ioi))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a ∩ Iic b = Ioc a b)) Ioi_inter_Iic))
        (Eq.refl (Ioc a b))))

@[simp] theorem Ioi_diff_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi a \ Ici b = Ioo a b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a \ Ici b = Ioo a b)) (diff_eq (Ioi a) (Ici b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a ∩ (Ici bᶜ) = Ioo a b)) compl_Ici))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Ioi a ∩ Iio b = Ioo a b)) Ioi_inter_Iio))
        (Eq.refl (Ioo a b))))

@[simp] theorem Iic_diff_Iic {α : Type u} [linear_order α] {a : α} {b : α} :
    Iic b \ Iic a = Ioc a b :=
  sorry

@[simp] theorem Iio_diff_Iic {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio b \ Iic a = Ioo a b :=
  sorry

@[simp] theorem Iic_diff_Iio {α : Type u} [linear_order α] {a : α} {b : α} :
    Iic b \ Iio a = Icc a b :=
  sorry

@[simp] theorem Iio_diff_Iio {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio b \ Iio a = Ico a b :=
  sorry

theorem Ioo_eq_empty_iff {α : Type u} [linear_order α] {a : α} {b : α} [densely_ordered α] :
    Ioo a b = ∅ ↔ b ≤ a :=
  sorry

theorem Ico_eq_empty_iff {α : Type u} [linear_order α] {a : α} {b : α} : Ico a b = ∅ ↔ b ≤ a :=
  sorry

theorem Icc_eq_empty_iff {α : Type u} [linear_order α] {a : α} {b : α} : Icc a b = ∅ ↔ b < a :=
  sorry

theorem Ico_subset_Ico_iff {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h₁ : a₁ < b₁) : Ico a₁ b₁ ⊆ Ico a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ ≤ b₂ :=
  sorry

theorem Ioo_subset_Ioo_iff {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    [densely_ordered α] (h₁ : a₁ < b₁) : Ioo a₁ b₁ ⊆ Ioo a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ ≤ b₂ :=
  sorry

theorem Ico_eq_Ico_iff {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h : a₁ < b₁ ∨ a₂ < b₂) : Ico a₁ b₁ = Ico a₂ b₂ ↔ a₁ = a₂ ∧ b₁ = b₂ :=
  sorry

@[simp] theorem Ioi_subset_Ioi_iff {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi b ⊆ Ioi a ↔ a ≤ b :=
  { mp :=
      fun (h : Ioi b ⊆ Ioi a) =>
        decidable.by_contradiction fun (ba : ¬a ≤ b) => lt_irrefl a (h (iff.mp not_le ba)),
    mpr := fun (h : a ≤ b) => Ioi_subset_Ioi h }

@[simp] theorem Ioi_subset_Ici_iff {α : Type u} [linear_order α] {a : α} {b : α}
    [densely_ordered α] : Ioi b ⊆ Ici a ↔ a ≤ b :=
  sorry

@[simp] theorem Iio_subset_Iio_iff {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio a ⊆ Iio b ↔ a ≤ b :=
  { mp :=
      fun (h : Iio a ⊆ Iio b) =>
        decidable.by_contradiction fun (ab : ¬a ≤ b) => lt_irrefl b (h (iff.mp not_le ab)),
    mpr := fun (h : a ≤ b) => Iio_subset_Iio h }

@[simp] theorem Iio_subset_Iic_iff {α : Type u} [linear_order α] {a : α} {b : α}
    [densely_ordered α] : Iio a ⊆ Iic b ↔ a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Iio a ⊆ Iic b ↔ a ≤ b)) (Eq.symm (propext diff_eq_empty))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (Iio a \ Iic b = ∅ ↔ a ≤ b)) Iio_diff_Iic))
      (eq.mpr (id (Eq._oldrec (Eq.refl (Ioo b a = ∅ ↔ a ≤ b)) (propext Ioo_eq_empty_iff)))
        (iff.refl (a ≤ b))))

/-! ### Unions of adjacent intervals -/

/-! #### Two infinite intervals -/

@[simp] theorem Iic_union_Ici {α : Type u} [linear_order α] {a : α} : Iic a ∪ Ici a = univ :=
  eq_univ_of_forall fun (x : α) => le_total x a

@[simp] theorem Iio_union_Ici {α : Type u} [linear_order α] {a : α} : Iio a ∪ Ici a = univ :=
  eq_univ_of_forall fun (x : α) => lt_or_le x a

@[simp] theorem Iic_union_Ioi {α : Type u} [linear_order α] {a : α} : Iic a ∪ Ioi a = univ :=
  eq_univ_of_forall fun (x : α) => le_or_lt x a

/-! #### A finite and an infinite interval -/

theorem Ioo_union_Ioi' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c < b) :
    Ioo a b ∪ Ioi c = Ioi (min a c) :=
  sorry

theorem Ioo_union_Ioi {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h : c < max a b) :
    Ioo a b ∪ Ioi c = Ioi (min a c) :=
  sorry

theorem Ioi_subset_Ioo_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi a ⊆ Ioo a b ∪ Ici b :=
  fun (x : α) (hx : x ∈ Ioi a) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr hxb

@[simp] theorem Ioo_union_Ici_eq_Ioi {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) :
    Ioo a b ∪ Ici b = Ioi a :=
  subset.antisymm (fun (x : α) (hx : x ∈ Ioo a b ∪ Ici b) => or.elim hx and.left (lt_of_lt_of_le h))
    Ioi_subset_Ioo_union_Ici

theorem Ici_subset_Ico_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ici a ⊆ Ico a b ∪ Ici b :=
  fun (x : α) (hx : x ∈ Ici a) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr hxb

@[simp] theorem Ico_union_Ici_eq_Ici {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Ico a b ∪ Ici b = Ici a :=
  subset.antisymm (fun (x : α) (hx : x ∈ Ico a b ∪ Ici b) => or.elim hx and.left (le_trans h))
    Ici_subset_Ico_union_Ici

theorem Ico_union_Ici' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c ≤ b) :
    Ico a b ∪ Ici c = Ici (min a c) :=
  sorry

theorem Ico_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h : c ≤ max a b) :
    Ico a b ∪ Ici c = Ici (min a c) :=
  sorry

theorem Ioi_subset_Ioc_union_Ioi {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi a ⊆ Ioc a b ∪ Ioi b :=
  fun (x : α) (hx : x ∈ Ioi a) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := hx, right := hxb })
      fun (hxb : b < x) => Or.inr hxb

@[simp] theorem Ioc_union_Ioi_eq_Ioi {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Ioc a b ∪ Ioi b = Ioi a :=
  subset.antisymm (fun (x : α) (hx : x ∈ Ioc a b ∪ Ioi b) => or.elim hx and.left (lt_of_le_of_lt h))
    Ioi_subset_Ioc_union_Ioi

theorem Ioc_union_Ioi' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c ≤ b) :
    Ioc a b ∪ Ioi c = Ioi (min a c) :=
  sorry

theorem Ioc_union_Ioi {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h : c ≤ max a b) :
    Ioc a b ∪ Ioi c = Ioi (min a c) :=
  sorry

theorem Ici_subset_Icc_union_Ioi {α : Type u} [linear_order α] {a : α} {b : α} :
    Ici a ⊆ Icc a b ∪ Ioi b :=
  fun (x : α) (hx : x ∈ Ici a) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := hx, right := hxb })
      fun (hxb : b < x) => Or.inr hxb

@[simp] theorem Icc_union_Ioi_eq_Ici {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Icc a b ∪ Ioi b = Ici a :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Icc a b ∪ Ioi b) =>
      or.elim hx and.left fun (hx : x ∈ Ioi b) => le_trans h (le_of_lt hx))
    Ici_subset_Icc_union_Ioi

theorem Ioi_subset_Ioc_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioi a ⊆ Ioc a b ∪ Ici b :=
  subset.trans Ioi_subset_Ioo_union_Ici (union_subset_union_left (Ici b) Ioo_subset_Ioc_self)

@[simp] theorem Ioc_union_Ici_eq_Ioi {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) :
    Ioc a b ∪ Ici b = Ioi a :=
  subset.antisymm (fun (x : α) (hx : x ∈ Ioc a b ∪ Ici b) => or.elim hx and.left (lt_of_lt_of_le h))
    Ioi_subset_Ioc_union_Ici

theorem Ici_subset_Icc_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} :
    Ici a ⊆ Icc a b ∪ Ici b :=
  subset.trans Ici_subset_Ico_union_Ici (union_subset_union_left (Ici b) Ico_subset_Icc_self)

@[simp] theorem Icc_union_Ici_eq_Ici {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Icc a b ∪ Ici b = Ici a :=
  subset.antisymm (fun (x : α) (hx : x ∈ Icc a b ∪ Ici b) => or.elim hx and.left (le_trans h))
    Ici_subset_Icc_union_Ici

theorem Icc_union_Ici' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c ≤ b) :
    Icc a b ∪ Ici c = Ici (min a c) :=
  sorry

theorem Icc_union_Ici {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h : c ≤ max a b) :
    Icc a b ∪ Ici c = Ici (min a c) :=
  sorry

/-! #### An infinite and a finite interval -/

theorem Iic_subset_Iio_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} :
    Iic b ⊆ Iio a ∪ Icc a b :=
  fun (x : α) (hx : x ∈ Iic b) =>
    or.elim (lt_or_le x a) (fun (hxa : x < a) => Or.inl hxa)
      fun (hxa : a ≤ x) => Or.inr { left := hxa, right := hx }

@[simp] theorem Iio_union_Icc_eq_Iic {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Iio a ∪ Icc a b = Iic b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iio a ∪ Icc a b) =>
      or.elim hx (fun (hx : x ∈ Iio a) => le_trans (le_of_lt hx) h) and.right)
    Iic_subset_Iio_union_Icc

theorem Iio_subset_Iio_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio b ⊆ Iio a ∪ Ico a b :=
  fun (x : α) (hx : x ∈ Iio b) =>
    or.elim (lt_or_le x a) (fun (hxa : x < a) => Or.inl hxa)
      fun (hxa : a ≤ x) => Or.inr { left := hxa, right := hx }

@[simp] theorem Iio_union_Ico_eq_Iio {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Iio a ∪ Ico a b = Iio b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iio a ∪ Ico a b) =>
      or.elim hx (fun (hx : x ∈ Iio a) => lt_of_lt_of_le hx h) and.right)
    Iio_subset_Iio_union_Ico

theorem Iio_union_Ico' {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h₁ : c ≤ b) :
    Iio b ∪ Ico c d = Iio (max b d) :=
  sorry

theorem Iio_union_Ico {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h : min c d ≤ b) :
    Iio b ∪ Ico c d = Iio (max b d) :=
  sorry

theorem Iic_subset_Iic_union_Ioc {α : Type u} [linear_order α] {a : α} {b : α} :
    Iic b ⊆ Iic a ∪ Ioc a b :=
  fun (x : α) (hx : x ∈ Iic b) =>
    or.elim (le_or_lt x a) (fun (hxa : x ≤ a) => Or.inl hxa)
      fun (hxa : a < x) => Or.inr { left := hxa, right := hx }

@[simp] theorem Iic_union_Ioc_eq_Iic {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Iic a ∪ Ioc a b = Iic b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iic a ∪ Ioc a b) =>
      or.elim hx (fun (hx : x ∈ Iic a) => le_trans hx h) and.right)
    Iic_subset_Iic_union_Ioc

theorem Iic_union_Ioc' {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h₁ : c < b) :
    Iic b ∪ Ioc c d = Iic (max b d) :=
  sorry

theorem Iic_union_Ioc {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h : min c d < b) :
    Iic b ∪ Ioc c d = Iic (max b d) :=
  sorry

theorem Iio_subset_Iic_union_Ioo {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio b ⊆ Iic a ∪ Ioo a b :=
  fun (x : α) (hx : x ∈ Iio b) =>
    or.elim (le_or_lt x a) (fun (hxa : x ≤ a) => Or.inl hxa)
      fun (hxa : a < x) => Or.inr { left := hxa, right := hx }

@[simp] theorem Iic_union_Ioo_eq_Iio {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) :
    Iic a ∪ Ioo a b = Iio b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iic a ∪ Ioo a b) =>
      or.elim hx (fun (hx : x ∈ Iic a) => lt_of_le_of_lt hx h) and.right)
    Iio_subset_Iic_union_Ioo

theorem Iio_union_Ioo' {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h₁ : c < b) :
    Iio b ∪ Ioo c d = Iio (max b d) :=
  sorry

theorem Iio_union_Ioo {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h : min c d < b) :
    Iio b ∪ Ioo c d = Iio (max b d) :=
  sorry

theorem Iic_subset_Iic_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} :
    Iic b ⊆ Iic a ∪ Icc a b :=
  subset.trans Iic_subset_Iic_union_Ioc (union_subset_union_right (Iic a) Ioc_subset_Icc_self)

@[simp] theorem Iic_union_Icc_eq_Iic {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) :
    Iic a ∪ Icc a b = Iic b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iic a ∪ Icc a b) =>
      or.elim hx (fun (hx : x ∈ Iic a) => le_trans hx h) and.right)
    Iic_subset_Iic_union_Icc

theorem Iic_union_Icc' {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h₁ : c ≤ b) :
    Iic b ∪ Icc c d = Iic (max b d) :=
  sorry

theorem Iic_union_Icc {α : Type u} [linear_order α] {b : α} {c : α} {d : α} (h : min c d ≤ b) :
    Iic b ∪ Icc c d = Iic (max b d) :=
  sorry

theorem Iio_subset_Iic_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} :
    Iio b ⊆ Iic a ∪ Ico a b :=
  subset.trans Iio_subset_Iic_union_Ioo (union_subset_union_right (Iic a) Ioo_subset_Ico_self)

@[simp] theorem Iic_union_Ico_eq_Iio {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) :
    Iic a ∪ Ico a b = Iio b :=
  subset.antisymm
    (fun (x : α) (hx : x ∈ Iic a ∪ Ico a b) =>
      or.elim hx (fun (hx : x ∈ Iic a) => lt_of_le_of_lt hx h) and.right)
    Iio_subset_Iic_union_Ico

/-! #### Two finite intervals, `I?o` and `Ic?` -/

theorem Ioo_subset_Ioo_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioo a c ⊆ Ioo a b ∪ Ico b c :=
  fun (x : α) (hx : x ∈ Ioo a c) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ioo_union_Ico_eq_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a < b) (h₂ : b ≤ c) : Ioo a b ∪ Ico b c = Ioo a c :=
  sorry

theorem Ico_subset_Ico_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ico a c ⊆ Ico a b ∪ Ico b c :=
  fun (x : α) (hx : x ∈ Ico a c) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ico_union_Ico_eq_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b ≤ c) : Ico a b ∪ Ico b c = Ico a c :=
  sorry

theorem Ico_union_Ico' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α} (h₁ : c ≤ b)
    (h₂ : a ≤ d) : Ico a b ∪ Ico c d = Ico (min a c) (max b d) :=
  sorry

theorem Ico_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α}
    (h₁ : min a b ≤ max c d) (h₂ : min c d ≤ max a b) :
    Ico a b ∪ Ico c d = Ico (min a c) (max b d) :=
  sorry

theorem Icc_subset_Ico_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Icc a c ⊆ Ico a b ∪ Icc b c :=
  fun (x : α) (hx : x ∈ Icc a c) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ico_union_Icc_eq_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b ≤ c) : Ico a b ∪ Icc b c = Icc a c :=
  sorry

theorem Ioc_subset_Ioo_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioc a c ⊆ Ioo a b ∪ Icc b c :=
  fun (x : α) (hx : x ∈ Ioc a c) =>
    or.elim (lt_or_le x b) (fun (hxb : x < b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b ≤ x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ioo_union_Icc_eq_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a < b) (h₂ : b ≤ c) : Ioo a b ∪ Icc b c = Ioc a c :=
  sorry

/-! #### Two finite intervals, `I?c` and `Io?` -/

theorem Ioo_subset_Ioc_union_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioo a c ⊆ Ioc a b ∪ Ioo b c :=
  fun (x : α) (hx : x ∈ Ioo a c) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b < x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ioc_union_Ioo_eq_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b < c) : Ioc a b ∪ Ioo b c = Ioo a c :=
  sorry

theorem Ico_subset_Icc_union_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ico a c ⊆ Icc a b ∪ Ioo b c :=
  fun (x : α) (hx : x ∈ Ico a c) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b < x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Icc_union_Ioo_eq_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b < c) : Icc a b ∪ Ioo b c = Ico a c :=
  sorry

theorem Icc_subset_Icc_union_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Icc a c ⊆ Icc a b ∪ Ioc b c :=
  fun (x : α) (hx : x ∈ Icc a c) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b < x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Icc_union_Ioc_eq_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b ≤ c) : Icc a b ∪ Ioc b c = Icc a c :=
  sorry

theorem Ioc_subset_Ioc_union_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioc a c ⊆ Ioc a b ∪ Ioc b c :=
  fun (x : α) (hx : x ∈ Ioc a c) =>
    or.elim (le_or_lt x b) (fun (hxb : x ≤ b) => Or.inl { left := and.left hx, right := hxb })
      fun (hxb : b < x) => Or.inr { left := hxb, right := and.right hx }

@[simp] theorem Ioc_union_Ioc_eq_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b ≤ c) : Ioc a b ∪ Ioc b c = Ioc a c :=
  sorry

theorem Ioc_union_Ioc' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α} (h₁ : c ≤ b)
    (h₂ : a ≤ d) : Ioc a b ∪ Ioc c d = Ioc (min a c) (max b d) :=
  sorry

theorem Ioc_union_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α}
    (h₁ : min a b ≤ max c d) (h₂ : min c d ≤ max a b) :
    Ioc a b ∪ Ioc c d = Ioc (min a c) (max b d) :=
  sorry

/-! #### Two finite intervals with a common point -/

theorem Ioo_subset_Ioc_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioo a c ⊆ Ioc a b ∪ Ico b c :=
  subset.trans Ioo_subset_Ioc_union_Ioo (union_subset_union_right (Ioc a b) Ioo_subset_Ico_self)

@[simp] theorem Ioc_union_Ico_eq_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a < b) (h₂ : b < c) : Ioc a b ∪ Ico b c = Ioo a c :=
  sorry

theorem Ico_subset_Icc_union_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ico a c ⊆ Icc a b ∪ Ico b c :=
  subset.trans Ico_subset_Icc_union_Ioo (union_subset_union_right (Icc a b) Ioo_subset_Ico_self)

@[simp] theorem Icc_union_Ico_eq_Ico {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b < c) : Icc a b ∪ Ico b c = Ico a c :=
  sorry

theorem Icc_subset_Icc_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Icc a c ⊆ Icc a b ∪ Icc b c :=
  subset.trans Icc_subset_Icc_union_Ioc (union_subset_union_right (Icc a b) Ioc_subset_Icc_self)

@[simp] theorem Icc_union_Icc_eq_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a ≤ b) (h₂ : b ≤ c) : Icc a b ∪ Icc b c = Icc a c :=
  sorry

theorem Icc_union_Icc' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α} (h₁ : c ≤ b)
    (h₂ : a ≤ d) : Icc a b ∪ Icc c d = Icc (min a c) (max b d) :=
  sorry

/--
We cannot replace `<` by `≤` in the hypotheses.
Otherwise for `b < a = d < c` the l.h.s. is `∅` and the r.h.s. is `{a}`.
-/
theorem Icc_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α}
    (h₁ : min a b < max c d) (h₂ : min c d < max a b) :
    Icc a b ∪ Icc c d = Icc (min a c) (max b d) :=
  sorry

theorem Ioc_subset_Ioc_union_Icc {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioc a c ⊆ Ioc a b ∪ Icc b c :=
  subset.trans Ioc_subset_Ioc_union_Ioc (union_subset_union_right (Ioc a b) Ioc_subset_Icc_self)

@[simp] theorem Ioc_union_Icc_eq_Ioc {α : Type u} [linear_order α] {a : α} {b : α} {c : α}
    (h₁ : a < b) (h₂ : b ≤ c) : Ioc a b ∪ Icc b c = Ioc a c :=
  sorry

theorem Ioo_union_Ioo' {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α} (h₁ : c < b)
    (h₂ : a < d) : Ioo a b ∪ Ioo c d = Ioo (min a c) (max b d) :=
  sorry

theorem Ioo_union_Ioo {α : Type u} [linear_order α] {a : α} {b : α} {c : α} {d : α}
    (h₁ : min a b < max c d) (h₂ : min c d < max a b) :
    Ioo a b ∪ Ioo c d = Ioo (min a c) (max b d) :=
  sorry

@[simp] theorem Iic_inter_Iic {α : Type u} [semilattice_inf α] {a : α} {b : α} :
    Iic a ∩ Iic b = Iic (a ⊓ b) :=
  sorry

@[simp] theorem Iio_inter_Iio {α : Type u} [semilattice_inf α] [is_total α LessEq] {a : α} {b : α} :
    Iio a ∩ Iio b = Iio (a ⊓ b) :=
  sorry

@[simp] theorem Ici_inter_Ici {α : Type u} [semilattice_sup α] {a : α} {b : α} :
    Ici a ∩ Ici b = Ici (a ⊔ b) :=
  sorry

@[simp] theorem Ioi_inter_Ioi {α : Type u} [semilattice_sup α] [is_total α LessEq] {a : α} {b : α} :
    Ioi a ∩ Ioi b = Ioi (a ⊔ b) :=
  sorry

theorem Icc_inter_Icc {α : Type u} [lattice α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α} :
    Icc a₁ b₁ ∩ Icc a₂ b₂ = Icc (a₁ ⊔ a₂) (b₁ ⊓ b₂) :=
  sorry

@[simp] theorem Icc_inter_Icc_eq_singleton {α : Type u} [lattice α] {a : α} {b : α} {c : α}
    (hab : a ≤ b) (hbc : b ≤ c) : Icc a b ∩ Icc b c = singleton b :=
  sorry

theorem Ico_inter_Ico {α : Type u} [lattice α] [ht : is_total α LessEq] {a₁ : α} {a₂ : α} {b₁ : α}
    {b₂ : α} : Ico a₁ b₁ ∩ Ico a₂ b₂ = Ico (a₁ ⊔ a₂) (b₁ ⊓ b₂) :=
  sorry

theorem Ioc_inter_Ioc {α : Type u} [lattice α] [ht : is_total α LessEq] {a₁ : α} {a₂ : α} {b₁ : α}
    {b₂ : α} : Ioc a₁ b₁ ∩ Ioc a₂ b₂ = Ioc (a₁ ⊔ a₂) (b₁ ⊓ b₂) :=
  sorry

theorem Ioo_inter_Ioo {α : Type u} [lattice α] [ht : is_total α LessEq] {a₁ : α} {a₂ : α} {b₁ : α}
    {b₂ : α} : Ioo a₁ b₁ ∩ Ioo a₂ b₂ = Ioo (a₁ ⊔ a₂) (b₁ ⊓ b₂) :=
  sorry

theorem Ioc_inter_Ioo_of_left_lt {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h : b₁ < b₂) : Ioc a₁ b₁ ∩ Ioo a₂ b₂ = Ioc (max a₁ a₂) b₁ :=
  sorry

theorem Ioc_inter_Ioo_of_right_le {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h : b₂ ≤ b₁) : Ioc a₁ b₁ ∩ Ioo a₂ b₂ = Ioo (max a₁ a₂) b₂ :=
  sorry

theorem Ioo_inter_Ioc_of_left_le {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h : b₁ ≤ b₂) : Ioo a₁ b₁ ∩ Ioc a₂ b₂ = Ioo (max a₁ a₂) b₁ :=
  sorry

theorem Ioo_inter_Ioc_of_right_lt {α : Type u} [linear_order α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}
    (h : b₂ < b₁) : Ioo a₁ b₁ ∩ Ioc a₂ b₂ = Ioc (max a₁ a₂) b₂ :=
  sorry

@[simp] theorem Ico_diff_Iio {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ico a b \ Iio c = Ico (max a c) b :=
  sorry

@[simp] theorem Ico_inter_Iio {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ico a b ∩ Iio c = Ico a (min b c) :=
  sorry

@[simp] theorem Ioc_union_Ioc_right {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioc a b ∪ Ioc a c = Ioc a (max b c) :=
  sorry

@[simp] theorem Ioc_union_Ioc_left {α : Type u} [linear_order α] {a : α} {b : α} {c : α} :
    Ioc a c ∪ Ioc b c = Ioc (min a b) c :=
  sorry

@[simp] theorem Ioc_union_Ioc_symm {α : Type u} [linear_order α] {a : α} {b : α} :
    Ioc a b ∪ Ioc b a = Ioc (min a b) (max a b) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (Ioc a b ∪ Ioc b a = Ioc (min a b) (max a b))) (max_comm a b)))
    (Ioc_union_Ioc
      (eq.mpr (id (Eq._oldrec (Eq.refl (min a b ≤ max b a)) (max_comm b a))) min_le_max)
      (eq.mpr (id (Eq._oldrec (Eq.refl (min b a ≤ max a b)) (max_comm a b))) min_le_max))

@[simp] theorem Ioc_union_Ioc_union_Ioc_cycle {α : Type u} [linear_order α] {a : α} {b : α}
    {c : α} : Ioc a b ∪ Ioc b c ∪ Ioc c a = Ioc (min a (min b c)) (max a (max b c)) :=
  sorry

/-- If we remove a smaller interval from a larger, the result is nonempty -/
theorem nonempty_Ico_sdiff {α : Type u} [linear_ordered_add_comm_group α] {x : α} {dx : α} {y : α}
    {dy : α} (h : dy < dx) (hx : 0 < dx) : Nonempty ↥(Ico x (x + dx) \ Ico y (y + dy)) :=
  sorry

end Mathlib


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Neil Strickland

! This file was ported from Lean 3 source module data.pnat.prime
! leanprover-community/mathlib commit baba818b9acea366489e8ba32d2cc0fcaf50a1f7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Nat.Prime
import Mathbin.Data.Pnat.Basic

/-!
# Primality and GCD on pnat

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

This file extends the theory of `ℕ+` with `gcd`, `lcm` and `prime` functions, analogous to those on
`nat`.
-/

namespace Nat.Primes

#print Nat.Primes.coePNat /-
instance coePNat : Coe Nat.Primes ℕ+ :=
  ⟨fun p => ⟨(p : ℕ), p.property.Pos⟩⟩
#align nat.primes.coe_pnat Nat.Primes.coePNat
-/

#print Nat.Primes.coe_pnat_nat /-
@[norm_cast]
theorem coe_pnat_nat (p : Nat.Primes) : ((p : ℕ+) : ℕ) = p :=
  rfl
#align nat.primes.coe_pnat_nat Nat.Primes.coe_pnat_nat
-/

#print Nat.Primes.coe_pnat_injective /-
theorem coe_pnat_injective : Function.Injective (coe : Nat.Primes → ℕ+) := fun p q h =>
  Subtype.ext (congr_arg Subtype.val h : _)
#align nat.primes.coe_pnat_injective Nat.Primes.coe_pnat_injective
-/

#print Nat.Primes.coe_pnat_inj /-
@[norm_cast]
theorem coe_pnat_inj (p q : Nat.Primes) : (p : ℕ+) = (q : ℕ+) ↔ p = q :=
  coe_pnat_injective.eq_iff
#align nat.primes.coe_pnat_inj Nat.Primes.coe_pnat_inj
-/

end Nat.Primes

namespace PNat

open _Root_.Nat

#print PNat.gcd /-
/-- The greatest common divisor (gcd) of two positive natural numbers,
  viewed as positive natural number. -/
def gcd (n m : ℕ+) : ℕ+ :=
  ⟨Nat.gcd (n : ℕ) (m : ℕ), Nat.gcd_pos_of_pos_left (m : ℕ) n.Pos⟩
#align pnat.gcd PNat.gcd
-/

#print PNat.lcm /-
/-- The least common multiple (lcm) of two positive natural numbers,
  viewed as positive natural number. -/
def lcm (n m : ℕ+) : ℕ+ :=
  ⟨Nat.lcm (n : ℕ) (m : ℕ), by
    let h := mul_pos n.pos m.pos
    rw [← gcd_mul_lcm (n : ℕ) (m : ℕ), mul_comm] at h
    exact pos_of_dvd_of_pos (Dvd.intro (Nat.gcd (n : ℕ) (m : ℕ)) rfl) h⟩
#align pnat.lcm PNat.lcm
-/

#print PNat.gcd_coe /-
@[simp, norm_cast]
theorem gcd_coe (n m : ℕ+) : (gcd n m : ℕ) = Nat.gcd n m :=
  rfl
#align pnat.gcd_coe PNat.gcd_coe
-/

#print PNat.lcm_coe /-
@[simp, norm_cast]
theorem lcm_coe (n m : ℕ+) : (lcm n m : ℕ) = Nat.lcm n m :=
  rfl
#align pnat.lcm_coe PNat.lcm_coe
-/

#print PNat.gcd_dvd_left /-
theorem gcd_dvd_left (n m : ℕ+) : gcd n m ∣ n :=
  dvd_iff.2 (Nat.gcd_dvd_left (n : ℕ) (m : ℕ))
#align pnat.gcd_dvd_left PNat.gcd_dvd_left
-/

#print PNat.gcd_dvd_right /-
theorem gcd_dvd_right (n m : ℕ+) : gcd n m ∣ m :=
  dvd_iff.2 (Nat.gcd_dvd_right (n : ℕ) (m : ℕ))
#align pnat.gcd_dvd_right PNat.gcd_dvd_right
-/

#print PNat.dvd_gcd /-
theorem dvd_gcd {m n k : ℕ+} (hm : k ∣ m) (hn : k ∣ n) : k ∣ gcd m n :=
  dvd_iff.2 (@Nat.dvd_gcd (m : ℕ) (n : ℕ) (k : ℕ) (dvd_iff.1 hm) (dvd_iff.1 hn))
#align pnat.dvd_gcd PNat.dvd_gcd
-/

#print PNat.dvd_lcm_left /-
theorem dvd_lcm_left (n m : ℕ+) : n ∣ lcm n m :=
  dvd_iff.2 (Nat.dvd_lcm_left (n : ℕ) (m : ℕ))
#align pnat.dvd_lcm_left PNat.dvd_lcm_left
-/

#print PNat.dvd_lcm_right /-
theorem dvd_lcm_right (n m : ℕ+) : m ∣ lcm n m :=
  dvd_iff.2 (Nat.dvd_lcm_right (n : ℕ) (m : ℕ))
#align pnat.dvd_lcm_right PNat.dvd_lcm_right
-/

#print PNat.lcm_dvd /-
theorem lcm_dvd {m n k : ℕ+} (hm : m ∣ k) (hn : n ∣ k) : lcm m n ∣ k :=
  dvd_iff.2 (@Nat.lcm_dvd (m : ℕ) (n : ℕ) (k : ℕ) (dvd_iff.1 hm) (dvd_iff.1 hn))
#align pnat.lcm_dvd PNat.lcm_dvd
-/

/- warning: pnat.gcd_mul_lcm -> PNat.gcd_mul_lcm is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Eq.{1} PNat (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) (PNat.gcd n m) (PNat.lcm n m)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) n m)
but is expected to have type
  forall (n : PNat) (m : PNat), Eq.{1} PNat (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) (PNat.gcd n m) (PNat.lcm n m)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) n m)
Case conversion may be inaccurate. Consider using '#align pnat.gcd_mul_lcm PNat.gcd_mul_lcmₓ'. -/
theorem gcd_mul_lcm (n m : ℕ+) : gcd n m * lcm n m = n * m :=
  Subtype.eq (Nat.gcd_mul_lcm (n : ℕ) (m : ℕ))
#align pnat.gcd_mul_lcm PNat.gcd_mul_lcm

/- warning: pnat.eq_one_of_lt_two -> PNat.eq_one_of_lt_two is a dubious translation:
lean 3 declaration is
  forall {n : PNat}, (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid)))) n (OfNat.ofNat.{0} PNat 2 (OfNat.mk.{0} PNat 2 (bit0.{0} PNat PNat.hasAdd (One.one.{0} PNat PNat.hasOne))))) -> (Eq.{1} PNat n (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne))))
but is expected to have type
  forall {n : PNat}, (LT.lt.{0} PNat (Preorder.toLT.{0} PNat (PartialOrder.toPreorder.{0} PNat (OrderedCancelCommMonoid.toPartialOrder.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))) n (OfNat.ofNat.{0} PNat 2 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) -> (Eq.{1} PNat n (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))))
Case conversion may be inaccurate. Consider using '#align pnat.eq_one_of_lt_two PNat.eq_one_of_lt_twoₓ'. -/
theorem eq_one_of_lt_two {n : ℕ+} : n < 2 → n = 1 :=
  by
  intro h; apply le_antisymm; swap; apply PNat.one_le
  change n < 1 + 1 at h; rw [PNat.lt_add_one_iff] at h; apply h
#align pnat.eq_one_of_lt_two PNat.eq_one_of_lt_two

section Prime

/-! ### Prime numbers -/

#print PNat.Prime /-
/-- Primality predicate for `ℕ+`, defined in terms of `nat.prime`. -/
def Prime (p : ℕ+) : Prop :=
  (p : ℕ).Prime
#align pnat.prime PNat.Prime
-/

#print PNat.Prime.one_lt /-
theorem Prime.one_lt {p : ℕ+} : p.Prime → 1 < p :=
  Nat.Prime.one_lt
#align pnat.prime.one_lt PNat.Prime.one_lt
-/

/- warning: pnat.prime_two -> PNat.prime_two is a dubious translation:
lean 3 declaration is
  PNat.Prime (OfNat.ofNat.{0} PNat 2 (OfNat.mk.{0} PNat 2 (bit0.{0} PNat PNat.hasAdd (One.one.{0} PNat PNat.hasOne))))
but is expected to have type
  PNat.Prime (OfNat.ofNat.{0} PNat 2 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))
Case conversion may be inaccurate. Consider using '#align pnat.prime_two PNat.prime_twoₓ'. -/
theorem prime_two : (2 : ℕ+).Prime :=
  Nat.prime_two
#align pnat.prime_two PNat.prime_two

#print PNat.dvd_prime /-
theorem dvd_prime {p m : ℕ+} (pp : p.Prime) : m ∣ p ↔ m = 1 ∨ m = p :=
  by
  rw [PNat.dvd_iff]
  rw [Nat.dvd_prime pp]
  simp
#align pnat.dvd_prime PNat.dvd_prime
-/

#print PNat.Prime.ne_one /-
theorem Prime.ne_one {p : ℕ+} : p.Prime → p ≠ 1 :=
  by
  intro pp
  intro contra
  apply Nat.Prime.ne_one pp
  rw [PNat.coe_eq_one_iff]
  apply contra
#align pnat.prime.ne_one PNat.Prime.ne_one
-/

#print PNat.not_prime_one /-
@[simp]
theorem not_prime_one : ¬(1 : ℕ+).Prime :=
  Nat.not_prime_one
#align pnat.not_prime_one PNat.not_prime_one
-/

#print PNat.Prime.not_dvd_one /-
theorem Prime.not_dvd_one {p : ℕ+} : p.Prime → ¬p ∣ 1 := fun pp : p.Prime =>
  by
  rw [dvd_iff]
  apply Nat.Prime.not_dvd_one pp
#align pnat.prime.not_dvd_one PNat.Prime.not_dvd_one
-/

#print PNat.exists_prime_and_dvd /-
theorem exists_prime_and_dvd {n : ℕ+} (hn : n ≠ 1) : ∃ p : ℕ+, p.Prime ∧ p ∣ n :=
  by
  obtain ⟨p, hp⟩ := Nat.exists_prime_and_dvd (mt coe_eq_one_iff.mp hn)
  exists (⟨p, Nat.Prime.pos hp.left⟩ : ℕ+); rw [dvd_iff]; apply hp
#align pnat.exists_prime_and_dvd PNat.exists_prime_and_dvd
-/

end Prime

section Coprime

/-! ### Coprime numbers and gcd -/

#print PNat.Coprime /-
/-- Two pnats are coprime if their gcd is 1. -/
def Coprime (m n : ℕ+) : Prop :=
  m.gcd n = 1
#align pnat.coprime PNat.Coprime
-/

#print PNat.coprime_coe /-
@[simp, norm_cast]
theorem coprime_coe {m n : ℕ+} : Nat.coprime ↑m ↑n ↔ m.coprime n :=
  by
  unfold coprime
  unfold Nat.coprime
  rw [← coe_inj]
  simp
#align pnat.coprime_coe PNat.coprime_coe
-/

/- warning: pnat.coprime.mul -> PNat.Coprime.mul is a dubious translation:
lean 3 declaration is
  forall {k : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m k) -> (PNat.Coprime n k) -> (PNat.Coprime (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) m n) k)
but is expected to have type
  forall {k : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m k) -> (PNat.Coprime n k) -> (PNat.Coprime (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) m n) k)
Case conversion may be inaccurate. Consider using '#align pnat.coprime.mul PNat.Coprime.mulₓ'. -/
theorem Coprime.mul {k m n : ℕ+} : m.coprime k → n.coprime k → (m * n).coprime k :=
  by
  repeat' rw [← coprime_coe]
  rw [mul_coe]
  apply Nat.coprime.mul
#align pnat.coprime.mul PNat.Coprime.mul

/- warning: pnat.coprime.mul_right -> PNat.Coprime.mul_right is a dubious translation:
lean 3 declaration is
  forall {k : PNat} {m : PNat} {n : PNat}, (PNat.Coprime k m) -> (PNat.Coprime k n) -> (PNat.Coprime k (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) m n))
but is expected to have type
  forall {k : PNat} {m : PNat} {n : PNat}, (PNat.Coprime k m) -> (PNat.Coprime k n) -> (PNat.Coprime k (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) m n))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.mul_right PNat.Coprime.mul_rightₓ'. -/
theorem Coprime.mul_right {k m n : ℕ+} : k.coprime m → k.coprime n → k.coprime (m * n) :=
  by
  repeat' rw [← coprime_coe]
  rw [mul_coe]
  apply Nat.coprime.mul_right
#align pnat.coprime.mul_right PNat.Coprime.mul_right

#print PNat.gcd_comm /-
theorem gcd_comm {m n : ℕ+} : m.gcd n = n.gcd m :=
  by
  apply Eq
  simp only [gcd_coe]
  apply Nat.gcd_comm
#align pnat.gcd_comm PNat.gcd_comm
-/

#print PNat.gcd_eq_left_iff_dvd /-
theorem gcd_eq_left_iff_dvd {m n : ℕ+} : m ∣ n ↔ m.gcd n = m :=
  by
  rw [dvd_iff]
  rw [Nat.gcd_eq_left_iff_dvd]
  rw [← coe_inj]
  simp
#align pnat.gcd_eq_left_iff_dvd PNat.gcd_eq_left_iff_dvd
-/

#print PNat.gcd_eq_right_iff_dvd /-
theorem gcd_eq_right_iff_dvd {m n : ℕ+} : m ∣ n ↔ n.gcd m = m :=
  by
  rw [gcd_comm]
  apply gcd_eq_left_iff_dvd
#align pnat.gcd_eq_right_iff_dvd PNat.gcd_eq_right_iff_dvd
-/

/- warning: pnat.coprime.gcd_mul_left_cancel -> PNat.Coprime.gcd_mul_left_cancel is a dubious translation:
lean 3 declaration is
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k n) -> (Eq.{1} PNat (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) k m) n) (PNat.gcd m n))
but is expected to have type
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k n) -> (Eq.{1} PNat (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) k m) n) (PNat.gcd m n))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.gcd_mul_left_cancel PNat.Coprime.gcd_mul_left_cancelₓ'. -/
theorem Coprime.gcd_mul_left_cancel (m : ℕ+) {n k : ℕ+} : k.coprime n → (k * m).gcd n = m.gcd n :=
  by
  intro h; apply Eq; simp only [gcd_coe, mul_coe]
  apply Nat.coprime.gcd_mul_left_cancel; simpa
#align pnat.coprime.gcd_mul_left_cancel PNat.Coprime.gcd_mul_left_cancel

/- warning: pnat.coprime.gcd_mul_right_cancel -> PNat.Coprime.gcd_mul_right_cancel is a dubious translation:
lean 3 declaration is
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k n) -> (Eq.{1} PNat (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) m k) n) (PNat.gcd m n))
but is expected to have type
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k n) -> (Eq.{1} PNat (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) m k) n) (PNat.gcd m n))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.gcd_mul_right_cancel PNat.Coprime.gcd_mul_right_cancelₓ'. -/
theorem Coprime.gcd_mul_right_cancel (m : ℕ+) {n k : ℕ+} : k.coprime n → (m * k).gcd n = m.gcd n :=
  by rw [mul_comm]; apply coprime.gcd_mul_left_cancel
#align pnat.coprime.gcd_mul_right_cancel PNat.Coprime.gcd_mul_right_cancel

/- warning: pnat.coprime.gcd_mul_left_cancel_right -> PNat.Coprime.gcd_mul_left_cancel_right is a dubious translation:
lean 3 declaration is
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k m) -> (Eq.{1} PNat (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) k n)) (PNat.gcd m n))
but is expected to have type
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k m) -> (Eq.{1} PNat (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) k n)) (PNat.gcd m n))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.gcd_mul_left_cancel_right PNat.Coprime.gcd_mul_left_cancel_rightₓ'. -/
theorem Coprime.gcd_mul_left_cancel_right (m : ℕ+) {n k : ℕ+} :
    k.coprime m → m.gcd (k * n) = m.gcd n := by intro h; iterate 2 rw [gcd_comm]; symm;
  apply coprime.gcd_mul_left_cancel _ h
#align pnat.coprime.gcd_mul_left_cancel_right PNat.Coprime.gcd_mul_left_cancel_right

/- warning: pnat.coprime.gcd_mul_right_cancel_right -> PNat.Coprime.gcd_mul_right_cancel_right is a dubious translation:
lean 3 declaration is
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k m) -> (Eq.{1} PNat (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) n k)) (PNat.gcd m n))
but is expected to have type
  forall (m : PNat) {n : PNat} {k : PNat}, (PNat.Coprime k m) -> (Eq.{1} PNat (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) n k)) (PNat.gcd m n))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.gcd_mul_right_cancel_right PNat.Coprime.gcd_mul_right_cancel_rightₓ'. -/
theorem Coprime.gcd_mul_right_cancel_right (m : ℕ+) {n k : ℕ+} :
    k.coprime m → m.gcd (n * k) = m.gcd n := by rw [mul_comm];
  apply coprime.gcd_mul_left_cancel_right
#align pnat.coprime.gcd_mul_right_cancel_right PNat.Coprime.gcd_mul_right_cancel_right

#print PNat.one_gcd /-
@[simp]
theorem one_gcd {n : ℕ+} : gcd 1 n = 1 :=
  by
  rw [← gcd_eq_left_iff_dvd]
  apply one_dvd
#align pnat.one_gcd PNat.one_gcd
-/

#print PNat.gcd_one /-
@[simp]
theorem gcd_one {n : ℕ+} : gcd n 1 = 1 := by
  rw [gcd_comm]
  apply one_gcd
#align pnat.gcd_one PNat.gcd_one
-/

#print PNat.Coprime.symm /-
@[symm]
theorem Coprime.symm {m n : ℕ+} : m.coprime n → n.coprime m :=
  by
  unfold coprime
  rw [gcd_comm]
  simp
#align pnat.coprime.symm PNat.Coprime.symm
-/

#print PNat.one_coprime /-
@[simp]
theorem one_coprime {n : ℕ+} : (1 : ℕ+).coprime n :=
  one_gcd
#align pnat.one_coprime PNat.one_coprime
-/

#print PNat.coprime_one /-
@[simp]
theorem coprime_one {n : ℕ+} : n.coprime 1 :=
  Coprime.symm one_coprime
#align pnat.coprime_one PNat.coprime_one
-/

#print PNat.Coprime.coprime_dvd_left /-
theorem Coprime.coprime_dvd_left {m k n : ℕ+} : m ∣ k → k.coprime n → m.coprime n :=
  by
  rw [dvd_iff]
  repeat' rw [← coprime_coe]
  apply Nat.coprime.coprime_dvd_left
#align pnat.coprime.coprime_dvd_left PNat.Coprime.coprime_dvd_left
-/

/- warning: pnat.coprime.factor_eq_gcd_left -> PNat.Coprime.factor_eq_gcd_left is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) a m) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) a b) m))
but is expected to have type
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) a m) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) a b) m))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.factor_eq_gcd_left PNat.Coprime.factor_eq_gcd_leftₓ'. -/
theorem Coprime.factor_eq_gcd_left {a b m n : ℕ+} (cop : m.coprime n) (am : a ∣ m) (bn : b ∣ n) :
    a = (a * b).gcd m := by
  rw [gcd_eq_left_iff_dvd] at am
  conv_lhs => rw [← am]; symm
  apply coprime.gcd_mul_right_cancel a
  apply coprime.coprime_dvd_left bn cop.symm
#align pnat.coprime.factor_eq_gcd_left PNat.Coprime.factor_eq_gcd_left

/- warning: pnat.coprime.factor_eq_gcd_right -> PNat.Coprime.factor_eq_gcd_right is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) a m) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) b a) m))
but is expected to have type
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) a m) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) b a) m))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.factor_eq_gcd_right PNat.Coprime.factor_eq_gcd_rightₓ'. -/
theorem Coprime.factor_eq_gcd_right {a b m n : ℕ+} (cop : m.coprime n) (am : a ∣ m) (bn : b ∣ n) :
    a = (b * a).gcd m := by rw [mul_comm]; apply coprime.factor_eq_gcd_left cop am bn
#align pnat.coprime.factor_eq_gcd_right PNat.Coprime.factor_eq_gcd_right

/- warning: pnat.coprime.factor_eq_gcd_left_right -> PNat.Coprime.factor_eq_gcd_left_right is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) a m) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) a b)))
but is expected to have type
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) a m) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) a b)))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.factor_eq_gcd_left_right PNat.Coprime.factor_eq_gcd_left_rightₓ'. -/
theorem Coprime.factor_eq_gcd_left_right {a b m n : ℕ+} (cop : m.coprime n) (am : a ∣ m)
    (bn : b ∣ n) : a = m.gcd (a * b) := by rw [gcd_comm]; apply coprime.factor_eq_gcd_left cop am bn
#align pnat.coprime.factor_eq_gcd_left_right PNat.Coprime.factor_eq_gcd_left_right

/- warning: pnat.coprime.factor_eq_gcd_right_right -> PNat.Coprime.factor_eq_gcd_right_right is a dubious translation:
lean 3 declaration is
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) a m) -> (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) b a)))
but is expected to have type
  forall {a : PNat} {b : PNat} {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) a m) -> (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) b n) -> (Eq.{1} PNat a (PNat.gcd m (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) b a)))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.factor_eq_gcd_right_right PNat.Coprime.factor_eq_gcd_right_rightₓ'. -/
theorem Coprime.factor_eq_gcd_right_right {a b m n : ℕ+} (cop : m.coprime n) (am : a ∣ m)
    (bn : b ∣ n) : a = m.gcd (b * a) := by rw [gcd_comm];
  apply coprime.factor_eq_gcd_right cop am bn
#align pnat.coprime.factor_eq_gcd_right_right PNat.Coprime.factor_eq_gcd_right_right

/- warning: pnat.coprime.gcd_mul -> PNat.Coprime.gcd_mul is a dubious translation:
lean 3 declaration is
  forall (k : PNat) {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Eq.{1} PNat (PNat.gcd k (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) m n)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) (PNat.gcd k m) (PNat.gcd k n)))
but is expected to have type
  forall (k : PNat) {m : PNat} {n : PNat}, (PNat.Coprime m n) -> (Eq.{1} PNat (PNat.gcd k (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) m n)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) (PNat.gcd k m) (PNat.gcd k n)))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.gcd_mul PNat.Coprime.gcd_mulₓ'. -/
theorem Coprime.gcd_mul (k : ℕ+) {m n : ℕ+} (h : m.coprime n) : k.gcd (m * n) = k.gcd m * k.gcd n :=
  by
  rw [← coprime_coe] at h; apply Eq
  simp only [gcd_coe, mul_coe]; apply Nat.coprime.gcd_mul k h
#align pnat.coprime.gcd_mul PNat.Coprime.gcd_mul

#print PNat.gcd_eq_left /-
theorem gcd_eq_left {m n : ℕ+} : m ∣ n → m.gcd n = m :=
  by
  rw [dvd_iff]
  intro h
  apply Eq
  simp only [gcd_coe]
  apply Nat.gcd_eq_left h
#align pnat.gcd_eq_left PNat.gcd_eq_left
-/

/- warning: pnat.coprime.pow -> PNat.Coprime.pow is a dubious translation:
lean 3 declaration is
  forall {m : PNat} {n : PNat} (k : Nat) (l : Nat), (PNat.Coprime m n) -> (PNat.Coprime (HPow.hPow.{0, 0, 0} PNat Nat PNat (instHPow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) m k) (HPow.hPow.{0, 0, 0} PNat Nat PNat (instHPow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) n l))
but is expected to have type
  forall {m : PNat} {n : PNat} (k : Nat) (l : Nat), (PNat.Coprime m n) -> (Nat.coprime (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) (PNat.val m) k) (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) (PNat.val n) l))
Case conversion may be inaccurate. Consider using '#align pnat.coprime.pow PNat.Coprime.powₓ'. -/
theorem Coprime.pow {m n : ℕ+} (k l : ℕ) (h : m.coprime n) : (m ^ k).coprime (n ^ l) := by
  rw [← coprime_coe] at *; simp only [pow_coe]; apply Nat.coprime.pow; apply h
#align pnat.coprime.pow PNat.Coprime.pow

end Coprime

end PNat


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

! This file was ported from Lean 3 source module data.fintype.option
! leanprover-community/mathlib commit 509de852e1de55e1efa8eacfa11df0823f26f226
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Fintype.Card
import Mathlib.Data.Finset.Option

/-!
# fintype instances for option
-/

open Function

open Nat

universe u v

variable {α β γ : Type _}

open Finset Function

instance {α : Type _} [Fintype α] : Fintype (Option α) :=
  ⟨Finset.insertNone univ, fun a => by simp⟩

theorem univ_option (α : Type _) [Fintype α] : (univ : Finset (Option α)) = insertNone univ :=
  rfl
#align univ_option univ_option

@[simp]
theorem Fintype.card_option {α : Type _} [Fintype α] :
    Fintype.card (Option α) = Fintype.card α + 1 :=
  (Finset.card_cons (by simp)).trans <| congr_arg₂ _ (card_map _) rfl
#align fintype.card_option Fintype.card_option

/-- If `Option α` is a `Fintype` then so is `α` -/
def fintypeOfOption {α : Type _} [Fintype (Option α)] : Fintype α :=
  ⟨Finset.eraseNone (Fintype.elems (α := Option α)), fun x =>
    mem_eraseNone.mpr (Fintype.complete (some x))⟩
#align fintype_of_option fintypeOfOption

/-- A type is a `Fintype` if its successor (using `Option`) is a `Fintype`. -/
def fintypeOfOptionEquiv [Fintype α] (f : α ≃ Option β) : Fintype β :=
  haveI := Fintype.ofEquiv _ f
  fintypeOfOption
#align fintype_of_option_equiv fintypeOfOptionEquiv

namespace Fintype

/-- A recursor principle for finite types, analogous to `Nat.rec`. It effectively says
that every `Fintype` is either `Empty` or `Option α`, up to an `Equiv`. -/
def truncRecEmptyOption {P : Type u → Sort v} (of_equiv : ∀ {α β}, α ≃ β → P α → P β)
    (h_empty : P PEmpty) (h_option : ∀ {α} [Fintype α] [DecidableEq α], P α → P (Option α))
    (α : Type u) [Fintype α] [DecidableEq α] : Trunc (P α) := by
  suffices ∀ n : ℕ, Trunc (P (ULift <| Fin n))
    by
    apply Trunc.bind (this (Fintype.card α))
    intro h
    apply Trunc.map _ (Fintype.truncEquivFin α)
    intro e
    exact of_equiv (Equiv.ulift.trans e.symm) h
  apply ind where
    -- porting note: do a manual recursion, instead of `induction` tactic,
    -- to ensure the result is computable
    /-- Internal induction hypothesis -/
    ind : ∀ n : ℕ, Trunc (P (ULift <| Fin n))
    | Nat.zero => by
          have : card PEmpty = card (ULift (Fin 0)) := by simp only [card_fin, card_pempty,
                                                                     card_ulift]
          apply Trunc.bind (truncEquivOfCardEq this)
          intro e
          apply Trunc.mk
          refine' of_equiv e h_empty
      | Nat.succ n => by
          have : card (Option (ULift (Fin n))) = card (ULift (Fin n.succ)) := by
            simp only [card_fin, card_option, card_ulift]
          apply Trunc.bind (truncEquivOfCardEq this)
          intro e
          apply Trunc.map _ (ind n)
          intro ih
          refine' of_equiv e (h_option ih)
#align fintype.trunc_rec_empty_option Fintype.truncRecEmptyOption

-- Porting note: due to instance inference issues in `SetTheory.Cardinal.Basic`
-- I had to explicitely name `h_fintype` in order to access it manually.
-- was `[Fintype α]`
/-- An induction principle for finite types, analogous to `Nat.rec`. It effectively says
that every `Fintype` is either `Empty` or `Option α`, up to an `Equiv`. -/
@[elab_as_elim]
theorem induction_empty_option {P : ∀ (α : Type u) [Fintype α], Prop}
    (of_equiv : ∀ (α β) [Fintype β] (e : α ≃ β), @P α (@Fintype.ofEquiv α β ‹_› e.symm) → @P β ‹_›)
    (h_empty : P PEmpty) (h_option : ∀ (α) [Fintype α], P α → P (Option α)) (α : Type u)
    [h_fintype : Fintype α] : P α := by
  obtain ⟨p⟩ :=
    let f_empty := fun i => by convert h_empty
    let h_option : ∀ {α : Type u} [Fintype α] [DecidableEq α],
          (∀ (h : Fintype α), P α) → ∀ (h : Fintype (Option α)), P (Option α)  := by
      rintro α hα - Pα hα'
      convert h_option α (Pα _)
    @truncRecEmptyOption (fun α => ∀ h, @P α h) (@fun α β e hα hβ => @of_equiv α β hβ e (hα _))
      f_empty h_option α _ (Classical.decEq α)
  · exact p _
  -- ·
#align fintype.induction_empty_option Fintype.induction_empty_option

end Fintype

/-- An induction principle for finite types, analogous to `Nat.rec`. It effectively says
that every `Fintype` is either `Empty` or `Option α`, up to an `Equiv`. -/
theorem Finite.induction_empty_option {P : Type u → Prop} (of_equiv : ∀ {α β}, α ≃ β → P α → P β)
    (h_empty : P PEmpty) (h_option : ∀ {α} [Fintype α], P α → P (Option α)) (α : Type u)
    [Finite α] : P α := by
  cases nonempty_fintype α
  refine' Fintype.induction_empty_option _ _ _ α
  exacts[fun α β _ => of_equiv, h_empty, @h_option]
#align finite.induction_empty_option Finite.induction_empty_option


/-
Copyright (c) 2021 Adam Topaz. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Adam Topaz
-/
import topology.category.Profinite.basic
import topology.locally_constant.basic
import topology.discrete_quotient

/-!
# Cofiltered limits of profinite sets.

This file contains some theorems about cofiltered limits of profinite sets.

## Main Results

- `exists_clopen_of_cofiltered` shows that any clopen set in a cofiltered limit of profinite
  sets is the pullback of a clopen set from one of the factors in the limit.
- `exists_locally_constant` shows that any locally constant function from a cofiltered limit
  of profinite sets factors through one of the components.
-/

namespace Profinite

open_locale classical
open category_theory
open category_theory.limits

universe u
variables {J : Type u} [small_category J] [is_cofiltered J]
  {F : J ⥤ Profinite.{u}} (C : cone F) (hC : is_limit C)

include hC

/--
If `X` is a cofiltered limit of profinite sets, then any clopen subset of `X` arises from
a clopen set in one of the terms in the limit.
-/
theorem exists_clopen_of_cofiltered {U : set C.X} (hU : is_clopen U) :
  ∃ (j : J) (V : set (F.obj j)) (hV : is_clopen V), U = C.π.app j ⁻¹' V :=
begin
  -- First, we have the topological basis of the cofiltered limit obtained by pulling back
  -- clopen sets from the factors in the limit. By continuity, all such sets are again clopen.
  have hB := Top.is_topological_basis_cofiltered_limit.{u}
    (F ⋙ Profinite.to_Top)
    (Profinite.to_Top.map_cone C)
    (is_limit_of_preserves _ hC)
    (λ j, {W | is_clopen W})
    _ (λ i, is_clopen_univ) (λ i U1 U2 hU1 hU2, hU1.inter hU2) _,
  rotate,
  { intros i,
    change topological_space.is_topological_basis
      {W : set (F.obj i) | is_clopen W},
    apply is_topological_basis_clopen },
  { rintros i j f V (hV : is_clopen _),
    refine ⟨hV.1.preimage _, hV.2.preimage _⟩;
    continuity },
  -- Using this, since `U` is open, we can write `U` as a union of clopen sets all of which
  -- are preimages of clopens from the factors in the limit.
  obtain ⟨S,hS,h⟩ := hB.open_eq_sUnion hU.1,
  clear hB,
  let j : S → J := λ s, (hS s.2).some,
  let V : Π (s : S), set (F.obj (j s)) := λ s, (hS s.2).some_spec.some,
  have hV : ∀ (s : S), is_clopen (V s) ∧ s.1 = C.π.app (j s) ⁻¹' (V s) :=
    λ s, (hS s.2).some_spec.some_spec,
  -- Since `U` is also closed, hence compact, it is covered by finitely many of the
  -- clopens constructed in the previous step.
  have := hU.2.is_compact.elim_finite_subcover (λ s : S, C.π.app (j s) ⁻¹' (V s)) _ _,
  rotate,
  { intros s,
    refine (hV s).1.1.preimage _,
    continuity },
  { dsimp only,
    rw h,
    rintro x ⟨T,hT,hx⟩,
    refine ⟨_,⟨⟨T,hT⟩,rfl⟩,_⟩,
    dsimp only,
    rwa ← (hV ⟨T,hT⟩).2 },
  -- We thus obtain a finite set `G : finset J` and a clopen set of `F.obj j` for each
  -- `j ∈ G` such that `U` is the union of the preimages of these clopen sets.
  obtain ⟨G,hG⟩ := this,
  -- Since `J` is cofiltered, we can find a single `j0` dominating all the `j ∈ G`.
  -- Pulling back all of the sets from the previous step to `F.obj j0` and taking a union,
  -- we obtain a clopen set in `F.obj j0` which works.
  obtain ⟨j0,hj0⟩ := is_cofiltered.inf_objs_exists (G.image j),
  let f : Π (s : S) (hs : s ∈ G), j0 ⟶ j s :=
    λ s hs, (hj0 (finset.mem_image.mpr ⟨s,hs,rfl⟩)).some,
  let W : S → set (F.obj j0) := λ s,
    if hs : s ∈ G then F.map (f s hs) ⁻¹' (V s) else set.univ,
  -- Conclude, using the `j0` and the clopen set of `F.obj j0` obtained above.
  refine ⟨j0, ⋃ (s : S) (hs : s ∈ G), W s, _, _⟩,
  { apply is_clopen_bUnion_finset,
    intros s hs,
    dsimp only [W],
    rw dif_pos hs,
    refine ⟨(hV s).1.1.preimage _, (hV s).1.2.preimage _⟩;
    continuity },
  { ext x,
    split,
    { intro hx,
      simp_rw [set.preimage_Union, set.mem_Union],
      obtain ⟨_, ⟨s,rfl⟩, _, ⟨hs, rfl⟩, hh⟩ := hG hx,
      refine ⟨s, hs, _⟩,
      dsimp only [W] at hh ⊢,
      rwa [dif_pos hs, ← set.preimage_comp, ← Profinite.coe_comp, C.w] },
    { intro hx,
      simp_rw [set.preimage_Union, set.mem_Union] at hx,
      obtain ⟨s,hs,hx⟩ := hx,
      rw h,
      refine ⟨s.1,s.2,_⟩,
      rw (hV s).2,
      dsimp only [W] at hx,
      rwa [dif_pos hs, ← set.preimage_comp, ← Profinite.coe_comp, C.w] at hx } }
end

lemma exists_locally_constant_fin_two (f : locally_constant C.X (fin 2)) :
  ∃ (j : J) (g : locally_constant (F.obj j) (fin 2)), f = g.comap (C.π.app _) :=
begin
  let U := f ⁻¹' {0},
  have hU : is_clopen U := f.is_locally_constant.is_clopen_fiber _,
  obtain ⟨j,V,hV,h⟩ := exists_clopen_of_cofiltered C hC hU,
  use [j, locally_constant.of_clopen hV],
  apply locally_constant.locally_constant_eq_of_fiber_zero_eq,
  rw locally_constant.coe_comap _ _ (C.π.app j).continuous,
  conv_rhs { rw set.preimage_comp },
  rw [locally_constant.of_clopen_fiber_zero hV, ← h],
end

theorem exists_locally_constant_finite_nonempty {α : Type*} [finite α] [nonempty α]
  (f : locally_constant C.X α) :
  ∃ (j : J) (g : locally_constant (F.obj j) α), f = g.comap (C.π.app _) :=
begin
  inhabit α,
  obtain ⟨j,gg,h⟩ := exists_locally_constant_finite_aux _ hC f,
  let ι : α → α → fin 2 := λ a b, if a = b then 0 else 1,
  let σ : (α → fin 2) → α := λ f, if h : ∃ (a : α), ι a = f then h.some else arbitrary _,
  refine ⟨j, gg.map σ, _⟩,
  ext,
  rw locally_constant.coe_comap _ _ (C.π.app j).continuous,
  dsimp [σ],
  have h1 : ι (f x) = gg (C.π.app j x),
  { change f.map (λ a b, if a = b then (0 : fin 2) else 1) x = _,
    rw [h, locally_constant.coe_comap _ _ (C.π.app j).continuous] },
  have h2 : ∃ a : α, ι a = gg (C.π.app j x) := ⟨f x, h1⟩,
  rw dif_pos h2,
  apply_fun ι,
  { rw h2.some_spec,
    exact h1 },
  { intros a b hh,
    apply_fun (λ e, e a) at hh,
    dsimp [ι] at hh,
    rw if_pos rfl at hh,
    split_ifs at hh with hh1 hh1,
    { exact hh1.symm },
    { exact false.elim (bot_ne_top hh) } }
end

/-- Any locally constant function from a cofiltered limit of profinite sets factors through
one of the components. -/
theorem exists_locally_constant {α : Type*} (f : locally_constant C.X α) :
  ∃ (j : J) (g : locally_constant (F.obj j) α), f = g.comap (C.π.app _) :=
begin
  let S := f.discrete_quotient,
  let ff : S → α := f.lift,
  casesI is_empty_or_nonempty S,
  { suffices : ∃ j, is_empty (F.obj j),
    { refine this.imp (λ j hj, _),
      refine ⟨⟨hj.elim, λ A, _⟩, _⟩,
      { convert is_open_empty,
        exact @set.eq_empty_of_is_empty _ hj _ },
      { ext x,
        exact hj.elim' (C.π.app j x) } },
    simp only [← not_nonempty_iff, ← not_forall],
    intros h,
    haveI : ∀ j : J, nonempty ((F ⋙ Profinite.to_Top).obj j) := h,
    haveI : ∀ j : J, t2_space ((F ⋙ Profinite.to_Top).obj j) := λ j,
      (infer_instance : t2_space (F.obj j)),
    haveI : ∀ j : J, compact_space ((F ⋙ Profinite.to_Top).obj j) := λ j,
      (infer_instance : compact_space (F.obj j)),
    have cond := Top.nonempty_limit_cone_of_compact_t2_cofiltered_system
      (F ⋙ Profinite.to_Top),
    suffices : nonempty C.X, from is_empty.false (S.proj this.some),
    let D := Profinite.to_Top.map_cone C,
    have hD : is_limit D := is_limit_of_preserves Profinite.to_Top hC,
    have CD := (hD.cone_point_unique_up_to_iso (Top.limit_cone_is_limit.{u} _)).inv,
    exact cond.map CD },
  { let f' : locally_constant C.X S := ⟨S.proj, S.proj_is_locally_constant⟩,
    obtain ⟨j, g', hj⟩ := exists_locally_constant_finite_nonempty _ hC f',
    refine ⟨j, ⟨ff ∘ g', g'.is_locally_constant.comp _⟩,_⟩,
    ext1 t,
    apply_fun (λ e, e t) at hj,
    rw locally_constant.coe_comap _ _ (C.π.app j).continuous at hj ⊢,
    dsimp at hj ⊢,
    rw ← hj,
    refl },
end

end Profinite


import data.nat.prime
open nat

theorem GcdM : ∀ a b : nat, ∃ g : nat, g ∣ a ∧ g ∣ b ∧ ∀ h : nat, h ∣ a ∧ h ∣ b → h ∣ g :=
begin
  intro a,

  -- As we will prove this by Strong Induction, we state the strong induction hypotheses
  have StrongInductionHyp : ∀ n : nat, (∀ a < n, ∀ b : nat, (∃ g : nat, g ∣ a ∧ g ∣ b ∧ ∀ h : nat, h ∣ a ∧ h ∣ b → h ∣ g)) →
                  (∀ b : nat, ∃ g : nat, g ∣ n ∧ g ∣ b ∧ ∀ h : nat, h ∣ n ∧ h ∣ b → h ∣ g) :=
  begin
    intros n HypRec,
    intro b,
    cases n with n,

    -- n = 0 case
    use b,
    split,
    simp,
    split,
    simp,
    intros h hz,
    cases hz,
    assumption,

    -- n > 0 case
    have T := mod_lt b (zero_lt_succ n),
    have Y := HypRec (b % n.succ) T n.succ,
    cases Y with g,
    use g,

    cases Y_h with gbns temp,
    cases temp with gns maxi,

    split,
    assumption,
    split,

    have M := mod_add_div' b n.succ,
    cases gbns with r gbns,
    cases gns with s gns,
    rw gbns at M,
    rw gns at M,
    rw ← mul_assoc at M,
    rw mul_comm _ g at M,
    rw mul_assoc at M,
    rw ← mul_add at M,
    use (r + b / (g * s) * s),
    rw eq_comm at M,
    assumption,

    intro h,
    intro hns,
    cases hns with hns1 hns2,
    have X : h ∣ b % n.succ := (dvd_mod_iff hns1).mpr hns2,
    exact maxi h (and.intro X hns1),
  end,

  exact nat.strong_induction_on a StrongInductionHyp,
end


lemma succ_add (a b : mynat) : succ a + b = succ (a + b) :=
begin
induction b with h hd,
rw add_zero,
rw add_zero,
refl,
rw add_succ,
rw add_succ,
rw ← hd,
refl,
end


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.fin
import Mathlib.data.equiv.basic
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
# Equivalences for `fin n`
-/

/-- Equivalence between `fin 0` and `empty`. -/
def fin_zero_equiv : fin 0 ≃ empty := equiv.mk fin_zero_elim empty.elim sorry sorry

/-- Equivalence between `fin 0` and `pempty`. -/
def fin_zero_equiv' : fin 0 ≃ pempty := equiv.equiv_pempty sorry

/-- Equivalence between `fin 1` and `punit`. -/
def fin_one_equiv : fin 1 ≃ PUnit :=
  equiv.mk (fun (_x : fin 1) => Unit.unit) (fun (_x : PUnit) => 0) sorry sorry

/-- Equivalence between `fin 2` and `bool`. -/
def fin_two_equiv : fin (bit0 1) ≃ Bool :=
  equiv.mk (fin.cases false fun (_x : fin 1) => tt) (fun (b : Bool) => cond b 1 0) sorry sorry

/-- Equivalence between `fin n.succ` and `option (fin n)` -/
def fin_succ_equiv (n : ℕ) : fin (Nat.succ n) ≃ Option (fin n) :=
  equiv.mk (fun (x : fin (Nat.succ n)) => fin.cases none some x)
    (fun (x : Option (fin n)) => option.rec_on x 0 fin.succ) sorry sorry

@[simp] theorem fin_succ_equiv_zero {n : ℕ} : coe_fn (fin_succ_equiv n) 0 = none := rfl

@[simp] theorem fin_succ_equiv_succ {n : ℕ} (m : fin n) :
    coe_fn (fin_succ_equiv n) (fin.succ m) = some m :=
  sorry

@[simp] theorem fin_succ_equiv_symm_none {n : ℕ} :
    coe_fn (equiv.symm (fin_succ_equiv n)) none = 0 :=
  rfl

@[simp] theorem fin_succ_equiv_symm_some {n : ℕ} (m : fin n) :
    coe_fn (equiv.symm (fin_succ_equiv n)) (some m) = fin.succ m :=
  rfl

/-- Equivalence between `fin m ⊕ fin n` and `fin (m + n)` -/
def sum_fin_sum_equiv {m : ℕ} {n : ℕ} : fin m ⊕ fin n ≃ fin (m + n) :=
  equiv.mk
    (fun (x : fin m ⊕ fin n) =>
      sum.rec_on x (fun (y : fin m) => { val := subtype.val y, property := sorry })
        fun (y : fin n) => { val := m + subtype.val y, property := sorry })
    (fun (x : fin (m + n)) =>
      dite (subtype.val x < m)
        (fun (H : subtype.val x < m) => sum.inl { val := subtype.val x, property := H })
        fun (H : ¬subtype.val x < m) => sum.inr { val := subtype.val x - m, property := sorry })
    sorry sorry

/-- Equivalence between `fin m × fin n` and `fin (m * n)` -/
def fin_prod_fin_equiv {m : ℕ} {n : ℕ} : fin m × fin n ≃ fin (m * n) :=
  equiv.mk
    (fun (x : fin m × fin n) =>
      { val := subtype.val (prod.snd x) + n * subtype.val (prod.fst x), property := sorry })
    (fun (x : fin (m * n)) =>
      (fun (H : 0 < n) =>
          ({ val := subtype.val x / n, property := sorry },
          { val := subtype.val x % n, property := sorry }))
        sorry)
    sorry sorry

/-- `fin 0` is a subsingleton. -/
protected instance subsingleton_fin_zero : subsingleton (fin 0) := equiv.subsingleton fin_zero_equiv

/-- `fin 1` is a subsingleton. -/
protected instance subsingleton_fin_one : subsingleton (fin 1) := equiv.subsingleton fin_one_equiv

end Mathlib


import init.data.set
import set_theory.cardinal.basic
import set_relations

open set
open_locale cardinal

theorem set_lt_powerset {α : Type} (f : α → set α) : # α ≤ # (set α) :=
begin
  apply mk_le_of_injective,
  exact singleton_injective,
end