/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Paul Reichert, Robin Arnez
-/
module

prelude
import Init.Data.List.Lemmas
import Init.Data.Array.DecidableEq
public import Init.Data.Ord.Array
public import Init.Data.BEq

public section

/-!
# Type classes related to `Ord`

This file provides several typeclasses encode properties of an `Ord` instance. For each typeclass,
there is also a variant that does not depend on an `Ord` instance and takes an explicit comparison
function `cmp : α → α → Ordering` instead.
-/

set_option autoImplicit false
set_option linter.missingDocs true

universe u

namespace Std

section Refl

/-- A typeclass for comparison functions `cmp` for which `cmp a a = .eq` for all `a`. -/
class ReflCmp {α : Type u} (cmp : α → α → Ordering) : Prop where
  /-- Comparison is reflexive. -/
  compare_self {a : α} : cmp a a = .eq

namespace ReflCmp

theorem cmp_eq_of_eq {α : Type u} {cmp : α → α → Ordering} [Std.ReflCmp cmp] {a b : α} : a = b → cmp a b = .eq := by
  intro h; subst a; apply compare_self

theorem ne_of_cmp_ne_eq {α : Type u} {cmp : α → α → Ordering} [Std.ReflCmp cmp] {a b : α} :
    cmp a b ≠ .eq → a ≠ b :=
  mt cmp_eq_of_eq

end ReflCmp

/-- A typeclasses for ordered types for which `compare a a = .eq` for all `a`. -/
abbrev ReflOrd (α : Type u) [Ord α] := ReflCmp (compare : α → α → Ordering)

@[simp]
theorem ReflOrd.compare_self {α : Type u} [Ord α] [ReflOrd α] {a : α} : compare a a = .eq :=
    ReflCmp.compare_self

theorem ReflCmp.isLE_rfl {α} {cmp : α → α → Ordering} [ReflCmp cmp] {a : α} :
    (cmp a a).isLE := by
  simp [ReflCmp.compare_self (cmp := cmp)]

@[simp]
theorem ReflOrd.isLE_rfl {α} [Ord α] [ReflOrd α] {a : α} :
    (compare a a).isLE :=
  ReflCmp.isLE_rfl

export ReflOrd (compare_self)

end Refl

section Oriented

/--
A typeclass for functions `α → α → Ordering` which are oriented: flipping the arguments amounts
to applying `Ordering.swap` to the return value.
-/
class OrientedCmp {α : Type u} (cmp : α → α → Ordering) : Prop where
  /-- Swapping the arguments to `cmp` swaps the outcome. -/
  eq_swap {a b : α} : cmp a b = (cmp b a).swap

/--
A typeclass for types with an oriented comparison function: flipping the arguments amounts to
applying `Ordering.swap` to the return value.
-/
abbrev OrientedOrd (α : Type u) [Ord α] := OrientedCmp (compare : α → α → Ordering)

variable {α : Type u} {cmp : α → α → Ordering}

theorem OrientedOrd.eq_swap [Ord α] [OrientedOrd α] {a b : α} :
    compare a b = (compare b a).swap := OrientedCmp.eq_swap

instance [OrientedCmp cmp] : ReflCmp cmp where
  compare_self := Ordering.eq_eq_of_eq_swap OrientedCmp.eq_swap

instance OrientedCmp.opposite [OrientedCmp cmp] : OrientedCmp fun a b => cmp b a where
  eq_swap := OrientedCmp.eq_swap (cmp := cmp)

instance OrientedOrd.opposite [Ord α] [OrientedOrd α] : letI := Ord.opposite (α := α) ‹_›; OrientedOrd α :=
  OrientedCmp.opposite (cmp := compare)

theorem OrientedCmp.gt_iff_lt [OrientedCmp cmp] {a b : α} : cmp a b = .gt ↔ cmp b a = .lt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.isGT_eq_isLT [OrientedCmp cmp] {a b : α} : (cmp a b).isGT = (cmp b a).isLT := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b), Ordering.isGT_swap]

theorem OrientedCmp.lt_of_gt [OrientedCmp cmp] {a b : α} : cmp a b = .gt → cmp b a = .lt :=
  OrientedCmp.gt_iff_lt.1

theorem OrientedCmp.gt_of_lt [OrientedCmp cmp] {a b : α} : cmp a b = .lt → cmp b a = .gt :=
  OrientedCmp.gt_iff_lt.2

theorem OrientedCmp.isGE_eq_isLE [OrientedCmp cmp] {a b : α} : (cmp a b).isGE = (cmp b a).isLE := by
  rw [OrientedCmp.eq_swap (cmp := cmp), Ordering.isGE_swap]

theorem OrientedCmp.isGE_iff_isLE [OrientedCmp cmp] {a b : α} : (cmp a b).isGE ↔ (cmp b a).isLE :=
  Bool.coe_iff_coe.mpr isGE_eq_isLE

theorem OrientedCmp.isLE_of_isGE [OrientedCmp cmp] {a b : α} : (cmp b a).isGE → (cmp a b).isLE :=
  OrientedCmp.isGE_iff_isLE.1

theorem OrientedCmp.isGE_of_isLE [OrientedCmp cmp] {a b : α} : (cmp b a).isLE → (cmp a b).isGE :=
  OrientedCmp.isGE_iff_isLE.2

theorem OrientedCmp.eq_comm [OrientedCmp cmp] {a b : α} : cmp a b = .eq ↔ cmp b a = .eq := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> decide

theorem OrientedCmp.eq_symm [OrientedCmp cmp] {a b : α} : cmp a b = .eq → cmp b a = .eq :=
  OrientedCmp.eq_comm.1

theorem OrientedCmp.not_isLE_of_lt [OrientedCmp cmp] {a b : α} :
    cmp a b = .lt → ¬ (cmp b a).isLE := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  simp

theorem OrientedCmp.not_isGE_of_gt [OrientedCmp cmp] {a b : α} :
    cmp a b = .gt → ¬ (cmp b a).isGE := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  simp

theorem OrientedCmp.not_lt_of_isLE [OrientedCmp cmp] {a b : α} :
    (cmp a b).isLE → cmp b a ≠ .lt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.not_gt_of_isGE [OrientedCmp cmp] {a b : α} :
    (cmp a b).isGE → cmp b a ≠ .gt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.not_lt_of_lt [OrientedCmp cmp] {a b : α} :
    cmp a b = .lt → cmp b a ≠ .lt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.not_gt_of_gt [OrientedCmp cmp] {a b : α} :
    cmp a b = .gt → cmp b a ≠ .gt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.lt_of_not_isLE [OrientedCmp cmp] {a b : α} :
    ¬ (cmp a b).isLE → cmp b a = .lt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.gt_of_not_isGE [OrientedCmp cmp] {a b : α} :
    ¬ (cmp a b).isGE → cmp b a = .gt := by
  rw [OrientedCmp.eq_swap (cmp := cmp) (a := a) (b := b)]
  cases cmp b a <;> simp

theorem OrientedCmp.isLE_antisymm [OrientedCmp cmp] {a b : α} (h₁ : cmp a b |>.isLE) (h₂ : cmp b a |>.isLE) :
    cmp a b = .eq := by
  rw [OrientedCmp.eq_swap (cmp := cmp)] at h₂
  cases h : cmp a b <;> rw [h] at h₁ h₂ <;> simp at h₁ h₂

theorem OrientedCmp.isGE_antisymm [OrientedCmp cmp] {a b : α} (h₁ : cmp a b |>.isGE) (h₂ : cmp b a |>.isGE) :
    cmp a b = .eq := by
  rw [OrientedCmp.eq_swap (cmp := cmp)] at h₂
  cases h : cmp a b <;> rw [h] at h₁ h₂ <;> simp at h₁ h₂

end Oriented

section Trans

/-- A typeclass for functions `α → α → Ordering` which are transitive. -/
class TransCmp {α : Type u} (cmp : α → α → Ordering) : Prop extends OrientedCmp cmp where
  /-- Transitivity of `cmp`, expressed via `Ordering.isLE`. -/
  isLE_trans {a b c : α} : (cmp a b).isLE → (cmp b c).isLE → (cmp a c).isLE

/-- A typeclass for types with a transitive ordering function. -/
abbrev TransOrd (α : Type u) [Ord α] := TransCmp (compare : α → α → Ordering)

variable {α : Type u} {cmp : α → α → Ordering}

theorem TransOrd.isLE_trans [Ord α] [TransOrd α] {a b c : α} :
    (compare a b).isLE → (compare b c).isLE → (compare a c).isLE :=
  TransCmp.isLE_trans

theorem TransCmp.isGE_trans [TransCmp cmp] {a b c : α} (h₁ : (cmp a b).isGE) (h₂ : (cmp b c).isGE) :
    (cmp a c).isGE := by
  rw [OrientedCmp.isGE_iff_isLE] at *
  exact TransCmp.isLE_trans h₂ h₁

theorem TransOrd.isGE_trans [Ord α] [TransOrd α] {a b c : α} :
    (compare a b).isGE → (compare b c).isGE → (compare a c).isGE :=
  TransCmp.isGE_trans

instance TransCmp.opposite [TransCmp cmp] : TransCmp fun a b => cmp b a where
  isLE_trans := flip TransCmp.isLE_trans

instance TransOrd.opposite [Ord α] [TransOrd α] : letI := Ord.opposite (α := α) ‹_›; TransOrd α :=
  TransCmp.opposite (cmp := compare)

theorem TransCmp.lt_of_lt_of_eq [TransCmp cmp] {a b c : α} (hab : cmp a b = .lt)
    (hbc : cmp b c = .eq) : cmp a c = .lt := by
  apply OrientedCmp.lt_of_not_isLE
  intro hca
  suffices cmp a b ≠ .lt from absurd hab this
  exact OrientedCmp.not_lt_of_isLE (TransCmp.isLE_trans (Ordering.isLE_of_eq_eq hbc) hca)

theorem TransCmp.lt_of_eq_of_lt [TransCmp cmp] {a b c : α} (hab : cmp a b = .eq)
    (hbc : cmp b c = .lt) : cmp a c = .lt := by
  apply OrientedCmp.lt_of_not_isLE
  intro hca
  suffices cmp b c ≠ .lt from absurd hbc this
  exact OrientedCmp.not_lt_of_isLE (TransCmp.isLE_trans hca (Ordering.isLE_of_eq_eq hab))

theorem TransCmp.gt_of_eq_of_gt [TransCmp cmp] {a b c : α} (hab : cmp a b = .eq)
    (hbc : cmp b c = .gt) : cmp a c = .gt := by
  rw [OrientedCmp.gt_iff_lt] at *
  exact TransCmp.lt_of_lt_of_eq hbc (OrientedCmp.eq_symm hab)

theorem TransCmp.gt_of_gt_of_eq [TransCmp cmp] {a b c : α} (hab : cmp a b = .gt)
    (hbc : cmp b c = .eq) : cmp a c = .gt := by
  rw [OrientedCmp.gt_iff_lt] at *
  exact TransCmp.lt_of_eq_of_lt (OrientedCmp.eq_symm hbc) hab

theorem TransCmp.lt_trans [TransCmp cmp] {a b c : α} (hab : cmp a b = .lt) (hbc : cmp b c = .lt) :
    cmp a c = .lt := by
  cases hac : cmp a c
  · rfl
  · suffices cmp a b ≠ .lt from absurd hab this
    exact OrientedCmp.not_lt_of_isLE (TransCmp.isLE_trans (Ordering.isLE_of_eq_lt hbc)
      (Ordering.isLE_of_eq_eq (OrientedCmp.eq_symm hac)))
  · suffices cmp a b ≠ .lt from absurd hab this
    exact OrientedCmp.not_lt_of_isLE (TransCmp.isLE_trans (Ordering.isLE_of_eq_lt hbc)
      (Ordering.isLE_of_eq_lt (OrientedCmp.lt_of_gt hac)))

theorem TransCmp.gt_trans [TransCmp cmp] {a b c : α} (hab : cmp a b = .gt) (hbc : cmp b c = .gt) :
    cmp a c = .gt := by
  rw [OrientedCmp.gt_iff_lt (cmp := cmp)] at *
  exact lt_trans hbc hab

theorem TransCmp.lt_of_lt_of_isLE [TransCmp cmp] {a b c : α} (hab : cmp a b = .lt)
    (hbc : (cmp b c).isLE) : cmp a c = .lt := by
  rw [Ordering.isLE_iff_eq_lt_or_eq_eq] at hbc
  obtain hbc|hbc := hbc
  · exact TransCmp.lt_trans hab hbc
  · exact TransCmp.lt_of_lt_of_eq hab hbc

theorem TransCmp.lt_of_isLE_of_lt [TransCmp cmp] {a b c : α} (hab : (cmp a b).isLE)
    (hbc : cmp b c = .lt) : cmp a c = .lt := by
  rw [Ordering.isLE_iff_eq_lt_or_eq_eq] at hab
  obtain hab|hab := hab
  · exact TransCmp.lt_trans hab hbc
  · exact TransCmp.lt_of_eq_of_lt hab hbc

theorem TransCmp.gt_of_gt_of_isGE [TransCmp cmp] {a b c : α} (hab : cmp a b = .gt)
    (hbc : (cmp b c).isGE) : cmp a c = .gt := by
  rw [OrientedCmp.gt_iff_lt, OrientedCmp.isGE_iff_isLE] at *
  exact TransCmp.lt_of_isLE_of_lt hbc hab

theorem TransCmp.gt_of_gt_of_gt [TransCmp cmp] {a b c : α} (hab : cmp a b = .gt)
    (hbc : cmp b c = .gt) : cmp a c = .gt := by
  apply gt_of_gt_of_isGE hab (Ordering.isGE_of_eq_gt hbc)

theorem TransCmp.gt_of_isGE_of_gt [TransCmp cmp] {a b c : α} (hab : (cmp a b).isGE)
    (hbc : cmp b c = .gt) : cmp a c = .gt := by
  rw [OrientedCmp.gt_iff_lt, OrientedCmp.isGE_iff_isLE] at *
  exact TransCmp.lt_of_lt_of_isLE hbc hab

theorem TransCmp.eq_trans [TransCmp cmp] {a b c : α} (hab : cmp a b = .eq)
    (hbc : cmp b c = .eq) : cmp a c = .eq := by
  apply Ordering.eq_eq_of_isLE_of_isLE_swap
  · exact TransCmp.isLE_trans (Ordering.isLE_of_eq_eq hab) (Ordering.isLE_of_eq_eq hbc)
  · rw [← OrientedCmp.eq_swap]
    exact TransCmp.isLE_trans (Ordering.isLE_of_eq_eq (OrientedCmp.eq_symm hbc))
      (Ordering.isLE_of_eq_eq (OrientedCmp.eq_symm hab))

theorem TransCmp.congr_left [TransCmp cmp] {a b c : α} (hab : cmp a b = .eq) :
    cmp a c = cmp b c := by
  cases hbc : cmp b c with
  | lt => exact TransCmp.lt_of_eq_of_lt hab hbc
  | eq => exact TransCmp.eq_trans hab hbc
  | gt =>
      exact OrientedCmp.gt_of_lt
        (TransCmp.lt_of_lt_of_eq (OrientedCmp.lt_of_gt hbc) (OrientedCmp.eq_symm hab))

theorem TransCmp.congr_right [TransCmp cmp] {a b c : α} (hbc : cmp b c = .eq) :
    cmp a b = cmp a c := by
  cases hab : cmp a b with
  | lt => exact TransCmp.lt_of_lt_of_eq hab hbc |>.symm
  | eq => exact TransCmp.eq_trans hab hbc |>.symm
  | gt =>
    exact OrientedCmp.gt_of_lt
      (TransCmp.lt_of_eq_of_lt (OrientedCmp.eq_symm hbc) (OrientedCmp.lt_of_gt hab)) |>.symm

end Trans

section LawfulEq

/--
A typeclass for comparison functions satisfying `cmp a b = .eq` if and only if the logical equality
`a = b` holds.

This typeclass distinguishes itself from `LawfulBEqCmp` by using logical equality (`=`) instead of
boolean equality (`==`).
-/
class LawfulEqCmp {α : Type u} (cmp : α → α → Ordering) : Prop extends ReflCmp cmp where
  /-- If two values compare equal, then they are logically equal. -/
  eq_of_compare {a b : α} : cmp a b = .eq → a = b

/--
A typeclass for types with a comparison function that satisfies `compare a b = .eq` if and only if
the logical equality `a = b` holds.

This typeclass distinguishes itself from `LawfulBEqOrd` by using logical equality (`=`) instead of
boolean equality (`==`).
-/
abbrev LawfulEqOrd (α : Type u) [Ord α] := LawfulEqCmp (compare : α → α → Ordering)

variable {α : Type u} {cmp : α → α → Ordering} [LawfulEqCmp cmp]

theorem LawfulEqOrd.eq_of_compare [Ord α] [LawfulEqOrd α] {a b : α} :
    compare a b = .eq → a = b := LawfulEqCmp.eq_of_compare

instance LawfulEqCmp.opposite [OrientedCmp cmp] [LawfulEqCmp cmp] :
    LawfulEqCmp (fun a b => cmp b a) where
  eq_of_compare := by
    simp only [OrientedCmp.eq_comm (cmp := cmp)]
    exact LawfulEqCmp.eq_of_compare

instance LawfulEqOrd.opposite [Ord α] [OrientedOrd α] [LawfulEqOrd α] :
    letI := Ord.opposite (α := α) ‹_›; LawfulEqOrd α :=
  LawfulEqCmp.opposite (cmp := compare)

@[simp]
theorem LawfulEqCmp.compare_eq_iff_eq {a b : α} : cmp a b = .eq ↔ a = b :=
  ⟨LawfulEqCmp.eq_of_compare, fun h => h ▸ ReflCmp.compare_self⟩

theorem LawfulEqCmp.compare_beq_iff_eq {a b : α} : cmp a b == .eq ↔ a = b :=
  beq_iff_eq.trans compare_eq_iff_eq

/-- The corresponding lemma for `LawfulEqCmp` is `LawfulEqCmp.compare_eq_iff_eq` -/
@[simp, grind =]
theorem LawfulEqOrd.compare_eq_iff_eq [Ord α] [LawfulEqOrd α] {a b : α} :
    compare a b = .eq ↔ a = b :=
  LawfulEqCmp.compare_eq_iff_eq

/-- The corresponding lemma for `LawfulEqCmp` is `LawfulEqCmp.compare_beq_iff_eq` -/
@[grind =]
theorem LawfulEqOrd.compare_beq_iff_eq [Ord α] [LawfulEqOrd α] {a b : α} :
    compare a b == .eq ↔ a = b :=
  LawfulEqCmp.compare_beq_iff_eq

export LawfulEqOrd (compare_eq_iff_eq compare_beq_iff_eq)

end LawfulEq

section LawfulBEq

/--
A typeclass for comparison functions satisfying `cmp a b = .eq` if and only if the boolean equality
`a == b` holds.

This typeclass distinguishes itself from `LawfulEqCmp` by using boolean equality (`==`) instead of
logical equality (`=`).
-/
class LawfulBEqCmp {α : Type u} [BEq α] (cmp : α → α → Ordering) : Prop where
  /-- If two values compare equal, then they are boolean equal. -/
  compare_eq_iff_beq {a b : α} : cmp a b = .eq ↔ a == b

theorem LawfulBEqCmp.not_compare_eq_iff_beq_eq_false {α : Type u} [BEq α] {cmp}
    [LawfulBEqCmp (α := α) cmp] {a b : α} : ¬ cmp a b = .eq ↔ (a == b) = false := by
  rw [Bool.eq_false_iff, ne_eq, not_congr]
  exact compare_eq_iff_beq

theorem LawfulBEqCmp.compare_beq_eq_beq {α : Type u} [BEq α] {cmp}
    [LawfulBEqCmp (α := α) cmp] {a b : α} : (cmp a b == .eq) = (a == b) := by
  rw [Bool.eq_iff_iff, beq_iff_eq, compare_eq_iff_beq]

theorem LawfulBEqCmp.isEq_compare_eq_beq {α : Type u} [BEq α] {cmp}
    [LawfulBEqCmp (α := α) cmp] {a b : α} : (cmp a b).isEq = (a == b) := by
  rw [Bool.eq_iff_iff, Ordering.isEq_iff_eq_eq, compare_eq_iff_beq]

/--
A typeclass for types with a comparison function that satisfies `compare a b = .eq` if and only if
the boolean equality `a == b` holds.

This typeclass distinguishes itself from `LawfulEqOrd` by using boolean equality (`==`) instead of
logical equality (`=`).
-/
abbrev LawfulBEqOrd (α : Type u) [BEq α] [Ord α] := LawfulBEqCmp (compare : α → α → Ordering)

variable {α : Type u} [BEq α] {cmp : α → α → Ordering}

theorem LawfulBEqOrd.compare_eq_iff_beq {α : Type u} {_ : Ord α} {_ : BEq α}
    [LawfulBEqOrd α] {a b : α} : compare a b = .eq ↔ (a == b) = true :=
  LawfulBEqCmp.compare_eq_iff_beq

theorem LawfulBEqOrd.not_compare_eq_iff_beq_eq_false {α : Type u} {_ : BEq α} {_ : Ord α}
    [LawfulBEqOrd α] {a b : α} : ¬ compare a b = .eq ↔ (a == b) = false :=
  LawfulBEqCmp.not_compare_eq_iff_beq_eq_false

theorem LawfulBEqOrd.compare_beq_eq_beq {α : Type u} {_ : Ord α} {_ : BEq α}
    [LawfulBEqOrd α] {a b : α} : (compare a b == .eq) = (a == b) :=
  LawfulBEqCmp.compare_beq_eq_beq

theorem LawfulBEqOrd.isEq_compare_eq_beq {α : Type u} {_ : Ord α} {_ : BEq α}
    [LawfulBEqOrd α] {a b : α} : (compare a b).isEq = (a == b) :=
  LawfulBEqCmp.isEq_compare_eq_beq

export LawfulBEqOrd (compare_eq_iff_beq not_compare_eq_iff_beq_eq_false
  compare_beq_eq_beq isEq_compare_eq_beq)

instance [LawfulEqCmp cmp] [LawfulBEq α] : LawfulBEqCmp cmp where
  compare_eq_iff_beq := LawfulEqCmp.compare_eq_iff_eq.trans beq_iff_eq.symm

theorem LawfulBEqCmp.reflBEq [inst : LawfulBEqCmp cmp] [ReflCmp cmp] : ReflBEq α where
  rfl := inst.compare_eq_iff_beq.mp ReflCmp.compare_self

theorem LawfulBEqCmp.equivBEq [inst : LawfulBEqCmp cmp] [TransCmp cmp] : EquivBEq α where
  toReflBEq := reflBEq (cmp := cmp)
  symm := by
    simp only [← inst.compare_eq_iff_beq]
    exact OrientedCmp.eq_symm
  trans := by
    simp only [← inst.compare_eq_iff_beq]
    exact TransCmp.eq_trans

instance LawfulBEqOrd.equivBEq [Ord α] [LawfulBEqOrd α] [TransOrd α] : EquivBEq α :=
  LawfulBEqCmp.equivBEq (cmp := compare)

theorem LawfulBEqCmp.lawfulBEq [inst : LawfulBEqCmp cmp] [LawfulEqCmp cmp] : LawfulBEq α where
  toReflBEq := reflBEq (cmp := cmp)
  eq_of_beq := by simp [← inst.compare_eq_iff_beq]

instance LawfulBEqOrd.lawfulBEq [Ord α] [LawfulBEqOrd α] [LawfulEqOrd α] : LawfulBEq α :=
  LawfulBEqCmp.lawfulBEq (cmp := compare)

instance LawfulBEqCmp.lawfulEqCmp [inst : LawfulBEqCmp cmp] [LawfulBEq α] : LawfulEqCmp cmp where
  compare_self := by simp only [compare_eq_iff_beq, beq_self_eq_true, implies_true]
  eq_of_compare := by simp only [compare_eq_iff_beq, beq_iff_eq, imp_self, implies_true]

theorem LawfulBEqOrd.lawfulEqOrd [Ord α] [LawfulBEqOrd α] [LawfulBEq α] : LawfulEqOrd α :=
  LawfulBEqCmp.lawfulEqCmp

instance LawfulBEqCmp.opposite [OrientedCmp cmp] [LawfulBEqCmp cmp] :
    LawfulBEqCmp (fun a b => cmp b a) where
  compare_eq_iff_beq := by
    simp [OrientedCmp.eq_comm (cmp := cmp), LawfulBEqCmp.compare_eq_iff_beq]

instance LawfulBEqOrd.opposite [Ord α] [OrientedOrd α] [LawfulBEqOrd α] :
    letI := Ord.opposite (α := α) ‹_›; LawfulBEqOrd α :=
  LawfulBEqCmp.opposite (cmp := compare)

end LawfulBEq

attribute [local instance] beqOfOrd in
instance {α : Type u} {_ : Ord α} : LawfulBEqOrd α where
  compare_eq_iff_beq {a b} := by simp only [beqOfOrd, Ordering.isEq_iff_eq_eq]

end Std

section Instances

open Std

theorem Std.TransOrd.compareOfLessAndEq_of_lt_trans_of_lt_iff
    {α : Type u} [LT α] [DecidableLT α] [DecidableEq α]
    (lt_trans : ∀ {a b c : α}, a < b → b < c → a < c)
    (h : ∀ x y : α, x < y ↔ ¬ y < x ∧ x ≠ y) :
    TransCmp (fun x y : α => compareOfLessAndEq x y) where
  eq_swap := compareOfLessAndEq_eq_swap_of_lt_iff_not_gt_and_ne h
  isLE_trans {x y z} h₁ h₂ := by
    simp only [compareOfLessAndEq, apply_ite Ordering.isLE,
      Ordering.isLE_lt, Ordering.isLE_eq, Ordering.isLE_gt] at h₁ h₂ ⊢
    simp only [Bool.if_true_left, Bool.or_false, Bool.or_eq_true, decide_eq_true_eq] at h₁ h₂ ⊢
    rcases h₁ with (h₁ | rfl)
    · rcases h₂ with (h₂ | rfl)
      · exact .inl (lt_trans h₁ h₂)
      · exact .inl h₁
    · exact h₂

theorem Std.TransOrd.compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le
    {α : Type u} [LT α] [LE α] [DecidableLT α] [DecidableLE α] [DecidableEq α]
    (antisymm : ∀ {x y : α}, x ≤ y → y ≤ x → x = y)
    (trans : ∀ {x y z : α}, x ≤ y → y ≤ z → x ≤ z) (total : ∀ (x y : α), x ≤ y ∨ y ≤ x)
    (not_le : ∀ {x y : α}, ¬ x ≤ y ↔ y < x) :
    TransCmp (fun x y : α => compareOfLessAndEq x y) := by
  refine compareOfLessAndEq_of_lt_trans_of_lt_iff ?_ ?_
  · intro a b c
    simp only [← not_le]
    intro h₁ h₂ h₃
    replace h₁ := (total _ _).resolve_left h₁
    exact h₂ (trans h₃ h₁)
  · exact lt_iff_not_gt_and_ne_of_antisymm_of_total_of_not_le antisymm total not_le

namespace Bool

instance : TransOrd Bool where
  eq_swap {x y} := by cases x <;> cases y <;> rfl
  isLE_trans {x y z} h₁ h₂ := by cases x <;> cases y <;> cases z <;> trivial

instance : LawfulEqOrd Bool where
  eq_of_compare {x y} := by cases x <;> cases y <;> simp

end Bool

namespace Nat

instance : TransOrd Nat :=
  TransOrd.compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le
    Nat.le_antisymm Nat.le_trans Nat.le_total Nat.not_le

instance : LawfulEqOrd Nat where
  eq_of_compare := compareOfLessAndEq_eq_eq Nat.le_refl Nat.not_le |>.mp

end Nat

namespace Int

instance : TransOrd Int :=
  TransOrd.compareOfLessAndEq_of_antisymm_of_trans_of_total_of_not_le
    Int.le_antisymm Int.le_trans Int.le_total Int.not_le

instance : LawfulEqOrd Int where
  eq_of_compare := compareOfLessAndEq_eq_eq Int.le_refl Int.not_le |>.mp

end Int

namespace Fin

variable (n : Nat)

instance : OrientedOrd (Fin n) where
  eq_swap := OrientedOrd.eq_swap (α := Nat)

instance : TransOrd (Fin n) where
  isLE_trans := TransOrd.isLE_trans (α := Nat)

instance : LawfulEqOrd (Fin n) where
  eq_of_compare h := Fin.eq_of_val_eq <| LawfulEqOrd.eq_of_compare h

end Fin

namespace Option

instance {α} [Ord α] [OrientedOrd α] : OrientedOrd (Option α) where
  eq_swap {a b} := by cases a <;> cases b <;> simp [Ord.compare, ← OrientedOrd.eq_swap]

instance {α} [Ord α] [TransOrd α] : TransOrd (Option α) where
  isLE_trans {a b c} hab hbc := by
    cases a <;> cases b <;> cases c <;> (try simp_all [Ord.compare]; done)
    simp only [Ord.compare] at *
    apply TransOrd.isLE_trans <;> assumption

instance {α} [Ord α] [ReflOrd α] : ReflOrd (Option α) where
  compare_self {a} := by cases a <;> simp [Ord.compare]

instance {α} [Ord α] [LawfulEqOrd α] : LawfulEqOrd (Option α) where
  eq_of_compare {a b} := by
    cases a <;> cases b <;> simp [Ord.compare]

instance {α} [Ord α] [BEq α] [LawfulBEqOrd α] : LawfulBEqOrd (Option α) where
  compare_eq_iff_beq {a b} := by
    cases a <;> cases b <;> simp [Ord.compare, compare_eq_iff_beq]

end Option

namespace Std

section Lex

instance {α} {cmp₁ cmp₂} [ReflCmp cmp₁] [ReflCmp cmp₂] :
    ReflCmp (α := α) (compareLex cmp₁ cmp₂) where
  compare_self {a} := by simp [compareLex, ReflCmp.compare_self]

instance {α} {cmp₁ cmp₂} [OrientedCmp cmp₁] [OrientedCmp cmp₂] :
    OrientedCmp (α := α) (compareLex cmp₁ cmp₂) where
  eq_swap {a b} := by
    rw [compareLex, compareLex, OrientedCmp.eq_swap (cmp := cmp₁) (a := b), Ordering.swap_then,
      Ordering.swap_swap, ← OrientedCmp.eq_swap]

instance {α} {cmp₁ cmp₂} [TransCmp cmp₁] [TransCmp cmp₂] :
    TransCmp (α := α) (compareLex cmp₁ cmp₂) where
  isLE_trans {a b c} hab hbc := by
    simp only [compareLex] at *
    simp only [Ordering.isLE_then_iff_and] at *
    refine ⟨TransCmp.isLE_trans hab.1 hbc.1, ?_⟩
    cases hab.2
    case inl hab' => exact Or.inl <| TransCmp.lt_of_lt_of_isLE hab' hbc.1
    case inr hab' =>
      cases hbc.2
      case inl hbc' => exact Or.inl <| TransCmp.lt_of_isLE_of_lt hab.1 hbc'
      case inr hbc' => exact Or.inr <| TransCmp.isLE_trans hab' hbc'

instance {α β} {f : α → β} [Ord β] [ReflOrd β] :
    ReflCmp (compareOn f) where
  compare_self := ReflOrd.compare_self (α := β)

instance {α β} {f : α → β} [Ord β] [OrientedOrd β] :
    OrientedCmp (compareOn f) where
  eq_swap := OrientedOrd.eq_swap (α := β)

instance {α β} {f : α → β} [Ord β] [TransOrd β] :
    TransCmp (compareOn f) where
  isLE_trans := TransOrd.isLE_trans (α := β)

attribute [local instance] lexOrd

instance {α β} [Ord α] [Ord β] [ReflOrd α] [ReflOrd β] :
    ReflOrd (α × β) :=
  inferInstanceAs <| ReflCmp (compareLex _ _)

instance {α β} [Ord α] [Ord β] [OrientedOrd α] [OrientedOrd β] :
    OrientedOrd (α × β) :=
  inferInstanceAs <| OrientedCmp (compareLex _ _)

instance {α β} [Ord α] [Ord β] [TransOrd α] [TransOrd β] :
    TransOrd (α × β) :=
  inferInstanceAs <| TransCmp (compareLex _ _)

instance {α β} [Ord α] [Ord β] [LawfulEqOrd α] [LawfulEqOrd β] : LawfulEqOrd (α × β) where
  eq_of_compare {a b} h := by
    simp only [lexOrd, compareLex_eq_eq, compareOn] at h
    ext
    · exact LawfulEqOrd.eq_of_compare h.1
    · exact LawfulEqOrd.eq_of_compare h.2

end Lex

end Std

end Instances

namespace List

open Std

variable {α} {cmp : α → α → Ordering}

instance [ReflCmp cmp] : ReflCmp (List.compareLex cmp) where
  compare_self {a} := by
    induction a with
    | nil => rfl
    | cons x xs h =>
      simp [List.compareLex_cons_cons, ReflCmp.compare_self, h]

instance [LawfulEqCmp cmp] : LawfulEqCmp (List.compareLex cmp) where
  eq_of_compare {a b} h := by
    induction a generalizing b with
    | nil => simpa [List.compareLex_nil_left_eq_eq] using h
    | cons x xs ih =>
      cases b
      · simp [List.compareLex_nil_right_eq_eq] at h
      · simp only [List.compareLex_cons_cons, Ordering.then_eq_eq, LawfulEqCmp.compare_eq_iff_eq,
          List.cons.injEq] at *
        exact ⟨h.1, ih h.2⟩

instance [BEq α] [LawfulBEqCmp cmp] : LawfulBEqCmp (List.compareLex cmp) where
  compare_eq_iff_beq {a b} := by
    induction a generalizing b with
    | nil => simp [List.compareLex_nil_left_eq_eq]
    | cons x xs ih =>
      cases b
      · simp [List.compareLex_cons_nil]
      · simp [List.compareLex_cons_cons, Ordering.then_eq_eq, LawfulBEqCmp.compare_eq_iff_beq, ih]

instance [OrientedCmp cmp] : OrientedCmp (List.compareLex cmp) where
  eq_swap {a b} := by
    induction a generalizing b with
    | nil =>
      cases b
      · simp [List.compareLex_nil_nil]
      · simp [List.compareLex_nil_cons, List.compareLex_cons_nil]
    | cons x xs ih =>
      cases b
      · simp [List.compareLex_nil_cons, List.compareLex_cons_nil]
      · simp [OrientedCmp.eq_swap (a := x), List.compareLex_cons_cons, ih, Ordering.swap_then]

instance [TransCmp cmp] : TransCmp (List.compareLex cmp) where
  isLE_trans {a b c} (hab hbc) := by
    induction a generalizing b c with
    | nil => exact List.isLE_compareLex_nil_left
    | cons _ _ ih =>
      cases b <;> cases c <;> (try simp [List.compareLex_cons_nil] at *; done)
      simp only [List.compareLex_cons_cons, Ordering.isLE_then_iff_and] at *
      apply And.intro
      · exact TransCmp.isLE_trans hab.1 hbc.1
      · obtain ⟨hable, (hab | hab)⟩ := hab
        · exact Or.inl <| TransCmp.lt_of_lt_of_isLE hab hbc.1
        · obtain ⟨_, (hbc | hbc)⟩ := hbc
          · exact Or.inl <| TransCmp.lt_of_isLE_of_lt hable hbc
          · exact Or.inr <| ih hab hbc

instance [Ord α] [ReflOrd α] : ReflOrd (List α) :=
  inferInstanceAs <| ReflCmp (List.compareLex compare)

instance [Ord α] [LawfulEqOrd α] : LawfulEqOrd (List α) :=
  inferInstanceAs <| LawfulEqCmp (List.compareLex compare)

instance [Ord α] [BEq α] [LawfulBEqOrd α] : LawfulBEqOrd (List α) :=
  inferInstanceAs <| LawfulBEqCmp (List.compareLex compare)

instance [Ord α] [OrientedOrd α] : OrientedOrd (List α) :=
  inferInstanceAs <| OrientedCmp (List.compareLex compare)

instance [Ord α] [TransOrd α] : TransOrd (List α) :=
  inferInstanceAs <| TransCmp (List.compareLex compare)

end List

namespace Array

open Std

variable {α} {cmp : α → α → Ordering}

instance [ReflCmp cmp] : ReflCmp (Array.compareLex cmp) where
  compare_self {a} := by simp [Array.compareLex_eq_compareLex_toList, ReflCmp.compare_self]

instance [LawfulEqCmp cmp] : LawfulEqCmp (Array.compareLex cmp) where
  eq_of_compare {a b} := by
    simp only [Array.compareLex_eq_compareLex_toList, LawfulEqCmp.compare_eq_iff_eq]
    exact congrArg List.toArray

instance [BEq α] [LawfulBEqCmp cmp] : LawfulBEqCmp (Array.compareLex cmp) where
  compare_eq_iff_beq {a b} := by
    simp only [Array.compareLex_eq_compareLex_toList, BEq.beq, ← Array.isEqv_toList,
      LawfulBEqCmp.compare_eq_iff_beq, List.beq_eq_isEqv]

instance [OrientedCmp cmp] : OrientedCmp (Array.compareLex cmp) where
  eq_swap {a b} := by simp [Array.compareLex_eq_compareLex_toList, ← OrientedCmp.eq_swap]

instance [TransCmp cmp] : TransCmp (Array.compareLex cmp) where
  isLE_trans {a b c} hab hac := by
    simp only [Array.compareLex_eq_compareLex_toList] at *
    exact TransCmp.isLE_trans hab hac

instance [Ord α] [ReflOrd α] : ReflOrd (Array α) :=
  inferInstanceAs <| ReflCmp (Array.compareLex compare)

instance [Ord α] [LawfulEqOrd α] : LawfulEqOrd (Array α) :=
  inferInstanceAs <| LawfulEqCmp (Array.compareLex compare)

instance [Ord α] [BEq α] [LawfulBEqOrd α] : LawfulBEqOrd (Array α) :=
  inferInstanceAs <| LawfulBEqCmp (Array.compareLex compare)

instance [Ord α] [OrientedOrd α] : OrientedOrd (Array α) :=
  inferInstanceAs <| OrientedCmp (Array.compareLex compare)

instance [Ord α] [TransOrd α] : TransOrd (Array α) :=
  inferInstanceAs <| TransCmp (Array.compareLex compare)

end Array
