import tactic topology.basic data.set.basic data.real.basic
import topologies
open classical topological_space

-- Reals positius
def preal := {y: ℝ | 0 < y}

-- Invers d'un natural positiu
noncomputable def InversN (n : pnat) : preal :=
begin
	use n⁻¹,
	sorry,
end

-- Definició d'espai métric.
structure EspaiMetric (X : Type) :=
(d : X × X → ℝ)
(no_deg : ∀ (x y : X), d(x, y) = 0 ↔ x = y)
(sim : ∀ (x y : X), d(x, y) = d(y, x))
(triang : ∀ (x y z : X), d(x, z) ≤ d(x, y) + d(y, z))

-- La distància d'un espai métric és definida positiva.
theorem def_pos (X: Type) (em : EspaiMetric X) : ∀(x y : X), em.d(x, y) ≥ 0 :=
begin
	intros x y,
	have h1 : em.d(x, x) ≤ em.d(x,y) + em.d(y, x),
		exact em.triang x y x,
	have h2 : x = x,
		refl,
	have h3 : em.d(x,x) = 0,
		exact (em.no_deg x x).mpr rfl,
	rw h3 at h1,
	rw em.sim x y at h1,
	have h4 : em.d(y, x) + em.d(y, x) = 2 * em.d(y, x),
		linarith,
	rw h4 at h1,
	rw em.sim at h1,
	linarith,
end

-- Definició de bola oberta.
def Bola (X: Type) (em : EspaiMetric X) (x: X) (ε: preal) : set X :=
{y : X | em.d(x, y) < ε}

--section MetricaDiscreta
--noncomputable def dD (X: Type)  : X × X → ℝ :=
--begin
--rintro ⟨x, y⟩,
----have h1 : (x = y) ∨ (¬ (x = y)),
----exact classical.em (x = y),
--by_cases h1 : x = y,
--exact 0,
--exact 1,
--end
--
--noncomputable theorem TD (X: Type) : EspaiMetric X :=
--begin
--use dD X,
---- Ax2.
--intros x y,
--split,
----
--assume h1,
--rw dD at h1,
--simp at h1,
--exact not_ne_iff.mp h1,
----
--assume h1,
--rw h1,
--have h2 : y = y,
--refl,
--sorry,
--end
--
--end MetricaDiscreta

section ValorAbsolut
def dVAbs : ℝ × ℝ → ℝ :=
begin
	rintros ⟨x, y⟩,
	exact abs(x-y),
end

noncomputable theorem EspaiMetricVAbs : EspaiMetric ℝ :=
begin
	use dVAbs,
	-- Objectiu 1: No degeneració
	{ intros x y,
		split,
		{ assume h1,
			rw dVAbs at h1,
			simp at h1,
			linarith, },
		{ assume h1,
			rw dVAbs,
			simp,
			linarith, }, },
	-- Objectiu 2: Simetria
	{ rw dVAbs,
		simp,
		intros x y,
		exact abs_sub_comm x y, },

	-- Objectiu 3: Desigualtat triangular
	{ intros x y z,
		rw dVAbs,
		simp,
		exact abs_sub_le x y z, },
end
end ValorAbsolut

section TopologiaMetrica
-- Definició de topologia associada a un espai métric.
def TopologiaMetrica (X: Type) (em: EspaiMetric X) : set X → Prop :=
begin
	assume A,
	exact (∀ (x : A), ∃ (ε : preal), Bola X em x ε ⊆ A),
end

-- La topologia associada a un espai métric és una topologia.
noncomputable theorem EspaiMetricTop (X: Type) (em: EspaiMetric X) : topological_space X :=
begin
	use TopologiaMetrica X em,
	{sorry, },
	{sorry, },
	{sorry, },
end
end TopologiaMetrica

-- -- Donades dues topologies sobre un mateix conjunt, direm que ...
-- def Menorque {X : Type} : (set X → Prop) → (set X → Prop) → Prop :=
-- λ (T S : set X → Prop), ∀ (A: set X), T A → S A

-- def ExtraccioTop {X: Type} (h1: topological_space X) : set X → Prop :=
-- begin
-- exact h1.is_open,
-- end

-- theorem DiscContainer (X: Type) (h1: topological_space X) :
-- 	Menorque (ExtraccioTop h1) (ExtraccioTop TopDisc) :=
-- begin
-- 	rw Menorque,
-- 	intro A,
-- 	assume h2,
-- 	exact true.intro, -- exact trivial,
-- end

-- Definició de conjunt tancat.
def es_tancat {X: Type} (top : topological_space X) : set X → Prop :=
begin
	intro C,
	exact top.is_open (Cᶜ),
end

-- El conjunt total és tancat.
theorem TotalTancat {X : Type} (top : topological_space X) : (es_tancat top set.univ) :=
begin
	rw es_tancat,
	have h2: set.univᶜ = (∅: set X),
		exact set.compl_univ,
	rw h2,
	exact BuitObert top,
end

-- El conjunt buit és tancat.
theorem BuitTancat {X : Type} (top : topological_space X) : (es_tancat top (∅ : set X)) :=
begin
	rw es_tancat,
	have h2: (∅ : set X)ᶜ = set.univ,
		exact set.compl_empty,
	rw h2,
	exact top.is_open_univ,
end

-- Definició d'entorn d'un punt.
def es_entorn {X : Type} (top : topological_space X) : set X → X → Prop :=
begin
	intro U,
	intro x,
	exact (∃ (A : set X), (top.is_open A) ∧ (x ∈ A) ∧ (A ⊆ U)),
end

def Entorns {X : Type} (top : topological_space X) (x : X) : set (set X) :=
	{U: set X | es_entorn top U x}

def EntornSubconjunt {X: Type} (top : topological_space X) (x : X) (A : set X) : set (set X):=
	{U ∈ Entorns top x | U ⊆ A}

def ObertEnmig {X: Type} (top : topological_space X) (x : X) (A : set X) : set (set X) :=
	{U : set X | top.is_open U ∧ x ∈ U ∧ U ⊆ A}

def MaxObertEnmig {X: Type} (h1: topological_space X) (x: X) (A: set X) : set X :=
	⋃₀ (ObertEnmig h1 x A)

theorem MOEObert {X: Type} (top : topological_space X) (x: X) (A: set X) :
top.is_open (MaxObertEnmig top x A) :=
begin
	have h2 : (∀ (t : set X), t ∈ (ObertEnmig top x A) → top.is_open t),
		intros t ht,
		rw ObertEnmig at ht,
		cases ht with ht1 ht2,
		exact ht1,
	exact top.is_open_sUnion (ObertEnmig top x A) h2,
end

def Proba {X: Type} (top : topological_space X) (A: set X) : set (set X) :=
	{U : set X | ∃ (x: X), U = MaxObertEnmig top x A}

def ProbaTot {X : Type} (top : topological_space X) (A: set X) : set X :=
	⋃₀ (Proba top A)

theorem PTObert {X : Type} (top : topological_space X) (A: set X) : top.is_open (ProbaTot top A) :=
begin
	rw ProbaTot,
	rw Proba,
	have h2: ∀ (t : set X), t ∈ {U : set X | ∃ (x : X), U = MaxObertEnmig top x A} → top.is_open t,
	intros t ht,
	cases ht with x hx,
	rw hx,
	exact MOEObert top x A,
	exact top.is_open_sUnion (Proba top A) (h2),
end

theorem EntornIgual {X: Type} (top : topological_space X) (A: set X) (h2 : ∀ (x : X), x ∈ A → A ∈ Entorns top x):
ProbaTot top A = A :=
begin
	ext z,
	split,
	assume h3,
	cases h3 with A1 h3,
	cases h3 with hA1 hz,
	cases hA1 with x hx,
	rw MaxObertEnmig at hx,
	rw ObertEnmig at hx,
	--have h3 : ⋃₀ {U : set X | h1.is_open U ∧ x ∈ U ∧ U ⊆ A} = A1,
	--exact hx.symm,
	--have h3 : A1 ⊆ A,
	rw hx at hz,
	cases hz with A2 hA2,
	cases hA2 with hA2 h3,
	--cases hA2 with hA2a hA2b,
	--cases hA2b with hA2b hA2c,
	exact (hA2.elim_right).elim_right h3,
	assume h3,
	specialize h2 z h3,
	cases h2 with U hU,
	use (MaxObertEnmig top z A),
	split,
	use z,
	use U,
	exact and.intro hU (hU.elim_right).elim_left,
end

theorem ObertEntornSii (X: Type) (top : topological_space X) (A: set X) :
top.is_open A ↔ (∀ (x ∈ A), A ∈ (Entorns top x)) :=
begin
	split,
	---
	intro h2,
	intros x hx,
	rw Entorns,
	rw es_entorn,
	simp,
	use A,
	split,
	exact h2,
	split,
	exact hx,
	exact rfl.subset,

	---
	intro h2,
	have h3: ∀ (x ∈ A), ∃ (A_1 : set X), top.is_open A_1 ∧ x ∈ A_1 ∧ A_1 ⊆ A,
	intro x,
	specialize h2 x,
	exact h2,
	rw (EntornIgual top A h2).symm,
	rw ProbaTot,
	have h4: ∀ (t ∈ Proba top A), top.is_open t,
	intros t hT,
	cases hT with z hz,
	specialize h3 z,
	rw hz,
	exact MOEObert top z A,
	exact top.is_open_sUnion (Proba top A) h4,
end

-- Definició de base d'entorns.
structure BaseEntorns (X : Type) (top : topological_space X) (x : X) :=
	(B : set (set X))
	(Ax1 : ∀ (U ∈ B), U ∈ (Entorns top x))
	(Ax2 : ∀ (V ∈ (Entorns top x)), ∃ (U ∈ B), U ⊆ V)

noncomputable theorem BaseTopMetrica (X : Type) (em : EspaiMetric X) (x : X) : BaseEntorns X (EspaiMetricTop X em) x :=
begin
	let B := {U : set X | ∃ (n : pnat), U = Bola X em x (InversN n)},
	use B,
	{ intros U hU,
		cases hU with n hn,
		rw hn,
		rw Entorns,
		rw es_entorn,
		simp,
		use (Bola X em x (InversN n)),
		split,
		sorry, sorry, },
	{ sorry, },
end

structure BaseOberts (X : Type) (top : topological_space X) :=
  (B : set (set X))
	(Ax1 : ∀ (U ∈ B), top.is_open U)
	(Ax2 : ∀ (A : set X), top.is_open A → ∃ (𝕌 ⊆ B), A = ⋃₀ 𝕌)

structure BaseOberts2 (X : Type) (top : topological_space X) :=
	(B : set (set X))
	(Ax1 : ∀ (U ∈ B), top.is_open U)
	(Ax2 : ∀ (A : set X), ∀ (x : X), top.is_open A ∧ x ∈ A → ∃ (U ∈ B), x ∈ U ∧ U ⊆ A)

theorem BaseOberts12 (X : Type) (top : topological_space X) (BO : BaseOberts X top) : (∀ (A : set X), ∀ (x : X), top.is_open A ∧ x ∈ A → ∃ (U ∈ BO.B), x ∈ U ∧ U ⊆ A) :=
begin
	sorry,
end

theorem BaseOberts21 (X : Type) (top : topological_space X) (BO : BaseOberts2 X top) : (∀ (A : set X), top.is_open A → ∃ (𝕌 ⊆ BO.B), A = ⋃₀ 𝕌) :=
begin
	sorry,
end


/-
Copyright (c) 2022 Pierre-Alexandre Bazin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Pierre-Alexandre Bazin
-/
import algebra.module
import linear_algebra.quotient
import ring_theory.ideal.quotient
import ring_theory.non_zero_divisors
import group_theory.torsion

/-!
# Torsion submodules

## Main definitions

* `submodule.torsion_by R M a` : the `a`-torsion submodule, containing all elements `x` of `M` such
  that `a • x = 0`.
* `submodule.torsion' R M S` : the `S`-torsion submodule, containing all elements `x` of `M` such
  that `a • x = 0` for some `a` in `S`.
* `submodule.torsion R M` : the torsion submoule, containing all elements `x` of `M` such that
  `a • x = 0` for some non-zero-divisor `a` in `R`.
* `module.is_torsion_by R M a` : the property that defines a `a`-torsion module. Similarly,
  `is_torsion'` and `is_torsion`.

## Main statements

* `torsion' R M S` and `torsion R M` are submodules.
* `torsion_by R M a` can be viewed as a `(R ⧸ R∙a)`-module.
* `submodule.torsion_by_is_torsion_by` : the `a`-torsion submodule is a `a`-torsion module.
  Similar lemmas for `torsion'` and `torsion`.
* `submodule.no_zero_smul_divisors_iff_torsion_bot` : a module over a domain has
  `no_zero_smul_divisors` (that is, there is no non-zero `a`, `x` such that `a • x = 0`)
  iff its torsion submodule is trivial.
* `submodule.quotient_torsion.torsion_eq_bot` : quotienting by the torsion submodule makes the
  torsion submodule of the new module trivial. If `R` is a domain, we can derive an instance
  `submodule.quotient_torsion.no_zero_smul_divisors : no_zero_smul_divisors R (M ⧸ torsion R M)`.

## Notation

* The notions are defined for a `comm_semiring R` and a `module R M`. Some additional hypotheses on
  `R` and `M` are required by some lemmas.
* The letters `a`, `b`, ... are used for scalars (in `R`), while `x`, `y`, ... are used for vectors
  (in `M`).

## Tags

Torsion, submodule, module, quotient
-/

open_locale non_zero_divisors

section defs

variables (R M : Type*) [comm_semiring R] [add_comm_monoid M] [module R M]

namespace submodule

/-- The `a`-torsion submodule for `a` in `R`, containing all elements `x` of `M` such that
  `a • x = 0`. -/
@[simps] def torsion_by (a : R) : submodule R M := (distrib_mul_action.to_linear_map _ _ a).ker

/-- The `S`-torsion submodule, containing all elements `x` of `M` such that `a • x = 0` for some
`a` in `S`. -/
@[simps] def torsion' (S : Type*)
  [comm_monoid S] [distrib_mul_action S M] [smul_comm_class S R M] :
  submodule R M :=
{ carrier := { x | ∃ a : S, a • x = 0 },
  zero_mem' := ⟨1, smul_zero _⟩,
  add_mem' := λ x y ⟨a, hx⟩ ⟨b, hy⟩,
    ⟨b * a,
      by rw [smul_add, mul_smul, mul_comm, mul_smul, hx, hy, smul_zero, smul_zero, add_zero]⟩,
  smul_mem' := λ a x ⟨b, h⟩, ⟨b, by rw [smul_comm, h, smul_zero]⟩ }

/-- The torsion submodule, containing all elements `x` of `M` such that  `a • x = 0` for some
  non-zero-divisor `a` in `R`. -/
@[reducible] def torsion := torsion' R M R⁰
end submodule

namespace module

/-- A `a`-torsion module is a module where every element is `a`-torsion. -/
@[reducible] def is_torsion_by (a : R) := ∀ ⦃x : M⦄, a • x = 0

/-- A `S`-torsion module is a module where every element is `a`-torsion for some `a` in `S`. -/
@[reducible] def is_torsion' (S : Type*) [has_scalar S M] := ∀ ⦃x : M⦄, ∃ a : S, a • x = 0

/-- A torsion module is a module where every element is `a`-torsion for some non-zero-divisor `a`.
-/
@[reducible] def is_torsion := ∀ ⦃x : M⦄, ∃ a : R⁰, a • x = 0

end module

end defs

namespace submodule

open module

variables {R M : Type*}

section torsion_by

variables [comm_semiring R] [add_comm_monoid M] [module R M] (a : R)

@[simp] lemma smul_torsion_by (x : torsion_by R M a) : a • x = 0 := subtype.ext x.prop
@[simp] lemma smul_coe_torsion_by (x : torsion_by R M a) : a • (x : M) = 0 := x.prop
@[simp] lemma mem_torsion_by_iff (x : M) : x ∈ torsion_by R M a ↔ a • x = 0 := iff.rfl

/-- A `a`-torsion module is a module whose `a`-torsion submodule is the full space. -/
lemma is_torsion_by_iff_torsion_by_eq_top : is_torsion_by R M a ↔ torsion_by R M a = ⊤ :=
⟨λ h, eq_top_iff.mpr (λ _ _, @h _), λ h x, by { rw [← mem_torsion_by_iff, h], trivial }⟩

/-- The `a`-torsion submodule is a `a`-torsion module. -/
lemma torsion_by_is_torsion_by : is_torsion_by R (torsion_by R M a) a := λ _, smul_torsion_by _ _

@[simp] lemma torsion_by_torsion_by_eq_top : torsion_by R (torsion_by R M a) a = ⊤ :=
(is_torsion_by_iff_torsion_by_eq_top a).mp $ torsion_by_is_torsion_by a

end torsion_by

section quotient

variables [comm_ring R] [add_comm_group M] [module R M] (a : R)

instance : has_scalar (R ⧸ R ∙ a) (torsion_by R M a) :=
{ smul := λ b x, quotient.lift_on' b (• x) $ λ b₁ b₂ (h : b₁ - b₂ ∈ _), begin
    show b₁ • x = b₂ • x,
    obtain ⟨c, h⟩ := ideal.mem_span_singleton'.mp h,
    rw [← sub_eq_zero, ← sub_smul, ← h, mul_smul, smul_torsion_by, smul_zero],
  end }

@[simp] lemma torsion_by.mk_smul (b : R) (x : torsion_by R M a) :
  ideal.quotient.mk (R ∙ a) b • x = b • x := rfl

/-- The `a`-torsion submodule as a `(R ⧸ R∙a)`-module. -/
instance : module (R ⧸ R ∙ a) (torsion_by R M a) :=
@function.surjective.module_left _ _ (torsion_by R M a) _ _ _ _ _ (ideal.quotient.mk $ R ∙ a)
  ideal.quotient.mk_surjective (torsion_by.mk_smul _)

instance {S : Type*} [has_scalar S R] [has_scalar S M]
  [is_scalar_tower S R M] [is_scalar_tower S R R] :
  is_scalar_tower S (R ⧸ R ∙ a) (torsion_by R M a) :=
{ smul_assoc := λ b d x, quotient.induction_on' d $ λ c, (smul_assoc b c x : _) }

end quotient

section torsion'

variables [comm_semiring R] [add_comm_monoid M] [module R M]
variables (S : Type*) [comm_monoid S] [distrib_mul_action S M] [smul_comm_class S R M]

@[simp] lemma mem_torsion'_iff (x : M) : x ∈ torsion' R M S ↔ ∃ a : S, a • x = 0 := iff.rfl
@[simp] lemma mem_torsion_iff (x : M) : x ∈ torsion R M ↔ ∃ a : R⁰, a • x = 0 := iff.rfl

@[simps] instance : has_scalar S (torsion' R M S) :=
⟨λ s x, ⟨s • x, by { obtain ⟨x, a, h⟩ := x, use a, dsimp, rw [smul_comm, h, smul_zero] }⟩⟩
instance : distrib_mul_action S (torsion' R M S) := subtype.coe_injective.distrib_mul_action
  ((torsion' R M S).subtype).to_add_monoid_hom (λ (c : S) x, rfl)
instance : smul_comm_class S R (torsion' R M S) := ⟨λ s a x, subtype.ext $ smul_comm _ _ _⟩

/-- A `S`-torsion module is a module whose `S`-torsion submodule is the full space. -/
lemma is_torsion'_iff_torsion'_eq_top : is_torsion' M S ↔ torsion' R M S = ⊤ :=
⟨λ h, eq_top_iff.mpr (λ _ _, @h _), λ h x, by { rw [← @mem_torsion'_iff R, h], trivial }⟩

/-- The `S`-torsion submodule is a `S`-torsion module. -/
lemma torsion'_is_torsion' : is_torsion' (torsion' R M S) S := λ ⟨x, ⟨a, h⟩⟩, ⟨a, subtype.ext h⟩

@[simp] lemma torsion'_torsion'_eq_top : torsion' R (torsion' R M S) S = ⊤ :=
(is_torsion'_iff_torsion'_eq_top S).mp $ torsion'_is_torsion' S

/-- The torsion submodule of the torsion submodule (viewed as a module) is the full
torsion module. -/
@[simp] lemma torsion_torsion_eq_top : torsion R (torsion R M) = ⊤ := torsion'_torsion'_eq_top R⁰

/-- The torsion submodule is always a torsion module. -/
lemma torsion_is_torsion : module.is_torsion R (torsion R M) := torsion'_is_torsion' R⁰

end torsion'

section torsion
variables [comm_semiring R] [add_comm_monoid M] [module R M] [no_zero_divisors R] [nontrivial R]

lemma coe_torsion_eq_annihilator_ne_bot :
  (torsion R M : set M) = { x : M | (R ∙ x).annihilator ≠ ⊥ } :=
begin
  ext x, simp_rw [submodule.ne_bot_iff, mem_annihilator, mem_span_singleton],
  exact ⟨λ ⟨a, hax⟩, ⟨a, λ _ ⟨b, hb⟩, by rw [← hb, smul_comm, ← submonoid.smul_def, hax, smul_zero],
    non_zero_divisors.coe_ne_zero _⟩,
    λ ⟨a, hax, ha⟩, ⟨⟨_, mem_non_zero_divisors_of_ne_zero ha⟩, hax x ⟨1, one_smul _ _⟩⟩⟩
end

/-- A module over a domain has `no_zero_smul_divisors` iff its torsion submodule is trivial. -/
lemma no_zero_smul_divisors_iff_torsion_eq_bot :
  no_zero_smul_divisors R M ↔ torsion R M = ⊥ :=
begin
  split; intro h,
  { haveI : no_zero_smul_divisors R M := h,
    rw eq_bot_iff, rintro x ⟨a, hax⟩,
    change (a : R) • x = 0 at hax,
    cases eq_zero_or_eq_zero_of_smul_eq_zero hax with h0 h0,
    { exfalso, exact non_zero_divisors.coe_ne_zero a h0 }, { exact h0 } },
  { exact { eq_zero_or_eq_zero_of_smul_eq_zero := λ a x hax, begin
      by_cases ha : a = 0,
      { left, exact ha },
      { right, rw [← mem_bot _, ← h],
        exact ⟨⟨a, mem_non_zero_divisors_of_ne_zero ha⟩, hax⟩ }
    end } }
end
end torsion

namespace quotient_torsion

variables [comm_ring R] [add_comm_group M] [module R M]

/-- Quotienting by the torsion submodule gives a torsion-free module. -/
@[simp] lemma torsion_eq_bot : torsion R (M ⧸ torsion R M) = ⊥ :=
eq_bot_iff.mpr $ λ z, quotient.induction_on' z $ λ x ⟨a, hax⟩,
begin
  rw [quotient.mk'_eq_mk, ← quotient.mk_smul, quotient.mk_eq_zero] at hax,
  rw [mem_bot, quotient.mk'_eq_mk, quotient.mk_eq_zero],
  cases hax with b h,
  exact ⟨b * a, (mul_smul _ _ _).trans h⟩
end

instance no_zero_smul_divisors [is_domain R] : no_zero_smul_divisors R (M ⧸ torsion R M) :=
no_zero_smul_divisors_iff_torsion_eq_bot.mpr torsion_eq_bot

end quotient_torsion

end submodule

namespace add_monoid

variables {M : Type*}

theorem is_torsion_iff_is_torsion_nat [add_comm_monoid M] :
  add_monoid.is_torsion M ↔ module.is_torsion ℕ M :=
begin
  refine ⟨λ h x, _, λ h x, _⟩,
  { obtain ⟨n, h0, hn⟩ := (is_of_fin_add_order_iff_nsmul_eq_zero x).mp (h x),
    exact ⟨⟨n, mem_non_zero_divisors_of_ne_zero $ ne_of_gt h0⟩, hn⟩ },
  { rw is_of_fin_add_order_iff_nsmul_eq_zero,
    obtain ⟨n, hn⟩ := @h x,
    refine ⟨n, nat.pos_of_ne_zero (non_zero_divisors.coe_ne_zero _), hn⟩ }
end

theorem is_torsion_iff_is_torsion_int [add_comm_group M] :
  add_monoid.is_torsion M ↔ module.is_torsion ℤ M :=
begin
  refine ⟨λ h x, _, λ h x, _⟩,
  { obtain ⟨n, h0, hn⟩ := (is_of_fin_add_order_iff_nsmul_eq_zero x).mp (h x),
    exact ⟨⟨n, mem_non_zero_divisors_of_ne_zero $ ne_of_gt $ int.coe_nat_pos.mpr h0⟩,
      (coe_nat_zsmul _ _).trans hn⟩ },
  { rw is_of_fin_add_order_iff_nsmul_eq_zero,
    obtain ⟨n, hn⟩ := @h x,
    exact exists_nsmul_eq_zero_of_zsmul_eq_zero (non_zero_divisors.coe_ne_zero n) hn }
end

end add_monoid


import path
import graph_induction

-- from math 688 notes, lec-19

universe u
variables (V : Type u)

-- TO DO:
    -- define components (they are used twice here), give them some lemmas
    -- prove that removing a vertex from a tree results in a graph whose components are trees with smaller size
    -- concrete development of the category of graphs so we can prove useful properties (see Hedetniemi branch)
    -- replace (h : 2 ≤ fintype.card V) with [nontrivial V]

-- might be useful:
    -- `finset.eq_singleton_iff_unique_mem` says `s = {a} ↔ a ∈ s ∧ ∀ x ∈ s, x = a`

open_locale classical

namespace simple_graph

def induced_subgraph (G : simple_graph V) (S : set V) : simple_graph S :=
{
  adj := λ a b, G.adj a b,
  sym := λ a b h, G.sym h,
  loopless := λ x h, G.loopless x h
}

-- define map from graph to subgraph, use `is_subgraph` property from graph_induction.lean

--variables (s : set V) (S : simple_graph s)

-- two ideas:
    -- `induced_subgraph.to_supergraph`
    -- `simple_graph.to_induced_subgraph`

/-def induced_subgraph.to_supergraph {S : simple_graph s} {G : simple_graph V} : S.E → G.E
    | -/

/-def path.to_supergraph {V : Type u} : T.path → S.path
    | []     := ∅
    | (h::t) := {h} ∪ list.to_set t-/

-- create some sort of type coercion for edges in subgraph and supergraph

variables {V} (T : simple_graph V) (a b : V) --(S : simple_graph (λ v, v ≠ a))

#check T.adj a b

def connected : Prop := ∀ (a b : V), ∃ (p : T.path), a = p.head ∧ b = p.last

def acyclic : Prop := ∀ (p : T.path), ¬ p.simple_cycle

class tree : Prop :=
(connected : connected T)
(acyclic : acyclic T)
#check T.connected

--def tree : Prop := connected T ∧ acyclic T

def leaf (v : V) [fintype (T.neighbor_set v)] : Prop := T.degree v = 1

variables [∀ v, fintype (T.neighbor_set v)] [tree T] (p : path T)

#check fintype.card

-- move this to simple_graph later (need to prove that `p.is_tour` and therefore `p.is_maximal` exists in any finite simple graph)
lemma fin_max_path [fintype V] (h : 2 ≤ fintype.card V) : ∃ (p : path T), p.is_maximal :=
begin

    -- show that if the number of vertices is finite then the path lengths are all finite
    -- use the fact that you have an upper bound for sets of integers
    /-have h2 := fintype.exists_pair_of_one_lt_card h,
    cases h2 with a hb,
    cases hb with b h3,-/ -- don't think we want to pick arbitrary a and b
    --have h4 := T.connected,
    -- we're gonna need the bijection (λ (p : path), p.length) in order to find a maximum because then we can find a finite set on ℕ

    sorry,
end

/- Theorem 1: every tree on n ≥ 2 vertices contains at least two vertices of degree 1 -/
lemma two_deg_one [fintype V] (h : 2 ≤ fintype.card V) : ∃ (v₁ v₂ : V), v₁ ≠ v₂ ∧ T.leaf v₁ ∧ T.leaf v₂ :=
begin
    -- Proof outline:
    have h2 := fintype.exists_pair_of_one_lt_card h,
    cases h2 with a hb,
    cases hb with b h3,
    use a,
    use b,
    split,
    exact h3,

    split,
    -- let p = x0 x1 ... xk in T be a maximal path in tree T
        -- Sub-lemma : prove that maximal paths exist
        -- use `p.tail.length` (number of edges in `p`)
    -- assume for contradiction that we have some neighbor y of x0, where y ≠ x1. this gives us two cases:
        -- either y is contained in a path that links back up with the original path, so acyclicity is violated
        -- or y is contained in a path that does not link back up, which then makes that new path an extension of the original, so maximality of p is violated
            -- `p.cons e hp hs` is the path extending `p` by edge `e`.
            -- (these two things should probably be their own lemmas so we can just apply them to x0 and xk)
    -- so then x0 does not have any neighbors besides x1, and therefore has degree 1
    -- similar argument goes for xk, which gives us at least two vertices in T that are leaves
    sorry,
    sorry,
end

/- Theorem 2: if T is a tree on n ≥ 2 vertices and x is a leaf, then the graph obtained by removing x from T is a tree on n - 1 vertices -/
-- this should probably be made with more general lemmas
section other
lemma acyclic_subgraph_acyclic (t : acyclic T) (s : set V) : acyclic (induced_subgraph V T s) :=
begin
    -- Proof outline:
    -- T has no cycles so T \ {x} has no cycles
    sorry,
end -- generalize to any subgraph once that's defined

variable (x : V)

--vertex_mem (v : V) (p : G.path) : Prop := v ∈ p.vertices

-- is this actually useful? who knows
/-lemma leaf_path_endpoint (p : T.path) (h1 : T.leaf x) (h2 : x ∈ p.vertices) : p.head = x ∨ p.last = x :=
begin
    -- `rw mem_neighbor_finset` says `w ∈ G.neighbor_finset v ↔ G.adj v w`
    unfold leaf at h1,
    unfold degree at h1,
    rw finset.card_eq_one at h1,
    cases h1 with w hw,
    rw finset.eq_singleton_iff_unique_mem at hw,
    cases hw with hm hu,
    -- reverse (head :: tail) to, well, reverse it
    -- list.head (list.reverse (head :: tail) is the new list head
    sorry,
end-/

lemma connected_rmleaf_connected (t : connected T) {x : V} (h : T.leaf x) : connected (induced_subgraph V T (λ v, v ≠ x)) :=
begin
    -- Proof outline:
    -- there are uv paths for all u v in T

    -- if T.leaf x and x ∈ p, x must either be p.head or p.last ?
        -- should this be its own sub-lemma?
    -- T.connected means there are paths from every vertex to every vertex
    unfold connected,
    unfold connected at t,
    intros h1 h2,
    sorry,
end

end other

instance tree_rmleaf_is_tree {x : V} (h : T.leaf x) : tree (induced_subgraph V T (λ v, v ≠ x)) :=
{ connected := T.connected_rmleaf_connected tree.connected h,
  acyclic := T.acyclic_subgraph_acyclic tree.acyclic _
}

/- Theorem 3: TFAE
    (a) T is a tree
    (b) There exists a unique path between any two distinct vertices of T
    (c) T is a connected graph on n vertices with n-1 edges
    (d) T is an acyclic graph on n vertices with n-1 edges -/

-- Proof outline:
/- Lemma 1: (a) → (b) : T is a tree → there exists a unique path between any two distinct vertices -/
lemma tree_unique_path [inhabited V] (t : tree T) (u v : V) (p : T.path) (q : T.path) : (p.head = q.head) ∧ (p.last = q.last) → p = q :=
begin
-- Subproof outline:
    -- let u,v be distinct vertices in T
    -- T is a tree, so a uv path p exists
        -- we already have uv path guaranteed by definition of tree
    intro h,
    cases h with hh hl,
    rw path.eq_of_vertices_eq,
    -- suppose for contradiction that another path uv path q exists, p ≠ q (negation of eq_of_vertices_eq?)
    by_contra,
    -- there exists w s.t. w ∈ p and w ∈ q, where w is the last vertex before p and q diverge (maybe make a lemma for this)
        -- shit this is gonna be tricky
        -- (this doesn't cover the edge case that u is adjacent to v, which is false by the condition that we have a simple graph. this is a problem. fix the definition somehow)
    -- p.last = q.last, so we must have a vertex w' in p,q (could be v) such that (figure out how to say this correctly) w'.tail ∈ p ∧ w'.tail ∈ q (also this should probably be a path lemma)
    -- now, this means that we can build a path from w back to itself using the segments w to w' in p and q (do we have reversible paths in path.lean?)
    sorry,
end

/- Lemma 2: (b) → (c) : there exists a unique path between any two distinct vertices → T is connected on n vertices with n - 1 edges -/
-- note: no mention of T being a tree here, so that's not one of our assumptions (and can be used on any simple graph i think)
-- Subproof outline: (strong induction on V.card)
    -- Base Case: trivially true for V.card = 1, but that's because we have no edges
        -- this might be a problem - if we don't have loops, how can we say the individual vertex has a path to itself?
        -- maybe need to show it's true for V.card = 2?
    -- IH: assume the statement holds for V.card < n vertices
    -- let V.card = n
    -- let u,v be distinct vertices in T
    -- by assumption, a uv path p exists
    -- since p is unique, removing an edge x from p (and in fact the edge set of T. maybe new induced subgraph definition?) disconnects u from v
    -- the resultant graph contains two components H and K on vertex sets U and W, where U.card < n and W.card < n, so by IH we have U.card - 1 edges in H and W.card - 1 edges in K, and U.card + W.card = n - 1, which gives us n - 2 edges in H ∪ K. then adding back x, we have n - 1 edges in T.

/- Lemma 3: (c) → (d) : T is connected on n vertices with n - 1 edges → T is acyclic on n vertices with n - 1 edges -/
-- note: no mention of T being a tree here, so that's not one of our assumptions (and can be used on any simple graph)
-- Subproof outline:
    -- suppose for contradiction that T has a cycle C of length k ≥ 3.
    -- C contains k vertices and k edges (i think there are lemmas for this in path.lean)
    -- since k ≤ n (by (c)), there are n - k vertices in T that are not in C
    -- ∀ v : V, v ∉ C
        -- consider a path connecting v to a vertex in C
        -- each path will have at least 1 edge
        -- the number of edges connecting the rest of the vertices in V to those in C is at least n - k
        -- this means we have n - k + k = n ≤ edges in T, which is a contradiction

/- Lemma 4: (d) → (a) : T is acyclic on n vertices with n - 1 edges → T is a tree -/
-- note: this time we want to prove that T is a tree, so that's not one of our assumptions (and can be used on any simple graph)
-- Subproof outline:
    -- T is acyclic, V.card = n
    -- suppose T has k ≥ 1 components T1, T2, ... , Tk (oh boy finset time)
    -- since T is acyclic and components are connected, each Ti is a tree (say they're on Vi, and Vi.card = ni)
    -- since each Ti is a tree, we can use (a) → (b) → (c) to get that each Ti has ni - 1 edges
    -- therefore, T has ∑ (ni - 1) = (∑ ni) - k = n - k edges
    -- since n - k = n - 1, we have k = 1, so we have 1 component (which is connected by definition of component)
    -- the one component is all of T (make this a lemma about components)
    -- therefore T is acyclic and connected, and must be a tree

-- i guess make a bunch of iff lemmas out of that so i can rw stuff

-- make Prüfer codes

end simple_graph


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Scott Morrison, Adam Topaz
-/

import category_theory.skeletal
import tactic.linarith
import data.fintype.sort
import order.category.NonemptyFinLinOrd
import category_theory.functor.reflects_isomorphisms

/-! # The simplex category

We construct a skeletal model of the simplex category, with objects `ℕ` and the
morphism `n ⟶ m` being the monotone maps from `fin (n+1)` to `fin (m+1)`.

We show that this category is equivalent to `NonemptyFinLinOrd`.

## Remarks

The definitions `simplex_category` and `simplex_category.hom` are marked as irreducible.

We provide the following functions to work with these objects:
1. `simplex_category.mk` creates an object of `simplex_category` out of a natural number.
  Use the notation `[n]` in the `simplicial` locale.
2. `simplex_category.len` gives the "length" of an object of `simplex_category`, as a natural.
3. `simplex_category.hom.mk` makes a morphism out of a monotone map between `fin`'s.
4. `simplex_category.hom.to_order_hom` gives the underlying monotone map associated to a
  term of `simplex_category.hom`.

-/

universe v

open category_theory

/-- The simplex category:
* objects are natural numbers `n : ℕ`
* morphisms from `n` to `m` are monotone functions `fin (n+1) → fin (m+1)`
-/
@[derive inhabited, irreducible]
def simplex_category := ℕ

namespace simplex_category

section
local attribute [semireducible] simplex_category

-- TODO: Make `mk` irreducible.
/-- Interpet a natural number as an object of the simplex category. -/
def mk (n : ℕ) : simplex_category := n

localized "notation `[`n`]` := simplex_category.mk n" in simplicial

-- TODO: Make `len` irreducible.
/-- The length of an object of `simplex_category`. -/
def len (n : simplex_category) : ℕ := n

@[ext] lemma ext (a b : simplex_category) : a.len = b.len → a = b := id
@[simp] lemma len_mk (n : ℕ) : [n].len = n := rfl
@[simp] lemma mk_len (n : simplex_category) : [n.len] = n := rfl

/-- Morphisms in the simplex_category. -/
@[irreducible, nolint has_inhabited_instance]
protected def hom (a b : simplex_category) := fin (a.len + 1) →o fin (b.len + 1)

namespace hom

local attribute [semireducible] simplex_category.hom

/-- Make a moprhism in `simplex_category` from a monotone map of fin's. -/
def mk {a b : simplex_category} (f : fin (a.len + 1) →o fin (b.len + 1)) :
  simplex_category.hom a b := f

/-- Recover the monotone map from a morphism in the simplex category. -/
def to_order_hom {a b : simplex_category} (f : simplex_category.hom a b) :
  fin (a.len + 1) →o fin (b.len + 1) := f

@[ext] lemma ext {a b : simplex_category} (f g : simplex_category.hom a b) :
  f.to_order_hom = g.to_order_hom → f = g := id

@[simp] lemma mk_to_order_hom {a b : simplex_category}
  (f : simplex_category.hom a b) : mk (f.to_order_hom) = f := rfl

@[simp] lemma to_order_hom_mk {a b : simplex_category}
  (f : fin (a.len + 1) →o fin (b.len + 1)) : (mk f).to_order_hom = f := rfl

lemma mk_to_order_hom_apply {a b : simplex_category}
  (f : fin (a.len + 1) →o fin (b.len + 1)) (i : fin (a.len + 1)) :
  (mk f).to_order_hom i = f i := rfl

/-- Identity morphisms of `simplex_category`. -/
@[simp]
def id (a : simplex_category) :
  simplex_category.hom a a :=
mk order_hom.id

/-- Composition of morphisms of `simplex_category`. -/
@[simp]
def comp {a b c : simplex_category} (f : simplex_category.hom b c)
  (g : simplex_category.hom a b) : simplex_category.hom a c :=
mk $ f.to_order_hom.comp g.to_order_hom

end hom

@[simps]
instance small_category : small_category.{0} simplex_category :=
{ hom := λ n m, simplex_category.hom n m,
  id := λ m, simplex_category.hom.id _,
  comp := λ _ _ _ f g, simplex_category.hom.comp g f, }

/-- The constant morphism from [0]. -/
def const (x : simplex_category) (i : fin (x.len+1)) : [0] ⟶ x :=
  hom.mk $ ⟨λ _, i, by tauto⟩

@[simp]
lemma const_comp (x y : simplex_category) (i : fin (x.len + 1)) (f : x ⟶ y) :
  const x i ≫ f = const y (f.to_order_hom i) := rfl

/--
Make a morphism `[n] ⟶ [m]` from a monotone map between fin's.
This is useful for constructing morphisms beetween `[n]` directly
without identifying `n` with `[n].len`.
-/
@[simp]
def mk_hom {n m : ℕ} (f : (fin (n+1)) →o (fin (m+1))) : [n] ⟶ [m] :=
simplex_category.hom.mk f

lemma hom_zero_zero (f : [0] ⟶ [0]) : f = 𝟙 _ :=
by { ext : 2, dsimp, apply subsingleton.elim }

end

open_locale simplicial

section generators
/-!
## Generating maps for the simplex category

TODO: prove that the simplex category is equivalent to
one given by the following generators and relations.
-/

/-- The `i`-th face map from `[n]` to `[n+1]` -/
def δ {n} (i : fin (n+2)) : [n] ⟶ [n+1] :=
mk_hom (fin.succ_above i).to_order_hom

/-- The `i`-th degeneracy map from `[n+1]` to `[n]` -/
def σ {n} (i : fin (n+1)) : [n+1] ⟶ [n] := mk_hom
{ to_fun := fin.pred_above i,
  monotone' := fin.pred_above_right_monotone i }

/-- The generic case of the first simplicial identity -/
lemma δ_comp_δ {n} {i j : fin (n+2)} (H : i ≤ j) :
  δ i ≫ δ j.succ = δ j ≫ δ i.cast_succ :=
begin
  ext k,
  dsimp [δ, fin.succ_above],
  simp only [order_embedding.to_order_hom_coe,
    order_embedding.coe_of_strict_mono,
    function.comp_app,
    simplex_category.hom.to_order_hom_mk,
    order_hom.comp_coe],
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  rcases k with ⟨k, _⟩,
  split_ifs; { simp at *; linarith },
end

/-- The special case of the first simplicial identity -/
lemma δ_comp_δ_self {n} {i : fin (n+2)} : δ i ≫ δ i.cast_succ = δ i ≫ δ i.succ :=
(δ_comp_δ (le_refl i)).symm

/-- The second simplicial identity -/
lemma δ_comp_σ_of_le {n} {i : fin (n+2)} {j : fin (n+1)} (H : i ≤ j.cast_succ) :
  δ i.cast_succ ≫ σ j.succ = σ j ≫ δ i :=
begin
  ext k,
  suffices : ite (j.succ.cast_succ < ite (k < i) k.cast_succ k.succ)
    (ite (k < i) (k:ℕ) (k + 1) - 1) (ite (k < i) k (k + 1)) =
      ite ((if h : (j:ℕ) < k
        then k.pred (by { rintro rfl, exact nat.not_lt_zero _ h })
        else k.cast_lt (by { cases j, cases k, simp only [len_mk], linarith })).cast_succ < i)
          (ite (j.cast_succ < k) (k - 1) k) (ite (j.cast_succ < k) (k - 1) k + 1),
  { dsimp [δ, σ, fin.succ_above, fin.pred_above],
    simpa [fin.pred_above] with push_cast },
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  rcases k with ⟨k, _⟩,
  simp only [subtype.mk_le_mk, fin.cast_succ_mk] at H,
  dsimp, simp only [if_congr, subtype.mk_lt_mk, dif_ctx_congr],
  split_ifs,
  -- Most of the goals can now be handled by `linarith`,
  -- but we have to deal with two of them by hand.
  swap 8,
  { exact (nat.succ_pred_eq_of_pos (lt_of_le_of_lt (zero_le _) ‹_›)).symm, },
  swap 7,
  { have : k ≤ i := nat.le_of_pred_lt ‹_›, linarith, },
  -- Hope for the best from `linarith`:
  all_goals { try { refl <|> simp at * }; linarith, },
end

/-- The first part of the third simplicial identity -/
lemma δ_comp_σ_self {n} {i : fin (n+1)} :
  δ i.cast_succ ≫ σ i = 𝟙 [n] :=
begin
  ext j,
  suffices : ite (fin.cast_succ i < ite (j < i) (fin.cast_succ j) j.succ)
    (ite (j < i) (j:ℕ) (j + 1) - 1) (ite (j < i) j (j + 1)) = j,
  { dsimp [δ, σ, fin.succ_above, fin.pred_above], simpa [fin.pred_above] with push_cast },
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  dsimp, simp only [if_congr, subtype.mk_lt_mk],
  split_ifs; { simp at *; linarith, },
end

/-- The second part of the third simplicial identity -/
lemma δ_comp_σ_succ {n} {i : fin (n+1)} :
  δ i.succ ≫ σ i = 𝟙 [n] :=
begin
  ext j,
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  dsimp [δ, σ, fin.succ_above, fin.pred_above],
  simp [fin.pred_above] with push_cast,
  split_ifs; { simp at *; linarith, },
end

/-- The fourth simplicial identity -/
lemma δ_comp_σ_of_gt {n} {i : fin (n+2)} {j : fin (n+1)} (H : j.cast_succ < i) :
  δ i.succ ≫ σ j.cast_succ = σ j ≫ δ i :=
begin
  ext k,
  dsimp [δ, σ, fin.succ_above, fin.pred_above],
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  rcases k with ⟨k, _⟩,
  simp only [subtype.mk_lt_mk, fin.cast_succ_mk] at H,
  suffices : ite (_ < ite (k < i + 1) _ _) _ _ =
    ite _ (ite (j < k) (k - 1) k) (ite (j < k) (k - 1) k + 1),
  { simpa [apply_dite fin.cast_succ, fin.pred_above] with push_cast, },
  split_ifs,
  -- Most of the goals can now be handled by `linarith`,
  -- but we have to deal with three of them by hand.
  swap 2,
  { simp only [subtype.mk_lt_mk] at h_1,
    simp only [not_lt] at h_2,
    simp only [self_eq_add_right, one_ne_zero],
    exact lt_irrefl (k - 1) (lt_of_lt_of_le
      (nat.pred_lt (ne_of_lt (lt_of_le_of_lt (zero_le _) h_1)).symm)
      (le_trans (nat.le_of_lt_succ h) h_2)) },
  swap 4,
  { simp only [subtype.mk_lt_mk] at h_1,
    simp only [not_lt] at h,
    simp only [nat.add_succ_sub_one, add_zero],
    exfalso,
    exact lt_irrefl _ (lt_of_le_of_lt (nat.le_pred_of_lt (nat.lt_of_succ_le h)) h_3), },
  swap 4,
  { simp only [subtype.mk_lt_mk] at h_1,
    simp only [not_lt] at h_3,
    simp only [nat.add_succ_sub_one, add_zero],
    exact (nat.succ_pred_eq_of_pos (lt_of_le_of_lt (zero_le _) h_2)).symm, },
  -- Hope for the best from `linarith`:
  all_goals { simp at h_1 h_2 ⊢; linarith, },
end

local attribute [simp] fin.pred_mk

/-- The fifth simplicial identity -/
lemma σ_comp_σ {n} {i j : fin (n+1)} (H : i ≤ j) :
  σ i.cast_succ ≫ σ j = σ j.succ ≫ σ i :=
begin
  ext k,
  dsimp [σ, fin.pred_above],
  rcases i with ⟨i, _⟩,
  rcases j with ⟨j, _⟩,
  rcases k with ⟨k, _⟩,
  simp only [subtype.mk_le_mk] at H,
  -- At this point `simp with push_cast` makes good progress, but neither `simp?` nor `squeeze_simp`
  -- return usable sets of lemmas.
  -- To avoid using a non-terminal simp, we make a `suffices` statement indicating the shape
  -- of the goal we're looking for, and then use `simpa with push_cast`.
  -- I'm not sure this is actually much more robust that a non-terminal simp.
  suffices : ite (_ < dite (i < k) _ _) _ _ =
    ite (_ < dite (j + 1 < k) _ _) _ _,
  { simpa [fin.pred_above] with push_cast, },
  split_ifs,
  -- `split_ifs` created 12 goals.
  -- Most of them are dealt with `by simp at *; linarith`,
  -- but we pull out two harder ones to do by hand.
  swap 3,
  { simp only [not_lt] at h_2,
    exact false.elim
    (lt_irrefl (k - 1)
      (lt_of_lt_of_le (nat.pred_lt (id (ne_of_lt (lt_of_le_of_lt (zero_le i) h)).symm))
        (le_trans h_2 (nat.succ_le_of_lt h_1)))) },
  swap 3,
  { simp only [subtype.mk_lt_mk, not_lt] at h_1,
    exact false.elim
    (lt_irrefl j (lt_of_lt_of_le (nat.pred_lt_pred (nat.succ_ne_zero j) h_2) h_1)) },
  -- Deal with the rest automatically.
  all_goals { simp at *; linarith, },
end

end generators

section skeleton

/-- The functor that exhibits `simplex_category` as skeleton
of `NonemptyFinLinOrd` -/
@[simps obj map]
def skeletal_functor : simplex_category ⥤ NonemptyFinLinOrd.{v} :=
{ obj := λ a, NonemptyFinLinOrd.of $ ulift (fin (a.len + 1)),
  map := λ a b f,
    ⟨λ i, ulift.up (f.to_order_hom i.down), λ i j h, f.to_order_hom.monotone h⟩,
  map_id' := λ a, by { ext, simp, },
  map_comp' := λ a b c f g, by { ext, simp, }, }

lemma skeletal : skeletal simplex_category :=
λ X Y ⟨I⟩,
begin
  suffices : fintype.card (fin (X.len+1)) = fintype.card (fin (Y.len+1)),
  { ext, simpa },
  { apply fintype.card_congr,
    refine equiv.ulift.symm.trans (((skeletal_functor ⋙ forget _).map_iso I).to_equiv.trans _),
    apply equiv.ulift }
end

namespace skeletal_functor

instance : full skeletal_functor.{v} :=
{ preimage := λ a b f, simplex_category.hom.mk ⟨λ i, (f (ulift.up i)).down, λ i j h, f.monotone h⟩,
  witness' := by { intros m n f, dsimp at *, ext1 ⟨i⟩, ext1, ext1, cases x, simp, } }

instance : faithful skeletal_functor.{v} :=
{ map_injective' := λ m n f g h,
  begin
    ext1, ext1, ext1 i, apply ulift.up.inj,
    change (skeletal_functor.map f) ⟨i⟩ = (skeletal_functor.map g) ⟨i⟩,
    rw h,
  end }

instance : ess_surj skeletal_functor.{v} :=
{ mem_ess_image := λ X, ⟨mk (fintype.card X - 1 : ℕ), ⟨begin
    have aux : fintype.card X = fintype.card X - 1 + 1,
    { exact (nat.succ_pred_eq_of_pos $ fintype.card_pos_iff.mpr ⟨⊥⟩).symm, },
    let f := mono_equiv_of_fin X aux,
    have hf := (finset.univ.order_emb_of_fin aux).strict_mono,
    refine
    { hom := ⟨λ i, f i.down, _⟩,
      inv := ⟨λ i, ⟨f.symm i⟩, _⟩,
      hom_inv_id' := _,
      inv_hom_id' := _ },
    { rintro ⟨i⟩ ⟨j⟩ h, show f i ≤ f j, exact hf.monotone h, },
    { intros i j h, show f.symm i ≤ f.symm j, rw ← hf.le_iff_le,
      show f (f.symm i) ≤ f (f.symm j), simpa only [order_iso.apply_symm_apply], },
    { ext1, ext1 ⟨i⟩, ext1, exact f.symm_apply_apply i },
    { ext1, ext1 i, exact f.apply_symm_apply i },
  end⟩⟩, }

noncomputable instance is_equivalence : is_equivalence (skeletal_functor.{v}) :=
equivalence.of_fully_faithfully_ess_surj skeletal_functor

end skeletal_functor

/-- The equivalence that exhibits `simplex_category` as skeleton
of `NonemptyFinLinOrd` -/
noncomputable def skeletal_equivalence : simplex_category ≌ NonemptyFinLinOrd.{v} :=
functor.as_equivalence skeletal_functor

end skeleton

/--
`simplex_category` is a skeleton of `NonemptyFinLinOrd`.
-/
noncomputable
def is_skeleton_of : is_skeleton_of NonemptyFinLinOrd simplex_category skeletal_functor.{v} :=
{ skel := skeletal,
  eqv := skeletal_functor.is_equivalence }

/-- The truncated simplex category. -/
@[derive small_category]
def truncated (n : ℕ) := {a : simplex_category // a.len ≤ n}

namespace truncated

instance {n} : inhabited (truncated n) := ⟨⟨[0],by simp⟩⟩

/--
The fully faithful inclusion of the truncated simplex category into the usual
simplex category.
-/
@[derive [full, faithful]]
def inclusion {n : ℕ} : simplex_category.truncated n ⥤ simplex_category :=
full_subcategory_inclusion _

end truncated

section concrete

instance : concrete_category.{0} simplex_category :=
{ forget :=
  { obj := λ i, fin (i.len + 1),
    map := λ i j f, f.to_order_hom },
  forget_faithful := {} }

end concrete

section epi_mono

/-- A morphism in `simplex_category` is a monomorphism precisely when it is an injective function
-/
theorem mono_iff_injective {n m : simplex_category} {f : n ⟶ m} :
  mono f ↔ function.injective f.to_order_hom :=
begin
  split,
  { introsI m x y h,
    have H : const n x ≫ f = const n y ≫ f,
    { dsimp, rw h },
    change (n.const x).to_order_hom 0 = (n.const y).to_order_hom 0,
    rw cancel_mono f at H,
    rw H },
  { exact concrete_category.mono_of_injective f }
end

/-- A morphism in `simplex_category` is an epimorphism if and only if it is a surjective function
-/
lemma epi_iff_surjective {n m : simplex_category} {f: n ⟶ m} :
  epi f ↔ function.surjective f.to_order_hom :=
begin
  split,
  { introsI hyp_f_epi x,
    by_contra' h_ab,
    -- The proof is by contradiction: assume f is not surjective,
    -- then introduce two non-equal auxiliary functions equalizing f, and get a contradiction.
    -- First we define the two auxiliary functions.
    set chi_1 : m ⟶ [1] := hom.mk ⟨λ u, if u ≤ x then 0 else 1, begin
      intros a b h,
      dsimp only [],
      split_ifs with h1 h2 h3,
      any_goals { exact le_rfl },
      { exact bot_le },
      { exact false.elim (h1 (le_trans h h3)) }
    end ⟩,
    set chi_2 : m ⟶ [1] := hom.mk ⟨λ u, if u < x then 0 else 1, begin
      intros a b h,
      dsimp only [],
      split_ifs with h1 h2 h3,
      any_goals { exact le_rfl },
      { exact bot_le },
      { exact false.elim (h1 (lt_of_le_of_lt h h3)) }
    end ⟩,
    -- The two auxiliary functions equalize f
    have f_comp_chi_i : f ≫ chi_1 = f ≫ chi_2,
    { dsimp,
      ext,
      simp [le_iff_lt_or_eq, h_ab x_1] },
    -- We now just have to show the two auxiliary functions are not equal.
    rw category_theory.cancel_epi f at f_comp_chi_i, rename f_comp_chi_i eq_chi_i,
    apply_fun (λ e, e.to_order_hom x) at eq_chi_i,
    suffices : (0 : fin 2) = 1, by exact bot_ne_top this,
    simpa using eq_chi_i },
  { exact concrete_category.epi_of_surjective f }
end

/-- A monomorphism in `simplex_category` must increase lengths-/
lemma len_le_of_mono {x y : simplex_category} {f : x ⟶ y} :
  mono f → (x.len ≤ y.len) :=
begin
  intro hyp_f_mono,
  have f_inj : function.injective f.to_order_hom.to_fun,
  { exact mono_iff_injective.elim_left (hyp_f_mono) },
  simpa using fintype.card_le_of_injective f.to_order_hom.to_fun f_inj,
end

/-- An epimorphism in `simplex_category` must decrease lengths-/
lemma len_le_of_epi {x y : simplex_category} {f : x ⟶ y} :
  epi f → y.len ≤ x.len :=
begin
  intro hyp_f_epi,
  have f_surj : function.surjective f.to_order_hom.to_fun,
  { exact epi_iff_surjective.elim_left (hyp_f_epi) },
  simpa using fintype.card_le_of_surjective f.to_order_hom.to_fun f_surj,
end

lemma le_of_epi {n m : ℕ} {f : [n] ⟶ [m]} : epi f → (m ≤ n) :=
len_le_of_epi

instance {n : ℕ} {i : fin (n+2)} : mono (δ i) :=
begin
  rw mono_iff_injective,
  exact fin.succ_above_right_injective,
end

instance {n : ℕ} {i : fin (n+1)} : epi (σ i) :=
begin
  rw epi_iff_surjective,
  intro b,
  simp only [σ, mk_hom, hom.to_order_hom_mk, order_hom.coe_fun_mk],
  by_cases b ≤ i,
  { use b,
    rw fin.pred_above_below i b (by simpa only [fin.coe_eq_cast_succ] using h),
    simp only [fin.coe_eq_cast_succ, fin.cast_pred_cast_succ], },
  { use b.succ,
    rw [fin.pred_above_above i b.succ _, fin.pred_succ],
    rw not_le at h,
    rw fin.lt_iff_coe_lt_coe at h ⊢,
    simpa only [fin.coe_succ, fin.coe_cast_succ] using nat.lt.step h, }
end

instance : reflects_isomorphisms (forget simplex_category) :=
⟨begin
  intros x y f,
  introI,
  exact is_iso.of_iso
  { hom := f,
    inv := hom.mk
    { to_fun := inv ((forget simplex_category).map f),
      monotone' :=λ y₁ y₂ h, begin
          by_cases h' : y₁ < y₂,
          { by_contradiction h'',
            have eq := λ i, congr_hom (iso.inv_hom_id (as_iso ((forget _).map f))) i,
            have ineq := f.to_order_hom.monotone' (le_of_not_ge h''),
            dsimp at ineq,
            erw [eq, eq] at ineq,
            exact not_le.mpr h' ineq, },
          { rw eq_of_le_of_not_lt h h', }
        end, },
    hom_inv_id' := by { ext1, ext1, exact iso.hom_inv_id (as_iso ((forget _).map f)), },
    inv_hom_id' := by { ext1, ext1, exact iso.inv_hom_id (as_iso ((forget _).map f)), }, },
end⟩

lemma is_iso_of_bijective {x y : simplex_category} {f : x ⟶ y}
  (hf : function.bijective (f.to_order_hom.to_fun)) : is_iso f :=
begin
  haveI : is_iso ((forget simplex_category).map f) := (is_iso_iff_bijective _).mpr hf,
  exact is_iso_of_reflects_iso f (forget simplex_category),
end

/-- An isomorphism in `simplex_category` induces an `order_iso`. -/
@[simp]
def order_iso_of_iso {x y : simplex_category} (e : x ≅ y) :
  fin (x.len+1) ≃o fin (y.len+1) :=
equiv.to_order_iso
  { to_fun    := e.hom.to_order_hom,
    inv_fun   := e.inv.to_order_hom,
    left_inv  := λ i, by simpa only using congr_arg (λ φ, (hom.to_order_hom φ) i) e.hom_inv_id',
    right_inv := λ i, by simpa only using congr_arg (λ φ, (hom.to_order_hom φ) i) e.inv_hom_id', }
  e.hom.to_order_hom.monotone e.inv.to_order_hom.monotone

lemma iso_eq_iso_refl {x : simplex_category} (e : x ≅ x) :
  e = iso.refl x :=
begin
  have h : (finset.univ : finset (fin (x.len+1))).card = x.len+1 := finset.card_fin (x.len+1),
  have eq₁ := finset.order_emb_of_fin_unique' h
    (λ i, finset.mem_univ ((order_iso_of_iso e) i)),
  have eq₂ := finset.order_emb_of_fin_unique' h
    (λ i, finset.mem_univ ((order_iso_of_iso (iso.refl x)) i)),
  ext1, ext1,
  convert congr_arg (λ φ, (order_embedding.to_order_hom φ)) (eq₁.trans eq₂.symm),
  ext1, ext1 i,
  refl,
end

lemma eq_id_of_is_iso {x : simplex_category} {f : x ⟶ x} (hf : is_iso f) : f = 𝟙 _ :=
congr_arg (λ (φ : _ ≅ _), φ.hom) (iso_eq_iso_refl (as_iso f))

lemma eq_σ_comp_of_not_injective' {n : ℕ} {Δ' : simplex_category} (θ : mk (n+1) ⟶ Δ')
  (i : fin (n+1)) (hi : θ.to_order_hom i.cast_succ = θ.to_order_hom i.succ):
  ∃ (θ' : mk n ⟶ Δ'), θ = σ i ≫ θ' :=
begin
  use δ i.succ ≫ θ,
  ext1, ext1, ext1 x,
  simp only [hom.to_order_hom_mk, function.comp_app, order_hom.comp_coe,
    hom.comp, small_category_comp, σ, mk_hom, order_hom.coe_fun_mk],
  by_cases h' : x ≤ i.cast_succ,
  { rw fin.pred_above_below i x h',
    have eq := fin.cast_succ_cast_pred (gt_of_gt_of_ge (fin.cast_succ_lt_last i) h'),
    erw fin.succ_above_below i.succ x.cast_pred _, swap,
    { rwa [eq, ← fin.le_cast_succ_iff], },
    rw eq, },
  { simp only [not_le] at h',
    let y := x.pred begin
      intro h,
      rw h at h',
      simpa only [fin.lt_iff_coe_lt_coe, nat.not_lt_zero, fin.coe_zero] using h',
    end,
    simp only [show x = y.succ, by rw fin.succ_pred] at h' ⊢,
    rw [fin.pred_above_above i y.succ h', fin.pred_succ],
    by_cases h'' : y = i,
    { rw h'',
      convert hi.symm,
      erw fin.succ_above_below i.succ _,
      exact fin.lt_succ, },
    { erw fin.succ_above_above i.succ _,
      simp only [fin.lt_iff_coe_lt_coe, fin.le_iff_coe_le_coe, fin.coe_succ,
        fin.coe_cast_succ, nat.lt_succ_iff, fin.ext_iff] at h' h'' ⊢,
      cases nat.le.dest h' with c hc,
      cases c,
      { exfalso,
        rw [add_zero] at hc,
        rw [hc] at h'',
        exact h'' rfl, },
      { rw ← hc,
        simp only [add_le_add_iff_left, nat.succ_eq_add_one,
          le_add_iff_nonneg_left, zero_le], }, }, }
end

lemma eq_σ_comp_of_not_injective {n : ℕ} {Δ' : simplex_category} (θ : mk (n+1) ⟶ Δ')
  (hθ : ¬function.injective θ.to_order_hom) :
  ∃ (i : fin (n+1)) (θ' : mk n ⟶ Δ'), θ = σ i ≫ θ' :=
begin
  simp only [function.injective, exists_prop, not_forall] at hθ,
  -- as θ is not injective, there exists `x<y` such that `θ x = θ y`
  -- and then, `θ x = θ (x+1)`
  have hθ₂ : ∃ (x y : fin (n+2)), (hom.to_order_hom θ) x = (hom.to_order_hom θ) y ∧ x<y,
  { rcases hθ with ⟨x, y, ⟨h₁, h₂⟩⟩,
    by_cases x<y,
    { exact ⟨x, y, ⟨h₁, h⟩⟩, },
    { refine ⟨y, x, ⟨h₁.symm, _⟩⟩,
      cases lt_or_eq_of_le (not_lt.mp h) with h' h',
      { exact h', },
      { exfalso,
        exact h₂ h'.symm, }, }, },
  rcases hθ₂ with ⟨x, y, ⟨h₁, h₂⟩⟩,
  let z := x.cast_pred,
  use z,
  simp only [← (show z.cast_succ = x,
    by exact fin.cast_succ_cast_pred (lt_of_lt_of_le h₂ (fin.le_last y)))] at h₁ h₂,
  apply eq_σ_comp_of_not_injective',
  rw fin.cast_succ_lt_iff_succ_le at h₂,
  apply le_antisymm,
  { exact θ.to_order_hom.monotone (le_of_lt (fin.cast_succ_lt_succ z)), },
  { rw h₁,
    exact θ.to_order_hom.monotone h₂, },
end

lemma eq_comp_δ_of_not_surjective' {n : ℕ} {Δ : simplex_category} (θ : Δ ⟶ mk (n+1))
  (i : fin (n+2)) (hi : ∀ x, θ.to_order_hom x ≠ i) :
  ∃ (θ' : Δ ⟶ (mk n)), θ = θ' ≫ δ i :=
begin
  by_cases i < fin.last (n+1),
  { use θ ≫ σ (fin.cast_pred i),
    ext1, ext1, ext1 x,
    simp only [hom.to_order_hom_mk, function.comp_app,
      order_hom.comp_coe, hom.comp, small_category_comp],
    by_cases h' : θ.to_order_hom x ≤ i,
    { simp only [σ, mk_hom, hom.to_order_hom_mk, order_hom.coe_fun_mk],
      erw fin.pred_above_below (fin.cast_pred i) (θ.to_order_hom x)
        (by simpa [fin.cast_succ_cast_pred h] using h'),
      erw fin.succ_above_below i, swap,
      { simp only [fin.lt_iff_coe_lt_coe, fin.coe_cast_succ],
        exact lt_of_le_of_lt (fin.coe_cast_pred_le_self _)
          (fin.lt_iff_coe_lt_coe.mp ((ne.le_iff_lt (hi x)).mp h')), },
      rw fin.cast_succ_cast_pred,
      apply lt_of_le_of_lt h' h, },
    { simp only [not_le] at h',
      simp only [σ, mk_hom, hom.to_order_hom_mk, order_hom.coe_fun_mk,
        fin.pred_above_above (fin.cast_pred i) (θ.to_order_hom x)
        (by simpa only [fin.cast_succ_cast_pred h] using h')],
      erw [fin.succ_above_above i _, fin.succ_pred],
      simpa only [fin.le_iff_coe_le_coe, fin.coe_cast_succ, fin.coe_pred]
          using nat.le_pred_of_lt (fin.lt_iff_coe_lt_coe.mp h'), }, },
  { have h' := le_antisymm (fin.le_last i) (not_lt.mp h),
    subst h',
    use θ ≫ σ (fin.last _),
    ext1, ext1, ext1 x,
    simp only [hom.to_order_hom_mk, function.comp_app, order_hom.comp_coe, hom.comp,
      small_category_comp, σ, δ, mk_hom, order_hom.coe_fun_mk,
      order_embedding.to_order_hom_coe, fin.pred_above_last, fin.succ_above_last,
      fin.cast_succ_cast_pred ((ne.le_iff_lt (hi x)).mp (fin.le_last _))], },
end

lemma eq_comp_δ_of_not_surjective {n : ℕ} {Δ : simplex_category} (θ : Δ ⟶ mk (n+1))
  (hθ : ¬function.surjective θ.to_order_hom) :
  ∃ (i : fin (n+2)) (θ' : Δ ⟶ (mk n)), θ = θ' ≫ δ i :=
begin
  cases not_forall.mp hθ with i hi,
  use i,
  exact eq_comp_δ_of_not_surjective' θ i (not_exists.mp hi),
end

lemma eq_id_of_mono {x : simplex_category} (i : x ⟶ x) [mono i] : i = 𝟙 _ :=
begin
  apply eq_id_of_is_iso,
  apply is_iso_of_bijective,
  erw [fintype.bijective_iff_injective_and_card i.to_order_hom, ← mono_iff_injective,
    eq_self_iff_true, and_true],
  apply_instance,
end

lemma eq_id_of_epi {x : simplex_category} (i : x ⟶ x) [epi i] : i = 𝟙 _ :=
begin
  apply eq_id_of_is_iso,
  apply is_iso_of_bijective,
  erw [fintype.bijective_iff_surjective_and_card i.to_order_hom, ← epi_iff_surjective,
    eq_self_iff_true, and_true],
  apply_instance,
end

lemma eq_σ_of_epi {n : ℕ} (θ : mk (n+1) ⟶ mk n) [epi θ] : ∃ (i : fin (n+1)), θ = σ i :=
begin
  rcases eq_σ_comp_of_not_injective θ _ with ⟨i, θ', h⟩, swap,
  { by_contradiction,
    simpa only [nat.one_ne_zero, add_le_iff_nonpos_right, nonpos_iff_eq_zero]
      using le_of_mono (mono_iff_injective.mpr h), },
  use i,
  haveI : epi (σ i ≫ θ') := by { rw ← h, apply_instance, },
  haveI := category_theory.epi_of_epi (σ i) θ',
  erw [h, eq_id_of_epi θ', category.comp_id],
end

lemma eq_δ_of_mono {n : ℕ} (θ : mk n ⟶ mk (n+1)) [mono θ] : ∃ (i : fin (n+2)), θ = δ i :=
begin
  rcases eq_comp_δ_of_not_surjective θ _ with ⟨i, θ', h⟩, swap,
  { by_contradiction,
    simpa only [add_le_iff_nonpos_right, nonpos_iff_eq_zero]
      using le_of_epi (epi_iff_surjective.mpr h), },
  use i,
  haveI : mono (θ' ≫ δ i) := by { rw ← h, apply_instance, },
  haveI := category_theory.mono_of_mono θ' (δ i),
  erw [h, eq_id_of_mono θ', category.id_comp],
end

end epi_mono

end simplex_category


/-
Copyright (c) 2020 Fox Thomson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Fox Thomson
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.set_theory.game.winner
import Mathlib.tactic.nth_rewrite.default
import Mathlib.tactic.equiv_rw
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Basic definitions about impartial (pre-)games

We will define an impartial game, one in which left and right can make exactly the same moves.
Our definition differs slightly by saying that the game is always equivalent to its negative,
no matter what moves are played. This allows for games such as poker-nim to be classifed as
impartial.
-/

namespace pgame

/-- The definition for a impartial game, defined using Conway induction -/
def impartial : pgame → Prop := sorry

theorem impartial_def {G : pgame} :
    impartial G ↔
        equiv G (-G) ∧
          (∀ (i : left_moves G), impartial (move_left G i)) ∧
            ∀ (j : right_moves G), impartial (move_right G j) :=
  sorry

namespace impartial

protected instance impartial_zero : impartial 0 := sorry

theorem neg_equiv_self (G : pgame) [h : impartial G] : equiv G (-G) :=
  and.left (iff.mp impartial_def h)

protected instance move_left_impartial {G : pgame} [h : impartial G] (i : left_moves G) :
    impartial (move_left G i) :=
  and.left (and.right (iff.mp impartial_def h)) i

protected instance move_right_impartial {G : pgame} [h : impartial G] (j : right_moves G) :
    impartial (move_right G j) :=
  and.right (and.right (iff.mp impartial_def h)) j

protected instance impartial_add (G : pgame) (H : pgame) [impartial G] [impartial H] :
    impartial (G + H) :=
  sorry

protected instance impartial_neg (G : pgame) [impartial G] : impartial (-G) := sorry

theorem winner_cases (G : pgame) [impartial G] : first_loses G ∨ first_wins G := sorry

theorem not_first_wins (G : pgame) [impartial G] : ¬first_wins G ↔ first_loses G := sorry

theorem not_first_loses (G : pgame) [impartial G] : ¬first_loses G ↔ first_wins G :=
  iff.symm (iff.mp iff_not_comm (iff.symm (not_first_wins G)))

theorem add_self (G : pgame) [impartial G] : first_loses (G + G) :=
  iff.mpr first_loses_is_zero
    (equiv_trans (add_congr (neg_equiv_self G) (equiv_refl G)) add_left_neg_equiv)

theorem equiv_iff_sum_first_loses (G : pgame) (H : pgame) [impartial G] [impartial H] :
    equiv G H ↔ first_loses (G + H) :=
  sorry

theorem le_zero_iff {G : pgame} [impartial G] : G ≤ 0 ↔ 0 ≤ G :=
  eq.mpr (id (Eq._oldrec (Eq.refl (G ≤ 0 ↔ 0 ≤ G)) (propext le_zero_iff_zero_le_neg)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (0 ≤ -G ↔ 0 ≤ G))
          (propext (le_congr (equiv_refl 0) (neg_equiv_self G)))))
      (iff.refl (0 ≤ -G)))

theorem lt_zero_iff {G : pgame} [impartial G] : G < 0 ↔ 0 < G := sorry

theorem first_loses_symm (G : pgame) [impartial G] : first_loses G ↔ G ≤ 0 :=
  { mp := and.left, mpr := fun (h : G ≤ 0) => { left := h, right := iff.mp le_zero_iff h } }

theorem first_wins_symm (G : pgame) [impartial G] : first_wins G ↔ G < 0 :=
  { mp := and.right, mpr := fun (h : G < 0) => { left := iff.mp lt_zero_iff h, right := h } }

theorem first_loses_symm' (G : pgame) [impartial G] : first_loses G ↔ 0 ≤ G :=
  { mp := and.right, mpr := fun (h : 0 ≤ G) => { left := iff.mpr le_zero_iff h, right := h } }

theorem first_wins_symm' (G : pgame) [impartial G] : first_wins G ↔ 0 < G :=
  { mp := and.left, mpr := fun (h : 0 < G) => { left := h, right := iff.mpr lt_zero_iff h } }

theorem no_good_left_moves_iff_first_loses (G : pgame) [impartial G] :
    (∀ (i : left_moves G), first_wins (move_left G i)) ↔ first_loses G :=
  sorry

theorem no_good_right_moves_iff_first_loses (G : pgame) [impartial G] :
    (∀ (j : right_moves G), first_wins (move_right G j)) ↔ first_loses G :=
  sorry

theorem good_left_move_iff_first_wins (G : pgame) [impartial G] :
    (∃ (i : left_moves G), first_loses (move_left G i)) ↔ first_wins G :=
  sorry

theorem good_right_move_iff_first_wins (G : pgame) [impartial G] :
    (∃ (j : right_moves G), first_loses (move_right G j)) ↔ first_wins G :=
  sorry

end Mathlib