/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Robert Y. Lewis
-/

import ring_theory.witt_vector.truncated
import ring_theory.witt_vector.identities
import number_theory.padics.ring_homs

/-!

# Comparison isomorphism between `witt_vector p (zmod p)` and `ℤ_[p]`

We construct a ring isomorphism between `witt_vector p (zmod p)` and `ℤ_[p]`.
This isomorphism follows from the fact that both satisfy the universal property
of the inverse limit of `zmod (p^n)`.

## Main declarations

* `witt_vector.to_zmod_pow`: a family of compatible ring homs `𝕎 (zmod p) → zmod (p^k)`
* `witt_vector.equiv`: the isomorphism

## References

* [Hazewinkel, *Witt Vectors*][Haze09]

* [Commelin and Lewis, *Formalizing the Ring of Witt Vectors*][CL21]
-/

noncomputable theory

variables {p : ℕ} [hp : fact p.prime]

local notation `𝕎` := witt_vector p

include hp

namespace truncated_witt_vector

variables (p) (n : ℕ) (R : Type*) [comm_ring R]

lemma eq_of_le_of_cast_pow_eq_zero [char_p R p] (i : ℕ) (hin : i ≤ n)
  (hpi : (p ^ i : truncated_witt_vector p n R) = 0) :
  i = n :=
begin
  contrapose! hpi,
  replace hin := lt_of_le_of_ne hin hpi, clear hpi,
  have : (↑p ^ i : truncated_witt_vector p n R) = witt_vector.truncate n (↑p ^ i),
  { rw [ring_hom.map_pow, map_nat_cast] },
  rw [this, ext_iff, not_forall], clear this,
  use ⟨i, hin⟩,
  rw [witt_vector.coeff_truncate, coeff_zero, fin.coe_mk, witt_vector.coeff_p_pow],
  haveI : nontrivial R := char_p.nontrivial_of_char_ne_one hp.1.ne_one,
  exact one_ne_zero
end

section iso

variables (p n) {R}

lemma card_zmod : fintype.card (truncated_witt_vector p n (zmod p)) = p ^ n :=
by rw [card, zmod.card]

lemma char_p_zmod : char_p (truncated_witt_vector p n (zmod p)) (p ^ n) :=
char_p_of_prime_pow_injective _ _ _ (card_zmod _ _)
    (eq_of_le_of_cast_pow_eq_zero p n (zmod p))

local attribute [instance] char_p_zmod

/--
The unique isomorphism between `zmod p^n` and `truncated_witt_vector p n (zmod p)`.

This isomorphism exists, because `truncated_witt_vector p n (zmod p)` is a finite ring
with characteristic and cardinality `p^n`.
-/
def zmod_equiv_trunc : zmod (p^n) ≃+* truncated_witt_vector p n (zmod p) :=
zmod.ring_equiv (truncated_witt_vector p n (zmod p)) (card_zmod _ _)

lemma zmod_equiv_trunc_apply {x : zmod (p^n)} :
  zmod_equiv_trunc p n x = zmod.cast_hom (by refl) (truncated_witt_vector p n (zmod p)) x :=
rfl

/--
The following diagram commutes:
```text
          zmod (p^n) ----------------------------> zmod (p^m)
            |                                        |
            |                                        |
            v                                        v
truncated_witt_vector p n (zmod p) ----> truncated_witt_vector p m (zmod p)
```
Here the vertical arrows are `truncated_witt_vector.zmod_equiv_trunc`,
the horizontal arrow at the top is `zmod.cast_hom`,
and the horizontal arrow at the bottom is `truncated_witt_vector.truncate`.
-/
lemma commutes {m : ℕ} (hm : n ≤ m) :
  (truncate hm).comp (zmod_equiv_trunc p m).to_ring_hom =
    (zmod_equiv_trunc p n).to_ring_hom.comp (zmod.cast_hom (pow_dvd_pow p hm) _) :=
ring_hom.ext_zmod _ _

lemma commutes' {m : ℕ} (hm : n ≤ m) (x : zmod (p^m)) :
  truncate hm (zmod_equiv_trunc p m x) =
    zmod_equiv_trunc p n (zmod.cast_hom (pow_dvd_pow p hm) _ x) :=
show (truncate hm).comp (zmod_equiv_trunc p m).to_ring_hom x = _,
by rw commutes _ _ hm; refl

lemma commutes_symm' {m : ℕ} (hm : n ≤ m) (x : truncated_witt_vector p m (zmod p)) :
  (zmod_equiv_trunc p n).symm (truncate hm x) =
    zmod.cast_hom (pow_dvd_pow p hm) _ ((zmod_equiv_trunc p m).symm x) :=
begin
  apply (zmod_equiv_trunc p n).injective,
  rw ← commutes',
  simp
end

/--
The following diagram commutes:
```text
truncated_witt_vector p n (zmod p) ----> truncated_witt_vector p m (zmod p)
            |                                        |
            |                                        |
            v                                        v
          zmod (p^n) ----------------------------> zmod (p^m)
```
Here the vertical arrows are `(truncated_witt_vector.zmod_equiv_trunc p _).symm`,
the horizontal arrow at the top is `zmod.cast_hom`,
and the horizontal arrow at the bottom is `truncated_witt_vector.truncate`.
-/
lemma commutes_symm {m : ℕ} (hm : n ≤ m) :
  (zmod_equiv_trunc p n).symm.to_ring_hom.comp (truncate hm) =
    (zmod.cast_hom (pow_dvd_pow p hm) _).comp (zmod_equiv_trunc p m).symm.to_ring_hom :=
by ext; apply commutes_symm'

end iso

end truncated_witt_vector

namespace witt_vector
open truncated_witt_vector

variables (p)

/--
`to_zmod_pow` is a family of compatible ring homs. We get this family by composing
`truncated_witt_vector.zmod_equiv_trunc` (in right-to-left direction)
with `witt_vector.truncate`.
-/
def to_zmod_pow (k : ℕ) : 𝕎 (zmod p) →+* zmod (p ^ k) :=
(zmod_equiv_trunc p k).symm.to_ring_hom.comp (truncate k)

lemma to_zmod_pow_compat (m n : ℕ) (h : m ≤ n) :
  (zmod.cast_hom (pow_dvd_pow p h) (zmod (p ^ m))).comp (to_zmod_pow p n) = to_zmod_pow p m :=
calc (zmod.cast_hom _ (zmod (p ^ m))).comp
      ((zmod_equiv_trunc p n).symm.to_ring_hom.comp (truncate n)) =
  ((zmod_equiv_trunc p m).symm.to_ring_hom.comp
    (truncated_witt_vector.truncate h)).comp (truncate n) :
  by rw [commutes_symm, ring_hom.comp_assoc]
... = (zmod_equiv_trunc p m).symm.to_ring_hom.comp (truncate m) :
  by rw [ring_hom.comp_assoc, truncate_comp_witt_vector_truncate]

/--
`to_padic_int` lifts `to_zmod_pow : 𝕎 (zmod p) →+* zmod (p ^ k)` to a ring hom to `ℤ_[p]`
using `padic_int.lift`, the universal property of `ℤ_[p]`.
-/
def to_padic_int : 𝕎 (zmod p) →+* ℤ_[p] := padic_int.lift $ to_zmod_pow_compat p

lemma zmod_equiv_trunc_compat (k₁ k₂ : ℕ) (hk : k₁ ≤ k₂) :
    (truncated_witt_vector.truncate hk).comp
        ((zmod_equiv_trunc p k₂).to_ring_hom.comp
           (padic_int.to_zmod_pow k₂)) =
      (zmod_equiv_trunc p k₁).to_ring_hom.comp (padic_int.to_zmod_pow k₁) :=
by rw [← ring_hom.comp_assoc, commutes, ring_hom.comp_assoc, padic_int.zmod_cast_comp_to_zmod_pow]

/--
`from_padic_int` uses `witt_vector.lift` to lift `truncated_witt_vector.zmod_equiv_trunc`
composed with `padic_int.to_zmod_pow` to a ring hom `ℤ_[p] →+* 𝕎 (zmod p)`.
-/
def from_padic_int : ℤ_[p] →+* 𝕎 (zmod p) :=
witt_vector.lift (λ k, (zmod_equiv_trunc p k).to_ring_hom.comp (padic_int.to_zmod_pow k)) $
  zmod_equiv_trunc_compat _

lemma to_padic_int_comp_from_padic_int :
  (to_padic_int p).comp (from_padic_int p) = ring_hom.id ℤ_[p] :=
begin
  rw ← padic_int.to_zmod_pow_eq_iff_ext,
  intro n,
  rw [← ring_hom.comp_assoc, to_padic_int, padic_int.lift_spec],
  simp only [from_padic_int, to_zmod_pow, ring_hom.comp_id],
  rw [ring_hom.comp_assoc, truncate_comp_lift, ← ring_hom.comp_assoc],
  simp only [ring_equiv.symm_to_ring_hom_comp_to_ring_hom, ring_hom.id_comp]
end

lemma to_padic_int_comp_from_padic_int_ext (x) :
  (to_padic_int p).comp (from_padic_int p) x = ring_hom.id ℤ_[p] x :=
by rw to_padic_int_comp_from_padic_int

lemma from_padic_int_comp_to_padic_int :
  (from_padic_int p).comp (to_padic_int p) = ring_hom.id (𝕎 (zmod p)) :=
begin
  apply witt_vector.hom_ext,
  intro n,
  rw [from_padic_int, ← ring_hom.comp_assoc, truncate_comp_lift, ring_hom.comp_assoc],
  simp only [to_padic_int, to_zmod_pow, ring_hom.comp_id, padic_int.lift_spec, ring_hom.id_comp,
    ← ring_hom.comp_assoc, ring_equiv.to_ring_hom_comp_symm_to_ring_hom]
end

lemma from_padic_int_comp_to_padic_int_ext (x) :
  (from_padic_int p).comp (to_padic_int p) x = ring_hom.id (𝕎 (zmod p)) x :=
by rw from_padic_int_comp_to_padic_int

/--
The ring of Witt vectors over `zmod p` is isomorphic to the ring of `p`-adic integers. This
equivalence is witnessed by `witt_vector.to_padic_int` with inverse `witt_vector.from_padic_int`.
-/
def equiv : 𝕎 (zmod p) ≃+* ℤ_[p] :=
{ to_fun := to_padic_int p,
  inv_fun := from_padic_int p,
  left_inv := from_padic_int_comp_to_padic_int_ext _,
  right_inv := to_padic_int_comp_from_padic_int_ext _,
  map_mul' := ring_hom.map_mul _,
  map_add' := ring_hom.map_add _ }

end witt_vector


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

import data.set.pointwise.finite
import group_theory.quotient_group
import group_theory.submonoid.operations
import group_theory.subgroup.basic
import set_theory.cardinal.finite
import data.finset.preimage

/-!
# Finitely generated monoids and groups

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

We define finitely generated monoids and groups. See also `submodule.fg` and `module.finite` for
finitely-generated modules.

## Main definition

* `submonoid.fg S`, `add_submonoid.fg S` : A submonoid `S` is finitely generated.
* `monoid.fg M`, `add_monoid.fg M` : A typeclass indicating a type `M` is finitely generated as a
monoid.
* `subgroup.fg S`, `add_subgroup.fg S` : A subgroup `S` is finitely generated.
* `group.fg M`, `add_group.fg M` : A typeclass indicating a type `M` is finitely generated as a
group.

-/

/-! ### Monoids and submonoids -/

open_locale pointwise
variables {M N : Type*} [monoid M] [add_monoid N]

section submonoid

/-- A submonoid of `M` is finitely generated if it is the closure of a finite subset of `M`. -/
@[to_additive]
def submonoid.fg (P : submonoid M) : Prop := ∃ S : finset M, submonoid.closure ↑S = P

/-- An additive submonoid of `N` is finitely generated if it is the closure of a finite subset of
`M`. -/
add_decl_doc add_submonoid.fg

/-- An equivalent expression of `submonoid.fg` in terms of `set.finite` instead of `finset`. -/
@[to_additive "An equivalent expression of `add_submonoid.fg` in terms of `set.finite` instead of
`finset`."]
lemma submonoid.fg_iff (P : submonoid M) : submonoid.fg P ↔
  ∃ S : set M, submonoid.closure S = P ∧ S.finite :=
⟨λ ⟨S, hS⟩, ⟨S, hS, finset.finite_to_set S⟩, λ ⟨S, hS, hf⟩, ⟨set.finite.to_finset hf, by simp [hS]⟩⟩

lemma submonoid.fg_iff_add_fg (P : submonoid M) : P.fg ↔ P.to_add_submonoid.fg :=
⟨λ h, let ⟨S, hS, hf⟩ := (submonoid.fg_iff _).1 h in (add_submonoid.fg_iff _).mpr
  ⟨additive.to_mul ⁻¹' S, by simp [← submonoid.to_add_submonoid_closure, hS], hf⟩,
 λ h, let ⟨T, hT, hf⟩ := (add_submonoid.fg_iff _).1 h in (submonoid.fg_iff _).mpr
  ⟨multiplicative.of_add ⁻¹' T, by simp [← add_submonoid.to_submonoid'_closure, hT], hf⟩⟩

lemma add_submonoid.fg_iff_mul_fg (P : add_submonoid N) : P.fg ↔ P.to_submonoid.fg :=
begin
  convert (submonoid.fg_iff_add_fg P.to_submonoid).symm,
  exact set_like.ext' rfl
end

end submonoid

section monoid

variables (M N)

/-- A monoid is finitely generated if it is finitely generated as a submonoid of itself. -/
class monoid.fg : Prop := (out : (⊤ : submonoid M).fg)

/-- An additive monoid is finitely generated if it is finitely generated as an additive submonoid of
itself. -/
class add_monoid.fg : Prop := (out : (⊤ : add_submonoid N).fg)

attribute [to_additive] monoid.fg

variables {M N}

lemma monoid.fg_def : monoid.fg M ↔ (⊤ : submonoid M).fg := ⟨λ h, h.1, λ h, ⟨h⟩⟩

lemma add_monoid.fg_def : add_monoid.fg N ↔ (⊤ : add_submonoid N).fg := ⟨λ h, h.1, λ h, ⟨h⟩⟩

/-- An equivalent expression of `monoid.fg` in terms of `set.finite` instead of `finset`. -/
@[to_additive "An equivalent expression of `add_monoid.fg` in terms of `set.finite` instead of
`finset`."]
lemma monoid.fg_iff : monoid.fg M ↔
  ∃ S : set M, submonoid.closure S = (⊤ : submonoid M) ∧ S.finite :=
⟨λ h, (submonoid.fg_iff ⊤).1 h.out, λ h, ⟨(submonoid.fg_iff ⊤).2 h⟩⟩

lemma monoid.fg_iff_add_fg : monoid.fg M ↔ add_monoid.fg (additive M) :=
⟨λ h, ⟨(submonoid.fg_iff_add_fg ⊤).1 h.out⟩, λ h, ⟨(submonoid.fg_iff_add_fg ⊤).2 h.out⟩⟩

lemma add_monoid.fg_iff_mul_fg : add_monoid.fg N ↔ monoid.fg (multiplicative N) :=
⟨λ h, ⟨(add_submonoid.fg_iff_mul_fg ⊤).1 h.out⟩, λ h, ⟨(add_submonoid.fg_iff_mul_fg ⊤).2 h.out⟩⟩

instance add_monoid.fg_of_monoid_fg [monoid.fg M] : add_monoid.fg (additive M) :=
monoid.fg_iff_add_fg.1 ‹_›

instance monoid.fg_of_add_monoid_fg [add_monoid.fg N] : monoid.fg (multiplicative N) :=
add_monoid.fg_iff_mul_fg.1 ‹_›

@[to_additive, priority 100]
instance monoid.fg_of_finite [finite M] : monoid.fg M :=
by { casesI nonempty_fintype M,
  exact ⟨⟨finset.univ, by rw finset.coe_univ; exact submonoid.closure_univ⟩⟩ }

end monoid

@[to_additive]
lemma submonoid.fg.map {M' : Type*} [monoid M'] {P : submonoid M} (h : P.fg) (e : M →* M') :
  (P.map e).fg :=
begin
  classical,
  obtain ⟨s, rfl⟩ := h,
  exact ⟨s.image e, by rw [finset.coe_image, monoid_hom.map_mclosure]⟩
end

@[to_additive]
lemma submonoid.fg.map_injective {M' : Type*} [monoid M'] {P : submonoid M}
  (e : M →* M') (he : function.injective e) (h : (P.map e).fg) : P.fg :=
begin
  obtain ⟨s, hs⟩ := h,
  use s.preimage e (he.inj_on _),
  apply submonoid.map_injective_of_injective he,
  rw [← hs, e.map_mclosure, finset.coe_preimage],
  congr,
  rw [set.image_preimage_eq_iff, ← e.coe_mrange, ← submonoid.closure_le, hs, e.mrange_eq_map],
  exact submonoid.monotone_map le_top
end

@[simp, to_additive]
lemma monoid.fg_iff_submonoid_fg (N : submonoid M) : monoid.fg N ↔ N.fg :=
begin
  conv_rhs { rw [← N.range_subtype, monoid_hom.mrange_eq_map] },
  exact ⟨λ h, h.out.map N.subtype, λ h, ⟨h.map_injective N.subtype subtype.coe_injective⟩⟩
end

@[to_additive]
lemma monoid.fg_of_surjective {M' : Type*} [monoid M'] [monoid.fg M]
  (f : M →* M') (hf : function.surjective f) : monoid.fg M' :=
begin
  classical,
  obtain ⟨s, hs⟩ := monoid.fg_def.mp ‹_›,
  use s.image f,
  rwa [finset.coe_image, ← monoid_hom.map_mclosure, hs, ← monoid_hom.mrange_eq_map,
    monoid_hom.mrange_top_iff_surjective],
end

@[to_additive]
instance monoid.fg_range {M' : Type*} [monoid M'] [monoid.fg M] (f : M →* M') :
  monoid.fg f.mrange :=
monoid.fg_of_surjective f.mrange_restrict f.mrange_restrict_surjective

@[to_additive add_submonoid.multiples_fg]
lemma submonoid.powers_fg (r : M) : (submonoid.powers r).fg :=
⟨{r}, (finset.coe_singleton r).symm ▸ (submonoid.powers_eq_closure r).symm⟩

@[to_additive add_monoid.multiples_fg]
instance monoid.powers_fg (r : M) : monoid.fg (submonoid.powers r) :=
(monoid.fg_iff_submonoid_fg _).mpr (submonoid.powers_fg r)

@[to_additive] instance monoid.closure_finset_fg (s : finset M) :
  monoid.fg (submonoid.closure (s : set M)) :=
begin
  refine ⟨⟨s.preimage coe (subtype.coe_injective.inj_on _), _⟩⟩,
  rw [finset.coe_preimage, submonoid.closure_closure_coe_preimage],
end

@[to_additive] instance monoid.closure_finite_fg (s : set M) [finite s] :
  monoid.fg (submonoid.closure s) :=
begin
  haveI := fintype.of_finite s,
  exact s.coe_to_finset ▸ monoid.closure_finset_fg s.to_finset,
end

/-! ### Groups and subgroups -/

variables {G H : Type*} [group G] [add_group H]

section subgroup

/-- A subgroup of `G` is finitely generated if it is the closure of a finite subset of `G`. -/
@[to_additive]
def subgroup.fg (P : subgroup G) : Prop := ∃ S : finset G, subgroup.closure ↑S = P

/-- An additive subgroup of `H` is finitely generated if it is the closure of a finite subset of
`H`. -/
add_decl_doc add_subgroup.fg

/-- An equivalent expression of `subgroup.fg` in terms of `set.finite` instead of `finset`. -/
@[to_additive "An equivalent expression of `add_subgroup.fg` in terms of `set.finite` instead of
`finset`."]
lemma subgroup.fg_iff (P : subgroup G) : subgroup.fg P ↔
  ∃ S : set G, subgroup.closure S = P ∧ S.finite :=
⟨λ⟨S, hS⟩, ⟨S, hS, finset.finite_to_set S⟩, λ⟨S, hS, hf⟩, ⟨set.finite.to_finset hf, by simp [hS]⟩⟩

/-- A subgroup is finitely generated if and only if it is finitely generated as a submonoid. -/
@[to_additive add_subgroup.fg_iff_add_submonoid.fg "An additive subgroup is finitely generated if
and only if it is finitely generated as an additive submonoid."]
lemma subgroup.fg_iff_submonoid_fg (P : subgroup G) : P.fg ↔ P.to_submonoid.fg :=
begin
  split,
  { rintro ⟨S, rfl⟩,
    rw submonoid.fg_iff,
    refine ⟨S ∪ S⁻¹, _, S.finite_to_set.union S.finite_to_set.inv⟩,
    exact (subgroup.closure_to_submonoid _).symm },
  { rintro ⟨S, hS⟩,
    refine ⟨S, le_antisymm _ _⟩,
    { rw [subgroup.closure_le, ←subgroup.coe_to_submonoid, ←hS],
      exact submonoid.subset_closure },
    { rw [← subgroup.to_submonoid_le, ← hS, submonoid.closure_le],
      exact subgroup.subset_closure } }
end

lemma subgroup.fg_iff_add_fg (P : subgroup G) : P.fg ↔ P.to_add_subgroup.fg :=
begin
  rw [subgroup.fg_iff_submonoid_fg, add_subgroup.fg_iff_add_submonoid.fg],
  exact (subgroup.to_submonoid P).fg_iff_add_fg
end

lemma add_subgroup.fg_iff_mul_fg (P : add_subgroup H) :
  P.fg ↔ P.to_subgroup.fg :=
begin
  rw [add_subgroup.fg_iff_add_submonoid.fg, subgroup.fg_iff_submonoid_fg],
  exact add_submonoid.fg_iff_mul_fg (add_subgroup.to_add_submonoid P)
end

end subgroup

section group

variables (G H)

/-- A group is finitely generated if it is finitely generated as a submonoid of itself. -/
class group.fg : Prop := (out : (⊤ : subgroup G).fg)

/-- An additive group is finitely generated if it is finitely generated as an additive submonoid of
itself. -/
class add_group.fg : Prop := (out : (⊤ : add_subgroup H).fg)

attribute [to_additive] group.fg

variables {G H}

lemma group.fg_def : group.fg G ↔ (⊤ : subgroup G).fg := ⟨λ h, h.1, λ h, ⟨h⟩⟩

lemma add_group.fg_def : add_group.fg H ↔ (⊤ : add_subgroup H).fg := ⟨λ h, h.1, λ h, ⟨h⟩⟩

/-- An equivalent expression of `group.fg` in terms of `set.finite` instead of `finset`. -/
@[to_additive "An equivalent expression of `add_group.fg` in terms of `set.finite` instead of
`finset`."]
lemma group.fg_iff : group.fg G ↔
  ∃ S : set G, subgroup.closure S = (⊤ : subgroup G) ∧ S.finite :=
⟨λ h, (subgroup.fg_iff ⊤).1 h.out, λ h, ⟨(subgroup.fg_iff ⊤).2 h⟩⟩

@[to_additive] lemma group.fg_iff' :
  group.fg G ↔ ∃ n (S : finset G), S.card = n ∧ subgroup.closure (S : set G) = ⊤ :=
group.fg_def.trans ⟨λ ⟨S, hS⟩, ⟨S.card, S, rfl, hS⟩, λ ⟨n, S, hn, hS⟩, ⟨S, hS⟩⟩

/-- A group is finitely generated if and only if it is finitely generated as a monoid. -/
@[to_additive add_group.fg_iff_add_monoid.fg "An additive group is finitely generated if and only
if it is finitely generated as an additive monoid."]
lemma group.fg_iff_monoid.fg : group.fg G ↔ monoid.fg G :=
⟨λ h, monoid.fg_def.2 $ (subgroup.fg_iff_submonoid_fg ⊤).1 (group.fg_def.1 h),
    λ h, group.fg_def.2 $ (subgroup.fg_iff_submonoid_fg ⊤).2 (monoid.fg_def.1 h)⟩

lemma group_fg.iff_add_fg : group.fg G ↔ add_group.fg (additive G) :=
⟨λ h, ⟨(subgroup.fg_iff_add_fg ⊤).1 h.out⟩, λ h, ⟨(subgroup.fg_iff_add_fg ⊤).2 h.out⟩⟩

lemma add_group.fg_iff_mul_fg : add_group.fg H ↔ group.fg (multiplicative H) :=
⟨λ h, ⟨(add_subgroup.fg_iff_mul_fg ⊤).1 h.out⟩, λ h, ⟨(add_subgroup.fg_iff_mul_fg ⊤).2 h.out⟩⟩

instance add_group.fg_of_group_fg [group.fg G] : add_group.fg (additive G) :=
group_fg.iff_add_fg.1 ‹_›

instance group.fg_of_mul_group_fg [add_group.fg H] : group.fg (multiplicative H) :=
add_group.fg_iff_mul_fg.1 ‹_›

@[to_additive, priority 100]
instance group.fg_of_finite [finite G] : group.fg G :=
by { casesI nonempty_fintype G,
  exact ⟨⟨finset.univ, by rw finset.coe_univ; exact subgroup.closure_univ⟩⟩ }

@[to_additive]
lemma group.fg_of_surjective {G' : Type*} [group G'] [hG : group.fg G] {f : G →* G'}
  (hf : function.surjective f) : group.fg G' :=
group.fg_iff_monoid.fg.mpr $ @monoid.fg_of_surjective G _ G' _ (group.fg_iff_monoid.fg.mp hG) f hf

@[to_additive]
instance group.fg_range {G' : Type*} [group G'] [group.fg G] (f : G →* G') : group.fg f.range :=
group.fg_of_surjective f.range_restrict_surjective

@[to_additive] instance group.closure_finset_fg (s : finset G) :
  group.fg (subgroup.closure (s : set G)) :=
begin
  refine ⟨⟨s.preimage coe (subtype.coe_injective.inj_on _), _⟩⟩,
  rw [finset.coe_preimage, ←subgroup.coe_subtype, subgroup.closure_preimage_eq_top],
end

@[to_additive] instance group.closure_finite_fg (s : set G) [finite s] :
  group.fg (subgroup.closure s) :=
begin
  haveI := fintype.of_finite s,
  exact s.coe_to_finset ▸ group.closure_finset_fg s.to_finset,
end

variables (G)

/-- The minimum number of generators of a group. -/
@[to_additive "The minimum number of generators of an additive group"]
noncomputable def group.rank [h : group.fg G] :=
@nat.find _ (classical.dec_pred _) (group.fg_iff'.mp h)

@[to_additive] lemma group.rank_spec [h : group.fg G] :
  ∃ S : finset G, S.card = group.rank G ∧ subgroup.closure (S : set G) = ⊤ :=
@nat.find_spec _ (classical.dec_pred _) (group.fg_iff'.mp h)

@[to_additive] lemma group.rank_le [h : group.fg G]
  {S : finset G} (hS : subgroup.closure (S : set G) = ⊤) : group.rank G ≤ S.card :=
@nat.find_le _ _ (classical.dec_pred _) (group.fg_iff'.mp h) ⟨S, rfl, hS⟩

variables {G} {G' : Type*} [group G']

@[to_additive] lemma group.rank_le_of_surjective [group.fg G] [group.fg G'] (f : G →* G')
  (hf : function.surjective f) : group.rank G' ≤ group.rank G :=
begin
  classical,
  obtain ⟨S, hS1, hS2⟩ := group.rank_spec G,
  transitivity (S.image f).card,
  { apply group.rank_le,
    rw [finset.coe_image, ←monoid_hom.map_closure, hS2, subgroup.map_top_of_surjective f hf] },
  { exact finset.card_image_le.trans_eq hS1 },
end

@[to_additive] lemma group.rank_range_le [group.fg G] {f : G →* G'} :
  group.rank f.range ≤ group.rank G :=
group.rank_le_of_surjective f.range_restrict f.range_restrict_surjective

@[to_additive] lemma group.rank_congr [group.fg G] [group.fg G'] (f : G ≃* G') :
  group.rank G = group.rank G' :=
le_antisymm (group.rank_le_of_surjective f.symm f.symm.surjective)
  (group.rank_le_of_surjective f f.surjective)

end group

namespace subgroup

@[to_additive] lemma rank_congr {H K : subgroup G} [group.fg H] [group.fg K] (h : H = K) :
  group.rank H = group.rank K :=
by unfreezingI { subst h }

@[to_additive] lemma rank_closure_finset_le_card (s : finset G) :
  group.rank (closure (s : set G)) ≤ s.card :=
begin
  classical,
  let t : finset (closure (s : set G)) := s.preimage coe (subtype.coe_injective.inj_on _),
  have ht : closure (t : set (closure (s : set G))) = ⊤,
  { rw finset.coe_preimage,
    exact closure_preimage_eq_top s },
  apply (group.rank_le (closure (s : set G)) ht).trans,
  rw [←finset.card_image_of_inj_on, finset.image_preimage],
  { apply finset.card_filter_le },
  { apply subtype.coe_injective.inj_on },
end

@[to_additive] lemma rank_closure_finite_le_nat_card (s : set G) [finite s] :
  group.rank (closure s) ≤ nat.card s :=
begin
  haveI := fintype.of_finite s,
  rw [nat.card_eq_fintype_card, ←s.to_finset_card, ←rank_congr (congr_arg _ s.coe_to_finset)],
  exact rank_closure_finset_le_card s.to_finset,
end

end subgroup

section quotient_group

@[to_additive]
instance quotient_group.fg [group.fg G] (N : subgroup G) [subgroup.normal N] : group.fg $ G ⧸ N :=
group.fg_of_surjective $ quotient_group.mk'_surjective N

end quotient_group


/-
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


import game.world7.level9 -- hide
import tactic.tauto
local attribute [instance, priority 10] classical.prop_decidable -- we are mathematicians
/-
# Advanced proposition world.

## Level 10: the law of the excluded middle.

We proved earlier that `(P → Q) → (¬ Q → ¬ P)`. The converse,
that `(¬ Q → ¬ P) → (P → Q)` is certainly true, but trying to prove
it using what we've learnt so far is impossible (because it is not provable in
constructive logic). For example, after

```
intro h,
intro p,
repeat {rw not_iff_imp_false at h},
```

in the below, you are left with
```
P Q : Prop,
h : (Q → false) → P → false
p : P
⊢ Q
```

The tools you have are not sufficient to continue. But you can just
prove this, and any other basic lemmas of this form like `¬ ¬ P → P`,
using the `by_cases` tactic. Instead of starting with all the `intro`s,
try this instead:

`by_cases p : P; by_cases q : Q,`

**Note the semicolon**! It means "do the next tactic to all the goals, not just the top one".
After it, there are four goals, one for each of the four possibilities PQ=TT, TF, FT, FF.
You can see that `p` is a proof of `P` in some of the goals, and a proof of `¬ P` in others.
Similar comments apply to `q`.

`repeat {cc}` then finishes the job.

This approach assumed that `P ∨ ¬ P` was true; the `by_cases` tactic just does `cases` on
this result. This is called the law of the excluded middle, and it cannot be proved just
using tactics such as `intro` and `apply`.

-/
/- Lemma : no-side-bar
If $P$ and $Q$ are true/false statements, then
$$(\lnot Q\implies \lnot P)\implies(P\implies Q).$$
-/
lemma contrapositive2 (P Q : Prop) : (¬ Q → ¬ P) → (P → Q) :=
begin
  by_cases p : P; by_cases q : Q,
  repeat {cc},

end

/-
OK that's enough logic -- now perhaps it's time to go on to Advanced Addition World!
Get to it via the main menu.
-/

/-
## Pro tip

In fact the tactic `tauto!` just kills this goal (and many other logic goals) immediately.
-/

/- Tactic : by_cases

## Summary

`by_cases h : P` does a cases split on whether `P` is true or false.

## Details

Some logic goals cannot be proved with `intro` and `apply` and `exact`.
The simplest example is the law of the excluded middle `¬ ¬ P → P`.
You can prove this using truth tables but not with `intro`, `apply` etc.
To do a truth table proof, the tactic `by_cases h : P` will turn a goal of
`⊢ ¬ ¬ P → P` into two goals

```
P : Prop,
h : P
⊢ ¬¬P → P

P : Prop,
h : ¬P
⊢ ¬¬P → P
```

Each of these can now be proved using `intro`, `apply`, `exact` and `exfalso`.
Remember though that in these simple logic cases, high-powered logic
tactics like `cc` and `tauto!` will just prove everything.

-/

/- Tactic : tauto

## Summary

The `tauto` tactic (and its variant `tauto!`) will close various logic
goals.

## Details

`tauto` is an all-purpose logic tactic which will try to solve goals using pure
logical reasoning -- for example it will close the following goal:

```
P Q : Prop,
hP : P,
hQ : Q
⊢ P ∧ Q
```

`tauto` is supposed to only use constructive logic, but its big brother `tauto!` uses classical logic
and hence closes more goals.
-/