/-
Copyright (c) 2022 Yaël Dillies, Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Violeta Hernández Palacios, Grayson Burton, Vladimir Ivanov

! This file was ported from Lean 3 source module order.grade
! leanprover-community/mathlib commit cc70d9141824ea8982d1562ce009952f2c3ece30
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.Basic
import Mathbin.Data.Int.SuccPred

/-!
# Graded orders

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

This file defines graded orders, also known as ranked orders.

A `𝕆`-graded order is an order `α` equipped with a distinguished "grade" function `α → 𝕆` which
should be understood as giving the "height" of the elements. Usual graded orders are `ℕ`-graded,
cograded orders are `ℕᵒᵈ`-graded, but we can also grade by `ℤ`, and polytopes are naturally
`fin n`-graded.

Visually, `grade ℕ a` is the height of `a` in the Hasse diagram of `α`.

## Main declarations

* `grade_order`: Graded order.
* `grade_min_order`: Graded order where minimal elements have minimal grades.
* `grade_max_order`: Graded order where maximal elements have maximal grades.
* `grade_bounded_order`: Graded order where minimal elements have minimal grades and maximal
  elements have maximal grades.
* `grade`: The grade of an element. Because an order can admit several gradings, the first argument
  is the order we grade by.
* `grade_max_order`: Graded orders with maximal elements. All maximal elements have the same grade.
* `max_grade`: The maximum grade in a `grade_max_order`.
* `order_embedding.grade`: The grade of an element in a linear order as an order embedding.

## How to grade your order

Here are the translations between common references and our `grade_order`:
* [Stanley][stanley2012] defines a graded order of rank `n` as an order where all maximal chains
  have "length" `n` (so the number of elements of a chain is `n + 1`). This corresponds to
  `grade_bounded_order (fin (n + 1)) α`.
* [Engel][engel1997]'s ranked orders are somewhere between `grade_order ℕ α` and
  `grade_min_order ℕ α`, in that he requires `∃ a, is_min a ∧ grade ℕ a + 0` rather than
  `∀ a, is_min a → grade ℕ a = 0`. He defines a graded order as an order where all minimal elements
  have grade `0` and all maximal elements have the same grade. This is roughly a less bundled
  version of `grade_bounded_order (fin n) α`, assuming we discard orders with infinite chains.

## Implementation notes

One possible definition of graded orders is as the bounded orders whose flags (maximal chains)
all have the same finite length (see Stanley p. 99). However, this means that all graded orders must
have minimal and maximal elements and that the grade is not data.

Instead, we define graded orders by their grade function, without talking about flags yet.

## References

* [Konrad Engel, *Sperner Theory*][engel1997]
* [Richard Stanley, *Enumerative Combinatorics*][stanley2012]
-/

open Finset Nat OrderDual

variable {𝕆 ℙ α β : Type _}

#print GradeOrder /-
/-- An `𝕆`-graded order is an order `α` equipped with a strictly monotone function `grade 𝕆 : α → 𝕆`
which preserves order covering (`covby`). -/
class GradeOrder (𝕆 α : Type _) [Preorder 𝕆] [Preorder α] where
  grade : α → 𝕆
  grade_strictMono : StrictMono grade
  covby_grade ⦃a b : α⦄ : a ⋖ b → grade a ⋖ grade b
#align grade_order GradeOrder
-/

#print GradeMinOrder /-
/-- A `𝕆`-graded order where minimal elements have minimal grades. -/
class GradeMinOrder (𝕆 α : Type _) [Preorder 𝕆] [Preorder α] extends GradeOrder 𝕆 α where
  isMin_grade ⦃a : α⦄ : IsMin a → IsMin (grade a)
#align grade_min_order GradeMinOrder
-/

#print GradeMaxOrder /-
/-- A `𝕆`-graded order where maximal elements have maximal grades. -/
class GradeMaxOrder (𝕆 α : Type _) [Preorder 𝕆] [Preorder α] extends GradeOrder 𝕆 α where
  isMax_grade ⦃a : α⦄ : IsMax a → IsMax (grade a)
#align grade_max_order GradeMaxOrder
-/

#print GradeBoundedOrder /-
/-- A `𝕆`-graded order where minimal elements have minimal grades and maximal elements have maximal
grades. -/
class GradeBoundedOrder (𝕆 α : Type _) [Preorder 𝕆] [Preorder α] extends GradeMinOrder 𝕆 α,
  GradeMaxOrder 𝕆 α
#align grade_bounded_order GradeBoundedOrder
-/

section Preorder

-- grading
variable [Preorder 𝕆]

section Preorder

-- graded order
variable [Preorder α]

section GradeOrder

variable (𝕆) [GradeOrder 𝕆 α] {a b : α}

/- warning: grade -> grade is a dubious translation:
lean 3 declaration is
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2], α -> 𝕆
but is expected to have type
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1], α -> 𝕆
Case conversion may be inaccurate. Consider using '#align grade gradeₓ'. -/
/-- The grade of an element in a graded order. Morally, this is the number of elements you need to
go down by to get to `⊥`. -/
def grade : α → 𝕆 :=
  GradeOrder.grade
#align grade grade

/- warning: covby.grade -> Covby.grade is a dubious translation:
lean 3 declaration is
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α} {b : α}, (Covby.{u2} α (Preorder.toLT.{u2} α _inst_2) a b) -> (Covby.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 b))
but is expected to have type
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1] {a : α} {b : α}, (Covby.{u2} α (Preorder.toLT.{u2} α _inst_1) a b) -> (Covby.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_2) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 b))
Case conversion may be inaccurate. Consider using '#align covby.grade Covby.gradeₓ'. -/
protected theorem Covby.grade (h : a ⋖ b) : grade 𝕆 a ⋖ grade 𝕆 b :=
  GradeOrder.covby_grade h
#align covby.grade Covby.grade

variable {𝕆}

/- warning: grade_strict_mono -> grade_strictMono is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2], StrictMono.{u2, u1} α 𝕆 _inst_2 _inst_1 (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3)
but is expected to have type
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1], StrictMono.{u2, u1} α 𝕆 _inst_1 _inst_2 (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3)
Case conversion may be inaccurate. Consider using '#align grade_strict_mono grade_strictMonoₓ'. -/
theorem grade_strictMono : StrictMono (grade 𝕆 : α → 𝕆) :=
  GradeOrder.grade_strictMono
#align grade_strict_mono grade_strictMono

/- warning: covby_iff_lt_covby_grade -> covby_iff_lt_covby_grade is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α} {b : α}, Iff (Covby.{u2} α (Preorder.toLT.{u2} α _inst_2) a b) (And (LT.lt.{u2} α (Preorder.toLT.{u2} α _inst_2) a b) (Covby.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 b)))
but is expected to have type
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1] {a : α} {b : α}, Iff (Covby.{u2} α (Preorder.toLT.{u2} α _inst_1) a b) (And (LT.lt.{u2} α (Preorder.toLT.{u2} α _inst_1) a b) (Covby.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_2) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3 b)))
Case conversion may be inaccurate. Consider using '#align covby_iff_lt_covby_grade covby_iff_lt_covby_gradeₓ'. -/
theorem covby_iff_lt_covby_grade : a ⋖ b ↔ a < b ∧ grade 𝕆 a ⋖ grade 𝕆 b :=
  ⟨fun h => ⟨h.1, h.grade _⟩,
    And.imp_right fun h c ha hb => h.2 (grade_strictMono ha) <| grade_strictMono hb⟩
#align covby_iff_lt_covby_grade covby_iff_lt_covby_grade

end GradeOrder

section GradeMinOrder

variable (𝕆) [GradeMinOrder 𝕆 α] {a : α}

/- warning: is_min.grade -> IsMin.grade is a dubious translation:
lean 3 declaration is
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeMinOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α}, (IsMin.{u2} α (Preorder.toLE.{u2} α _inst_2) a) -> (IsMin.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMinOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3) a))
but is expected to have type
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeMinOrder.{u1, u2} 𝕆 α _inst_2 _inst_1] {a : α}, (IsMin.{u2} α (Preorder.toLE.{u2} α _inst_1) a) -> (IsMin.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_2) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMinOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1 _inst_3) a))
Case conversion may be inaccurate. Consider using '#align is_min.grade IsMin.gradeₓ'. -/
protected theorem IsMin.grade (h : IsMin a) : IsMin (grade 𝕆 a) :=
  GradeMinOrder.isMin_grade h
#align is_min.grade IsMin.grade

variable {𝕆}

/- warning: is_min_grade_iff -> isMin_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeMinOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α}, Iff (IsMin.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMinOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3) a)) (IsMin.{u2} α (Preorder.toLE.{u2} α _inst_2) a)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeMinOrder.{u2, u1} 𝕆 α _inst_2 _inst_1] {a : α}, Iff (IsMin.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 _inst_2) (grade.{u2, u1} 𝕆 α _inst_1 _inst_2 (GradeMinOrder.toGradeOrder.{u2, u1} 𝕆 α _inst_2 _inst_1 _inst_3) a)) (IsMin.{u1} α (Preorder.toLE.{u1} α _inst_1) a)
Case conversion may be inaccurate. Consider using '#align is_min_grade_iff isMin_grade_iffₓ'. -/
@[simp]
theorem isMin_grade_iff : IsMin (grade 𝕆 a) ↔ IsMin a :=
  ⟨grade_strictMono.isMin_of_apply, IsMin.grade _⟩
#align is_min_grade_iff isMin_grade_iff

end GradeMinOrder

section GradeMaxOrder

variable (𝕆) [GradeMaxOrder 𝕆 α] {a : α}

/- warning: is_max.grade -> IsMax.grade is a dubious translation:
lean 3 declaration is
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeMaxOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α}, (IsMax.{u2} α (Preorder.toLE.{u2} α _inst_2) a) -> (IsMax.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMaxOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3) a))
but is expected to have type
  forall (𝕆 : Type.{u1}) {α : Type.{u2}} [_inst_1 : Preorder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeMaxOrder.{u1, u2} 𝕆 α _inst_2 _inst_1] {a : α}, (IsMax.{u2} α (Preorder.toLE.{u2} α _inst_1) a) -> (IsMax.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_2) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMaxOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_2 _inst_1 _inst_3) a))
Case conversion may be inaccurate. Consider using '#align is_max.grade IsMax.gradeₓ'. -/
protected theorem IsMax.grade (h : IsMax a) : IsMax (grade 𝕆 a) :=
  GradeMaxOrder.isMax_grade h
#align is_max.grade IsMax.grade

variable {𝕆}

/- warning: is_max_grade_iff -> isMax_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : GradeMaxOrder.{u1, u2} 𝕆 α _inst_1 _inst_2] {a : α}, Iff (IsMax.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 _inst_2 (GradeMaxOrder.toGradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_2 _inst_3) a)) (IsMax.{u2} α (Preorder.toLE.{u2} α _inst_2) a)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeMaxOrder.{u2, u1} 𝕆 α _inst_2 _inst_1] {a : α}, Iff (IsMax.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 _inst_2) (grade.{u2, u1} 𝕆 α _inst_1 _inst_2 (GradeMaxOrder.toGradeOrder.{u2, u1} 𝕆 α _inst_2 _inst_1 _inst_3) a)) (IsMax.{u1} α (Preorder.toLE.{u1} α _inst_1) a)
Case conversion may be inaccurate. Consider using '#align is_max_grade_iff isMax_grade_iffₓ'. -/
@[simp]
theorem isMax_grade_iff : IsMax (grade 𝕆 a) ↔ IsMax a :=
  ⟨grade_strictMono.isMax_of_apply, IsMax.grade _⟩
#align is_max_grade_iff isMax_grade_iff

end GradeMaxOrder

end Preorder

/- warning: grade_mono -> grade_mono is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : PartialOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α _inst_2)], Monotone.{u2, u1} α 𝕆 (PartialOrder.toPreorder.{u2} α _inst_2) _inst_1 (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α _inst_2) _inst_3)
but is expected to have type
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u2} α _inst_1)], Monotone.{u2, u1} α 𝕆 (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 (grade.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u2} α _inst_1) _inst_2 _inst_3)
Case conversion may be inaccurate. Consider using '#align grade_mono grade_monoₓ'. -/
-- graded order
theorem grade_mono [PartialOrder α] [GradeOrder 𝕆 α] : Monotone (grade 𝕆 : α → 𝕆) :=
  grade_strictMono.Monotone
#align grade_mono grade_mono

section LinearOrder

-- graded order
variable [LinearOrder α] [GradeOrder 𝕆 α] {a b : α}

/- warning: grade_injective -> grade_injective is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))], Function.Injective.{succ u2, succ u1} α 𝕆 (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3)
but is expected to have type
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : Preorder.{u1} 𝕆] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))], Function.Injective.{succ u2, succ u1} α 𝕆 (grade.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) _inst_2 _inst_3)
Case conversion may be inaccurate. Consider using '#align grade_injective grade_injectiveₓ'. -/
theorem grade_injective : Function.Injective (grade 𝕆 : α → 𝕆) :=
  grade_strictMono.Injective
#align grade_injective grade_injective

/- warning: grade_le_grade_iff -> grade_le_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))] {a : α} {b : α}, Iff (LE.le.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 b)) (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))) a b)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeOrder.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))] {a : α} {b : α}, Iff (LE.le.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 _inst_2) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 a) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 b)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a b)
Case conversion may be inaccurate. Consider using '#align grade_le_grade_iff grade_le_grade_iffₓ'. -/
@[simp]
theorem grade_le_grade_iff : grade 𝕆 a ≤ grade 𝕆 b ↔ a ≤ b :=
  grade_strictMono.le_iff_le
#align grade_le_grade_iff grade_le_grade_iff

/- warning: grade_lt_grade_iff -> grade_lt_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))] {a : α} {b : α}, Iff (LT.lt.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 b)) (LT.lt.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))) a b)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeOrder.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))] {a : α} {b : α}, Iff (LT.lt.{u2} 𝕆 (Preorder.toLT.{u2} 𝕆 _inst_2) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 a) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 b)) (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a b)
Case conversion may be inaccurate. Consider using '#align grade_lt_grade_iff grade_lt_grade_iffₓ'. -/
@[simp]
theorem grade_lt_grade_iff : grade 𝕆 a < grade 𝕆 b ↔ a < b :=
  grade_strictMono.lt_iff_lt
#align grade_lt_grade_iff grade_lt_grade_iff

/- warning: grade_eq_grade_iff -> grade_eq_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))] {a : α} {b : α}, Iff (Eq.{succ u1} 𝕆 (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 b)) (Eq.{succ u2} α a b)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeOrder.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))] {a : α} {b : α}, Iff (Eq.{succ u2} 𝕆 (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 a) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 b)) (Eq.{succ u1} α a b)
Case conversion may be inaccurate. Consider using '#align grade_eq_grade_iff grade_eq_grade_iffₓ'. -/
@[simp]
theorem grade_eq_grade_iff : grade 𝕆 a = grade 𝕆 b ↔ a = b :=
  grade_injective.eq_iff
#align grade_eq_grade_iff grade_eq_grade_iff

/- warning: grade_ne_grade_iff -> grade_ne_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))] {a : α} {b : α}, Iff (Ne.{succ u1} 𝕆 (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 b)) (Ne.{succ u2} α a b)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeOrder.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))] {a : α} {b : α}, Iff (Ne.{succ u2} 𝕆 (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 a) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 b)) (Ne.{succ u1} α a b)
Case conversion may be inaccurate. Consider using '#align grade_ne_grade_iff grade_ne_grade_iffₓ'. -/
theorem grade_ne_grade_iff : grade 𝕆 a ≠ grade 𝕆 b ↔ a ≠ b :=
  grade_injective.ne_iff
#align grade_ne_grade_iff grade_ne_grade_iff

/- warning: grade_covby_grade_iff -> grade_covby_grade_iff is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_2 : LinearOrder.{u2} α] [_inst_3 : GradeOrder.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))] {a : α} {b : α}, Iff (Covby.{u1} 𝕆 (Preorder.toLT.{u1} 𝕆 _inst_1) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 a) (grade.{u1, u2} 𝕆 α _inst_1 (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2)))) _inst_3 b)) (Covby.{u2} α (Preorder.toLT.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (LinearOrder.toLattice.{u2} α _inst_2))))) a b)
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} 𝕆] [_inst_3 : GradeOrder.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))] {a : α} {b : α}, Iff (Covby.{u2} 𝕆 (Preorder.toLT.{u2} 𝕆 _inst_2) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 a) (grade.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 _inst_3 b)) (Covby.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a b)
Case conversion may be inaccurate. Consider using '#align grade_covby_grade_iff grade_covby_grade_iffₓ'. -/
theorem grade_covby_grade_iff : grade 𝕆 a ⋖ grade 𝕆 b ↔ a ⋖ b :=
  (covby_iff_lt_covby_grade.trans <| and_iff_right_of_imp fun h => grade_lt_grade_iff.1 h.1).symm
#align grade_covby_grade_iff grade_covby_grade_iff

end LinearOrder

-- graded order
end Preorder

-- grading
section PartialOrder

variable [PartialOrder 𝕆] [Preorder α]

/- warning: grade_bot -> grade_bot is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : OrderBot.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 (PartialOrder.toPreorder.{u1} 𝕆 _inst_1))] [_inst_4 : OrderBot.{u2} α (Preorder.toLE.{u2} α _inst_2)] [_inst_5 : GradeMinOrder.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2], Eq.{succ u1} 𝕆 (grade.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2 (GradeMinOrder.toGradeOrder.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2 _inst_5) (Bot.bot.{u2} α (OrderBot.toHasBot.{u2} α (Preorder.toLE.{u2} α _inst_2) _inst_4))) (Bot.bot.{u1} 𝕆 (OrderBot.toHasBot.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 (PartialOrder.toPreorder.{u1} 𝕆 _inst_1)) _inst_3))
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u2} 𝕆] [_inst_2 : Preorder.{u1} α] [_inst_3 : OrderBot.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1))] [_inst_4 : OrderBot.{u1} α (Preorder.toLE.{u1} α _inst_2)] [_inst_5 : GradeMinOrder.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) _inst_2], Eq.{succ u2} 𝕆 (grade.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) (GradeMinOrder.toGradeOrder.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) _inst_2 _inst_5) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α _inst_2) _inst_4))) (Bot.bot.{u2} 𝕆 (OrderBot.toBot.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1)) _inst_3))
Case conversion may be inaccurate. Consider using '#align grade_bot grade_botₓ'. -/
@[simp]
theorem grade_bot [OrderBot 𝕆] [OrderBot α] [GradeMinOrder 𝕆 α] : grade 𝕆 (⊥ : α) = ⊥ :=
  (isMin_bot.grade _).eq_bot
#align grade_bot grade_bot

/- warning: grade_top -> grade_top is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : PartialOrder.{u1} 𝕆] [_inst_2 : Preorder.{u2} α] [_inst_3 : OrderTop.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 (PartialOrder.toPreorder.{u1} 𝕆 _inst_1))] [_inst_4 : OrderTop.{u2} α (Preorder.toLE.{u2} α _inst_2)] [_inst_5 : GradeMaxOrder.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2], Eq.{succ u1} 𝕆 (grade.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2 (GradeMaxOrder.toGradeOrder.{u1, u2} 𝕆 α (PartialOrder.toPreorder.{u1} 𝕆 _inst_1) _inst_2 _inst_5) (Top.top.{u2} α (OrderTop.toHasTop.{u2} α (Preorder.toLE.{u2} α _inst_2) _inst_4))) (Top.top.{u1} 𝕆 (OrderTop.toHasTop.{u1} 𝕆 (Preorder.toLE.{u1} 𝕆 (PartialOrder.toPreorder.{u1} 𝕆 _inst_1)) _inst_3))
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : PartialOrder.{u2} 𝕆] [_inst_2 : Preorder.{u1} α] [_inst_3 : OrderTop.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1))] [_inst_4 : OrderTop.{u1} α (Preorder.toLE.{u1} α _inst_2)] [_inst_5 : GradeMaxOrder.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) _inst_2], Eq.{succ u2} 𝕆 (grade.{u2, u1} 𝕆 α _inst_2 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) (GradeMaxOrder.toGradeOrder.{u2, u1} 𝕆 α (PartialOrder.toPreorder.{u2} 𝕆 _inst_1) _inst_2 _inst_5) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α _inst_2) _inst_4))) (Top.top.{u2} 𝕆 (OrderTop.toTop.{u2} 𝕆 (Preorder.toLE.{u2} 𝕆 (PartialOrder.toPreorder.{u2} 𝕆 _inst_1)) _inst_3))
Case conversion may be inaccurate. Consider using '#align grade_top grade_topₓ'. -/
@[simp]
theorem grade_top [OrderTop 𝕆] [OrderTop α] [GradeMaxOrder 𝕆 α] : grade 𝕆 (⊤ : α) = ⊤ :=
  (isMax_top.grade _).eq_top
#align grade_top grade_top

end PartialOrder

/-! ### Instances -/

variable [Preorder 𝕆] [Preorder ℙ] [Preorder α] [Preorder β]

#print Preorder.toGradeBoundedOrder /-
instance Preorder.toGradeBoundedOrder : GradeBoundedOrder α α
    where
  grade := id
  isMin_grade _ := id
  isMax_grade _ := id
  grade_strictMono := strictMono_id
  covby_grade a b := id
#align preorder.to_grade_bounded_order Preorder.toGradeBoundedOrder
-/

#print grade_self /-
@[simp]
theorem grade_self (a : α) : grade α a = a :=
  rfl
#align grade_self grade_self
-/

/-! #### Dual -/

instance [GradeOrder 𝕆 α] : GradeOrder 𝕆ᵒᵈ αᵒᵈ
    where
  grade := toDual ∘ grade 𝕆 ∘ ofDual
  grade_strictMono := grade_strictMono.dual
  covby_grade a b h := (h.ofDual.grade _).toDual

instance [GradeMaxOrder 𝕆 α] : GradeMinOrder 𝕆ᵒᵈ αᵒᵈ :=
  { OrderDual.gradeOrder with isMin_grade := fun _ => IsMax.grade _ }

instance [GradeMinOrder 𝕆 α] : GradeMaxOrder 𝕆ᵒᵈ αᵒᵈ :=
  { OrderDual.gradeOrder with isMax_grade := fun _ => IsMin.grade _ }

instance [GradeBoundedOrder 𝕆 α] : GradeBoundedOrder 𝕆ᵒᵈ αᵒᵈ :=
  { OrderDual.gradeMinOrder, OrderDual.gradeMaxOrder with }

/- warning: grade_to_dual -> grade_toDual is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_3 : Preorder.{u2} α] [_inst_5 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_3] (a : α), Eq.{succ u1} (OrderDual.{u1} 𝕆) (grade.{u1, u2} (OrderDual.{u1} 𝕆) (OrderDual.{u2} α) (OrderDual.preorder.{u1} 𝕆 _inst_1) (OrderDual.preorder.{u2} α _inst_3) (OrderDual.gradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_3 _inst_5) (coeFn.{succ u2, succ u2} (Equiv.{succ u2, succ u2} α (OrderDual.{u2} α)) (fun (_x : Equiv.{succ u2, succ u2} α (OrderDual.{u2} α)) => α -> (OrderDual.{u2} α)) (Equiv.hasCoeToFun.{succ u2, succ u2} α (OrderDual.{u2} α)) (OrderDual.toDual.{u2} α) a)) (coeFn.{succ u1, succ u1} (Equiv.{succ u1, succ u1} 𝕆 (OrderDual.{u1} 𝕆)) (fun (_x : Equiv.{succ u1, succ u1} 𝕆 (OrderDual.{u1} 𝕆)) => 𝕆 -> (OrderDual.{u1} 𝕆)) (Equiv.hasCoeToFun.{succ u1, succ u1} 𝕆 (OrderDual.{u1} 𝕆)) (OrderDual.toDual.{u1} 𝕆) (grade.{u1, u2} 𝕆 α _inst_1 _inst_3 _inst_5 a))
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : Preorder.{u2} 𝕆] [_inst_3 : Preorder.{u1} α] [_inst_5 : GradeOrder.{u2, u1} 𝕆 α _inst_1 _inst_3] (a : α), Eq.{succ u2} (OrderDual.{u2} 𝕆) (grade.{u2, u1} (OrderDual.{u2} 𝕆) ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : α) => OrderDual.{u1} α) a) (OrderDual.preorder.{u1} α _inst_3) (OrderDual.preorder.{u2} 𝕆 _inst_1) (OrderDual.gradeOrder.{u2, u1} 𝕆 α _inst_1 _inst_3 _inst_5) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.{succ u1, succ u1} α (OrderDual.{u1} α)) α (fun (_x : α) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : α) => OrderDual.{u1} α) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} α (OrderDual.{u1} α)) (OrderDual.toDual.{u1} α) a)) (FunLike.coe.{succ u2, succ u2, succ u2} (Equiv.{succ u2, succ u2} 𝕆 (OrderDual.{u2} 𝕆)) 𝕆 (fun (_x : 𝕆) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : 𝕆) => OrderDual.{u2} 𝕆) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u2} 𝕆 (OrderDual.{u2} 𝕆)) (OrderDual.toDual.{u2} 𝕆) (grade.{u2, u1} 𝕆 α _inst_3 _inst_1 _inst_5 a))
Case conversion may be inaccurate. Consider using '#align grade_to_dual grade_toDualₓ'. -/
@[simp]
theorem grade_toDual [GradeOrder 𝕆 α] (a : α) : grade 𝕆ᵒᵈ (toDual a) = toDual (grade 𝕆 a) :=
  rfl
#align grade_to_dual grade_toDual

/- warning: grade_of_dual -> grade_ofDual is a dubious translation:
lean 3 declaration is
  forall {𝕆 : Type.{u1}} {α : Type.{u2}} [_inst_1 : Preorder.{u1} 𝕆] [_inst_3 : Preorder.{u2} α] [_inst_5 : GradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_3] (a : OrderDual.{u2} α), Eq.{succ u1} 𝕆 (grade.{u1, u2} 𝕆 α _inst_1 _inst_3 _inst_5 (coeFn.{succ u2, succ u2} (Equiv.{succ u2, succ u2} (OrderDual.{u2} α) α) (fun (_x : Equiv.{succ u2, succ u2} (OrderDual.{u2} α) α) => (OrderDual.{u2} α) -> α) (Equiv.hasCoeToFun.{succ u2, succ u2} (OrderDual.{u2} α) α) (OrderDual.ofDual.{u2} α) a)) (coeFn.{succ u1, succ u1} (Equiv.{succ u1, succ u1} (OrderDual.{u1} 𝕆) 𝕆) (fun (_x : Equiv.{succ u1, succ u1} (OrderDual.{u1} 𝕆) 𝕆) => (OrderDual.{u1} 𝕆) -> 𝕆) (Equiv.hasCoeToFun.{succ u1, succ u1} (OrderDual.{u1} 𝕆) 𝕆) (OrderDual.ofDual.{u1} 𝕆) (grade.{u1, u2} (OrderDual.{u1} 𝕆) (OrderDual.{u2} α) (OrderDual.preorder.{u1} 𝕆 _inst_1) (OrderDual.preorder.{u2} α _inst_3) (OrderDual.gradeOrder.{u1, u2} 𝕆 α _inst_1 _inst_3 _inst_5) a))
but is expected to have type
  forall {𝕆 : Type.{u2}} {α : Type.{u1}} [_inst_1 : Preorder.{u2} 𝕆] [_inst_3 : Preorder.{u1} α] [_inst_5 : GradeOrder.{u2, u1} 𝕆 α _inst_1 _inst_3] (a : OrderDual.{u1} α), Eq.{succ u2} 𝕆 (grade.{u2, u1} 𝕆 ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : OrderDual.{u1} α) => α) a) _inst_3 _inst_1 _inst_5 (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.{succ u1, succ u1} (OrderDual.{u1} α) α) (OrderDual.{u1} α) (fun (_x : OrderDual.{u1} α) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : OrderDual.{u1} α) => α) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} (OrderDual.{u1} α) α) (OrderDual.ofDual.{u1} α) a)) (FunLike.coe.{succ u2, succ u2, succ u2} (Equiv.{succ u2, succ u2} (OrderDual.{u2} 𝕆) 𝕆) (OrderDual.{u2} 𝕆) (fun (_x : OrderDual.{u2} 𝕆) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : OrderDual.{u2} 𝕆) => 𝕆) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u2} (OrderDual.{u2} 𝕆) 𝕆) (OrderDual.ofDual.{u2} 𝕆) (grade.{u2, u1} (OrderDual.{u2} 𝕆) (OrderDual.{u1} α) (OrderDual.preorder.{u1} α _inst_3) (OrderDual.preorder.{u2} 𝕆 _inst_1) (OrderDual.gradeOrder.{u2, u1} 𝕆 α _inst_1 _inst_3 _inst_5) a))
Case conversion may be inaccurate. Consider using '#align grade_of_dual grade_ofDualₓ'. -/
@[simp]
theorem grade_ofDual [GradeOrder 𝕆 α] (a : αᵒᵈ) : grade 𝕆 (ofDual a) = ofDual (grade 𝕆ᵒᵈ a) :=
  rfl
#align grade_of_dual grade_ofDual

/-! #### Lifting a graded order -/

#print GradeOrder.liftLeft /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeOrder.liftLeft [GradeOrder 𝕆 α] (f : 𝕆 → ℙ) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) : GradeOrder ℙ α
    where
  grade := f ∘ grade 𝕆
  grade_strictMono := hf.comp grade_strictMono
  covby_grade a b h := hcovby _ _ <| h.grade _
#align grade_order.lift_left GradeOrder.liftLeft
-/

#print GradeMinOrder.liftLeft /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeMinOrder.liftLeft [GradeMinOrder 𝕆 α] (f : 𝕆 → ℙ) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmin : ∀ a, IsMin a → IsMin (f a)) : GradeMinOrder ℙ α :=
  { GradeOrder.liftLeft f hf hcovby with isMin_grade := fun a ha => hmin _ <| ha.grade _ }
#align grade_min_order.lift_left GradeMinOrder.liftLeft
-/

#print GradeMaxOrder.liftLeft /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeMaxOrder.liftLeft [GradeMaxOrder 𝕆 α] (f : 𝕆 → ℙ) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmax : ∀ a, IsMax a → IsMax (f a)) : GradeMaxOrder ℙ α :=
  { GradeOrder.liftLeft f hf hcovby with isMax_grade := fun a ha => hmax _ <| ha.grade _ }
#align grade_max_order.lift_left GradeMaxOrder.liftLeft
-/

#print GradeBoundedOrder.liftLeft /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeBoundedOrder.liftLeft [GradeBoundedOrder 𝕆 α] (f : 𝕆 → ℙ) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmin : ∀ a, IsMin a → IsMin (f a))
    (hmax : ∀ a, IsMax a → IsMax (f a)) : GradeBoundedOrder ℙ α :=
  { GradeMinOrder.liftLeft f hf hcovby hmin, GradeMaxOrder.liftLeft f hf hcovby hmax with }
#align grade_bounded_order.lift_left GradeBoundedOrder.liftLeft
-/

#print GradeOrder.liftRight /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeOrder.liftRight [GradeOrder 𝕆 β] (f : α → β) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) : GradeOrder 𝕆 α
    where
  grade := grade 𝕆 ∘ f
  grade_strictMono := grade_strictMono.comp hf
  covby_grade a b h := (hcovby _ _ h).grade _
#align grade_order.lift_right GradeOrder.liftRight
-/

#print GradeMinOrder.liftRight /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeMinOrder.liftRight [GradeMinOrder 𝕆 β] (f : α → β) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmin : ∀ a, IsMin a → IsMin (f a)) : GradeMinOrder 𝕆 α :=
  { GradeOrder.liftRight f hf hcovby with isMin_grade := fun a ha => (hmin _ ha).grade _ }
#align grade_min_order.lift_right GradeMinOrder.liftRight
-/

#print GradeMaxOrder.liftRight /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeMaxOrder.liftRight [GradeMaxOrder 𝕆 β] (f : α → β) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmax : ∀ a, IsMax a → IsMax (f a)) : GradeMaxOrder 𝕆 α :=
  { GradeOrder.liftRight f hf hcovby with isMax_grade := fun a ha => (hmax _ ha).grade _ }
#align grade_max_order.lift_right GradeMaxOrder.liftRight
-/

#print GradeBoundedOrder.liftRight /-
-- See note [reducible non-instances]
/-- Lifts a graded order along a strictly monotone function. -/
@[reducible]
def GradeBoundedOrder.liftRight [GradeBoundedOrder 𝕆 β] (f : α → β) (hf : StrictMono f)
    (hcovby : ∀ a b, a ⋖ b → f a ⋖ f b) (hmin : ∀ a, IsMin a → IsMin (f a))
    (hmax : ∀ a, IsMax a → IsMax (f a)) : GradeBoundedOrder 𝕆 α :=
  { GradeMinOrder.liftRight f hf hcovby hmin, GradeMaxOrder.liftRight f hf hcovby hmax with }
#align grade_bounded_order.lift_right GradeBoundedOrder.liftRight
-/

/-! #### `fin n`-graded to `ℕ`-graded to `ℤ`-graded -/

/- warning: grade_order.fin_to_nat -> GradeOrder.finToNat is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_3 : Preorder.{u1} α] (n : Nat) [_inst_5 : GradeOrder.{0, u1} (Fin n) α (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) _inst_3], GradeOrder.{0, u1} Nat α (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) _inst_3
but is expected to have type
  forall {α : Type.{u1}} [_inst_3 : Preorder.{u1} α] (n : Nat) [_inst_5 : GradeOrder.{0, u1} (Fin n) α (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) _inst_3], GradeOrder.{0, u1} Nat α (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) _inst_3
Case conversion may be inaccurate. Consider using '#align grade_order.fin_to_nat GradeOrder.finToNatₓ'. -/
-- See note [reducible non-instances]
/-- A `fin n`-graded order is also `ℕ`-graded. We do not mark this an instance because `n` is not
inferrable. -/
@[reducible]
def GradeOrder.finToNat (n : ℕ) [GradeOrder (Fin n) α] : GradeOrder ℕ α :=
  GradeOrder.liftLeft (_ : Fin n → ℕ) Fin.val_strictMono fun _ _ => Covby.coe_fin
#align grade_order.fin_to_nat GradeOrder.finToNat

/- warning: grade_min_order.fin_to_nat -> GradeMinOrder.finToNat is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_3 : Preorder.{u1} α] (n : Nat) [_inst_5 : GradeMinOrder.{0, u1} (Fin n) α (PartialOrder.toPreorder.{0} (Fin n) (Fin.partialOrder n)) _inst_3], GradeMinOrder.{0, u1} Nat α (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) _inst_3
but is expected to have type
  forall {α : Type.{u1}} [_inst_3 : Preorder.{u1} α] (n : Nat) [_inst_5 : GradeMinOrder.{0, u1} (Fin n) α (PartialOrder.toPreorder.{0} (Fin n) (Fin.instPartialOrderFin n)) _inst_3], GradeMinOrder.{0, u1} Nat α (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) _inst_3
Case conversion may be inaccurate. Consider using '#align grade_min_order.fin_to_nat GradeMinOrder.finToNatₓ'. -/
-- See note [reducible non-instances]
/-- A `fin n`-graded order is also `ℕ`-graded. We do not mark this an instance because `n` is not
inferrable. -/
@[reducible]
def GradeMinOrder.finToNat (n : ℕ) [GradeMinOrder (Fin n) α] : GradeMinOrder ℕ α :=
  GradeMinOrder.liftLeft (_ : Fin n → ℕ) Fin.val_strictMono (fun _ _ => Covby.coe_fin) fun a h =>
    by
    cases n
    · exact ((@Fin.elim0 fun _ => False) <| grade (Fin 0) a).elim
    rw [h.eq_bot, Fin.bot_eq_zero]
    exact isMin_bot
#align grade_min_order.fin_to_nat GradeMinOrder.finToNat

#print GradeOrder.natToInt /-
instance GradeOrder.natToInt [GradeOrder ℕ α] : GradeOrder ℤ α :=
  GradeOrder.liftLeft _ Int.coe_nat_strictMono fun _ _ => Covby.cast_int
#align grade_order.nat_to_int GradeOrder.natToInt
-/


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

Theory of Cauchy filters in uniform spaces. Complete uniform spaces. Totally bounded subsets.
-/
import topology.uniform_space.basic

open filter topological_space lattice set classical
local attribute [instance, priority 0] prop_decidable
variables {α : Type*} {β : Type*} [uniform_space α]
universe u

local notation `𝓤` := uniformity

/-- A filter `f` is Cauchy if for every entourage `r`, there exists an
  `s ∈ f` such that `s × s ⊆ r`. This is a generalization of Cauchy
  sequences, because if `a : ℕ → α` then the filter of sets containing
  cofinitely many of the `a n` is Cauchy iff `a` is a Cauchy sequence. -/
def cauchy (f : filter α) := f ≠ ⊥ ∧ filter.prod f f ≤ (𝓤 α)

def is_complete (s : set α) := ∀f, cauchy f → f ≤ principal s → ∃x∈s, f ≤ nhds x

lemma cauchy_iff {f : filter α} :
  cauchy f ↔ (f ≠ ⊥ ∧ (∀ s ∈ 𝓤 α, ∃t∈f.sets, set.prod t t ⊆ s)) :=
and_congr (iff.refl _) $ forall_congr $ assume s, forall_congr $ assume hs, mem_prod_same_iff

lemma cauchy_map_iff {l : filter β} {f : β → α} :
  cauchy (l.map f) ↔ (l ≠ ⊥ ∧ tendsto (λp:β×β, (f p.1, f p.2)) (l.prod l) (𝓤 α)) :=
by rw [cauchy, (≠), map_eq_bot_iff, prod_map_map_eq]; refl

lemma cauchy_downwards {f g : filter α} (h_c : cauchy f) (hg : g ≠ ⊥) (h_le : g ≤ f) : cauchy g :=
⟨hg, le_trans (filter.prod_mono h_le h_le) h_c.right⟩

lemma cauchy_nhds {a : α} : cauchy (nhds a) :=
⟨nhds_neq_bot,
  calc filter.prod (nhds a) (nhds a) =
    (𝓤 α).lift (λs:set (α×α), (𝓤 α).lift' (λt:set(α×α),
      set.prod {y : α | (y, a) ∈ s} {y : α | (a, y) ∈ t})) : nhds_nhds_eq_uniformity_uniformity_prod
    ... ≤ (𝓤 α).lift' (λs:set (α×α), comp_rel s s) :
      le_infi $ assume s, le_infi $ assume hs,
      infi_le_of_le s $ infi_le_of_le hs $ infi_le_of_le s $ infi_le_of_le hs $
      principal_mono.mpr $
      assume ⟨x, y⟩ ⟨(hx : (x, a) ∈ s), (hy : (a, y) ∈ s)⟩, ⟨a, hx, hy⟩
    ... ≤ 𝓤 α : comp_le_uniformity⟩

lemma cauchy_pure {a : α} : cauchy (pure a) :=
cauchy_downwards cauchy_nhds
  (show principal {a} ≠ ⊥, by simp)
  (pure_le_nhds a)

lemma le_nhds_of_cauchy_adhp {f : filter α} {x : α} (hf : cauchy f)
  (adhs : f ⊓ nhds x ≠ ⊥) : f ≤ nhds x :=
have ∀s∈f.sets, x ∈ closure s,
begin
  intros s hs,
  simp [closure_eq_nhds, inf_comm],
  exact assume h', adhs $ bot_unique $ h' ▸ inf_le_inf (by simp; exact hs) (le_refl _)
end,
calc f ≤ f.lift' (λs:set α, {y | x ∈ closure s ∧ y ∈ closure s}) :
    le_infi $ assume s, le_infi $ assume hs,
    begin
      rw [←forall_sets_neq_empty_iff_neq_bot] at adhs,
      simp [this s hs],
      exact mem_sets_of_superset hs subset_closure
    end
  ... ≤ f.lift' (λs:set α, {y | (x, y) ∈ closure (set.prod s s)}) :
    by simp [closure_prod_eq]; exact le_refl _
  ... = (filter.prod f f).lift' (λs:set (α×α), {y | (x, y) ∈ closure s}) :
  begin
    rw [prod_same_eq],
    rw [lift'_lift'_assoc],
    exact monotone_prod monotone_id monotone_id,
    exact monotone_comp (assume s t h x h', closure_mono h h') monotone_preimage
  end
  ... ≤ (𝓤 α).lift' (λs:set (α×α), {y | (x, y) ∈ closure s}) : lift'_mono hf.right (le_refl _)
  ... = ((𝓤 α).lift' closure).lift' (λs:set (α×α), {y | (x, y) ∈ s}) :
  begin
    rw [lift'_lift'_assoc],
    exact assume s t h, closure_mono h,
    exact monotone_preimage
  end
  ... = (𝓤 α).lift' (λs:set (α×α), {y | (x, y) ∈ s}) :
    by rw [←uniformity_eq_uniformity_closure]
  ... = nhds x :
    by rw [nhds_eq_uniformity]

lemma le_nhds_iff_adhp_of_cauchy {f : filter α} {x : α} (hf : cauchy f) :
  f ≤ nhds x ↔ f ⊓ nhds x ≠ ⊥ :=
⟨assume h, (inf_of_le_left h).symm ▸ hf.left,
le_nhds_of_cauchy_adhp hf⟩

lemma cauchy_map [uniform_space β] {f : filter α} {m : α → β}
  (hm : uniform_continuous m) (hf : cauchy f) : cauchy (map m f) :=
⟨have f ≠ ⊥, from hf.left, by simp; assumption,
  calc filter.prod (map m f) (map m f) =
          map (λp:α×α, (m p.1, m p.2)) (filter.prod f f) : filter.prod_map_map_eq
    ... ≤ map (λp:α×α, (m p.1, m p.2)) (𝓤 α) : map_mono hf.right
    ... ≤ 𝓤 β : hm⟩

lemma cauchy_comap [uniform_space β] {f : filter β} {m : α → β}
  (hm : comap (λp:α×α, (m p.1, m p.2)) (𝓤 β) ≤ 𝓤 α)
  (hf : cauchy f) (hb : comap m f ≠ ⊥) : cauchy (comap m f) :=
⟨hb,
  calc filter.prod (comap m f) (comap m f) =
          comap (λp:α×α, (m p.1, m p.2)) (filter.prod f f) : filter.prod_comap_comap_eq
    ... ≤ comap (λp:α×α, (m p.1, m p.2)) (𝓤 β) : comap_mono hf.right
    ... ≤ 𝓤 α : hm⟩

/-- Cauchy sequences. Usually defined on ℕ, but often it is also useful to say that a function
defined on ℝ is Cauchy at +∞ to deduce convergence. Therefore, we define it in a type class that
is general enough to cover both ℕ and ℝ, which are the main motivating examples. -/
def cauchy_seq [inhabited β] [semilattice_sup β] (u : β → α) := cauchy (at_top.map u)

lemma cauchy_seq_iff_prod_map [inhabited β] [semilattice_sup β] {u : β → α} :
  cauchy_seq u ↔ map (prod.map u u) at_top ≤ 𝓤 α :=
iff.trans (and_iff_right (map_ne_bot at_top_ne_bot)) (prod_map_at_top_eq u u ▸ iff.rfl)

/-- A complete space is defined here using uniformities. A uniform space
  is complete if every Cauchy filter converges. -/
class complete_space (α : Type u) [uniform_space α] : Prop :=
(complete : ∀{f:filter α}, cauchy f → ∃x, f ≤ nhds x)

lemma complete_univ {α : Type u} [uniform_space α] [complete_space α] :
  is_complete (univ : set α) :=
begin
  assume f hf _,
  rcases complete_space.complete hf with ⟨x, hx⟩,
  exact ⟨x, by simp, hx⟩
end

lemma cauchy_prod [uniform_space β] {f : filter α} {g : filter β} :
  cauchy f → cauchy g → cauchy (filter.prod f g)
| ⟨f_proper, hf⟩ ⟨g_proper, hg⟩ := ⟨filter.prod_neq_bot.2 ⟨f_proper, g_proper⟩,
  let p_α := λp:(α×β)×(α×β), (p.1.1, p.2.1), p_β := λp:(α×β)×(α×β), (p.1.2, p.2.2) in
  suffices (f.prod f).comap p_α ⊓ (g.prod g).comap p_β ≤ (𝓤 α).comap p_α ⊓ (𝓤 β).comap p_β,
    by simpa [uniformity_prod, filter.prod, filter.comap_inf, filter.comap_comap_comp, (∘),
        lattice.inf_assoc, lattice.inf_comm, lattice.inf_left_comm],
  lattice.inf_le_inf (filter.comap_mono hf) (filter.comap_mono hg)⟩

instance complete_space.prod [uniform_space β] [complete_space α] [complete_space β] :
  complete_space (α × β) :=
{ complete := λ f hf,
    let ⟨x1, hx1⟩ := complete_space.complete $ cauchy_map uniform_continuous_fst hf in
    let ⟨x2, hx2⟩ := complete_space.complete $ cauchy_map uniform_continuous_snd hf in
    ⟨(x1, x2), by rw [nhds_prod_eq, filter.prod_def];
      from filter.le_lift (λ s hs, filter.le_lift' $ λ t ht,
        have H1 : prod.fst ⁻¹' s ∈ f.sets := hx1 hs,
        have H2 : prod.snd ⁻¹' t ∈ f.sets := hx2 ht,
        filter.inter_mem_sets H1 H2)⟩ }

/--If `univ` is complete, the space is a complete space -/
lemma complete_space_of_is_complete_univ (h : is_complete (univ : set α)) : complete_space α :=
⟨λ f hf, let ⟨x, _, hx⟩ := h f hf ((@principal_univ α).symm ▸ le_top) in ⟨x, hx⟩⟩

lemma cauchy_iff_exists_le_nhds [uniform_space α] [complete_space α] {l : filter α} (hl : l ≠ ⊥) :
  cauchy l ↔ (∃x, l ≤ nhds x) :=
⟨complete_space.complete, assume ⟨x, hx⟩, cauchy_downwards cauchy_nhds hl hx⟩

lemma cauchy_map_iff_exists_tendsto [uniform_space α] [complete_space α] {l : filter β} {f : β → α}
  (hl : l ≠ ⊥) : cauchy (l.map f) ↔ (∃x, tendsto f l (nhds x)) :=
cauchy_iff_exists_le_nhds (map_ne_bot hl)

/-- A Cauchy sequence in a complete space converges -/
theorem cauchy_seq_tendsto_of_complete [inhabited β] [semilattice_sup β] [complete_space α]
  {u : β → α} (H : cauchy_seq u) : ∃x, tendsto u at_top (nhds x) :=
complete_space.complete H

theorem le_nhds_lim_of_cauchy {α} [uniform_space α] [complete_space α]
  [inhabited α] {f : filter α} (hf : cauchy f) : f ≤ nhds (lim f) :=
lim_spec (complete_space.complete hf)

lemma is_complete_of_is_closed [complete_space α] {s : set α}
  (h : is_closed s) : is_complete s :=
λ f cf fs, let ⟨x, hx⟩ := complete_space.complete cf in
⟨x, is_closed_iff_nhds.mp h x (neq_bot_of_le_neq_bot cf.left (le_inf hx fs)), hx⟩

/-- A set `s` is totally bounded if for every entourage `d` there is a finite
  set of points `t` such that every element of `s` is `d`-near to some element of `t`. -/
def totally_bounded (s : set α) : Prop :=
∀d ∈ 𝓤 α, ∃t : set α, finite t ∧ s ⊆ (⋃y∈t, {x | (x,y) ∈ d})

theorem totally_bounded_iff_subset {s : set α} : totally_bounded s ↔
  ∀d ∈ 𝓤 α, ∃t ⊆ s, finite t ∧ s ⊆ (⋃y∈t, {x | (x,y) ∈ d}) :=
⟨λ H d hd, begin
  rcases comp_symm_of_uniformity hd with ⟨r, hr, rs, rd⟩,
  rcases H r hr with ⟨k, fk, ks⟩,
  let u := {y ∈ k | ∃ x, x ∈ s ∧ (x, y) ∈ r},
  let f : u → α := λ x, classical.some x.2.2,
  have : ∀ x : u, f x ∈ s ∧ (f x, x.1) ∈ r := λ x, classical.some_spec x.2.2,
  refine ⟨range f, _, _, _⟩,
  { exact range_subset_iff.2 (λ x, (this x).1) },
  { have : finite u := finite_subset fk (λ x h, h.1),
    exact ⟨@set.fintype_range _ _ _ _ this.fintype⟩ },
  { intros x xs,
    have := ks xs, simp at this,
    rcases this with ⟨y, hy, xy⟩,
    let z : coe_sort u := ⟨y, hy, x, xs, xy⟩,
    exact mem_bUnion_iff.2 ⟨_, ⟨z, rfl⟩, rd $ mem_comp_rel.2 ⟨_, xy, rs (this z).2⟩⟩ }
end,
λ H d hd, let ⟨t, _, ht⟩ := H d hd in ⟨t, ht⟩⟩

lemma totally_bounded_subset [uniform_space α] {s₁ s₂ : set α} (hs : s₁ ⊆ s₂)
  (h : totally_bounded s₂) : totally_bounded s₁ :=
assume d hd, let ⟨t, ht₁, ht₂⟩ := h d hd in ⟨t, ht₁, subset.trans hs ht₂⟩

lemma totally_bounded_empty [uniform_space α] : totally_bounded (∅ : set α) :=
λ d hd, ⟨∅, finite_empty, empty_subset _⟩

lemma totally_bounded_closure [uniform_space α] {s : set α} (h : totally_bounded s) :
  totally_bounded (closure s) :=
assume t ht,
let ⟨t', ht', hct', htt'⟩ := mem_uniformity_is_closed ht, ⟨c, hcf, hc⟩ := h t' ht' in
⟨c, hcf,
  calc closure s ⊆ closure (⋃ (y : α) (H : y ∈ c), {x : α | (x, y) ∈ t'}) : closure_mono hc
    ... = _ : closure_eq_of_is_closed $ is_closed_Union hcf $ assume i hi,
      continuous_iff_is_closed.mp (continuous_id.prod_mk continuous_const) _ hct'
    ... ⊆ _ : bUnion_subset $ assume i hi, subset.trans (assume x, @htt' (x, i))
      (subset_bUnion_of_mem hi)⟩

lemma totally_bounded_image [uniform_space α] [uniform_space β] {f : α → β} {s : set α}
  (hf : uniform_continuous f) (hs : totally_bounded s) : totally_bounded (f '' s) :=
assume t ht,
have {p:α×α | (f p.1, f p.2) ∈ t} ∈ 𝓤 α,
  from hf ht,
let ⟨c, hfc, hct⟩ := hs _ this in
⟨f '' c, finite_image f hfc,
  begin
    simp [image_subset_iff],
    simp [subset_def] at hct,
    intros x hx, simp [-mem_image],
    exact let ⟨i, hi, ht⟩ := hct x hx in ⟨f i, mem_image_of_mem f hi, ht⟩
  end⟩

lemma cauchy_of_totally_bounded_of_ultrafilter {s : set α} {f : filter α}
  (hs : totally_bounded s) (hf : is_ultrafilter f) (h : f ≤ principal s) : cauchy f :=
⟨hf.left, assume t ht,
  let ⟨t', ht'₁, ht'_symm, ht'_t⟩ := comp_symm_of_uniformity ht in
  let ⟨i, hi, hs_union⟩ := hs t' ht'₁ in
  have (⋃y∈i, {x | (x,y) ∈ t'}) ∈ f.sets,
    from mem_sets_of_superset (le_principal_iff.mp h) hs_union,
  have ∃y∈i, {x | (x,y) ∈ t'} ∈ f.sets,
    from mem_of_finite_Union_ultrafilter hf hi this,
  let ⟨y, hy, hif⟩ := this in
  have set.prod {x | (x,y) ∈ t'} {x | (x,y) ∈ t'} ⊆ comp_rel t' t',
    from assume ⟨x₁, x₂⟩ ⟨(h₁ : (x₁, y) ∈ t'), (h₂ : (x₂, y) ∈ t')⟩,
      ⟨y, h₁, ht'_symm h₂⟩,
  (filter.prod f f).sets_of_superset (prod_mem_prod hif hif) (subset.trans this ht'_t)⟩

lemma totally_bounded_iff_filter {s : set α} :
  totally_bounded s ↔ (∀f, f ≠ ⊥ → f ≤ principal s → ∃c ≤ f, cauchy c) :=
⟨assume : totally_bounded s, assume f hf hs,
  ⟨ultrafilter_of f, ultrafilter_of_le,
    cauchy_of_totally_bounded_of_ultrafilter this
      (ultrafilter_ultrafilter_of hf) (le_trans ultrafilter_of_le hs)⟩,

  assume h : ∀f, f ≠ ⊥ → f ≤ principal s → ∃c ≤ f, cauchy c, assume d hd,
  classical.by_contradiction $ assume hs,
  have hd_cover : ∀{t:set α}, finite t → ¬ s ⊆ (⋃y∈t, {x | (x,y) ∈ d}),
    by simpa using hs,
  let
    f := ⨅t:{t : set α // finite t}, principal (s \ (⋃y∈t.val, {x | (x,y) ∈ d})),
    ⟨a, ha⟩ := @exists_mem_of_ne_empty α s
      (assume h, hd_cover finite_empty $ h.symm ▸ empty_subset _)
  in
  have f ≠ ⊥,
    from infi_neq_bot_of_directed ⟨a⟩
      (assume ⟨t₁, ht₁⟩ ⟨t₂, ht₂⟩, ⟨⟨t₁ ∪ t₂, finite_union ht₁ ht₂⟩,
        principal_mono.mpr $ diff_subset_diff_right $ Union_subset_Union $
          assume t, Union_subset_Union_const or.inl,
        principal_mono.mpr $ diff_subset_diff_right $ Union_subset_Union $
          assume t, Union_subset_Union_const or.inr⟩)
      (assume ⟨t, ht⟩, by simp [diff_eq_empty]; exact hd_cover ht),
  have f ≤ principal s, from infi_le_of_le ⟨∅, finite_empty⟩ $ by simp; exact subset.refl s,
  let
    ⟨c, (hc₁ : c ≤ f), (hc₂ : cauchy c)⟩ := h f ‹f ≠ ⊥› this,
    ⟨m, hm, (hmd : set.prod m m ⊆ d)⟩ := (@mem_prod_same_iff α c d).mp $ hc₂.right hd
  in
  have c ≤ principal s, from le_trans ‹c ≤ f› this,
  have m ∩ s ∈ c.sets, from inter_mem_sets hm $ le_principal_iff.mp this,
  let ⟨y, hym, hys⟩ := inhabited_of_mem_sets hc₂.left this in
  let ys := (⋃y'∈({y}:set α), {x | (x, y') ∈ d}) in
  have m ⊆ ys,
    from assume y' hy',
      show  y' ∈ (⋃y'∈({y}:set α), {x | (x, y') ∈ d}),
        by simp; exact @hmd (y', y) ⟨hy', hym⟩,
  have c ≤ principal (s - ys),
    from le_trans hc₁ $ infi_le_of_le ⟨{y}, finite_singleton _⟩ $ le_refl _,
  have (s - ys) ∩ (m ∩ s) ∈ c.sets,
    from inter_mem_sets (le_principal_iff.mp this) ‹m ∩ s ∈ c.sets›,
  have ∅ ∈ c.sets,
    from c.sets_of_superset this $ assume x ⟨⟨hxs, hxys⟩, hxm, _⟩, hxys $ ‹m ⊆ ys› hxm,
  hc₂.left $ empty_in_sets_eq_bot.mp this⟩

lemma totally_bounded_iff_ultrafilter {s : set α} :
  totally_bounded s ↔ (∀f, is_ultrafilter f → f ≤ principal s → cauchy f) :=
⟨assume hs f, cauchy_of_totally_bounded_of_ultrafilter hs,
  assume h, totally_bounded_iff_filter.mpr $ assume f hf hfs,
  have cauchy (ultrafilter_of f),
    from h (ultrafilter_of f) (ultrafilter_ultrafilter_of hf) (le_trans ultrafilter_of_le hfs),
  ⟨ultrafilter_of f, ultrafilter_of_le, this⟩⟩

lemma compact_iff_totally_bounded_complete {s : set α} :
  compact s ↔ totally_bounded s ∧ is_complete s :=
⟨λ hs, ⟨totally_bounded_iff_ultrafilter.2 (λ f hf1 hf2,
    let ⟨x, xs, fx⟩ := compact_iff_ultrafilter_le_nhds.1 hs f hf1 hf2 in
    cauchy_downwards (cauchy_nhds) (hf1.1) fx),
  λ f fc fs,
    let ⟨a, as, fa⟩ := hs f fc.1 fs in
    ⟨a, as, le_nhds_of_cauchy_adhp fc fa⟩⟩,
λ ⟨ht, hc⟩, compact_iff_ultrafilter_le_nhds.2
  (λf hf hfs, hc _ (totally_bounded_iff_ultrafilter.1 ht _ hf hfs) hfs)⟩

instance complete_of_compact {α : Type u} [uniform_space α] [compact_space α] : complete_space α :=
⟨λf hf, by simpa [principal_univ] using (compact_iff_totally_bounded_complete.1 compact_univ).2 f hf⟩

lemma compact_of_totally_bounded_is_closed [complete_space α] {s : set α}
  (ht : totally_bounded s) (hc : is_closed s) : compact s :=
(@compact_iff_totally_bounded_complete α _ s).2 ⟨ht, is_complete_of_is_closed hc⟩


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import category_theory.abelian.basic
import algebra.homology.exact
import tactic.tfae

/-!
# Exact sequences in abelian categories

In an abelian category, we get several interesting results related to exactness which are not
true in more general settings.

## Main results
* `(f, g)` is exact if and only if `f ≫ g = 0` and `kernel.ι g ≫ cokernel.π f = 0`. This
  characterisation tends to be less cumbersome to work with than the original definition involving
  the comparison map `image f ⟶ kernel g`.
* If `(f, g)` is exact, then `image.ι f` has the universal property of the kernel of `g`.
* `f` is a monomorphism iff `kernel.ι f = 0` iff `exact 0 f`, and `f` is an epimorphism iff
  `cokernel.π = 0` iff `exact f 0`.

-/

universes v u

noncomputable theory

open category_theory
open category_theory.limits
open category_theory.preadditive

variables {C : Type u} [category.{v} C] [abelian C]

namespace category_theory.abelian
variables {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z)

local attribute [instance] has_equalizers_of_has_kernels

/--
In an abelian category, a pair of morphisms `f : X ⟶ Y`, `g : Y ⟶ Z` is exact
iff `image_subobject f = kernel_subobject g`.
-/
theorem exact_iff_image_eq_kernel : exact f g ↔ image_subobject f = kernel_subobject g :=
begin
  split,
  { introI h,
    fapply subobject.eq_of_comm,
    { suffices : is_iso (image_to_kernel _ _ h.w),
      { exactI as_iso (image_to_kernel _ _ h.w), },
      exact is_iso_of_mono_of_epi _, },
    { simp, }, },
  { apply exact_of_image_eq_kernel, },
end

theorem exact_iff : exact f g ↔ f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0 :=
begin
  split,
  { introI h,
    exact ⟨h.1, kernel_comp_cokernel f g⟩ },
  { refine λ h, ⟨h.1, _⟩,
    suffices hl : is_limit
      (kernel_fork.of_ι (image_subobject f).arrow (image_subobject_arrow_comp_eq_zero h.1)),
    { have : image_to_kernel f g h.1 =
        (is_limit.cone_point_unique_up_to_iso hl (limit.is_limit _)).hom ≫
          (kernel_subobject_iso _).inv,
      { ext, simp },
      rw this,
      apply_instance, },
    refine is_limit.of_ι _ _ _ _ _,
    { refine λ W u hu,
        kernel.lift (cokernel.π f) u _ ≫ (image_iso_image f).hom ≫ (image_subobject_iso _).inv,
      rw [←kernel.lift_ι g u hu, category.assoc, h.2, has_zero_morphisms.comp_zero] },
    { tidy },
    { intros, rw [←cancel_mono (image_subobject f).arrow, w], simp, } }
end

theorem exact_iff' {cg : kernel_fork g} (hg : is_limit cg)
  {cf : cokernel_cofork f} (hf : is_colimit cf) : exact f g ↔ f ≫ g = 0 ∧ cg.ι ≫ cf.π = 0 :=
begin
  split,
  { introI h,
    exact ⟨h.1, fork_ι_comp_cofork_π f g cg cf⟩ },
  { rw exact_iff,
    refine λ h, ⟨h.1, _⟩,
    apply zero_of_epi_comp (is_limit.cone_point_unique_up_to_iso hg (limit.is_limit _)).hom,
    apply zero_of_comp_mono
      (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit _) hf).hom,
    simp [h.2] }
end

theorem exact_tfae :
  tfae [exact f g,
        f ≫ g = 0 ∧ kernel.ι g ≫ cokernel.π f = 0,
        image_subobject f = kernel_subobject g] :=
begin
  tfae_have : 1 ↔ 2, { apply exact_iff },
  tfae_have : 1 ↔ 3, { apply exact_iff_image_eq_kernel },
  tfae_finish
end

/-- If `(f, g)` is exact, then `images.image.ι f` is a kernel of `g`. -/
def is_limit_image [h : exact f g] :
  is_limit
    (kernel_fork.of_ι (images.image.ι f) (images.image_ι_comp_eq_zero h.1) : kernel_fork g) :=
begin
  rw exact_iff at h,
  refine is_limit.of_ι _ _ _ _ _,
  { refine λ W u hu, kernel.lift (cokernel.π f) u _,
    rw [←kernel.lift_ι g u hu, category.assoc, h.2, has_zero_morphisms.comp_zero] },
  tidy
end

/-- If `(f, g)` is exact, then `image.ι f` is a kernel of `g`. -/
def is_limit_image' [h : exact f g] :
  is_limit (kernel_fork.of_ι (image.ι f) (image_ι_comp_eq_zero h.1)) :=
is_kernel.iso_kernel _ _ (is_limit_image f g) (image_iso_image f).symm $ is_image.lift_fac _ _

/-- If `(f, g)` is exact, then `coimages.coimage.π g` is a cokernel of `f`. -/
def is_colimit_coimage [h : exact f g] : is_colimit (cokernel_cofork.of_π (coimages.coimage.π g)
  (coimages.comp_coimage_π_eq_zero h.1) : cokernel_cofork f) :=
begin
  rw exact_iff at h,
  refine is_colimit.of_π _ _ _ _ _,
  { refine λ W u hu, cokernel.desc (kernel.ι g) u _,
    rw [←cokernel.π_desc f u hu, ←category.assoc, h.2, has_zero_morphisms.zero_comp] },
  tidy
end

/-- If `(f, g)` is exact, then `factor_thru_image g` is a cokernel of `f`. -/
def is_colimit_image [h : exact f g] :
  is_colimit (cokernel_cofork.of_π (factor_thru_image g) (comp_factor_thru_image_eq_zero h.1)) :=
is_cokernel.cokernel_iso _ _ (is_colimit_coimage f g) (coimage_iso_image' g) $
  (cancel_mono (image.ι g)).1 $ by simp

lemma exact_cokernel : exact f (cokernel.π f) :=
by { rw exact_iff, tidy }

instance [exact f g] : mono (cokernel.desc f g (by simp)) :=
suffices h : cokernel.desc f g (by simp) =
  (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit _) (is_colimit_image f g)).hom
    ≫ image.ι g, by { rw h, apply mono_comp },
(cancel_epi (cokernel.π f)).1 $ by simp

section
variables (Z)

lemma tfae_mono : tfae [mono f, kernel.ι f = 0, exact (0 : Z ⟶ X) f] :=
begin
  tfae_have : 3 → 2,
  { introsI, exact kernel_ι_eq_zero_of_exact_zero_left Z },
  tfae_have : 1 → 3,
  { introsI, exact exact_zero_left_of_mono Z },
  tfae_have : 2 → 1,
  { exact mono_of_kernel_ι_eq_zero _ },
  tfae_finish
end

-- Note we've already proved `mono_iff_exact_zero_left : mono f ↔ exact (0 : Z ⟶ X) f`
-- in any preadditive category with kernels and images.

lemma mono_iff_kernel_ι_eq_zero : mono f ↔ kernel.ι f = 0 :=
(tfae_mono X f).out 0 1

lemma tfae_epi : tfae [epi f, cokernel.π f = 0, exact f (0 : Y ⟶ Z)] :=
begin
  tfae_have : 3 → 2,
  { rw exact_iff,
    rintro ⟨-, h⟩,
    exact zero_of_epi_comp _ h },
  tfae_have : 1 → 3,
  { rw exact_iff,
    introI,
    exact ⟨by simp, by simp [cokernel.π_of_epi]⟩ },
  tfae_have : 2 → 1,
  { exact epi_of_cokernel_π_eq_zero _ },
  tfae_finish
end

-- Note we've already proved `epi_iff_exact_zero_right : epi f ↔ exact f (0 : Y ⟶ Z)`
-- in any preadditive category with equalizers and images.

lemma epi_iff_cokernel_π_eq_zero : epi f ↔ cokernel.π f = 0 :=
(tfae_epi X f).out 0 1

end

end category_theory.abelian


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

! This file was ported from Lean 3 source module number_theory.modular_forms.basic
! leanprover-community/mathlib commit fe44cd36149e675eb5dec87acc7e8f1d6568e081
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Complex.UpperHalfPlane.FunctionsBoundedAtInfty
import Mathbin.Analysis.Complex.UpperHalfPlane.Topology
import Mathbin.NumberTheory.ModularForms.SlashInvariantForms

/-!
# Modular forms

This file defines modular forms and proves some basic properties about them.

We begin by defining modular forms and cusp forms as extension of `slash_invariant_forms` then we
define the space of modular forms, cusp forms and prove that the product of two modular forms is a
modular form.
-/

open Complex UpperHalfPlane

open Topology Manifold UpperHalfPlane

noncomputable section

-- mathport name: «expr↑ₘ »
local prefix:1024 "↑ₘ" => @coe _ (Matrix (Fin 2) (Fin 2) _) _

-- mathport name: «exprGL( , )⁺»
local notation "GL(" n ", " R ")" "⁺" => Matrix.gLPos (Fin n) R

-- mathport name: «exprSL( , )»
local notation "SL(" n ", " R ")" => Matrix.SpecialLinearGroup (Fin n) R

section ModularForm

open ModularForm

variable (F : Type _) (Γ : Subgroup SL(2, ℤ)) (k : ℤ)

-- mathport name: «expr ∣[ , ]»
local notation:73 f "∣[" k:0 "," A "]" => SlashAction.map ℂ k A f

/-- These are `slash_invariant_form`'s that are holomophic and bounded at infinity. -/
structure ModularForm extends SlashInvariantForm Γ k where
  holo' : Mdifferentiable 𝓘(ℂ) 𝓘(ℂ) (to_fun : ℍ → ℂ)
  bdd_at_infty' : ∀ A : SL(2, ℤ), IsBoundedAtImInfty (to_fun∣[k,A])
#align modular_form ModularForm

/-- The `slash_invariant_form` associated to a `modular_form`. -/
add_decl_doc ModularForm.toSlashInvariantForm

/-- These are `slash_invariant_form`s that are holomophic and zero at infinity. -/
structure CuspForm extends SlashInvariantForm Γ k where
  holo' : Mdifferentiable 𝓘(ℂ) 𝓘(ℂ) (to_fun : ℍ → ℂ)
  zero_at_infty' : ∀ A : SL(2, ℤ), IsZeroAtImInfty (to_fun∣[k,A])
#align cusp_form CuspForm

/-- The `slash_invariant_form` associated to a `cusp_form`. -/
add_decl_doc CuspForm.toSlashInvariantForm

/-- `modular_form_class F Γ k` says that `F` is a type of bundled functions that extend
`slash_invariant_forms_class` by requiring that the functions be holomorphic and bounded
at infinity. -/
class ModularFormClass extends SlashInvariantFormClass F Γ k where
  holo : ∀ f : F, Mdifferentiable 𝓘(ℂ) 𝓘(ℂ) (f : ℍ → ℂ)
  bdd_at_infty : ∀ (f : F) (A : SL(2, ℤ)), IsBoundedAtImInfty (f∣[k,A])
#align modular_form_class ModularFormClass

/-- `cusp_form_class F Γ k` says that `F` is a type of bundled functions that extend
`slash_invariant_forms_class` by requiring that the functions be holomorphic and zero
at infinity. -/
class CuspFormClass extends SlashInvariantFormClass F Γ k where
  holo : ∀ f : F, Mdifferentiable 𝓘(ℂ) 𝓘(ℂ) (f : ℍ → ℂ)
  zero_at_infty : ∀ (f : F) (A : SL(2, ℤ)), IsZeroAtImInfty (f∣[k,A])
#align cusp_form_class CuspFormClass

instance (priority := 100) ModularFormClass.modularForm : ModularFormClass (ModularForm Γ k) Γ k
    where
  coe := ModularForm.toFun
  coe_injective' f g h := by cases f <;> cases g <;> congr
  slash_action_eq := ModularForm.slash_action_eq'
  holo := ModularForm.holo'
  bdd_at_infty := ModularForm.bdd_at_infty'
#align modular_form_class.modular_form ModularFormClass.modularForm

instance (priority := 100) CuspFormClass.cuspForm : CuspFormClass (CuspForm Γ k) Γ k
    where
  coe := CuspForm.toFun
  coe_injective' f g h := by cases f <;> cases g <;> congr
  slash_action_eq := CuspForm.slash_action_eq'
  holo := CuspForm.holo'
  zero_at_infty := CuspForm.zero_at_infty'
#align cusp_form_class.cusp_form CuspFormClass.cuspForm

variable {F Γ k}

@[simp]
theorem modularForm_toFun_eq_coe {f : ModularForm Γ k} : f.toFun = (f : ℍ → ℂ) :=
  rfl
#align modular_form_to_fun_eq_coe modularForm_toFun_eq_coe

@[simp]
theorem cuspForm_toFun_eq_coe {f : CuspForm Γ k} : f.toFun = (f : ℍ → ℂ) :=
  rfl
#align cusp_form_to_fun_eq_coe cuspForm_toFun_eq_coe

@[ext]
theorem ModularForm.ext {f g : ModularForm Γ k} (h : ∀ x, f x = g x) : f = g :=
  FunLike.ext f g h
#align modular_form.ext ModularForm.ext

@[ext]
theorem CuspForm.ext {f g : CuspForm Γ k} (h : ∀ x, f x = g x) : f = g :=
  FunLike.ext f g h
#align cusp_form.ext CuspForm.ext

/-- Copy of a `modular_form` with a new `to_fun` equal to the old one. Useful to fix
definitional equalities. -/
protected def ModularForm.copy (f : ModularForm Γ k) (f' : ℍ → ℂ) (h : f' = ⇑f) : ModularForm Γ k
    where
  toFun := f'
  slash_action_eq' := h.symm ▸ f.slash_action_eq'
  holo' := h.symm ▸ f.holo'
  bdd_at_infty' A := h.symm ▸ f.bdd_at_infty' A
#align modular_form.copy ModularForm.copy

/-- Copy of a `cusp_form` with a new `to_fun` equal to the old one. Useful to fix
definitional equalities. -/
protected def CuspForm.copy (f : CuspForm Γ k) (f' : ℍ → ℂ) (h : f' = ⇑f) : CuspForm Γ k
    where
  toFun := f'
  slash_action_eq' := h.symm ▸ f.slash_action_eq'
  holo' := h.symm ▸ f.holo'
  zero_at_infty' A := h.symm ▸ f.zero_at_infty' A
#align cusp_form.copy CuspForm.copy

end ModularForm

namespace ModularForm

open SlashInvariantForm

variable {F : Type _} {Γ : Subgroup SL(2, ℤ)} {k : ℤ}

instance hasAdd : Add (ModularForm Γ k) :=
  ⟨fun f g =>
    {
      (f : SlashInvariantForm Γ k) +
        g with
      holo' := f.holo'.add g.holo'
      bdd_at_infty' := fun A => by simpa using (f.bdd_at_infty' A).add (g.bdd_at_infty' A) }⟩
#align modular_form.has_add ModularForm.hasAdd

@[simp]
theorem coe_add (f g : ModularForm Γ k) : ⇑(f + g) = f + g :=
  rfl
#align modular_form.coe_add ModularForm.coe_add

@[simp]
theorem add_apply (f g : ModularForm Γ k) (z : ℍ) : (f + g) z = f z + g z :=
  rfl
#align modular_form.add_apply ModularForm.add_apply

instance hasZero : Zero (ModularForm Γ k) :=
  ⟨{
      (0 :
        SlashInvariantForm Γ
          k) with
      holo' := fun _ => mdifferentiableAt_const 𝓘(ℂ, ℂ) 𝓘(ℂ, ℂ)
      bdd_at_infty' := fun A => by simpa using zero_form_is_bounded_at_im_infty }⟩
#align modular_form.has_zero ModularForm.hasZero

@[simp]
theorem coe_zero : ⇑(0 : ModularForm Γ k) = (0 : ℍ → ℂ) :=
  rfl
#align modular_form.coe_zero ModularForm.coe_zero

@[simp]
theorem zero_apply (z : ℍ) : (0 : ModularForm Γ k) z = 0 :=
  rfl
#align modular_form.zero_apply ModularForm.zero_apply

section

variable {α : Type _} [SMul α ℂ] [IsScalarTower α ℂ ℂ]

instance hasSmul : SMul α (ModularForm Γ k) :=
  ⟨fun c f =>
    { c • (f : SlashInvariantForm Γ k) with
      toFun := c • f
      holo' := by simpa using f.holo'.const_smul (c • (1 : ℂ))
      bdd_at_infty' := fun A => by simpa using (f.bdd_at_infty' A).const_smul_left (c • (1 : ℂ)) }⟩
#align modular_form.has_smul ModularForm.hasSmul

@[simp]
theorem coe_smul (f : ModularForm Γ k) (n : α) : ⇑(n • f) = n • f :=
  rfl
#align modular_form.coe_smul ModularForm.coe_smul

@[simp]
theorem smul_apply (f : ModularForm Γ k) (n : α) (z : ℍ) : (n • f) z = n • f z :=
  rfl
#align modular_form.smul_apply ModularForm.smul_apply

end

instance hasNeg : Neg (ModularForm Γ k) :=
  ⟨fun f =>
    { -(f : SlashInvariantForm Γ k) with
      toFun := -f
      holo' := f.holo'.neg
      bdd_at_infty' := fun A => by simpa using (f.bdd_at_infty' A).neg }⟩
#align modular_form.has_neg ModularForm.hasNeg

@[simp]
theorem coe_neg (f : ModularForm Γ k) : ⇑(-f) = -f :=
  rfl
#align modular_form.coe_neg ModularForm.coe_neg

@[simp]
theorem neg_apply (f : ModularForm Γ k) (z : ℍ) : (-f) z = -f z :=
  rfl
#align modular_form.neg_apply ModularForm.neg_apply

instance hasSub : Sub (ModularForm Γ k) :=
  ⟨fun f g => f + -g⟩
#align modular_form.has_sub ModularForm.hasSub

@[simp]
theorem coe_sub (f g : ModularForm Γ k) : ⇑(f - g) = f - g :=
  rfl
#align modular_form.coe_sub ModularForm.coe_sub

@[simp]
theorem sub_apply (f g : ModularForm Γ k) (z : ℍ) : (f - g) z = f z - g z :=
  rfl
#align modular_form.sub_apply ModularForm.sub_apply

instance : AddCommGroup (ModularForm Γ k) :=
  FunLike.coe_injective.AddCommGroup _ rfl coe_add coe_neg coe_sub coe_smul coe_smul

/-- Additive coercion from `modular_form` to `ℍ → ℂ`. -/
@[simps]
def coeHom : ModularForm Γ k →+ ℍ → ℂ where
  toFun f := f
  map_zero' := coe_zero
  map_add' _ _ := rfl
#align modular_form.coe_hom ModularForm.coeHom

instance : Module ℂ (ModularForm Γ k) :=
  Function.Injective.module ℂ coeHom FunLike.coe_injective fun _ _ => rfl

instance : Inhabited (ModularForm Γ k) :=
  ⟨0⟩

/-- The modular form of weight `k_1 + k_2` given by the product of two modular forms of weights
`k_1` and `k_2`. -/
def mul {k_1 k_2 : ℤ} {Γ : Subgroup SL(2, ℤ)} (f : ModularForm Γ k_1) (g : ModularForm Γ k_2) :
    ModularForm Γ (k_1 + k_2) where
  toFun := f * g
  slash_action_eq' A := by simp_rw [mul_slash_subgroup, ModularFormClass.slash_action_eq]
  holo' := f.holo'.mul g.holo'
  bdd_at_infty' A := by simpa using (f.bdd_at_infty' A).mul (g.bdd_at_infty' A)
#align modular_form.mul ModularForm.mul

@[simp]
theorem mul_coe {k_1 k_2 : ℤ} {Γ : Subgroup SL(2, ℤ)} (f : ModularForm Γ k_1)
    (g : ModularForm Γ k_2) : (f.mul g : ℍ → ℂ) = f * g :=
  rfl
#align modular_form.mul_coe ModularForm.mul_coe

instance : One (ModularForm Γ 0) :=
  ⟨{
      (1 :
        SlashInvariantForm Γ
          0) with
      holo' := fun x => mdifferentiableAt_const 𝓘(ℂ, ℂ) 𝓘(ℂ, ℂ)
      bdd_at_infty' := fun A => by simpa using at_im_infty.const_bounded_at_filter (1 : ℂ) }⟩

@[simp]
theorem one_coe_eq_one : ((1 : ModularForm Γ 0) : ℍ → ℂ) = 1 :=
  rfl
#align modular_form.one_coe_eq_one ModularForm.one_coe_eq_one

end ModularForm

namespace CuspForm

open ModularForm

variable {F : Type _} {Γ : Subgroup SL(2, ℤ)} {k : ℤ}

instance hasAdd : Add (CuspForm Γ k) :=
  ⟨fun f g =>
    { (f : SlashInvariantForm Γ k) + g with
      toFun := f + g
      holo' := f.holo'.add g.holo'
      zero_at_infty' := fun A => by simpa using (f.zero_at_infty' A).add (g.zero_at_infty' A) }⟩
#align cusp_form.has_add CuspForm.hasAdd

@[simp]
theorem coe_add (f g : CuspForm Γ k) : ⇑(f + g) = f + g :=
  rfl
#align cusp_form.coe_add CuspForm.coe_add

@[simp]
theorem add_apply (f g : CuspForm Γ k) (z : ℍ) : (f + g) z = f z + g z :=
  rfl
#align cusp_form.add_apply CuspForm.add_apply

instance hasZero : Zero (CuspForm Γ k) :=
  ⟨{ (0 : SlashInvariantForm Γ k) with
      toFun := 0
      holo' := fun _ => mdifferentiableAt_const 𝓘(ℂ, ℂ) 𝓘(ℂ, ℂ)
      zero_at_infty' := by simpa using Filter.zero_zeroAtFilter _ }⟩
#align cusp_form.has_zero CuspForm.hasZero

@[simp]
theorem coe_zero : ⇑(0 : CuspForm Γ k) = (0 : ℍ → ℂ) :=
  rfl
#align cusp_form.coe_zero CuspForm.coe_zero

@[simp]
theorem zero_apply (z : ℍ) : (0 : CuspForm Γ k) z = 0 :=
  rfl
#align cusp_form.zero_apply CuspForm.zero_apply

section

variable {α : Type _} [SMul α ℂ] [IsScalarTower α ℂ ℂ]

instance hasSmul : SMul α (CuspForm Γ k) :=
  ⟨fun c f =>
    { c • (f : SlashInvariantForm Γ k) with
      toFun := c • f
      holo' := by simpa using f.holo'.const_smul (c • (1 : ℂ))
      zero_at_infty' := fun A => by simpa using (f.zero_at_infty' A).smul (c • (1 : ℂ)) }⟩
#align cusp_form.has_smul CuspForm.hasSmul

@[simp]
theorem coe_smul (f : CuspForm Γ k) (n : α) : ⇑(n • f) = n • f :=
  rfl
#align cusp_form.coe_smul CuspForm.coe_smul

@[simp]
theorem smul_apply (f : CuspForm Γ k) (n : α) {z : ℍ} : (n • f) z = n • f z :=
  rfl
#align cusp_form.smul_apply CuspForm.smul_apply

end

instance hasNeg : Neg (CuspForm Γ k) :=
  ⟨fun f =>
    { -(f : SlashInvariantForm Γ k) with
      toFun := -f
      holo' := f.holo'.neg
      zero_at_infty' := fun A => by simpa using (f.zero_at_infty' A).neg }⟩
#align cusp_form.has_neg CuspForm.hasNeg

@[simp]
theorem coe_neg (f : CuspForm Γ k) : ⇑(-f) = -f :=
  rfl
#align cusp_form.coe_neg CuspForm.coe_neg

@[simp]
theorem neg_apply (f : CuspForm Γ k) (z : ℍ) : (-f) z = -f z :=
  rfl
#align cusp_form.neg_apply CuspForm.neg_apply

instance hasSub : Sub (CuspForm Γ k) :=
  ⟨fun f g => f + -g⟩
#align cusp_form.has_sub CuspForm.hasSub

@[simp]
theorem coe_sub (f g : CuspForm Γ k) : ⇑(f - g) = f - g :=
  rfl
#align cusp_form.coe_sub CuspForm.coe_sub

@[simp]
theorem sub_apply (f g : CuspForm Γ k) (z : ℍ) : (f - g) z = f z - g z :=
  rfl
#align cusp_form.sub_apply CuspForm.sub_apply

instance : AddCommGroup (CuspForm Γ k) :=
  FunLike.coe_injective.AddCommGroup _ rfl coe_add coe_neg coe_sub coe_smul coe_smul

/-- Additive coercion from `cusp_form` to `ℍ → ℂ`. -/
@[simps]
def coeHom : CuspForm Γ k →+ ℍ → ℂ where
  toFun f := f
  map_zero' := CuspForm.coe_zero
  map_add' _ _ := rfl
#align cusp_form.coe_hom CuspForm.coeHom

instance : Module ℂ (CuspForm Γ k) :=
  Function.Injective.module ℂ coeHom FunLike.coe_injective fun _ _ => rfl

instance : Inhabited (CuspForm Γ k) :=
  ⟨0⟩

instance (priority := 99) [CuspFormClass F Γ k] : ModularFormClass F Γ k
    where
  coe := FunLike.coe
  coe_injective' := FunLike.coe_injective'
  slash_action_eq := CuspFormClass.slash_action_eq
  holo := CuspFormClass.holo
  bdd_at_infty _ _ := (CuspFormClass.zero_at_infty _ _).BoundedAtFilter

end CuspForm


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import data.vector
import data.list.nodup
import data.list.of_fn
import control.applicative
/-!
# Additional theorems about the `vector` type

This file introduces the infix notation `::ᵥ` for `vector.cons`.
-/
universes u
variables {n : ℕ}

namespace vector
variables {α : Type*}

infixr `::ᵥ`:67  := vector.cons

attribute [simp] head_cons tail_cons

instance [inhabited α] : inhabited (vector α n) :=
⟨of_fn (λ _, default α)⟩

theorem to_list_injective : function.injective (@to_list α n) :=
subtype.val_injective

/-- Two `v w : vector α n` are equal iff they are equal at every single index. -/
@[ext] theorem ext : ∀ {v w : vector α n}
  (h : ∀ m : fin n, vector.nth v m = vector.nth w m), v = w
| ⟨v, hv⟩ ⟨w, hw⟩ h := subtype.eq (list.ext_le (by rw [hv, hw])
  (λ m hm hn, h ⟨m, hv ▸ hm⟩))

/-- The empty `vector` is a `subsingleton`. -/
instance zero_subsingleton : subsingleton (vector α 0) :=
⟨λ _ _, vector.ext (λ m, fin.elim0 m)⟩

@[simp] theorem cons_val (a : α) : ∀ (v : vector α n), (a ::ᵥ v).val = a :: v.val
| ⟨_, _⟩ := rfl

@[simp] theorem cons_head (a : α) : ∀ (v : vector α n), (a ::ᵥ v).head = a
| ⟨_, _⟩ := rfl

@[simp] theorem cons_tail (a : α) : ∀ (v : vector α n), (a ::ᵥ v).tail = v
| ⟨_, _⟩ := rfl

@[simp] theorem to_list_of_fn : ∀ {n} (f : fin n → α), to_list (of_fn f) = list.of_fn f
| 0     f := rfl
| (n+1) f := by rw [of_fn, list.of_fn_succ, to_list_cons, to_list_of_fn]

@[simp] theorem mk_to_list :
  ∀ (v : vector α n) h, (⟨to_list v, h⟩ : vector α n) = v
| ⟨l, h₁⟩ h₂ := rfl

@[simp] lemma to_list_map {β : Type*} (v : vector α n) (f : α → β) : (v.map f).to_list =
  v.to_list.map f := by cases v; refl

theorem nth_eq_nth_le : ∀ (v : vector α n) (i),
  nth v i = v.to_list.nth_le i.1 (by rw to_list_length; exact i.2)
| ⟨l, h⟩ i := rfl

@[simp] lemma nth_map {β : Type*} (v : vector α n) (f : α → β) (i : fin n) :
  (v.map f).nth i = f (v.nth i) :=
by simp [nth_eq_nth_le]

@[simp] theorem nth_of_fn {n} (f : fin n → α) (i) : nth (of_fn f) i = f i :=
by rw [nth_eq_nth_le, ← list.nth_le_of_fn f];
   congr; apply to_list_of_fn

@[simp] theorem of_fn_nth (v : vector α n) : of_fn (nth v) = v :=
begin
  rcases v with ⟨l, rfl⟩,
  apply to_list_injective,
  change nth ⟨l, eq.refl _⟩ with λ i, nth ⟨l, rfl⟩ i,
  simpa only [to_list_of_fn] using list.of_fn_nth_le _
end

@[simp] theorem nth_tail : ∀ (v : vector α n.succ) (i : fin n),
  nth (tail v) i = nth v i.succ
| ⟨a::l, e⟩ ⟨i, h⟩ := by simp [nth_eq_nth_le]; refl

@[simp] theorem tail_val : ∀ (v : vector α n.succ), v.tail.val = v.val.tail
| ⟨a::l, e⟩ := rfl

/-- The `tail` of a `nil` vector is `nil`. -/
@[simp] lemma tail_nil : (@nil α).tail = nil := rfl

/-- The `tail` of a vector made up of one element is `nil`. -/
@[simp] lemma singleton_tail (v : vector α 1) : v.tail = vector.nil :=
by simp only [←cons_head_tail, eq_iff_true_of_subsingleton]

@[simp] theorem tail_of_fn {n : ℕ} (f : fin n.succ → α) :
  tail (of_fn f) = of_fn (λ i, f i.succ) :=
(of_fn_nth _).symm.trans $ by congr; funext i; simp

/-- The list that makes up a `vector` made up of a single element,
retrieved via `to_list`, is equal to the list of that single element. -/
@[simp] lemma to_list_singleton (v : vector α 1) : v.to_list = [v.head] :=
begin
  rw ←v.cons_head_tail,
  simp only [to_list_cons, to_list_nil, cons_head, eq_self_iff_true,
             and_self, singleton_tail]
end

/-- Mapping under `id` does not change a vector. -/
@[simp] lemma map_id {n : ℕ} (v : vector α n) : vector.map id v = v :=
  vector.eq _ _ (by simp only [list.map_id, vector.to_list_map])

lemma mem_iff_nth {a : α} {v : vector α n} : a ∈ v.to_list ↔ ∃ i, v.nth i = a :=
by simp only [list.mem_iff_nth_le, fin.exists_iff, vector.nth_eq_nth_le];
  exact ⟨λ ⟨i, hi, h⟩, ⟨i, by rwa to_list_length at hi, h⟩,
    λ ⟨i, hi, h⟩, ⟨i, by rwa to_list_length, h⟩⟩

lemma nodup_iff_nth_inj {v : vector α n} : v.to_list.nodup ↔ function.injective v.nth :=
begin
  cases v with l hl,
  subst hl,
  simp only [list.nodup_iff_nth_le_inj],
  split,
  { intros h i j hij,
    cases i, cases j, ext, apply h, simpa },
  { intros h i j hi hj hij,
    have := @h ⟨i, hi⟩ ⟨j, hj⟩, simp [nth_eq_nth_le] at *, tauto }
end

@[simp] lemma nth_mem (i : fin n) (v : vector α n) : v.nth i ∈ v.to_list :=
by rw [nth_eq_nth_le]; exact list.nth_le_mem _ _ _

theorem head'_to_list : ∀ (v : vector α n.succ),
  (to_list v).head' = some (head v)
| ⟨a::l, e⟩ := rfl

def reverse (v : vector α n) : vector α n :=
⟨v.to_list.reverse, by simp⟩

/-- The `list` of a vector after a `reverse`, retrieved by `to_list` is equal
to the `list.reverse` after retrieving a vector's `to_list`. -/
lemma to_list_reverse {v : vector α n} : v.reverse.to_list = v.to_list.reverse := rfl

@[simp] theorem nth_zero : ∀ (v : vector α n.succ), nth v 0 = head v
| ⟨a::l, e⟩ := rfl

@[simp] theorem head_of_fn
  {n : ℕ} (f : fin n.succ → α) : head (of_fn f) = f 0 :=
by rw [← nth_zero, nth_of_fn]

@[simp] theorem nth_cons_zero
  (a : α) (v : vector α n) : nth (a ::ᵥ v) 0 = a :=
by simp [nth_zero]

/-- Accessing the `nth` element of a vector made up
of one element `x : α` is `x` itself. -/
@[simp] lemma nth_cons_nil {ix : fin 1}
  (x : α) : nth (x ::ᵥ nil) ix = x :=
by convert nth_cons_zero x nil

@[simp] theorem nth_cons_succ
  (a : α) (v : vector α n) (i : fin n) : nth (a ::ᵥ v) i.succ = nth v i :=
by rw [← nth_tail, tail_cons]

/-- The last element of a `vector`, given that the vector is at least one element. -/
def last (v : vector α (n + 1)) : α := v.nth (fin.last n)

/-- The last element of a `vector`, given that the vector is at least one element. -/
lemma last_def {v : vector α (n + 1)} : v.last = v.nth (fin.last n) := rfl

/-- The `last` element of a vector is the `head` of the `reverse` vector. -/
lemma reverse_nth_zero {v : vector α (n + 1)} : v.reverse.head = v.last :=
begin
  have : 0 = v.to_list.length - 1 - n,
    { simp only [nat.add_succ_sub_one, add_zero, to_list_length, nat.sub_self,
                 list.length_reverse] },
  rw [←nth_zero, last_def, nth_eq_nth_le, nth_eq_nth_le],
  simp_rw [to_list_reverse, fin.val_eq_coe, fin.coe_last, fin.coe_zero, this],
  rw list.nth_le_reverse,
end

section scan

variables {β : Type*}
variables (f : β → α → β) (b : β)
variables (v : vector α n)

/--
Construct a `vector β (n + 1)` from a `vector α n` by scanning `f : β → α → β`
from the "left", that is, from 0 to `fin.last n`, using `b : β` as the starting value.
-/
def scanl : vector β (n + 1) :=
⟨list.scanl f b v.to_list, by rw [list.length_scanl, to_list_length]⟩

/-- Providing an empty vector to `scanl` gives the starting value `b : β`. -/
@[simp] lemma scanl_nil : scanl f b nil = b ::ᵥ nil := rfl

/--
The recursive step of `scanl` splits a vector `x ::ᵥ v : vector α (n + 1)`
into the provided starting value `b : β` and the recursed `scanl`
`f b x : β` as the starting value.

This lemma is the `cons` version of `scanl_nth`.
-/
@[simp] lemma scanl_cons (x : α) : scanl f b (x ::ᵥ v) = b ::ᵥ scanl f (f b x) v :=
by simpa only [scanl, to_list_cons]

/--
The underlying `list` of a `vector` after a `scanl` is the `list.scanl`
of the underlying `list` of the original `vector`.
-/
@[simp] lemma scanl_val : ∀ {v : vector α n}, (scanl f b v).val = list.scanl f b v.val
| ⟨l, hl⟩ := rfl

/--
The `to_list` of a `vector` after a `scanl` is the `list.scanl`
of the `to_list` of the original `vector`.
-/
@[simp] lemma to_list_scanl : (scanl f b v).to_list = list.scanl f b v.to_list := rfl

/--
The recursive step of `scanl` splits a vector made up of a single element
`x ::ᵥ nil : vector α 1` into a `vector` of the provided starting value `b : β`
and the mapped `f b x : β` as the last value.
-/
@[simp] lemma scanl_singleton (v : vector α 1) : scanl f b v = b ::ᵥ f b v.head ::ᵥ nil :=
begin
  rw [←cons_head_tail v],
  simp only [scanl_cons, scanl_nil, cons_head, singleton_tail]
end

/--
The first element of `scanl` of a vector `v : vector α n`,
retrieved via `head`, is the starting value `b : β`.
-/
@[simp] lemma scanl_head : (scanl f b v).head = b :=
begin
  cases n,
  { have : v = nil := by simp only [eq_iff_true_of_subsingleton],
    simp only [this, scanl_nil, cons_head] },
  { rw ←cons_head_tail v,
    simp only [←nth_zero, nth_eq_nth_le, to_list_scanl,
                to_list_cons, list.scanl, fin.val_zero', list.nth_le] }
end

/--
For an index `i : fin n`, the `nth` element of `scanl` of a
vector `v : vector α n` at `i.succ`, is equal to the application
function `f : β → α → β` of the `i.cast_succ` element of
`scanl f b v` and `nth v i`.

This lemma is the `nth` version of `scanl_cons`.
-/
@[simp] lemma scanl_nth (i : fin n) :
  (scanl f b v).nth i.succ = f ((scanl f b v).nth i.cast_succ) (v.nth i) :=
begin
  cases n,
  { exact fin_zero_elim i },
  induction n with n hn generalizing b,
  { have i0 : i = 0 := by simp only [eq_iff_true_of_subsingleton],
    simpa only [scanl_singleton, i0, nth_zero] },
  { rw [←cons_head_tail v, scanl_cons, nth_cons_succ],
    refine fin.cases _ _ i,
    { simp only [nth_zero, scanl_head, fin.cast_succ_zero, cons_head] },
    { intro i',
      simp only [hn, fin.cast_succ_fin_succ, nth_cons_succ] } }
end

end scan

def m_of_fn {m} [monad m] {α : Type u} : ∀ {n}, (fin n → m α) → m (vector α n)
| 0     f := pure nil
| (n+1) f := do a ← f 0, v ← m_of_fn (λi, f i.succ), pure (a ::ᵥ v)

theorem m_of_fn_pure {m} [monad m] [is_lawful_monad m] {α} :
  ∀ {n} (f : fin n → α), @m_of_fn m _ _ _ (λ i, pure (f i)) = pure (of_fn f)
| 0     f := rfl
| (n+1) f := by simp [m_of_fn, @m_of_fn_pure n, of_fn]

def mmap {m} [monad m] {α} {β : Type u} (f : α → m β) :
  ∀ {n}, vector α n → m (vector β n)
| 0 xs := pure nil
| (n+1) xs := do h' ← f xs.head, t' ← @mmap n xs.tail, pure (h' ::ᵥ t')

@[simp] theorem mmap_nil {m} [monad m] {α β} (f : α → m β) :
  mmap f nil = pure nil := rfl

@[simp] theorem mmap_cons {m} [monad m] {α β} (f : α → m β) (a) :
  ∀ {n} (v : vector α n), mmap f (a ::ᵥ v) =
  do h' ← f a, t' ← mmap f v, pure (h' ::ᵥ t')
| _ ⟨l, rfl⟩ := rfl

/-- Define `C v` by induction on `v : vector α (n + 1)`, a vector of
at least one element.
This function has two arguments: `h0` handles the base case on `C nil`,
and `hs` defines the inductive step using `∀ x : α, C v → C (x ::ᵥ v)`. -/
@[elab_as_eliminator] def induction_on
  {α : Type*} {n : ℕ}
  {C : Π {n : ℕ}, vector α n → Sort*}
  (v : vector α (n + 1))
  (h0 : C nil)
  (hs : ∀ {n : ℕ} {x : α} {w : vector α n}, C w → C (x ::ᵥ w)) :
    C v :=
begin
  induction n with n hn,
  { rw ←v.cons_head_tail,
    convert hs h0 },
  { rw ←v.cons_head_tail,
    apply hs,
    apply hn }
end

def to_array : vector α n → array n α
| ⟨xs, h⟩ := cast (by rw h) xs.to_array

section insert_nth
variable {a : α}

def insert_nth (a : α) (i : fin (n+1)) (v : vector α n) : vector α (n+1) :=
⟨v.1.insert_nth i a,
  begin
    rw [list.length_insert_nth, v.2],
    rw [v.2, ← nat.succ_le_succ_iff],
    exact i.2
  end⟩

lemma insert_nth_val {i : fin (n+1)} {v : vector α n} :
  (v.insert_nth a i).val = v.val.insert_nth i.1 a :=
rfl

@[simp] lemma remove_nth_val {i : fin n} :
  ∀{v : vector α n}, (remove_nth i v).val = v.val.remove_nth i
| ⟨l, hl⟩ := rfl

lemma remove_nth_insert_nth {v : vector α n} {i : fin (n+1)} :
  remove_nth i (insert_nth a i v) = v :=
subtype.eq $ list.remove_nth_insert_nth i.1 v.1

lemma remove_nth_insert_nth' {v : vector α (n+1)} :
  ∀{i : fin (n+1)} {j : fin (n+2)},
    remove_nth (j.succ_above i) (insert_nth a j v) = insert_nth a (i.pred_above j) (remove_nth i v)
| ⟨i, hi⟩ ⟨j, hj⟩ :=
  begin
    dsimp [insert_nth, remove_nth, fin.succ_above, fin.pred_above],
    simp only [subtype.mk_eq_mk],
    split_ifs,
    { convert (list.insert_nth_remove_nth_of_ge i (j-1) _ _ _).symm,
      { convert (nat.succ_pred_eq_of_pos _).symm, exact lt_of_le_of_lt (zero_le _) h, },
      { apply remove_nth_val, },
      { convert hi, exact v.2, },
      { exact nat.le_pred_of_lt h, }, },
    { convert (list.insert_nth_remove_nth_of_le i j _ _ _).symm,
      { apply remove_nth_val, },
      { convert hi, exact v.2, },
      { simpa using h, }, }
  end

lemma insert_nth_comm (a b : α) (i j : fin (n+1)) (h : i ≤ j) :
  ∀(v : vector α n),
    (v.insert_nth a i).insert_nth b j.succ = (v.insert_nth b j).insert_nth a i.cast_succ
| ⟨l, hl⟩ :=
  begin
    refine subtype.eq _,
    simp only [insert_nth_val, fin.coe_succ, fin.cast_succ, fin.val_eq_coe, fin.coe_cast_add],
    apply list.insert_nth_comm,
    { assumption },
    { rw hl, exact nat.le_of_succ_le_succ j.2 }
  end

end insert_nth

section update_nth

/-- `update_nth v n a` replaces the `n`th element of `v` with `a` -/
def update_nth (v : vector α n) (i : fin n) (a : α) : vector α n :=
⟨v.1.update_nth i.1 a, by rw [list.update_nth_length, v.2]⟩

@[simp] lemma nth_update_nth_same (v : vector α n) (i : fin n) (a : α) :
  (v.update_nth i a).nth i = a :=
by cases v; cases i; simp [vector.update_nth, vector.nth_eq_nth_le]

lemma nth_update_nth_of_ne {v : vector α n} {i j : fin n} (h : i ≠ j) (a : α) :
  (v.update_nth i a).nth j = v.nth j :=
by cases v; cases i; cases j; simp [vector.update_nth, vector.nth_eq_nth_le,
  list.nth_le_update_nth_of_ne (fin.vne_of_ne h)]

lemma nth_update_nth_eq_if {v : vector α n} {i j : fin n} (a : α) :
  (v.update_nth i a).nth j = if i = j then a else v.nth j :=
by split_ifs; try {simp *}; try {rw nth_update_nth_of_ne}; assumption

end update_nth

end vector

namespace vector

section traverse

variables {F G : Type u → Type u}
variables [applicative F] [applicative G]

open applicative functor
open list (cons) nat

private def traverse_aux {α β : Type u} (f : α → F β) :
  Π (x : list α), F (vector β x.length)
| []      := pure vector.nil
| (x::xs) := vector.cons <$> f x <*> traverse_aux xs

protected def traverse {α β : Type u} (f : α → F β) : vector α n → F (vector β n)
| ⟨v, Hv⟩ := cast (by rw Hv) $ traverse_aux f v

variables [is_lawful_applicative F] [is_lawful_applicative G]
variables {α β γ : Type u}

@[simp] protected lemma traverse_def
  (f : α → F β) (x : α) : ∀ (xs : vector α n),
  (x ::ᵥ xs).traverse f = cons <$> f x <*> xs.traverse f :=
by rintro ⟨xs, rfl⟩; refl

protected lemma id_traverse : ∀ (x : vector α n), x.traverse id.mk = x :=
begin
  rintro ⟨x, rfl⟩, dsimp [vector.traverse, cast],
  induction x with x xs IH, {refl},
  simp! [IH], refl
end

open function

protected lemma comp_traverse (f : β → F γ) (g : α → G β) : ∀ (x : vector α n),
  vector.traverse (comp.mk ∘ functor.map f ∘ g) x =
  comp.mk (vector.traverse f <$> vector.traverse g x) :=
by rintro ⟨x, rfl⟩; dsimp [vector.traverse, cast];
   induction x with x xs; simp! [cast, *] with functor_norm;
   [refl, simp [(∘)]]

protected lemma traverse_eq_map_id {α β} (f : α → β) : ∀ (x : vector α n),
  x.traverse (id.mk ∘ f) = id.mk (map f x) :=
by rintro ⟨x, rfl⟩; simp!;
   induction x; simp! * with functor_norm; refl

variable (η : applicative_transformation F G)

protected lemma naturality {α β : Type*}
  (f : α → F β) : ∀ (x : vector α n),
  η (x.traverse f) = x.traverse (@η _ ∘ f) :=
by rintro ⟨x, rfl⟩; simp! [cast];
   induction x with x xs IH; simp! * with functor_norm

end traverse

instance : traversable.{u} (flip vector n) :=
{ traverse := @vector.traverse n,
  map := λ α β, @vector.map.{u u} α β n }

instance : is_lawful_traversable.{u} (flip vector n) :=
{ id_traverse := @vector.id_traverse n,
  comp_traverse := @vector.comp_traverse n,
  traverse_eq_map_id := @vector.traverse_eq_map_id n,
  naturality := @vector.naturality n,
  id_map := by intros; cases x; simp! [(<$>)],
  comp_map := by intros; cases x; simp! [(<$>)] }

end vector


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl

Hahn decomposition theorem

TODO:
* introduce finite measures (into ℝ≥0)
* show general for signed measures (into ℝ)
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.measure_space
import Mathlib.PostPort

universes u_1

namespace Mathlib

namespace measure_theory

-- suddenly this is necessary?!

theorem hahn_decomposition {α : Type u_1} [measurable_space α] {μ : measure α} {ν : measure α} (hμ : coe_fn μ set.univ < ⊤) (hν : coe_fn ν set.univ < ⊤) : ∃ (s : set α),
  is_measurable s ∧
    (∀ (t : set α), is_measurable t → t ⊆ s → coe_fn ν t ≤ coe_fn μ t) ∧
      ∀ (t : set α), is_measurable t → t ⊆ (sᶜ) → coe_fn μ t ≤ coe_fn ν t := sorry