/-
Copyright (c) 2022 Jake Levinson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jake Levinson

! This file was ported from Lean 3 source module combinatorics.young.young_diagram
! leanprover-community/mathlib commit 4c19a16e4b705bf135cf9a80ac18fcc99c438514
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.UpperLower.Basic
import Mathbin.Data.Finset.Preimage

/-!
# Young diagrams

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

A Young diagram is a finite set of up-left justified boxes:

```text
□□□□□
□□□
□□□
□
```
This Young diagram corresponds to the [5, 3, 3, 1] partition of 12.

We represent it as a lower set in `ℕ × ℕ` in the product partial order. We write `(i, j) ∈ μ`
to say that `(i, j)` (in matrix coordinates) is in the Young diagram `μ`.

## Main definitions

- `young_diagram` : Young diagrams
- `young_diagram.card` : the number of cells in a Young diagram (its *cardinality*)
- `young_diagram.distrib_lattice` : a distributive lattice instance for Young diagrams
  ordered by containment, with `(⊥ : young_diagram)` the empty diagram.
- `young_diagram.row` and `young_diagram.row_len`: rows of a Young diagram and their lengths
- `young_diagram.col` and `young_diagram.col_len`: columns of a Young diagram and their lengths

## Notation

In "English notation", a Young diagram is drawn so that (i1, j1) ≤ (i2, j2)
means (i1, j1) is weakly up-and-left of (i2, j2). This terminology is used
below, e.g. in `young_diagram.up_left_mem`.

## Tags

Young diagram

## References

<https://en.wikipedia.org/wiki/Young_tableau>

-/

open Function

#print YoungDiagram /-
/-- A Young diagram is a finite collection of cells on the `ℕ × ℕ` grid such that whenever
a cell is present, so are all the ones above and to the left of it. Like matrices, an `(i, j)` cell
is a cell in row `i` and column `j`, where rows are enumerated downward and columns rightward.

Young diagrams are modeled as finite sets in `ℕ × ℕ` that are lower sets with respect to the
standard order on products. -/
@[ext]
structure YoungDiagram where
  cells : Finset (ℕ × ℕ)
  IsLowerSet : IsLowerSet (cells : Set (ℕ × ℕ))
#align young_diagram YoungDiagram
-/

namespace YoungDiagram

instance : SetLike YoungDiagram (ℕ × ℕ)
    where
  coe := coe YoungDiagram.cells
  coe_injective' μ ν h := by rwa [YoungDiagram.ext_iff, ← Finset.coe_inj]

#print YoungDiagram.mem_cells /-
@[simp]
theorem mem_cells {μ : YoungDiagram} (c : ℕ × ℕ) : c ∈ μ.cells ↔ c ∈ μ :=
  Iff.rfl
#align young_diagram.mem_cells YoungDiagram.mem_cells
-/

#print YoungDiagram.mem_mk /-
@[simp]
theorem mem_mk (c : ℕ × ℕ) (cells) (is_lower_set) :
    c ∈ YoungDiagram.mk cells IsLowerSet ↔ c ∈ cells :=
  Iff.rfl
#align young_diagram.mem_mk YoungDiagram.mem_mk
-/

#print YoungDiagram.decidableMem /-
instance decidableMem (μ : YoungDiagram) : DecidablePred (· ∈ μ) :=
  show DecidablePred (· ∈ μ.cells) by infer_instance
#align young_diagram.decidable_mem YoungDiagram.decidableMem
-/

#print YoungDiagram.up_left_mem /-
/-- In "English notation", a Young diagram is drawn so that (i1, j1) ≤ (i2, j2)
    means (i1, j1) is weakly up-and-left of (i2, j2). -/
theorem up_left_mem (μ : YoungDiagram) {i1 i2 j1 j2 : ℕ} (hi : i1 ≤ i2) (hj : j1 ≤ j2)
    (hcell : (i2, j2) ∈ μ) : (i1, j1) ∈ μ :=
  μ.IsLowerSet (Prod.mk_le_mk.mpr ⟨hi, hj⟩) hcell
#align young_diagram.up_left_mem YoungDiagram.up_left_mem
-/

section DistribLattice

/- warning: young_diagram.cells_subset_iff -> YoungDiagram.cells_subset_iff is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (HasSubset.Subset.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.hasSubset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells μ) (YoungDiagram.cells ν)) (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ ν)
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (HasSubset.Subset.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.instHasSubsetFinset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells μ) (YoungDiagram.cells ν)) (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.instPartialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat))) μ ν)
Case conversion may be inaccurate. Consider using '#align young_diagram.cells_subset_iff YoungDiagram.cells_subset_iffₓ'. -/
@[simp]
theorem cells_subset_iff {μ ν : YoungDiagram} : μ.cells ⊆ ν.cells ↔ μ ≤ ν :=
  Iff.rfl
#align young_diagram.cells_subset_iff YoungDiagram.cells_subset_iff

/- warning: young_diagram.cells_ssubset_iff -> YoungDiagram.cells_ssubset_iff is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (HasSSubset.SSubset.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.hasSsubset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells μ) (YoungDiagram.cells ν)) (LT.lt.{0} YoungDiagram (Preorder.toLT.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ ν)
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (HasSSubset.SSubset.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.instHasSSubsetFinset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells μ) (YoungDiagram.cells ν)) (LT.lt.{0} YoungDiagram (Preorder.toLT.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.instPartialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat))) μ ν)
Case conversion may be inaccurate. Consider using '#align young_diagram.cells_ssubset_iff YoungDiagram.cells_ssubset_iffₓ'. -/
@[simp]
theorem cells_ssubset_iff {μ ν : YoungDiagram} : μ.cells ⊂ ν.cells ↔ μ < ν :=
  Iff.rfl
#align young_diagram.cells_ssubset_iff YoungDiagram.cells_ssubset_iff

instance : Sup YoungDiagram
    where sup μ ν :=
    { cells := μ.cells ∪ ν.cells
      IsLowerSet := by
        rw [Finset.coe_union]
        exact μ.is_lower_set.union ν.is_lower_set }

/- warning: young_diagram.cells_sup -> YoungDiagram.cells_sup is a dubious translation:
lean 3 declaration is
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Sup.sup.{0} YoungDiagram YoungDiagram.hasSup μ ν)) (Union.union.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.hasUnion.{0} (Prod.{0, 0} Nat Nat) (fun (a : Prod.{0, 0} Nat Nat) (b : Prod.{0, 0} Nat Nat) => Prod.decidableEq.{0, 0} Nat Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) a b)) (YoungDiagram.cells μ) (YoungDiagram.cells ν))
but is expected to have type
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Sup.sup.{0} YoungDiagram YoungDiagram.instSupYoungDiagram μ ν)) (Union.union.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.instUnionFinset.{0} (Prod.{0, 0} Nat Nat) (fun (a : Prod.{0, 0} Nat Nat) (b : Prod.{0, 0} Nat Nat) => instDecidableEqProd.{0, 0} Nat Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) a b)) (YoungDiagram.cells μ) (YoungDiagram.cells ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.cells_sup YoungDiagram.cells_supₓ'. -/
@[simp]
theorem cells_sup (μ ν : YoungDiagram) : (μ ⊔ ν).cells = μ.cells ∪ ν.cells :=
  rfl
#align young_diagram.cells_sup YoungDiagram.cells_sup

/- warning: young_diagram.coe_sup -> YoungDiagram.coe_sup is a dubious translation:
lean 3 declaration is
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Set.{0} (Prod.{0, 0} Nat Nat)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (HasLiftT.mk.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (CoeTCₓ.coe.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (SetLike.Set.hasCoeT.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (Sup.sup.{0} YoungDiagram YoungDiagram.hasSup μ ν)) (Union.union.{0} (Set.{0} (Prod.{0, 0} Nat Nat)) (Set.hasUnion.{0} (Prod.{0, 0} Nat Nat)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (HasLiftT.mk.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (CoeTCₓ.coe.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (SetLike.Set.hasCoeT.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (HasLiftT.mk.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (CoeTCₓ.coe.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (SetLike.Set.hasCoeT.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) ν))
but is expected to have type
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Set.{0} (Prod.{0, 0} Nat Nat)) (SetLike.coe.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat (Sup.sup.{0} YoungDiagram YoungDiagram.instSupYoungDiagram μ ν)) (Union.union.{0} (Set.{0} (Prod.{0, 0} Nat Nat)) (Set.instUnionSet.{0} (Prod.{0, 0} Nat Nat)) (SetLike.coe.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat μ) (SetLike.coe.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.coe_sup YoungDiagram.coe_supₓ'. -/
@[simp, norm_cast]
theorem coe_sup (μ ν : YoungDiagram) : ↑(μ ⊔ ν) = (μ ∪ ν : Set (ℕ × ℕ)) :=
  Finset.coe_union _ _
#align young_diagram.coe_sup YoungDiagram.coe_sup

/- warning: young_diagram.mem_sup -> YoungDiagram.mem_sup is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram} {x : Prod.{0, 0} Nat Nat}, Iff (Membership.Mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.hasMem.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike) x (Sup.sup.{0} YoungDiagram YoungDiagram.hasSup μ ν)) (Or (Membership.Mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.hasMem.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike) x μ) (Membership.Mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.hasMem.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike) x ν))
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram} {x : Prod.{0, 0} Nat Nat}, Iff (Membership.mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.instMembership.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat) x (Sup.sup.{0} YoungDiagram YoungDiagram.instSupYoungDiagram μ ν)) (Or (Membership.mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.instMembership.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat) x μ) (Membership.mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.instMembership.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat) x ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.mem_sup YoungDiagram.mem_supₓ'. -/
@[simp]
theorem mem_sup {μ ν : YoungDiagram} {x : ℕ × ℕ} : x ∈ μ ⊔ ν ↔ x ∈ μ ∨ x ∈ ν :=
  Finset.mem_union
#align young_diagram.mem_sup YoungDiagram.mem_sup

instance : Inf YoungDiagram
    where inf μ ν :=
    { cells := μ.cells ∩ ν.cells
      IsLowerSet := by
        rw [Finset.coe_inter]
        exact μ.is_lower_set.inter ν.is_lower_set }

/- warning: young_diagram.cells_inf -> YoungDiagram.cells_inf is a dubious translation:
lean 3 declaration is
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Inf.inf.{0} YoungDiagram YoungDiagram.hasInf μ ν)) (Inter.inter.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.hasInter.{0} (Prod.{0, 0} Nat Nat) (fun (a : Prod.{0, 0} Nat Nat) (b : Prod.{0, 0} Nat Nat) => Prod.decidableEq.{0, 0} Nat Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) a b)) (YoungDiagram.cells μ) (YoungDiagram.cells ν))
but is expected to have type
  forall (μ : YoungDiagram) (ν : YoungDiagram), Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Inf.inf.{0} YoungDiagram YoungDiagram.instInfYoungDiagram μ ν)) (Inter.inter.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.instInterFinset.{0} (Prod.{0, 0} Nat Nat) (fun (a : Prod.{0, 0} Nat Nat) (b : Prod.{0, 0} Nat Nat) => instDecidableEqProd.{0, 0} Nat Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) a b)) (YoungDiagram.cells μ) (YoungDiagram.cells ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.cells_inf YoungDiagram.cells_infₓ'. -/
@[simp]
theorem cells_inf (μ ν : YoungDiagram) : (μ ⊓ ν).cells = μ.cells ∩ ν.cells :=
  rfl
#align young_diagram.cells_inf YoungDiagram.cells_inf

#print YoungDiagram.coe_inf /-
@[simp, norm_cast]
theorem coe_inf (μ ν : YoungDiagram) : ↑(μ ⊓ ν) = (μ ∩ ν : Set (ℕ × ℕ)) :=
  Finset.coe_inter _ _
#align young_diagram.coe_inf YoungDiagram.coe_inf
-/

#print YoungDiagram.mem_inf /-
@[simp]
theorem mem_inf {μ ν : YoungDiagram} {x : ℕ × ℕ} : x ∈ μ ⊓ ν ↔ x ∈ μ ∧ x ∈ ν :=
  Finset.mem_inter
#align young_diagram.mem_inf YoungDiagram.mem_inf
-/

/-- The empty Young diagram is (⊥ : young_diagram). -/
instance : OrderBot YoungDiagram
    where
  bot :=
    { cells := ∅
      IsLowerSet := fun _ _ _ => False.elim }
  bot_le _ _ := False.elim

/- warning: young_diagram.cells_bot -> YoungDiagram.cells_bot is a dubious translation:
lean 3 declaration is
  Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Bot.bot.{0} YoungDiagram (OrderBot.toHasBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) YoungDiagram.orderBot))) (EmptyCollection.emptyCollection.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.hasEmptyc.{0} (Prod.{0, 0} Nat Nat)))
but is expected to have type
  Eq.{1} (Finset.{0} (Prod.{0, 0} Nat Nat)) (YoungDiagram.cells (Bot.bot.{0} YoungDiagram (OrderBot.toBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.instPartialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat))) YoungDiagram.instOrderBotYoungDiagramToLEToPreorderInstPartialOrderProdNatInstSetLikeYoungDiagramProdNat))) (EmptyCollection.emptyCollection.{0} (Finset.{0} (Prod.{0, 0} Nat Nat)) (Finset.instEmptyCollectionFinset.{0} (Prod.{0, 0} Nat Nat)))
Case conversion may be inaccurate. Consider using '#align young_diagram.cells_bot YoungDiagram.cells_botₓ'. -/
@[simp]
theorem cells_bot : (⊥ : YoungDiagram).cells = ∅ :=
  rfl
#align young_diagram.cells_bot YoungDiagram.cells_bot

/- warning: young_diagram.coe_bot -> YoungDiagram.coe_bot is a dubious translation:
lean 3 declaration is
  Eq.{1} (Set.{0} (Prod.{0, 0} Nat Nat)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (HasLiftT.mk.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (CoeTCₓ.coe.{1, 1} YoungDiagram (Set.{0} (Prod.{0, 0} Nat Nat)) (SetLike.Set.hasCoeT.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (Bot.bot.{0} YoungDiagram (OrderBot.toHasBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) YoungDiagram.orderBot))) (EmptyCollection.emptyCollection.{0} (Set.{0} (Prod.{0, 0} Nat Nat)) (Set.hasEmptyc.{0} (Prod.{0, 0} Nat Nat)))
but is expected to have type
  Eq.{1} (Set.{0} (Prod.{0, 0} Nat Nat)) (Finset.toSet.{0} (Prod.{0, 0} Nat Nat) (YoungDiagram.cells (Bot.bot.{0} YoungDiagram (OrderBot.toBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.instPartialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat))) YoungDiagram.instOrderBotYoungDiagramToLEToPreorderInstPartialOrderProdNatInstSetLikeYoungDiagramProdNat)))) (EmptyCollection.emptyCollection.{0} (Set.{0} (Prod.{0, 0} Nat Nat)) (Set.instEmptyCollectionSet.{0} (Prod.{0, 0} Nat Nat)))
Case conversion may be inaccurate. Consider using '#align young_diagram.coe_bot YoungDiagram.coe_botₓ'. -/
@[simp, norm_cast]
theorem coe_bot : ↑(⊥ : YoungDiagram) = (∅ : Set (ℕ × ℕ)) :=
  rfl
#align young_diagram.coe_bot YoungDiagram.coe_bot

/- warning: young_diagram.not_mem_bot -> YoungDiagram.not_mem_bot is a dubious translation:
lean 3 declaration is
  forall (x : Prod.{0, 0} Nat Nat), Not (Membership.Mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.hasMem.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike) x (Bot.bot.{0} YoungDiagram (OrderBot.toHasBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) YoungDiagram.orderBot)))
but is expected to have type
  forall (x : Prod.{0, 0} Nat Nat), Not (Membership.mem.{0, 0} (Prod.{0, 0} Nat Nat) YoungDiagram (SetLike.instMembership.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat) x (Bot.bot.{0} YoungDiagram (OrderBot.toBot.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.instPartialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.instSetLikeYoungDiagramProdNat))) YoungDiagram.instOrderBotYoungDiagramToLEToPreorderInstPartialOrderProdNatInstSetLikeYoungDiagramProdNat)))
Case conversion may be inaccurate. Consider using '#align young_diagram.not_mem_bot YoungDiagram.not_mem_botₓ'. -/
@[simp]
theorem not_mem_bot (x : ℕ × ℕ) : x ∉ (⊥ : YoungDiagram) :=
  Finset.not_mem_empty x
#align young_diagram.not_mem_bot YoungDiagram.not_mem_bot

instance : Inhabited YoungDiagram :=
  ⟨⊥⟩

instance : DistribLattice YoungDiagram :=
  Function.Injective.distribLattice YoungDiagram.cells (fun μ ν h => by rwa [YoungDiagram.ext_iff])
    (fun _ _ => rfl) fun _ _ => rfl

end DistribLattice

#print YoungDiagram.card /-
/-- Cardinality of a Young diagram -/
@[reducible]
protected def card (μ : YoungDiagram) : ℕ :=
  μ.cells.card
#align young_diagram.card YoungDiagram.card
-/

section Transpose

#print YoungDiagram.transpose /-
/-- The `transpose` of a Young diagram is obtained by swapping i's with j's. -/
def transpose (μ : YoungDiagram) : YoungDiagram
    where
  cells := (Equiv.prodComm _ _).finsetCongr μ.cells
  IsLowerSet _ _ h :=
    by
    simp only [Finset.mem_coe, Equiv.finsetCongr_apply, Finset.mem_map_equiv]
    intro hcell
    apply μ.is_lower_set _ hcell
    simp [h]
#align young_diagram.transpose YoungDiagram.transpose
-/

#print YoungDiagram.mem_transpose /-
@[simp]
theorem mem_transpose {μ : YoungDiagram} {c : ℕ × ℕ} : c ∈ μ.transpose ↔ c.symm ∈ μ := by
  simp [transpose]
#align young_diagram.mem_transpose YoungDiagram.mem_transpose
-/

#print YoungDiagram.transpose_transpose /-
@[simp]
theorem transpose_transpose (μ : YoungDiagram) : μ.transpose.transpose = μ :=
  by
  ext
  simp
#align young_diagram.transpose_transpose YoungDiagram.transpose_transpose
-/

#print YoungDiagram.transpose_eq_iff_eq_transpose /-
theorem transpose_eq_iff_eq_transpose {μ ν : YoungDiagram} : μ.transpose = ν ↔ μ = ν.transpose := by
  constructor <;>
    · rintro rfl
      simp
#align young_diagram.transpose_eq_iff_eq_transpose YoungDiagram.transpose_eq_iff_eq_transpose
-/

#print YoungDiagram.transpose_eq_iff /-
@[simp]
theorem transpose_eq_iff {μ ν : YoungDiagram} : μ.transpose = ν.transpose ↔ μ = ν :=
  by
  rw [transpose_eq_iff_eq_transpose]
  simp
#align young_diagram.transpose_eq_iff YoungDiagram.transpose_eq_iff
-/

/- warning: young_diagram.le_of_transpose_le -> YoungDiagram.le_of_transpose_le is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram}, (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (YoungDiagram.transpose μ) ν) -> (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ (YoungDiagram.transpose ν))
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram}, (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) (YoungDiagram.transpose μ) ν) -> (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) μ (YoungDiagram.transpose ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.le_of_transpose_le YoungDiagram.le_of_transpose_leₓ'. -/
-- This is effectively both directions of `transpose_le_iff` below.
protected theorem le_of_transpose_le {μ ν : YoungDiagram} (h_le : μ.transpose ≤ ν) :
    μ ≤ ν.transpose := fun c hc => by
  simp only [mem_transpose]
  apply h_le
  simpa
#align young_diagram.le_of_transpose_le YoungDiagram.le_of_transpose_le

/- warning: young_diagram.transpose_le_iff -> YoungDiagram.transpose_le_iff is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (YoungDiagram.transpose μ) (YoungDiagram.transpose ν)) (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ ν)
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram}, Iff (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) (YoungDiagram.transpose μ) (YoungDiagram.transpose ν)) (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) μ ν)
Case conversion may be inaccurate. Consider using '#align young_diagram.transpose_le_iff YoungDiagram.transpose_le_iffₓ'. -/
@[simp]
theorem transpose_le_iff {μ ν : YoungDiagram} : μ.transpose ≤ ν.transpose ↔ μ ≤ ν :=
  ⟨fun h => by
    convert YoungDiagram.le_of_transpose_le h
    simp, fun h => by
    convert@YoungDiagram.le_of_transpose_le _ _ _
    simpa⟩
#align young_diagram.transpose_le_iff YoungDiagram.transpose_le_iff

/- warning: young_diagram.transpose_mono -> YoungDiagram.transpose_mono is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {ν : YoungDiagram}, (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) μ ν) -> (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (YoungDiagram.transpose μ) (YoungDiagram.transpose ν))
but is expected to have type
  forall {μ : YoungDiagram} {ν : YoungDiagram}, (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) μ ν) -> (LE.le.{0} YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) (YoungDiagram.transpose μ) (YoungDiagram.transpose ν))
Case conversion may be inaccurate. Consider using '#align young_diagram.transpose_mono YoungDiagram.transpose_monoₓ'. -/
@[mono]
protected theorem transpose_mono {μ ν : YoungDiagram} (h_le : μ ≤ ν) : μ.transpose ≤ ν.transpose :=
  transpose_le_iff.mpr h_le
#align young_diagram.transpose_mono YoungDiagram.transpose_mono

/- warning: young_diagram.transpose_order_iso -> YoungDiagram.transposeOrderIso is a dubious translation:
lean 3 declaration is
  OrderIso.{0, 0} YoungDiagram YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike))) (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SetLike.partialOrder.{0, 0} YoungDiagram (Prod.{0, 0} Nat Nat) YoungDiagram.Prod.setLike)))
but is expected to have type
  OrderIso.{0, 0} YoungDiagram YoungDiagram (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram))))) (Preorder.toLE.{0} YoungDiagram (PartialOrder.toPreorder.{0} YoungDiagram (SemilatticeInf.toPartialOrder.{0} YoungDiagram (Lattice.toSemilatticeInf.{0} YoungDiagram (DistribLattice.toLattice.{0} YoungDiagram YoungDiagram.instDistribLatticeYoungDiagram)))))
Case conversion may be inaccurate. Consider using '#align young_diagram.transpose_order_iso YoungDiagram.transposeOrderIsoₓ'. -/
/-- Transposing Young diagrams is an `order_iso`. -/
@[simps]
def transposeOrderIso : YoungDiagram ≃o YoungDiagram :=
  ⟨⟨transpose, transpose, fun _ => by simp, fun _ => by simp⟩, by simp⟩
#align young_diagram.transpose_order_iso YoungDiagram.transposeOrderIso

end Transpose

section Rows

/-! ### Rows and row lengths of Young diagrams.

This section defines `μ.row` and `μ.row_len`, with the following API:
      1.  `(i, j) ∈ μ ↔ j < μ.row_len i`
      2.  `μ.row i = {i} ×ˢ (finset.range (μ.row_len i))`
      3.  `μ.row_len i = (μ.row i).card`
      4.  `∀ {i1 i2}, i1 ≤ i2 → μ.row_len i2 ≤ μ.row_len i1`

Note: #3 is not convenient for defining `μ.row_len`; instead, `μ.row_len` is defined
as the smallest `j` such that `(i, j) ∉ μ`. -/

#print YoungDiagram.row /-
/-- The `i`-th row of a Young diagram consists of the cells whose first coordinate is `i`. -/
def row (μ : YoungDiagram) (i : ℕ) : Finset (ℕ × ℕ) :=
  μ.cells.filterₓ fun c => c.fst = i
#align young_diagram.row YoungDiagram.row
-/

#print YoungDiagram.mem_row_iff /-
theorem mem_row_iff {μ : YoungDiagram} {i : ℕ} {c : ℕ × ℕ} : c ∈ μ.row i ↔ c ∈ μ ∧ c.fst = i := by
  simp [row]
#align young_diagram.mem_row_iff YoungDiagram.mem_row_iff
-/

#print YoungDiagram.mk_mem_row_iff /-
theorem mk_mem_row_iff {μ : YoungDiagram} {i j : ℕ} : (i, j) ∈ μ.row i ↔ (i, j) ∈ μ := by simp [row]
#align young_diagram.mk_mem_row_iff YoungDiagram.mk_mem_row_iff
-/

#print YoungDiagram.exists_not_mem_row /-
protected theorem exists_not_mem_row (μ : YoungDiagram) (i : ℕ) : ∃ j, (i, j) ∉ μ :=
  by
  obtain ⟨j, hj⟩ :=
    Infinite.exists_not_mem_finset
      (μ.cells.Preimage (Prod.mk i) fun _ _ _ _ h =>
        by
        cases h
        rfl)
  rw [Finset.mem_preimage] at hj
  exact ⟨j, hj⟩
#align young_diagram.exists_not_mem_row YoungDiagram.exists_not_mem_row
-/

#print YoungDiagram.rowLen /-
/-- Length of a row of a Young diagram -/
def rowLen (μ : YoungDiagram) (i : ℕ) : ℕ :=
  Nat.find <| μ.exists_not_mem_row i
#align young_diagram.row_len YoungDiagram.rowLen
-/

#print YoungDiagram.mem_iff_lt_rowLen /-
theorem mem_iff_lt_rowLen {μ : YoungDiagram} {i j : ℕ} : (i, j) ∈ μ ↔ j < μ.rowLen i :=
  by
  rw [row_len, Nat.lt_find_iff]
  push_neg
  exact ⟨fun h _ hmj => μ.up_left_mem (by rfl) hmj h, fun h => h _ (by rfl)⟩
#align young_diagram.mem_iff_lt_row_len YoungDiagram.mem_iff_lt_rowLen
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print YoungDiagram.row_eq_prod /-
theorem row_eq_prod {μ : YoungDiagram} {i : ℕ} : μ.row i = {i} ×ˢ Finset.range (μ.rowLen i) :=
  by
  ext ⟨a, b⟩
  simp only [Finset.mem_product, Finset.mem_singleton, Finset.mem_range, mem_row_iff,
    mem_iff_lt_row_len, and_comm', and_congr_right_iff]
  rintro rfl
  rfl
#align young_diagram.row_eq_prod YoungDiagram.row_eq_prod
-/

#print YoungDiagram.rowLen_eq_card /-
theorem rowLen_eq_card (μ : YoungDiagram) {i : ℕ} : μ.rowLen i = (μ.row i).card := by
  simp [row_eq_prod]
#align young_diagram.row_len_eq_card YoungDiagram.rowLen_eq_card
-/

#print YoungDiagram.rowLen_anti /-
@[mono]
theorem rowLen_anti (μ : YoungDiagram) (i1 i2 : ℕ) (hi : i1 ≤ i2) : μ.rowLen i2 ≤ μ.rowLen i1 :=
  by
  by_contra' h_lt
  rw [← lt_self_iff_false (μ.row_len i1)]
  rw [← mem_iff_lt_row_len] at h_lt⊢
  exact μ.up_left_mem hi (by rfl) h_lt
#align young_diagram.row_len_anti YoungDiagram.rowLen_anti
-/

end Rows

section Columns

/-! ### Columns and column lengths of Young diagrams.

This section has an identical API to the rows section. -/

#print YoungDiagram.col /-
/-- The `j`-th column of a Young diagram consists of the cells whose second coordinate is `j`. -/
def col (μ : YoungDiagram) (j : ℕ) : Finset (ℕ × ℕ) :=
  μ.cells.filterₓ fun c => c.snd = j
#align young_diagram.col YoungDiagram.col
-/

#print YoungDiagram.mem_col_iff /-
theorem mem_col_iff {μ : YoungDiagram} {j : ℕ} {c : ℕ × ℕ} : c ∈ μ.col j ↔ c ∈ μ ∧ c.snd = j := by
  simp [col]
#align young_diagram.mem_col_iff YoungDiagram.mem_col_iff
-/

#print YoungDiagram.mk_mem_col_iff /-
theorem mk_mem_col_iff {μ : YoungDiagram} {i j : ℕ} : (i, j) ∈ μ.col j ↔ (i, j) ∈ μ := by simp [col]
#align young_diagram.mk_mem_col_iff YoungDiagram.mk_mem_col_iff
-/

#print YoungDiagram.exists_not_mem_col /-
protected theorem exists_not_mem_col (μ : YoungDiagram) (j : ℕ) : ∃ i, (i, j) ∉ μ.cells :=
  by
  convert μ.transpose.exists_not_mem_row j
  simp
#align young_diagram.exists_not_mem_col YoungDiagram.exists_not_mem_col
-/

#print YoungDiagram.colLen /-
/-- Length of a column of a Young diagram -/
def colLen (μ : YoungDiagram) (j : ℕ) : ℕ :=
  Nat.find <| μ.exists_not_mem_col j
#align young_diagram.col_len YoungDiagram.colLen
-/

#print YoungDiagram.colLen_transpose /-
@[simp]
theorem colLen_transpose (μ : YoungDiagram) (j : ℕ) : μ.transpose.colLen j = μ.rowLen j := by
  simp [row_len, col_len]
#align young_diagram.col_len_transpose YoungDiagram.colLen_transpose
-/

#print YoungDiagram.rowLen_transpose /-
@[simp]
theorem rowLen_transpose (μ : YoungDiagram) (i : ℕ) : μ.transpose.rowLen i = μ.colLen i := by
  simp [row_len, col_len]
#align young_diagram.row_len_transpose YoungDiagram.rowLen_transpose
-/

#print YoungDiagram.mem_iff_lt_colLen /-
theorem mem_iff_lt_colLen {μ : YoungDiagram} {i j : ℕ} : (i, j) ∈ μ ↔ i < μ.colLen j :=
  by
  rw [← row_len_transpose, ← mem_iff_lt_row_len]
  simp
#align young_diagram.mem_iff_lt_col_len YoungDiagram.mem_iff_lt_colLen
-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print YoungDiagram.col_eq_prod /-
theorem col_eq_prod {μ : YoungDiagram} {j : ℕ} : μ.col j = Finset.range (μ.colLen j) ×ˢ {j} :=
  by
  ext ⟨a, b⟩
  simp only [Finset.mem_product, Finset.mem_singleton, Finset.mem_range, mem_col_iff,
    mem_iff_lt_col_len, and_comm', and_congr_right_iff]
  rintro rfl
  rfl
#align young_diagram.col_eq_prod YoungDiagram.col_eq_prod
-/

#print YoungDiagram.colLen_eq_card /-
theorem colLen_eq_card (μ : YoungDiagram) {j : ℕ} : μ.colLen j = (μ.col j).card := by
  simp [col_eq_prod]
#align young_diagram.col_len_eq_card YoungDiagram.colLen_eq_card
-/

#print YoungDiagram.colLen_anti /-
@[mono]
theorem colLen_anti (μ : YoungDiagram) (j1 j2 : ℕ) (hj : j1 ≤ j2) : μ.colLen j2 ≤ μ.colLen j1 := by
  convert μ.transpose.row_len_anti j1 j2 hj <;> simp
#align young_diagram.col_len_anti YoungDiagram.colLen_anti
-/

end Columns

section RowLens

/-! ### The list of row lengths of a Young diagram

This section defines `μ.row_lens : list ℕ`, the list of row lengths of a Young diagram `μ`.
  1. `young_diagram.row_lens_sorted` : It is weakly decreasing (`list.sorted (≥)`).
  2. `young_diagram.row_lens_pos` : It is strictly positive.

-/

#print YoungDiagram.rowLens /-
/-- List of row lengths of a Young diagram -/
def rowLens (μ : YoungDiagram) : List ℕ :=
  (List.range <| μ.colLen 0).map μ.rowLen
#align young_diagram.row_lens YoungDiagram.rowLens
-/

/- warning: young_diagram.nth_le_row_lens -> YoungDiagram.get_rowLens is a dubious translation:
lean 3 declaration is
  forall {μ : YoungDiagram} {i : Nat} {hi : LT.lt.{0} Nat Nat.hasLt i (List.length.{0} Nat (YoungDiagram.rowLens μ))}, Eq.{1} Nat (List.nthLe.{0} Nat (YoungDiagram.rowLens μ) i hi) (YoungDiagram.rowLen μ i)
but is expected to have type
  forall {μ : YoungDiagram} {i : Fin (List.length.{0} Nat (YoungDiagram.rowLens μ))}, Eq.{1} Nat (List.get.{0} Nat (YoungDiagram.rowLens μ) i) (YoungDiagram.rowLen μ (Fin.val (List.length.{0} Nat (YoungDiagram.rowLens μ)) i))
Case conversion may be inaccurate. Consider using '#align young_diagram.nth_le_row_lens YoungDiagram.get_rowLensₓ'. -/
@[simp]
theorem get_rowLens {μ : YoungDiagram} {i : ℕ} {hi : i < μ.rowLens.length} :
    μ.rowLens.nthLe i hi = μ.rowLen i := by simp only [row_lens, List.nthLe_range, List.nthLe_map']
#align young_diagram.nth_le_row_lens YoungDiagram.get_rowLens

#print YoungDiagram.length_rowLens /-
@[simp]
theorem length_rowLens {μ : YoungDiagram} : μ.rowLens.length = μ.colLen 0 := by
  simp only [row_lens, List.length_map, List.length_range]
#align young_diagram.length_row_lens YoungDiagram.length_rowLens
-/

#print YoungDiagram.rowLens_sorted /-
theorem rowLens_sorted (μ : YoungDiagram) : μ.rowLens.Sorted (· ≥ ·) :=
  (List.pairwise_le_range _).map _ μ.rowLen_anti
#align young_diagram.row_lens_sorted YoungDiagram.rowLens_sorted
-/

#print YoungDiagram.pos_of_mem_rowLens /-
theorem pos_of_mem_rowLens (μ : YoungDiagram) (x : ℕ) (hx : x ∈ μ.rowLens) : 0 < x :=
  by
  rw [row_lens, List.mem_map] at hx
  obtain ⟨i, hi, rfl : μ.row_len i = x⟩ := hx
  rwa [List.mem_range, ← mem_iff_lt_col_len, mem_iff_lt_row_len] at hi
#align young_diagram.pos_of_mem_row_lens YoungDiagram.pos_of_mem_rowLens
-/

end RowLens

section EquivListRowLens

/-! ### Equivalence between Young diagrams and lists of natural numbers

This section defines the equivalence between Young diagrams `μ` and weakly decreasing lists `w`
of positive natural numbers, corresponding to row lengths of the diagram:
  `young_diagram.equiv_list_row_lens :`
  `young_diagram ≃ {w : list ℕ // w.sorted (≥) ∧ ∀ x ∈ w, 0 < x}`

The two directions are `young_diagram.row_lens` (defined above) and `young_diagram.of_row_lens`.

-/

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print YoungDiagram.cellsOfRowLens /-
/-- The cells making up a `young_diagram` from a list of row lengths -/
protected def cellsOfRowLens : List ℕ → Finset (ℕ × ℕ)
  | [] => ∅
  | w::ws =>
    ({0} : Finset ℕ) ×ˢ Finset.range w ∪
      (cells_of_row_lens ws).map (Embedding.prodMap ⟨_, Nat.succ_injective⟩ (Embedding.refl ℕ))
#align young_diagram.cells_of_row_lens YoungDiagram.cellsOfRowLens
-/

#print YoungDiagram.mem_cellsOfRowLens /-
protected theorem mem_cellsOfRowLens {w : List ℕ} {c : ℕ × ℕ} :
    c ∈ YoungDiagram.cellsOfRowLens w ↔ ∃ h : c.fst < w.length, c.snd < w.nthLe c.fst h :=
  by
  induction w generalizing c <;> rw [YoungDiagram.cellsOfRowLens]
  · simp [YoungDiagram.cellsOfRowLens]
  · rcases c with ⟨⟨_, _⟩, _⟩
    · simp
    · simpa [w_ih, -Finset.singleton_product, Nat.succ_lt_succ_iff]
#align young_diagram.mem_cells_of_row_lens YoungDiagram.mem_cellsOfRowLens
-/

#print YoungDiagram.ofRowLens /-
/-- Young diagram from a sorted list -/
def ofRowLens (w : List ℕ) (hw : w.Sorted (· ≥ ·)) : YoungDiagram
    where
  cells := YoungDiagram.cellsOfRowLens w
  IsLowerSet := by
    rintro ⟨i2, j2⟩ ⟨i1, j1⟩ ⟨hi : i1 ≤ i2, hj : j1 ≤ j2⟩ hcell
    rw [Finset.mem_coe, YoungDiagram.mem_cellsOfRowLens] at hcell⊢
    obtain ⟨h1, h2⟩ := hcell
    refine' ⟨hi.trans_lt h1, _⟩
    calc
      j1 ≤ j2 := hj
      _ < w.nth_le i2 _ := h2
      _ ≤ w.nth_le i1 _ := _

    obtain rfl | h := eq_or_lt_of_le hi
    · rfl
    · apply list.pairwise_iff_nth_le.mp hw _ _ _ h
#align young_diagram.of_row_lens YoungDiagram.ofRowLens
-/

#print YoungDiagram.mem_ofRowLens /-
theorem mem_ofRowLens {w : List ℕ} {hw : w.Sorted (· ≥ ·)} {c : ℕ × ℕ} :
    c ∈ ofRowLens w hw ↔ ∃ h : c.fst < w.length, c.snd < w.nthLe c.fst h :=
  YoungDiagram.mem_cellsOfRowLens
#align young_diagram.mem_of_row_lens YoungDiagram.mem_ofRowLens
-/

#print YoungDiagram.rowLens_length_ofRowLens /-
/-- The number of rows in `of_row_lens w hw` is the length of `w` -/
theorem rowLens_length_ofRowLens {w : List ℕ} {hw : w.Sorted (· ≥ ·)} (hpos : ∀ x ∈ w, 0 < x) :
    (ofRowLens w hw).rowLens.length = w.length :=
  by
  simp only [length_row_lens, col_len, Nat.find_eq_iff, mem_cells, mem_of_row_lens,
    lt_self_iff_false, IsEmpty.exists_iff, Classical.not_not]
  exact ⟨id, fun n hn => ⟨hn, hpos _ (List.nthLe_mem _ _ hn)⟩⟩
#align young_diagram.row_lens_length_of_row_lens YoungDiagram.rowLens_length_ofRowLens
-/

/- warning: young_diagram.row_len_of_row_lens -> YoungDiagram.rowLen_ofRowLens is a dubious translation:
lean 3 declaration is
  forall {w : List.{0} Nat} {hw : List.Sorted.{0} Nat (GE.ge.{0} Nat Nat.hasLe) w} (i : Nat) (hi : LT.lt.{0} Nat Nat.hasLt i (List.length.{0} Nat w)), Eq.{1} Nat (YoungDiagram.rowLen (YoungDiagram.ofRowLens w hw) i) (List.nthLe.{0} Nat w i hi)
but is expected to have type
  forall {w : List.{0} Nat} {hw : List.Sorted.{0} Nat (fun (x._@.Mathlib.Combinatorics.Young.YoungDiagram._hyg.2936 : Nat) (x._@.Mathlib.Combinatorics.Young.YoungDiagram._hyg.2938 : Nat) => GE.ge.{0} Nat instLENat x._@.Mathlib.Combinatorics.Young.YoungDiagram._hyg.2936 x._@.Mathlib.Combinatorics.Young.YoungDiagram._hyg.2938) w} (i : Fin (List.length.{0} Nat w)), Eq.{1} Nat (YoungDiagram.rowLen (YoungDiagram.ofRowLens w hw) (Fin.val (List.length.{0} Nat w) i)) (List.get.{0} Nat w i)
Case conversion may be inaccurate. Consider using '#align young_diagram.row_len_of_row_lens YoungDiagram.rowLen_ofRowLensₓ'. -/
/-- The length of the `i`th row in `of_row_lens w hw` is the `i`th entry of `w` -/
theorem rowLen_ofRowLens {w : List ℕ} {hw : w.Sorted (· ≥ ·)} (i : ℕ) (hi : i < w.length) :
    (ofRowLens w hw).rowLen i = w.nthLe i hi := by
  simp [row_len, Nat.find_eq_iff, mem_of_row_lens, hi]
#align young_diagram.row_len_of_row_lens YoungDiagram.rowLen_ofRowLens

#print YoungDiagram.ofRowLens_to_rowLens_eq_self /-
/-- The left_inv direction of the equivalence -/
theorem ofRowLens_to_rowLens_eq_self {μ : YoungDiagram} : ofRowLens _ (rowLens_sorted μ) = μ :=
  by
  ext ⟨i, j⟩
  simp only [mem_cells, mem_of_row_lens, length_row_lens, nth_le_row_lens]
  simpa [← mem_iff_lt_col_len, mem_iff_lt_row_len] using j.zero_le.trans_lt
#align young_diagram.of_row_lens_to_row_lens_eq_self YoungDiagram.ofRowLens_to_rowLens_eq_self
-/

#print YoungDiagram.rowLens_ofRowLens_eq_self /-
/-- The right_inv direction of the equivalence -/
theorem rowLens_ofRowLens_eq_self {w : List ℕ} {hw : w.Sorted (· ≥ ·)} (hpos : ∀ x ∈ w, 0 < x) :
    (ofRowLens w hw).rowLens = w := by
  ext (i r)
  cases lt_or_ge i w.length
  · simp only [Option.mem_def, ← List.nthLe_eq_iff, h, row_lens_length_of_row_lens hpos]
    revert r
    simpa only [eq_iff_eq_cancel_right, nth_le_row_lens] using row_len_of_row_lens _ h
  · rw [list.nth_eq_none_iff.mpr h, list.nth_eq_none_iff.mpr]
    rwa [row_lens_length_of_row_lens hpos]
#align young_diagram.row_lens_of_row_lens_eq_self YoungDiagram.rowLens_ofRowLens_eq_self
-/

#print YoungDiagram.equivListRowLens /-
/-- Equivalence between Young diagrams and weakly decreasing lists of positive natural numbers.
A Young diagram `μ` is equivalent to a list of row lengths. -/
@[simps]
def equivListRowLens : YoungDiagram ≃ { w : List ℕ // w.Sorted (· ≥ ·) ∧ ∀ x ∈ w, 0 < x }
    where
  toFun μ := ⟨μ.rowLens, μ.rowLens_sorted, μ.pos_of_mem_rowLens⟩
  invFun ww := ofRowLens ww.1 ww.2.1
  left_inv μ := ofRowLens_to_rowLens_eq_self
  right_inv := fun ⟨w, hw⟩ => Subtype.mk_eq_mk.mpr (rowLens_ofRowLens_eq_self hw.2)
#align young_diagram.equiv_list_row_lens YoungDiagram.equivListRowLens
-/

end EquivListRowLens

end YoungDiagram


import .love10_denotational_semantics_demo

/- # LoVe Exercise 10: Denotational Semantics -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1: Monotonicity

1.1. Prove the following lemma from the lecture. -/

lemma monotone_comp {α β : Type} [partial_order α] (f g : α → set (β × β))
    (hf : monotone f) (hg : monotone g) :
  monotone (λa, f a ◯ g a) :=
begin
  intros a₁ a₂ ha b hb,
  cases' hb with m hm,
  cases' hm,
  apply exists.intro m,
  apply and.intro,
  { exact hf _ _ ha left },
  { exact hg _ _ ha right }
end

/- 1.2. Prove its cousin. -/

lemma monotone_restrict {α β : Type} [partial_order α] (f : α → set (β × β))
    (p : β → Prop) (hf : monotone f) :
  monotone (λa, f a ⇃ p) :=
begin
  intros a₁ a₂ ha b hb,
  cases' hb,
  apply and.intro,
  { exact left },
  { apply hf _ _ ha,
    exact right }
end

/- ## Question 2: Regular Expressions

__Regular expressions__, or __regexes__, are a highly popular tool for software
development, to analyze textual inputs. Regexes are generated by the following
grammar:

    R ::= ∅
        | ε
        | a
        | R ⬝ R
        | R + R
        | R*

Informally, the semantics of regular expressions is as follows:

* `∅` accepts nothing;
* `ε` accepts the empty string;
* `a` accepts the atom `a`;
* `R ⬝ R` accepts the concatenation of two regexes;
* `R + R` accepts either of two regexes;
* `R*` accepts arbitrary many repetitions of a regex.

Notice the rough correspondence with a WHILE language:

    `∅` ~ diverging statement (e.g., `while true do skip`)
    `ε` ~ `skip`
    `a` ~ `:=`
    `⬝` ~ `;`
    `+` ~ `if then else`
    `*` ~ `while` loop -/

inductive regex (α : Type) : Type
| nothing {} : regex
| empty {}   : regex
| atom       : α → regex
| concat     : regex → regex → regex
| alt        : regex → regex → regex
| star       : regex → regex

/- In this exercise, we explore an alternative semantics of regular
expressions. Namely, we can imagine that the atoms represent binary relations,
instead of letters or symbols. Concatenation corresponds to composition of
relations, and alternation is union. Mathematically, regexes and binary
relations are both instances of Kleene algebras.

2.1. Complete the following translation of regular expressions to relations.

Hint: Exploit the correspondence with the WHILE language. -/

def rel_of_regex {α : Type} : regex (set (α × α)) → set (α × α)
| regex.nothing        := ∅
| regex.empty          := Id
| (regex.atom s)       := s
| (regex.concat r₁ r₂) := rel_of_regex r₁ ◯ rel_of_regex r₂
| (regex.alt r₁ r₂)    := rel_of_regex r₁ ∪ rel_of_regex r₂
| (regex.star r)       := lfp (λX, (rel_of_regex r ◯ X) ∪ Id)

/- 2.2. Prove the following recursive equation about your definition. -/

lemma rel_of_regex_star {α : Type} (r : regex (set (α × α))) :
  rel_of_regex (regex.star r) =
  rel_of_regex (regex.alt (regex.concat r (regex.star r)) regex.empty) :=
begin
  apply lfp_eq,
  apply monotone_union,
  { apply monotone_comp,
    { exact monotone_const _ },
    { exact monotone_id } },
  { exact monotone_const _ }
end

end LoVe


/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers, Manuel Candales
-/
import geometry.euclidean.basic
import tactic.interval_cases

/-!
# Triangles

This file proves basic geometrical results about distances and angles
in (possibly degenerate) triangles in real inner product spaces and
Euclidean affine spaces.  More specialized results, and results
developed for simplices in general rather than just for triangles, are
in separate files.  Definitions and results that make sense in more
general affine spaces rather than just in the Euclidean case go under
`linear_algebra.affine_space`.

## Implementation notes

Results in this file are generally given in a form with only those
non-degeneracy conditions needed for the particular result, rather
than requiring affine independence of the points of a triangle
unnecessarily.

## References

* https://en.wikipedia.org/wiki/Pythagorean_theorem
* https://en.wikipedia.org/wiki/Law_of_cosines
* https://en.wikipedia.org/wiki/Pons_asinorum
* https://en.wikipedia.org/wiki/Sum_of_angles_of_a_triangle

-/

noncomputable theory
open_locale big_operators
open_locale classical
open_locale real
open_locale real_inner_product_space

namespace inner_product_geometry

/-!
### Geometrical results on triangles in real inner product spaces

This section develops some results on (possibly degenerate) triangles
in real inner product spaces, where those definitions and results can
most conveniently be developed in terms of vectors and then used to
deduce corresponding results for Euclidean affine spaces.
-/

variables {V : Type*} [inner_product_space ℝ V]

/-- Pythagorean theorem, if-and-only-if vector angle form. -/
lemma norm_add_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two (x y : V) :
  ∥x + y∥ * ∥x + y∥ = ∥x∥ * ∥x∥ + ∥y∥ * ∥y∥ ↔ angle x y = π / 2 :=
begin
  rw norm_add_sq_eq_norm_sq_add_norm_sq_iff_real_inner_eq_zero,
  exact inner_eq_zero_iff_angle_eq_pi_div_two x y
end

/-- Pythagorean theorem, vector angle form. -/
lemma norm_add_sq_eq_norm_sq_add_norm_sq' (x y : V) (h : angle x y = π / 2) :
  ∥x + y∥ * ∥x + y∥ = ∥x∥ * ∥x∥ + ∥y∥ * ∥y∥ :=
(norm_add_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two x y).2 h

/-- Pythagorean theorem, subtracting vectors, if-and-only-if vector angle form. -/
lemma norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two (x y : V) :
  ∥x - y∥ * ∥x - y∥ = ∥x∥ * ∥x∥ + ∥y∥ * ∥y∥ ↔ angle x y = π / 2 :=
begin
  rw norm_sub_sq_eq_norm_sq_add_norm_sq_iff_real_inner_eq_zero,
  exact inner_eq_zero_iff_angle_eq_pi_div_two x y
end

/-- Pythagorean theorem, subtracting vectors, vector angle form. -/
lemma norm_sub_sq_eq_norm_sq_add_norm_sq' (x y : V) (h : angle x y = π / 2) :
  ∥x - y∥ * ∥x - y∥ = ∥x∥ * ∥x∥ + ∥y∥ * ∥y∥ :=
(norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two x y).2 h

/-- Law of cosines (cosine rule), vector angle form. -/
lemma norm_sub_sq_eq_norm_sq_add_norm_sq_sub_two_mul_norm_mul_norm_mul_cos_angle
    (x y : V) :
  ∥x - y∥ * ∥x - y∥ = ∥x∥ * ∥x∥ + ∥y∥ * ∥y∥ - 2 * ∥x∥ * ∥y∥ * real.cos (angle x y) :=
by rw [(show 2 * ∥x∥ * ∥y∥ * real.cos (angle x y) =
             2 * (real.cos (angle x y) * (∥x∥ * ∥y∥)), by ring),
       cos_angle_mul_norm_mul_norm, ←real_inner_self_eq_norm_mul_norm,
       ←real_inner_self_eq_norm_mul_norm, ←real_inner_self_eq_norm_mul_norm,
       real_inner_sub_sub_self, sub_add_eq_add_sub]

/-- Pons asinorum, vector angle form. -/
lemma angle_sub_eq_angle_sub_rev_of_norm_eq {x y : V} (h : ∥x∥ = ∥y∥) :
  angle x (x - y) = angle y (y - x) :=
begin
  refine real.inj_on_cos ⟨angle_nonneg _ _, angle_le_pi _ _⟩ ⟨angle_nonneg _ _, angle_le_pi _ _⟩ _,
  rw [cos_angle, cos_angle, h, ←neg_sub, norm_neg, neg_sub,
    inner_sub_right, inner_sub_right, real_inner_self_eq_norm_mul_norm,
    real_inner_self_eq_norm_mul_norm, h, real_inner_comm x y]
end

/-- Converse of pons asinorum, vector angle form. -/
lemma norm_eq_of_angle_sub_eq_angle_sub_rev_of_angle_ne_pi {x y : V}
    (h : angle x (x - y) = angle y (y - x)) (hpi : angle x y ≠ π) : ∥x∥ = ∥y∥ :=
begin
  replace h := real.arccos_inj_on
    (abs_le.mp (abs_real_inner_div_norm_mul_norm_le_one x (x - y)))
    (abs_le.mp (abs_real_inner_div_norm_mul_norm_le_one y (y - x))) h,
  by_cases hxy : x = y,
  { rw hxy },
  { rw [←norm_neg (y - x), neg_sub, mul_comm, mul_comm ∥y∥, div_eq_mul_inv, div_eq_mul_inv,
        mul_inv_rev₀, mul_inv_rev₀, ←mul_assoc, ←mul_assoc] at h,
    replace h :=
      mul_right_cancel₀ (inv_ne_zero (λ hz, hxy (eq_of_sub_eq_zero (norm_eq_zero.1 hz)))) h,
    rw [inner_sub_right, inner_sub_right, real_inner_comm x y, real_inner_self_eq_norm_mul_norm,
        real_inner_self_eq_norm_mul_norm, mul_sub_right_distrib, mul_sub_right_distrib,
        mul_self_mul_inv, mul_self_mul_inv, sub_eq_sub_iff_sub_eq_sub,
        ←mul_sub_left_distrib] at h,
    by_cases hx0 : x = 0,
    { rw [hx0, norm_zero, inner_zero_left, zero_mul, zero_sub, neg_eq_zero] at h,
      rw [hx0, norm_zero, h] },
    { by_cases hy0 : y = 0,
      { rw [hy0, norm_zero, inner_zero_right, zero_mul, sub_zero] at h,
        rw [hy0, norm_zero, h] },
      { rw [inv_sub_inv (λ hz, hx0 (norm_eq_zero.1 hz)) (λ hz, hy0 (norm_eq_zero.1 hz)),
            ←neg_sub, ←mul_div_assoc, mul_comm, mul_div_assoc, ←mul_neg_one] at h,
        symmetry,
        by_contradiction hyx,
        replace h := (mul_left_cancel₀ (sub_ne_zero_of_ne hyx) h).symm,
        rw [real_inner_div_norm_mul_norm_eq_neg_one_iff, ←angle_eq_pi_iff] at h,
        exact hpi h } } }
end

/-- The cosine of the sum of two angles in a possibly degenerate
triangle (where two given sides are nonzero), vector angle form. -/
lemma cos_angle_sub_add_angle_sub_rev_eq_neg_cos_angle {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  real.cos (angle x (x - y) + angle y (y - x)) = -real.cos (angle x y) :=
begin
  by_cases hxy : x = y,
  { rw [hxy, angle_self hy],
    simp },
  { rw [real.cos_add, cos_angle, cos_angle, cos_angle],
    have hxn : ∥x∥ ≠ 0 := (λ h, hx (norm_eq_zero.1 h)),
    have hyn : ∥y∥ ≠ 0 := (λ h, hy (norm_eq_zero.1 h)),
    have hxyn : ∥x - y∥ ≠ 0 := (λ h, hxy (eq_of_sub_eq_zero (norm_eq_zero.1 h))),
    apply mul_right_cancel₀ hxn,
    apply mul_right_cancel₀ hyn,
    apply mul_right_cancel₀ hxyn,
    apply mul_right_cancel₀ hxyn,
    have H1 : real.sin (angle x (x - y)) * real.sin (angle y (y - x)) *
                ∥x∥ * ∥y∥ * ∥x - y∥ * ∥x - y∥ =
              (real.sin (angle x (x - y)) * (∥x∥ * ∥x - y∥)) *
                (real.sin (angle y (y - x)) * (∥y∥ * ∥x - y∥)), { ring },
    have H2 : ⟪x, x⟫ * (inner x x - inner x y - (inner x y - inner y y)) -
                (inner x x - inner x y) * (inner x x - inner x y) =
              inner x x * inner y y - inner x y * inner x y, { ring },
    have H3 : ⟪y, y⟫ * (inner y y - inner x y - (inner x y - inner x x)) -
                (inner y y - inner x y) * (inner y y - inner x y) =
              inner x x * inner y y - inner x y * inner x y, { ring },
    rw [mul_sub_right_distrib, mul_sub_right_distrib, mul_sub_right_distrib,
        mul_sub_right_distrib, H1, sin_angle_mul_norm_mul_norm, norm_sub_rev x y,
        sin_angle_mul_norm_mul_norm, norm_sub_rev y x, inner_sub_left, inner_sub_left,
        inner_sub_right, inner_sub_right, inner_sub_right, inner_sub_right, real_inner_comm x y, H2,
        H3, real.mul_self_sqrt (sub_nonneg_of_le (real_inner_mul_inner_self_le x y)),
        real_inner_self_eq_norm_mul_norm, real_inner_self_eq_norm_mul_norm,
        real_inner_eq_norm_mul_self_add_norm_mul_self_sub_norm_sub_mul_self_div_two],
    field_simp [hxn, hyn, hxyn],
    ring }
end

/-- The sine of the sum of two angles in a possibly degenerate
triangle (where two given sides are nonzero), vector angle form. -/
lemma sin_angle_sub_add_angle_sub_rev_eq_sin_angle {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  real.sin (angle x (x - y) + angle y (y - x)) = real.sin (angle x y) :=
begin
  by_cases hxy : x = y,
  { rw [hxy, angle_self hy],
    simp },
  { rw [real.sin_add, cos_angle, cos_angle],
    have hxn : ∥x∥ ≠ 0 := (λ h, hx (norm_eq_zero.1 h)),
    have hyn : ∥y∥ ≠ 0 := (λ h, hy (norm_eq_zero.1 h)),
    have hxyn : ∥x - y∥ ≠ 0 := (λ h, hxy (eq_of_sub_eq_zero (norm_eq_zero.1 h))),
    apply mul_right_cancel₀ hxn,
    apply mul_right_cancel₀ hyn,
    apply mul_right_cancel₀ hxyn,
    apply mul_right_cancel₀ hxyn,
    have H1 : real.sin (angle x (x - y)) * (⟪y, y - x⟫ / (∥y∥ * ∥y - x∥)) * ∥x∥ * ∥y∥ * ∥x - y∥ =
                real.sin (angle x (x - y)) * (∥x∥ * ∥x - y∥) *
                  (⟪y, y - x⟫ / (∥y∥ * ∥y - x∥)) * ∥y∥, { ring },
    have H2 : ⟪x, x - y⟫ / (∥x∥ * ∥y - x∥) * real.sin (angle y (y - x)) * ∥x∥ * ∥y∥ * ∥y - x∥ =
                ⟪x, x - y⟫ / (∥x∥ * ∥y - x∥) *
                  (real.sin (angle y (y - x)) * (∥y∥ * ∥y - x∥)) * ∥x∥, { ring },
    have H3 : ⟪x, x⟫ * (⟪x, x⟫ - ⟪x, y⟫ - (⟪x, y⟫ - ⟪y, y⟫)) -
                (⟪x, x⟫ - ⟪x, y⟫) * (⟪x, x⟫ - ⟪x, y⟫) =
              ⟪x, x⟫ * ⟪y, y⟫ - ⟪x, y⟫ * ⟪x, y⟫, { ring },
    have H4 : ⟪y, y⟫ * (⟪y, y⟫ - ⟪x, y⟫ - (⟪x, y⟫ - ⟪x, x⟫)) -
                (⟪y, y⟫ - ⟪x, y⟫) * (⟪y, y⟫ - ⟪x, y⟫) =
              ⟪x, x⟫ * ⟪y, y⟫ - ⟪x, y⟫  * ⟪x, y⟫, { ring },
    rw [right_distrib, right_distrib, right_distrib, right_distrib, H1,
        sin_angle_mul_norm_mul_norm, norm_sub_rev x y, H2, sin_angle_mul_norm_mul_norm,
        norm_sub_rev y x, mul_assoc (real.sin (angle x y)), sin_angle_mul_norm_mul_norm,
        inner_sub_left, inner_sub_left, inner_sub_right, inner_sub_right, inner_sub_right,
        inner_sub_right, real_inner_comm x y, H3, H4, real_inner_self_eq_norm_mul_norm,
        real_inner_self_eq_norm_mul_norm,
        real_inner_eq_norm_mul_self_add_norm_mul_self_sub_norm_sub_mul_self_div_two],
    field_simp [hxn, hyn, hxyn],
    ring }
end

/-- The cosine of the sum of the angles of a possibly degenerate
triangle (where two given sides are nonzero), vector angle form. -/
lemma cos_angle_add_angle_sub_add_angle_sub_eq_neg_one {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  real.cos (angle x y + angle x (x - y) + angle y (y - x)) = -1 :=
by rw [add_assoc, real.cos_add, cos_angle_sub_add_angle_sub_rev_eq_neg_cos_angle hx hy,
       sin_angle_sub_add_angle_sub_rev_eq_sin_angle hx hy, ←neg_mul_eq_mul_neg, ←neg_add',
       add_comm, ←sq, ←sq, real.sin_sq_add_cos_sq]

/-- The sine of the sum of the angles of a possibly degenerate
triangle (where two given sides are nonzero), vector angle form. -/
lemma sin_angle_add_angle_sub_add_angle_sub_eq_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  real.sin (angle x y + angle x (x - y) + angle y (y - x)) = 0 :=
begin
  rw [add_assoc, real.sin_add, cos_angle_sub_add_angle_sub_rev_eq_neg_cos_angle hx hy,
      sin_angle_sub_add_angle_sub_rev_eq_sin_angle hx hy],
  ring
end

/-- The sum of the angles of a possibly degenerate triangle (where the
two given sides are nonzero), vector angle form. -/
lemma angle_add_angle_sub_add_angle_sub_eq_pi {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) :
  angle x y + angle x (x - y) + angle y (y - x) = π :=
begin
  have hcos := cos_angle_add_angle_sub_add_angle_sub_eq_neg_one hx hy,
  have hsin := sin_angle_add_angle_sub_add_angle_sub_eq_zero hx hy,
  rw real.sin_eq_zero_iff at hsin,
  cases hsin with n hn,
  symmetry' at hn,
  have h0 : 0 ≤ angle x y + angle x (x - y) + angle y (y - x) :=
    add_nonneg (add_nonneg (angle_nonneg _ _) (angle_nonneg _ _)) (angle_nonneg _ _),
  have h3 : angle x y + angle x (x - y) + angle y (y - x) ≤ π + π + π :=
    add_le_add (add_le_add (angle_le_pi _ _) (angle_le_pi _ _)) (angle_le_pi _ _),
  have h3lt : angle x y + angle x (x - y) + angle y (y - x) < π + π + π,
  { by_contradiction hnlt,
    have hxy : angle x y = π,
    { by_contradiction hxy,
      exact hnlt (add_lt_add_of_lt_of_le (add_lt_add_of_lt_of_le
                                           (lt_of_le_of_ne (angle_le_pi _ _) hxy)
                                         (angle_le_pi _ _)) (angle_le_pi _ _)) },
    rw hxy at hnlt,
    rw angle_eq_pi_iff at hxy,
    rcases hxy with ⟨hx, ⟨r, ⟨hr, hxr⟩⟩⟩,
    rw [hxr, ←one_smul ℝ x, ←mul_smul, mul_one, ←sub_smul, one_smul, sub_eq_add_neg,
        angle_smul_right_of_pos _ _ (add_pos zero_lt_one (neg_pos_of_neg hr)), angle_self hx,
        add_zero] at hnlt,
    apply hnlt,
    rw add_assoc,
    exact add_lt_add_left (lt_of_le_of_lt (angle_le_pi _ _)
                                          (lt_add_of_pos_right π real.pi_pos)) _ },
  have hn0 : 0 ≤ n,
  { rw [hn, mul_nonneg_iff_left_nonneg_of_pos real.pi_pos] at h0,
    norm_cast at h0,
    exact h0 },
  have hn3 : n < 3,
  { rw [hn, (show π + π + π = 3 * π, by ring)] at h3lt,
    replace h3lt := lt_of_mul_lt_mul_right h3lt (le_of_lt real.pi_pos),
    norm_cast at h3lt,
    exact h3lt },
  interval_cases n,
  { rw hn at hcos,
    simp at hcos,
    norm_num at hcos },
  { rw hn,
    norm_num },
  { rw hn at hcos,
    simp at hcos,
    norm_num at hcos },
end

end inner_product_geometry

namespace euclidean_geometry
/-!
### Geometrical results on triangles in Euclidean affine spaces

This section develops some geometrical definitions and results on
(possible degenerate) triangles in Euclidean affine spaces.
-/
open inner_product_geometry

open_locale euclidean_geometry

variables {V : Type*} {P : Type*} [inner_product_space ℝ V] [metric_space P]
    [normed_add_torsor V P]
include V

/-- **Pythagorean theorem**, if-and-only-if angle-at-point form. -/
lemma dist_sq_eq_dist_sq_add_dist_sq_iff_angle_eq_pi_div_two (p1 p2 p3 : P) :
  dist p1 p3 * dist p1 p3 = dist p1 p2 * dist p1 p2 + dist p3 p2 * dist p3 p2 ↔
    ∠ p1 p2 p3 = π / 2 :=
by erw [pseudo_metric_space.dist_comm p3 p2, dist_eq_norm_vsub V p1 p3, dist_eq_norm_vsub V p1 p2,
        dist_eq_norm_vsub V p2 p3,
        ←norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two,
        vsub_sub_vsub_cancel_right p1, ←neg_vsub_eq_vsub_rev p2 p3, norm_neg]

/-- **Law of cosines** (cosine rule), angle-at-point form. -/
lemma dist_sq_eq_dist_sq_add_dist_sq_sub_two_mul_dist_mul_dist_mul_cos_angle
    (p1 p2 p3 : P) :
  dist p1 p3 * dist p1 p3 =
    dist p1 p2 * dist p1 p2 + dist p3 p2 * dist p3 p2 -
      2 * dist p1 p2 * dist p3 p2 * real.cos (∠ p1 p2 p3) :=
begin
  rw [dist_eq_norm_vsub V p1 p3, dist_eq_norm_vsub V p1 p2, dist_eq_norm_vsub V p3 p2],
  unfold angle,
  convert norm_sub_sq_eq_norm_sq_add_norm_sq_sub_two_mul_norm_mul_norm_mul_cos_angle
          (p1 -ᵥ p2 : V) (p3 -ᵥ p2 : V),
  { exact (vsub_sub_vsub_cancel_right p1 p3 p2).symm },
  { exact (vsub_sub_vsub_cancel_right p1 p3 p2).symm }
end

alias dist_sq_eq_dist_sq_add_dist_sq_sub_two_mul_dist_mul_dist_mul_cos_angle ← law_cos

/-- **Isosceles Triangle Theorem**: Pons asinorum, angle-at-point form. -/
lemma angle_eq_angle_of_dist_eq {p1 p2 p3 : P} (h : dist p1 p2 = dist p1 p3) :
  ∠ p1 p2 p3 = ∠ p1 p3 p2 :=
begin
  rw [dist_eq_norm_vsub V p1 p2, dist_eq_norm_vsub V p1 p3] at h,
  unfold angle,
  convert angle_sub_eq_angle_sub_rev_of_norm_eq h,
  { exact (vsub_sub_vsub_cancel_left p3 p2 p1).symm },
  { exact (vsub_sub_vsub_cancel_left p2 p3 p1).symm }
end

/-- Converse of pons asinorum, angle-at-point form. -/
lemma dist_eq_of_angle_eq_angle_of_angle_ne_pi {p1 p2 p3 : P} (h : ∠ p1 p2 p3 = ∠ p1 p3 p2)
    (hpi : ∠ p2 p1 p3 ≠ π) : dist p1 p2 = dist p1 p3 :=
begin
  unfold angle at h hpi,
  rw [dist_eq_norm_vsub V p1 p2, dist_eq_norm_vsub V p1 p3],
  rw [←angle_neg_neg, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev] at hpi,
  rw [←vsub_sub_vsub_cancel_left p3 p2 p1, ←vsub_sub_vsub_cancel_left p2 p3 p1] at h,
  exact norm_eq_of_angle_sub_eq_angle_sub_rev_of_angle_ne_pi h hpi
end

/-- The **sum of the angles of a triangle** (possibly degenerate, where the
given vertex is distinct from the others), angle-at-point. -/
lemma angle_add_angle_add_angle_eq_pi {p1 p2 p3 : P} (h2 : p2 ≠ p1) (h3 : p3 ≠ p1) :
  ∠ p1 p2 p3 + ∠ p2 p3 p1 + ∠ p3 p1 p2 = π :=
begin
  rw [add_assoc, add_comm, add_comm (∠ p2 p3 p1), angle_comm p2 p3 p1],
  unfold angle,
  rw [←angle_neg_neg (p1 -ᵥ p3), ←angle_neg_neg (p1 -ᵥ p2), neg_vsub_eq_vsub_rev,
      neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev,
      ←vsub_sub_vsub_cancel_right p3 p2 p1, ←vsub_sub_vsub_cancel_right p2 p3 p1],
  exact angle_add_angle_sub_add_angle_sub_eq_pi (λ he, h3 (vsub_eq_zero_iff_eq.1 he))
                                                (λ he, h2 (vsub_eq_zero_iff_eq.1 he))
end

/-- **Stewart's Theorem**. -/
theorem dist_sq_mul_dist_add_dist_sq_mul_dist (a b c p : P) (h : ∠ b p c = π) :
  dist a b ^ 2 * dist c p + dist a c ^ 2 * dist b p =
  dist b c * (dist a p ^ 2 + dist b p * dist c p) :=
begin
  rw [pow_two, pow_two, law_cos a p b, law_cos a p c,
      eq_sub_of_add_eq (angle_add_angle_eq_pi_of_angle_eq_pi a h), real.cos_pi_sub,
      dist_eq_add_dist_of_angle_eq_pi h],
  ring,
end

/-- **Apollonius's Theorem**. -/
theorem dist_sq_add_dist_sq_eq_two_mul_dist_midpoint_sq_add_half_dist_sq (a b c : P) :
  dist a b ^ 2 + dist a c ^ 2 = 2 * (dist a (midpoint ℝ b c) ^ 2 + (dist b c / 2) ^ 2) :=
begin
  by_cases hbc : b = c,
  { simp [hbc, midpoint_self, dist_self, two_mul] },
  { let m := midpoint ℝ b c,
    have : dist b c ≠ 0 := (dist_pos.mpr hbc).ne',
    have hm := dist_sq_mul_dist_add_dist_sq_mul_dist a b c m (angle_midpoint_eq_pi b c hbc),
    simp only [dist_left_midpoint, dist_right_midpoint, real.norm_two] at hm,
    calc  dist a b ^ 2 + dist a c ^ 2
        = 2 / dist b c * (dist a b ^ 2 * (2⁻¹ * dist b c) + dist a c ^ 2 * (2⁻¹ * dist b c)) :
          by { field_simp, ring }
    ... = 2 * (dist a (midpoint ℝ b c) ^ 2 + (dist b c / 2) ^ 2) :
          by { rw hm, field_simp, ring } },
end

lemma dist_mul_of_eq_angle_of_dist_mul (a b c a' b' c' : P) (r : ℝ) (h : ∠ a' b' c' = ∠ a b c)
  (hab : dist a' b' = r * dist a b) (hcb : dist c' b' = r * dist c b) :
  dist a' c' = r * dist a c :=
begin
  have h' : dist a' c' ^ 2 = (r * dist a c) ^ 2,
    calc  dist a' c' ^ 2
        = dist a' b' ^ 2 + dist c' b' ^ 2 - 2 * dist a' b' * dist c' b' * real.cos (∠ a' b' c') :
          by { simp [pow_two, law_cos a' b' c'] }
    ... = r ^ 2 * (dist a b ^ 2 + dist c b ^ 2 - 2 * dist a b * dist c b * real.cos (∠ a b c)) :
          by { rw [h, hab, hcb], ring }
    ... = (r * dist a c) ^ 2 : by simp [pow_two, ← law_cos a b c, mul_pow],
  by_cases hab₁ : a = b,
  { have hab'₁ : a' = b', { rw [← dist_eq_zero, hab, dist_eq_zero.mpr hab₁, mul_zero r] },
    rw [hab₁, hab'₁, dist_comm b' c', dist_comm b c, hcb] },
  { have h1 : 0 ≤ r * dist a b, { rw ← hab, exact dist_nonneg },
    have h2 : 0 ≤ r := nonneg_of_mul_nonneg_right h1 (dist_pos.mpr hab₁),
    exact (sq_eq_sq dist_nonneg (mul_nonneg h2 dist_nonneg)).mp h' },
end

end euclidean_geometry


import group_theory.quotient_group

variables (G : Type*) [group G] (N : subgroup G) [subgroup.normal N]

def the_fourth_isomorphism_theorem :
  subgroup (G ⧸ N) ≃o { K : subgroup G | N ≤ K } :=
let π : G →* G ⧸ N := quotient_group.mk' _ in
{ to_fun := λ H,
  { val := H.comap π,
    property := begin
      dsimp, intros n hn,
      dsimp,
      have : (n : G ⧸ N) = 1, by simpa,
      rw this,
      apply H.one_mem,
    end },
  inv_fun := λ H, (H : subgroup G).map π,
  left_inv := begin
    intros H, dsimp,
    ext x,
    split,
    { intros hx, simp at hx, obtain ⟨y,hy,rfl⟩ := hx, assumption, },
    { intros hx, simp, obtain ⟨y,rfl⟩ := quotient_group.mk_surjective x, use [y, hx, rfl] }
  end,
  right_inv := begin
    rintros ⟨H,hH⟩, dsimp at hH, apply subtype.ext, dsimp,
    apply le_antisymm,
    { intros x hx, simp at hx, obtain ⟨y,h1,h2⟩ := hx,
      have : y⁻¹ * x ∈ N,
      { rw ← quotient_group.eq, exact h2, },
      simpa using (H.mul_mem h1 (hH this)) },
    { exact subgroup.le_comap_map π H, }
  end,
  map_rel_iff' := begin
    intros A B,
    split,
    { intros H, dsimp at H,
      intros a ha,
      obtain ⟨a,rfl⟩ := quotient_group.mk_surjective a,
      replace ha := H ha,
      exact ha },
    { intros H, dsimp, intros a ha,
      apply H,
      exact ha }
  end }


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.skeletal
! leanprover-community/mathlib commit 3dadefa3f544b1db6214777fe47910739b54c66a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Adjunction.Basic
import Mathbin.CategoryTheory.Category.Preorder
import Mathbin.CategoryTheory.IsomorphismClasses
import Mathbin.CategoryTheory.Thin

/-!
# Skeleton of a category

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

Define skeletal categories as categories in which any two isomorphic objects are equal.

Construct the skeleton of an arbitrary category by taking isomorphism classes, and show it is a
skeleton of the original category.

In addition, construct the skeleton of a thin category as a partial ordering, and (noncomputably)
show it is a skeleton of the original category. The advantage of this special case being handled
separately is that lemmas and definitions about orderings can be used directly, for example for the
subobject lattice. In addition, some of the commutative diagrams about the functors commute
definitionally on the nose which is convenient in practice.
-/

universe v₁ v₂ v₃ u₁ u₂ u₃

namespace CategoryTheory

open Category

variable (C : Type u₁) [Category.{v₁} C]

variable (D : Type u₂) [Category.{v₂} D]

variable {E : Type u₃} [Category.{v₃} E]

#print CategoryTheory.Skeletal /-
/-- A category is skeletal if isomorphic objects are equal. -/
def Skeletal : Prop :=
  ∀ ⦃X Y : C⦄, IsIsomorphic X Y → X = Y
#align category_theory.skeletal CategoryTheory.Skeletal
-/

#print CategoryTheory.IsSkeletonOf /-
/-- `is_skeleton_of C D F` says that `F : D ⥤ C` exhibits `D` as a skeletal full subcategory of `C`,
in particular `F` is a (strong) equivalence and `D` is skeletal.
-/
structure IsSkeletonOf (F : D ⥤ C) where
  skel : Skeletal D
  eqv : IsEquivalence F
#align category_theory.is_skeleton_of CategoryTheory.IsSkeletonOf
-/

attribute [local instance] is_isomorphic_setoid

variable {C D}

#print CategoryTheory.Functor.eq_of_iso /-
/-- If `C` is thin and skeletal, then any naturally isomorphic functors to `C` are equal. -/
theorem Functor.eq_of_iso {F₁ F₂ : D ⥤ C} [Quiver.IsThin C] (hC : Skeletal C) (hF : F₁ ≅ F₂) :
    F₁ = F₂ :=
  Functor.ext (fun X => hC ⟨hF.app X⟩) fun _ _ _ => Subsingleton.elim _ _
#align category_theory.functor.eq_of_iso CategoryTheory.Functor.eq_of_iso
-/

#print CategoryTheory.functor_skeletal /-
/-- If `C` is thin and skeletal, `D ⥤ C` is skeletal.
`category_theory.functor_thin` shows it is thin also.
-/
theorem functor_skeletal [Quiver.IsThin C] (hC : Skeletal C) : Skeletal (D ⥤ C) := fun F₁ F₂ h =>
  h.elim (Functor.eq_of_iso hC)
#align category_theory.functor_skeletal CategoryTheory.functor_skeletal
-/

variable (C D)

#print CategoryTheory.Skeleton /-
/-- Construct the skeleton category as the induced category on the isomorphism classes, and derive
its category structure.
-/
def Skeleton : Type u₁ :=
  InducedCategory C Quotient.out deriving Category
#align category_theory.skeleton CategoryTheory.Skeleton
-/

instance [Inhabited C] : Inhabited (Skeleton C) :=
  ⟨⟦default⟧⟩

#print CategoryTheory.fromSkeleton /-
/-- The functor from the skeleton of `C` to `C`. -/
@[simps]
noncomputable def fromSkeleton : Skeleton C ⥤ C :=
  inducedFunctor _ deriving Full, Faithful
#align category_theory.from_skeleton CategoryTheory.fromSkeleton
-/

instance : EssSurj (fromSkeleton C) where mem_essImage X := ⟨Quotient.mk' X, Quotient.mk_out X⟩

noncomputable instance : IsEquivalence (fromSkeleton C) :=
  Equivalence.ofFullyFaithfullyEssSurj (fromSkeleton C)

/- warning: category_theory.skeleton_equivalence -> CategoryTheory.skeletonEquivalence is a dubious translation:
lean 3 declaration is
  forall (C : Type.{u2}) [_inst_1 : CategoryTheory.Category.{u1, u2} C], CategoryTheory.Equivalence.{u1, u1, u2, u2} (CategoryTheory.Skeleton.{u1, u2} C _inst_1) (CategoryTheory.Skeleton.category.{u2, u1} C _inst_1) C _inst_1
but is expected to have type
  forall (C : Type.{u2}) [_inst_1 : CategoryTheory.Category.{u1, u2} C], CategoryTheory.Equivalence.{u1, u1, u2, u2} (CategoryTheory.Skeleton.{u1, u2} C _inst_1) C (CategoryTheory.instCategorySkeleton.{u1, u2} C _inst_1) _inst_1
Case conversion may be inaccurate. Consider using '#align category_theory.skeleton_equivalence CategoryTheory.skeletonEquivalenceₓ'. -/
/-- The equivalence between the skeleton and the category itself. -/
noncomputable def skeletonEquivalence : Skeleton C ≌ C :=
  (fromSkeleton C).asEquivalence
#align category_theory.skeleton_equivalence CategoryTheory.skeletonEquivalence

#print CategoryTheory.skeleton_skeletal /-
theorem skeleton_skeletal : Skeletal (Skeleton C) :=
  by
  rintro X Y ⟨h⟩
  have : X.out ≈ Y.out := ⟨(from_skeleton C).mapIso h⟩
  simpa using Quotient.sound this
#align category_theory.skeleton_skeletal CategoryTheory.skeleton_skeletal
-/

#print CategoryTheory.skeletonIsSkeleton /-
/-- The `skeleton` of `C` given by choice is a skeleton of `C`. -/
noncomputable def skeletonIsSkeleton : IsSkeletonOf C (Skeleton C) (fromSkeleton C)
    where
  skel := skeleton_skeletal C
  eqv := fromSkeleton.isEquivalence C
#align category_theory.skeleton_is_skeleton CategoryTheory.skeletonIsSkeleton
-/

section

variable {C D}

/- warning: category_theory.equivalence.skeleton_equiv -> CategoryTheory.Equivalence.skeletonEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C _inst_1 D _inst_2) -> (Equiv.{succ u3, succ u4} (CategoryTheory.Skeleton.{u1, u3} C _inst_1) (CategoryTheory.Skeleton.{u2, u4} D _inst_2))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D], (CategoryTheory.Equivalence.{u1, u2, u3, u4} C D _inst_1 _inst_2) -> (Equiv.{succ u3, succ u4} (CategoryTheory.Skeleton.{u1, u3} C _inst_1) (CategoryTheory.Skeleton.{u2, u4} D _inst_2))
Case conversion may be inaccurate. Consider using '#align category_theory.equivalence.skeleton_equiv CategoryTheory.Equivalence.skeletonEquivₓ'. -/
/-- Two categories which are categorically equivalent have skeletons with equivalent objects.
-/
noncomputable def Equivalence.skeletonEquiv (e : C ≌ D) : Skeleton C ≃ Skeleton D :=
  let f := ((skeletonEquivalence C).trans e).trans (skeletonEquivalence D).symm
  { toFun := f.Functor.obj
    invFun := f.inverse.obj
    left_inv := fun X => skeleton_skeletal C ⟨(f.unitIso.app X).symm⟩
    right_inv := fun Y => skeleton_skeletal D ⟨f.counitIso.app Y⟩ }
#align category_theory.equivalence.skeleton_equiv CategoryTheory.Equivalence.skeletonEquiv

end

#print CategoryTheory.ThinSkeleton /-
/-- Construct the skeleton category by taking the quotient of objects. This construction gives a
preorder with nice definitional properties, but is only really appropriate for thin categories.
If your original category is not thin, you probably want to be using `skeleton` instead of this.
-/
def ThinSkeleton : Type u₁ :=
  Quotient (isIsomorphicSetoid C)
#align category_theory.thin_skeleton CategoryTheory.ThinSkeleton
-/

#print CategoryTheory.inhabitedThinSkeleton /-
instance inhabitedThinSkeleton [Inhabited C] : Inhabited (ThinSkeleton C) :=
  ⟨Quotient.mk' default⟩
#align category_theory.inhabited_thin_skeleton CategoryTheory.inhabitedThinSkeleton
-/

#print CategoryTheory.ThinSkeleton.preorder /-
instance ThinSkeleton.preorder : Preorder (ThinSkeleton C)
    where
  le :=
    Quotient.lift₂ (fun X Y => Nonempty (X ⟶ Y))
      (by
        rintro _ _ _ _ ⟨i₁⟩ ⟨i₂⟩
        exact
          propext
            ⟨Nonempty.map fun f => i₁.inv ≫ f ≫ i₂.hom, Nonempty.map fun f => i₁.hom ≫ f ≫ i₂.inv⟩)
  le_refl := by
    refine' Quotient.ind fun a => _
    exact ⟨𝟙 _⟩
  le_trans a b c := Quotient.induction_on₃ a b c fun A B C => Nonempty.map2 (· ≫ ·)
#align category_theory.thin_skeleton.preorder CategoryTheory.ThinSkeleton.preorder
-/

#print CategoryTheory.toThinSkeleton /-
/-- The functor from a category to its thin skeleton. -/
@[simps]
def toThinSkeleton : C ⥤ ThinSkeleton C
    where
  obj := Quotient.mk'
  map X Y f := homOfLE (Nonempty.intro f)
#align category_theory.to_thin_skeleton CategoryTheory.toThinSkeleton
-/

/-!
The constructions here are intended to be used when the category `C` is thin, even though
some of the statements can be shown without this assumption.
-/

namespace ThinSkeleton

#print CategoryTheory.ThinSkeleton.thin /-
/-- The thin skeleton is thin. -/
instance thin : Quiver.IsThin (ThinSkeleton C) := fun _ _ =>
  ⟨by
    rintro ⟨⟨f₁⟩⟩ ⟨⟨f₂⟩⟩
    rfl⟩
#align category_theory.thin_skeleton.thin CategoryTheory.ThinSkeleton.thin
-/

variable {C} {D}

#print CategoryTheory.ThinSkeleton.map /-
/-- A functor `C ⥤ D` computably lowers to a functor `thin_skeleton C ⥤ thin_skeleton D`. -/
@[simps]
def map (F : C ⥤ D) : ThinSkeleton C ⥤ ThinSkeleton D
    where
  obj := Quotient.map F.obj fun X₁ X₂ ⟨hX⟩ => ⟨F.mapIso hX⟩
  map X Y := Quotient.recOnSubsingleton₂ X Y fun x y k => homOfLE (k.le.elim fun t => ⟨F.map t⟩)
#align category_theory.thin_skeleton.map CategoryTheory.ThinSkeleton.map
-/

#print CategoryTheory.ThinSkeleton.comp_toThinSkeleton /-
theorem comp_toThinSkeleton (F : C ⥤ D) : F ⋙ toThinSkeleton D = toThinSkeleton C ⋙ map F :=
  rfl
#align category_theory.thin_skeleton.comp_to_thin_skeleton CategoryTheory.ThinSkeleton.comp_toThinSkeleton
-/

#print CategoryTheory.ThinSkeleton.mapNatTrans /-
/-- Given a natural transformation `F₁ ⟶ F₂`, induce a natural transformation `map F₁ ⟶ map F₂`.-/
def mapNatTrans {F₁ F₂ : C ⥤ D} (k : F₁ ⟶ F₂) : map F₁ ⟶ map F₂
    where app X := Quotient.recOnSubsingleton X fun x => ⟨⟨⟨k.app x⟩⟩⟩
#align category_theory.thin_skeleton.map_nat_trans CategoryTheory.ThinSkeleton.mapNatTrans
-/

#print CategoryTheory.ThinSkeleton.map₂ /-
-- TODO: state the lemmas about what happens when you compose with `to_thin_skeleton`
/-- A functor `C ⥤ D ⥤ E` computably lowers to a functor
`thin_skeleton C ⥤ thin_skeleton D ⥤ thin_skeleton E` -/
@[simps]
def map₂ (F : C ⥤ D ⥤ E) : ThinSkeleton C ⥤ ThinSkeleton D ⥤ ThinSkeleton E
    where
  obj x :=
    { obj := fun y =>
        Quotient.map₂ (fun X Y => (F.obj X).obj Y)
          (fun X₁ X₂ ⟨hX⟩ Y₁ Y₂ ⟨hY⟩ => ⟨(F.obj X₁).mapIso hY ≪≫ (F.mapIso hX).app Y₂⟩) x y
      map := fun y₁ y₂ =>
        Quotient.recOnSubsingleton x fun X =>
          Quotient.recOnSubsingleton₂ y₁ y₂ fun Y₁ Y₂ hY =>
            homOfLE (hY.le.elim fun g => ⟨(F.obj X).map g⟩) }
  map x₁ x₂ :=
    Quotient.recOnSubsingleton₂ x₁ x₂ fun X₁ X₂ f =>
      {
        app := fun y =>
          Quotient.recOnSubsingleton y fun Y => homOfLE (f.le.elim fun f' => ⟨(F.map f').app Y⟩) }
#align category_theory.thin_skeleton.map₂ CategoryTheory.ThinSkeleton.map₂
-/

variable (C)

section

variable [Quiver.IsThin C]

#print CategoryTheory.ThinSkeleton.toThinSkeleton_faithful /-
instance toThinSkeleton_faithful : Faithful (toThinSkeleton C) where
#align category_theory.thin_skeleton.to_thin_skeleton_faithful CategoryTheory.ThinSkeleton.toThinSkeleton_faithful
-/

#print CategoryTheory.ThinSkeleton.fromThinSkeleton /-
/-- Use `quotient.out` to create a functor out of the thin skeleton. -/
@[simps]
noncomputable def fromThinSkeleton : ThinSkeleton C ⥤ C
    where
  obj := Quotient.out
  map x y :=
    Quotient.recOnSubsingleton₂ x y fun X Y f =>
      (Nonempty.some (Quotient.mk_out X)).Hom ≫ f.le.some ≫ (Nonempty.some (Quotient.mk_out Y)).inv
#align category_theory.thin_skeleton.from_thin_skeleton CategoryTheory.ThinSkeleton.fromThinSkeleton
-/

#print CategoryTheory.ThinSkeleton.fromThinSkeletonEquivalence /-
noncomputable instance fromThinSkeletonEquivalence : IsEquivalence (fromThinSkeleton C)
    where
  inverse := toThinSkeleton C
  counitIso := NatIso.ofComponents (fun X => Nonempty.some (Quotient.mk_out X)) (by tidy)
  unitIso :=
    NatIso.ofComponents
      (fun x =>
        Quotient.recOnSubsingleton x fun X =>
          eqToIso (Quotient.sound ⟨(Nonempty.some (Quotient.mk_out X)).symm⟩))
      (by tidy)
#align category_theory.thin_skeleton.from_thin_skeleton_equivalence CategoryTheory.ThinSkeleton.fromThinSkeletonEquivalence
-/

/- warning: category_theory.thin_skeleton.equivalence -> CategoryTheory.ThinSkeleton.equivalence is a dubious translation:
lean 3 declaration is
  forall (C : Type.{u2}) [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_4 : Quiver.IsThin.{u2, succ u1} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1))], CategoryTheory.Equivalence.{u2, u1, u2, u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) (Preorder.smallCategory.{u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) (CategoryTheory.ThinSkeleton.preorder.{u1, u2} C _inst_1)) C _inst_1
but is expected to have type
  forall (C : Type.{u2}) [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_4 : Quiver.IsThin.{u2, succ u1} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1))], CategoryTheory.Equivalence.{u2, u1, u2, u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) C (Preorder.smallCategory.{u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) (CategoryTheory.ThinSkeleton.preorder.{u1, u2} C _inst_1)) _inst_1
Case conversion may be inaccurate. Consider using '#align category_theory.thin_skeleton.equivalence CategoryTheory.ThinSkeleton.equivalenceₓ'. -/
/-- The equivalence between the thin skeleton and the category itself. -/
noncomputable def equivalence : ThinSkeleton C ≌ C :=
  (fromThinSkeleton C).asEquivalence
#align category_theory.thin_skeleton.equivalence CategoryTheory.ThinSkeleton.equivalence

variable {C}

#print CategoryTheory.ThinSkeleton.equiv_of_both_ways /-
theorem equiv_of_both_ways {X Y : C} (f : X ⟶ Y) (g : Y ⟶ X) : X ≈ Y :=
  ⟨iso_of_both_ways f g⟩
#align category_theory.thin_skeleton.equiv_of_both_ways CategoryTheory.ThinSkeleton.equiv_of_both_ways
-/

#print CategoryTheory.ThinSkeleton.thinSkeletonPartialOrder /-
instance thinSkeletonPartialOrder : PartialOrder (ThinSkeleton C) :=
  { CategoryTheory.ThinSkeleton.preorder C with
    le_antisymm :=
      Quotient.ind₂
        (by
          rintro _ _ ⟨f⟩ ⟨g⟩
          apply Quotient.sound (equiv_of_both_ways f g)) }
#align category_theory.thin_skeleton.thin_skeleton_partial_order CategoryTheory.ThinSkeleton.thinSkeletonPartialOrder
-/

#print CategoryTheory.ThinSkeleton.skeletal /-
theorem skeletal : Skeletal (ThinSkeleton C) := fun X Y =>
  Quotient.induction_on₂ X Y fun x y h => h.elim fun i => i.1.le.antisymm i.2.le
#align category_theory.thin_skeleton.skeletal CategoryTheory.ThinSkeleton.skeletal
-/

#print CategoryTheory.ThinSkeleton.map_comp_eq /-
theorem map_comp_eq (F : E ⥤ D) (G : D ⥤ C) : map (F ⋙ G) = map F ⋙ map G :=
  Functor.eq_of_iso skeletal <|
    NatIso.ofComponents (fun X => Quotient.recOnSubsingleton X fun x => Iso.refl _) (by tidy)
#align category_theory.thin_skeleton.map_comp_eq CategoryTheory.ThinSkeleton.map_comp_eq
-/

#print CategoryTheory.ThinSkeleton.map_id_eq /-
theorem map_id_eq : map (𝟭 C) = 𝟭 (ThinSkeleton C) :=
  Functor.eq_of_iso skeletal <|
    NatIso.ofComponents (fun X => Quotient.recOnSubsingleton X fun x => Iso.refl _) (by tidy)
#align category_theory.thin_skeleton.map_id_eq CategoryTheory.ThinSkeleton.map_id_eq
-/

#print CategoryTheory.ThinSkeleton.map_iso_eq /-
theorem map_iso_eq {F₁ F₂ : D ⥤ C} (h : F₁ ≅ F₂) : map F₁ = map F₂ :=
  Functor.eq_of_iso skeletal
    { Hom := mapNatTrans h.Hom
      inv := mapNatTrans h.inv }
#align category_theory.thin_skeleton.map_iso_eq CategoryTheory.ThinSkeleton.map_iso_eq
-/

#print CategoryTheory.ThinSkeleton.thinSkeletonIsSkeleton /-
/-- `from_thin_skeleton C` exhibits the thin skeleton as a skeleton. -/
noncomputable def thinSkeletonIsSkeleton : IsSkeletonOf C (ThinSkeleton C) (fromThinSkeleton C)
    where
  skel := skeletal
  eqv := ThinSkeleton.fromThinSkeletonEquivalence C
#align category_theory.thin_skeleton.thin_skeleton_is_skeleton CategoryTheory.ThinSkeleton.thinSkeletonIsSkeleton
-/

#print CategoryTheory.ThinSkeleton.isSkeletonOfInhabited /-
noncomputable instance isSkeletonOfInhabited :
    Inhabited (IsSkeletonOf C (ThinSkeleton C) (fromThinSkeleton C)) :=
  ⟨thinSkeletonIsSkeleton⟩
#align category_theory.thin_skeleton.is_skeleton_of_inhabited CategoryTheory.ThinSkeleton.isSkeletonOfInhabited
-/

end

variable {C}

#print CategoryTheory.ThinSkeleton.lowerAdjunction /-
/-- An adjunction between thin categories gives an adjunction between their thin skeletons. -/
def lowerAdjunction (R : D ⥤ C) (L : C ⥤ D) (h : L ⊣ R) : ThinSkeleton.map L ⊣ ThinSkeleton.map R :=
  Adjunction.mkOfUnitCounit
    { Unit :=
        {
          app := fun X => by
            letI := is_isomorphic_setoid C
            refine' Quotient.recOnSubsingleton X fun x => hom_of_le ⟨h.unit.app x⟩ }
      -- TODO: make quotient.rec_on_subsingleton' so the letI isn't needed
      counit :=
        {
          app := fun X => by
            letI := is_isomorphic_setoid D
            refine' Quotient.recOnSubsingleton X fun x => hom_of_le ⟨h.counit.app x⟩ } }
#align category_theory.thin_skeleton.lower_adjunction CategoryTheory.ThinSkeleton.lowerAdjunction
-/

end ThinSkeleton

open ThinSkeleton

section

variable {C} {α : Type _} [PartialOrder α]

/- warning: category_theory.equivalence.thin_skeleton_order_iso -> CategoryTheory.Equivalence.thinSkeletonOrderIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {α : Type.{u3}} [_inst_4 : PartialOrder.{u3} α] [_inst_5 : Quiver.IsThin.{u2, succ u1} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1))], (CategoryTheory.Equivalence.{u1, u3, u2, u3} C _inst_1 α (Preorder.smallCategory.{u3} α (PartialOrder.toPreorder.{u3} α _inst_4))) -> (OrderIso.{u2, u3} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) α (Preorder.toLE.{u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) (CategoryTheory.ThinSkeleton.preorder.{u1, u2} C _inst_1)) (Preorder.toLE.{u3} α (PartialOrder.toPreorder.{u3} α _inst_4)))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {α : Type.{u3}} [_inst_4 : PartialOrder.{u3} α] [_inst_5 : Quiver.IsThin.{u2, succ u1} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1))], (CategoryTheory.Equivalence.{u1, u3, u2, u3} C α _inst_1 (Preorder.smallCategory.{u3} α (PartialOrder.toPreorder.{u3} α _inst_4))) -> (OrderIso.{u2, u3} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) α (Preorder.toLE.{u2} (CategoryTheory.ThinSkeleton.{u1, u2} C _inst_1) (CategoryTheory.ThinSkeleton.preorder.{u1, u2} C _inst_1)) (Preorder.toLE.{u3} α (PartialOrder.toPreorder.{u3} α _inst_4)))
Case conversion may be inaccurate. Consider using '#align category_theory.equivalence.thin_skeleton_order_iso CategoryTheory.Equivalence.thinSkeletonOrderIsoₓ'. -/
/--
When `e : C ≌ α` is a categorical equivalence from a thin category `C` to some partial order `α`,
the `thin_skeleton C` is order isomorphic to `α`.
-/
noncomputable def Equivalence.thinSkeletonOrderIso [Quiver.IsThin C] (e : C ≌ α) :
    ThinSkeleton C ≃o α :=
  ((ThinSkeleton.equivalence C).trans e).toOrderIso
#align category_theory.equivalence.thin_skeleton_order_iso CategoryTheory.Equivalence.thinSkeletonOrderIso

end

end CategoryTheory


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import data.finsupp.basic

/-!
# The pointwise product on `finsupp`.

For the convolution product on `finsupp` when the domain has a binary operation,
see the type synonyms `add_monoid_algebra`
(which is in turn used to define `polynomial` and `mv_polynomial`)
and `monoid_algebra`.
-/

noncomputable theory

open finset

universes u₁ u₂ u₃ u₄ u₅
variables {α : Type u₁} {β : Type u₂} {γ : Type u₃} {δ : Type u₄} {ι : Type u₅}

namespace finsupp

/-! ### Declarations about the pointwise product on `finsupp`s -/

section
variables [mul_zero_class β]

/-- The product of `f g : α →₀ β` is the finitely supported function
  whose value at `a` is `f a * g a`. -/
instance : has_mul (α →₀ β) := ⟨zip_with (*) (mul_zero 0)⟩

@[simp] lemma mul_apply {g₁ g₂ : α →₀ β} {a : α} : (g₁ * g₂) a = g₁ a * g₂ a :=
rfl

lemma support_mul [decidable_eq α] {g₁ g₂ : α →₀ β} : (g₁ * g₂).support ⊆ g₁.support ∩ g₂.support :=
begin
  intros a h,
  simp only [mul_apply, mem_support_iff] at h,
  simp only [mem_support_iff, mem_inter, ne.def],
  rw ←not_or_distrib,
  intro w,
  apply h,
  cases w; { rw w, simp },
end

instance : mul_zero_class (α →₀ β) :=
{ zero      := 0,
  mul       := (*),
  mul_zero  := λ f, by { ext, simp only [mul_apply, zero_apply, mul_zero], },
  zero_mul  := λ f, by { ext, simp only [mul_apply, zero_apply, zero_mul], }, }

end

instance [semigroup_with_zero β] : semigroup_with_zero (α →₀ β) :=
{ mul       := (*),
  mul_assoc := λ f g h, by { ext, simp only [mul_apply, mul_assoc], },
  ..(infer_instance : mul_zero_class (α →₀ β)) }

instance [non_unital_non_assoc_semiring β] : non_unital_non_assoc_semiring (α →₀ β) :=
{ left_distrib := λ f g h, by { ext, simp only [mul_apply, add_apply, left_distrib] {proj := ff} },
  right_distrib := λ f g h,
    by { ext, simp only [mul_apply, add_apply, right_distrib] {proj := ff} },
  ..(infer_instance : mul_zero_class (α →₀ β)),
  ..(infer_instance : add_comm_monoid (α →₀ β)) }

instance [non_unital_semiring β] : non_unital_semiring (α →₀ β) :=
{ ..(infer_instance : semigroup (α →₀ β)),
  ..(infer_instance : non_unital_non_assoc_semiring (α →₀ β)) }

instance [non_unital_non_assoc_ring β] : non_unital_non_assoc_ring (α →₀ β) :=
{ left_distrib := λ f g h, by { ext, simp only [mul_apply, add_apply, left_distrib] {proj := ff} },
  right_distrib := λ f g h,
    by { ext, simp only [mul_apply, add_apply, right_distrib] {proj := ff} },
  ..(infer_instance : mul_zero_class (α →₀ β)),
  ..(infer_instance : add_comm_group (α →₀ β)) }

-- TODO can this be generalized in the direction of `pi.has_scalar'`
-- (i.e. dependent functions and finsupps)
-- TODO in theory this could be generalised, we only really need `smul_zero` for the definition
/-- The pointwise multiplicative action of functions on finitely supported functions -/
instance pointwise_module [semiring β] : module (α → β) (α →₀ β) :=
{ smul := λ f g, finsupp.of_support_finite (λ a, f a • g a) begin
    apply set.finite.subset g.finite_support,
    simp only [function.support_subset_iff, finsupp.mem_support_iff, ne.def,
      finsupp.fun_support_eq, finset.mem_coe],
    intros x hx h,
    apply hx,
    rw [h, smul_zero],
  end,
  one_smul := λ b,
    by { ext a, simp only [one_smul, pi.one_apply, finsupp.of_support_finite_coe], },
  mul_smul := λ x y b, by simp [finsupp.of_support_finite_coe, mul_smul],
  smul_add := λ r x y, finsupp.ext (λ a, by simpa only [smul_add, pi.add_apply, coe_add]),
  smul_zero := λ b, finsupp.ext (by simp [finsupp.of_support_finite_coe, smul_zero]),
  zero_smul := λ a, finsupp.ext (λ b, by simp [finsupp.of_support_finite_coe]),
  add_smul := λ r s x, finsupp.ext (λ b, by simp [finsupp.of_support_finite_coe, add_smul]) }

@[simp]
lemma coe_pointwise_module [semiring β] (f : α → β) (g : α →₀ β) :
  ⇑(f • g) = f • g := rfl

end finsupp


-- 実数のインポート
import data.real.basic

import tactic.suggest

-- 排中律を認める
noncomputable theory
open_locale classical

universe u

-- mathlibの実数の上界の定義
#check upper_bounds

-- 実数の集合の上界の集合
def up_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, a ≤ x }

def is_maximum (a : ℝ) (A : set ℝ) := a ∈ A ∧ a ∈ up_bounds A
infix ` is_a_max_of `:55 := is_maximum

-- 実数の集合はただ一つの最大値を持つ．
lemma unique_max {A : set ℝ} {x y : ℝ} (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y :=
begin
  cases hx with x_in x_up,
  cases hy with y_in y_up,

  specialize x_up y,
  specialize x_up y_in,

  specialize y_up x,
  specialize y_up x_in,

  -- x ≤ y かつ y ≤ x ならば x = y
  linarith,
end

-- unique_maxの証明は次のようにも書ける．
example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y :=
begin
  have : x ≤ y := hy.2 x hx.1,
  have : y ≤ x := hx.2 y hy.1,
  linarith,
end

-- unique_maxの証明は次のようにも書ける．(2)
example (A : set ℝ) (x y : ℝ) (hx : x is_a_max_of A) (hy : y is_a_max_of A) : x = y :=
  le_antisymm (hy.2 x hx.1) (hx.2 y hy.1)

-- 実数の下界の集合
def low_bounds (A : set ℝ) := { x : ℝ | ∀ a ∈ A, x ≤ a}

-- infimum（下限）
def is_inf (x : ℝ) (A : set ℝ) := x is_a_max_of (low_bounds A)
infix ` is_an_inf_of `:55 := is_inf

-- Aの下限より大きい数は，あるAの要素より大きい数である
lemma inf_lt {A : set ℝ} {x : ℝ} (hx : x is_an_inf_of A) : ∀ y, x < y → ∃ a ∈ A, a < y :=
begin
  intro y,
  -- 対偶を示す
  contrapose,
  -- ¬∃を∀に変換
  push_neg,
  intro h, -- `h`は，`A`の下界が`y`であると主張している．
  exact hx.2 y h,
end

-- `contrapose`と`push_neg`は`contrapose!`で略すこともできる．

-- すべての正の`ε`に対して`y ≤ x + ε`ならば，`y ≤ x`
lemma le_of_le_add_eps {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) → y ≤ x :=
begin
  contrapose!,
  intro h,
  -- ある`ε`について，`((y - x) / 2)`を代入
  use ((y - x) / 2),
  split,
  linarith,
  linarith,
end

example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) →  y ≤ x :=
begin
  contrapose!,
  exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩,
  -- `∃ z, P z`の証明は，`z₀`の構成と，`P z₀`の証明の複合で成り立つ．これを`⟨z₀, h⟩`と表記する．
  -- `P ∧ Q`の証明も同様に`⟨P, Q⟩`と表記する．
  -- `⟨z₀, ⟨h₁, h₂⟩⟩`は右結合性より`⟨z₀, h₁, h₂⟩`と書ける．
end

-- `begin`/`end`は更に`by`を用いて次のように省略できる．
example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) →  y ≤ x :=
  by {contrapose!, exact assume h, ⟨(y-x)/2, by linarith, by linarith⟩}

example {x y : ℝ} : (∀ ε > 0, y ≤ x + ε) →  y ≤ x :=
begin
  intro h,
  -- 背理法
  by_contradiction H,
  push_neg at H,
  have key := calc
    y   ≤ x + (y - x)/2 : h _ (by linarith)
    ... = x/2 + y/2     : by ring -- 環の性質より
    ... < y             : by linarith, -- 線形性の性質より
  -- linarith
  apply lt_irrefl y,
  exact key,
end

-- 絶対値
local notation `|`x`|` := abs x

-- 実数列の収束について．実数列`u`の極限は`l`に収束する．
-- `(u n)`は数列`u`の`n`番目の項を指す．
def limit (u : ℕ → ℝ) (l : ℝ) := ∀ ε > 0, ∃ N, ∀ n ≥ N, |(u n) - l| ≤ ε

-- 実数列uのすべてのn番目の項以下なら，極限値よりも小さい．
lemma le_lim {x y : ℝ} {u : ℕ → ℝ} (hu : limit u x) (ineq : ∀ n, y ≤ (u n)) : y ≤ x :=
begin
  apply le_of_le_add_eps,
  intros ε ε_pos,
  cases hu ε ε_pos with N HN,
  calc
    y   ≤ (u N)         : ineq N
    ... = x + ((u N) - x) : by linarith
    ... ≤ x + |(u N) - x| : add_le_add (by linarith) (by library_search) --- `library_search`で適した定理や補題を自動で持ってくる
    ... ≤ x + ε           : add_le_add (by linarith) (HN N (by linarith)),
end

-- `(n + 1 : ℝ)`で自然数(ℕ)`n + 1`を実数(ℝ)に変換していることに注意．
lemma inv_suc_pos : ∀ n : ℕ, 1 / (n + 1 : ℝ) > 0 :=
begin
  intro n,
  suffices : (n + 1 : ℝ) > 0,
  { library_search }, -- 正の数の逆数は正であるという主張の補題を自動補完
  norm_cast, -- 自然数についての主張に戻す
  linarith,
end

example : ∀ n : ℕ, 1/(n+1 : ℝ) > 0 :=
  λ n, one_div_pos.mpr (by exact_mod_cast nat.succ_pos n)

lemma limit_inv_succ : ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, (1 / (n + 1 : ℝ)) ≤ ε :=
begin
  intros ε ε_pos,
  suffices : ∃ N : ℕ, 1 / ε ≤ N,
  {
    cases this with N HN,
    use N,
    intros n Hn,
    rw [div_le_iff', ← div_le_iff ε_pos],
    replace Hn : (N : ℝ) ≤ n, exact_mod_cast Hn,
    linarith,
    exact_mod_cast (nat.succ_pos n),
  },
  -- exact exists_nat_ge (1 / ε)
  exact archimedean_iff_nat_le.1 (by apply_instance) (1/ε),
end

lemma inf_seq (A : set ℝ) (x : ℝ) :
  (x is_an_inf_of A) ↔ (x ∈ low_bounds A ∧ (∃ u : ℕ → ℝ, ((limit u x) ∧ (∀ n, u n ∈ A)))) :=
begin
  split,
  {
    intro h,
    split,
    { exact h.1 },
    have key : ∀ n : ℕ, ∃ a ∈ A, a < x + 1/(n + 1),
    {
      intro n,
      apply inf_lt h,
      have : 0 < 1/(n+1:ℝ), from inv_suc_pos n,
      linarith,
    },
    {
      choose u hu using key, -- 選択公理
      use u,
      split,
      {
        intros ε ε_pos,
        cases (limit_inv_succ ε ε_pos) with N H,
        use N,
        intros n hn,
        have : x ≤ (u n), from h.1 _ (hu n).1,
        have : (u n) < x + ε := calc
          u n < x + 1/(n + 1) : (hu n).2
          ... ≤ x + ε         : add_le_add (le_refl x) (H n hn),
        rw abs_of_nonneg,
        linarith,
        linarith,
      },
      {
        intro n,
        exact (hu n).1,
      },
    }
  },
  {
    intro h,
    rcases h with ⟨ x_min, u, lim, huA⟩,
    fconstructor,
    exact x_min,
    intros y y_mino,
    apply le_lim lim,
    intro n,
    exact y_mino (u n) (huA n),
  },
end