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

prelude
public import Init.Data.Order.Classes
import Init.Classical

namespace Std

/-!
This module provides utilities for the creation of order-related typeclass instances.
-/

section OfLE

/--
Creates a `Min α` instance from `LE α` and `DecidableLE α` so that `min a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.

Has a `LawfulOrderLeftLeaningMin α` instance.
-/
@[inline]
public def _root_.Min.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Min α where
  min a b := if a ≤ b then a else b

/--
Creates a `Max α` instance from `LE α` and `DecidableLE α` so that `max a b` is either `a` or `b`,
preferring `a` over `b` when in doubt.

Has a `LawfulOrderLeftLeaningMax α` instance.
-/
@[inline]
public def _root_.Max.leftLeaningOfLE (α : Type u) [LE α] [DecidableLE α] : Max α where
  max a b := if b ≤ a then a else b

/--
This instance is only publicly defined in `Init.Data.Order.Lemmas`.
-/
instance {r : α → α → Prop} [Total r] : Refl r where
  refl a := by simpa using Total.total a a

/--
If an `LE α` instance is reflexive and transitive, then it represents a preorder.
-/
public theorem IsPreorder.of_le {α : Type u} [LE α]
    (le_refl : Std.Refl (α := α) (· ≤ ·) := by exact inferInstance)
    (le_trans : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·) := by exact inferInstance) :
    IsPreorder α where
  le_refl := le_refl.refl
  le_trans _ _ _ := le_trans.trans

/--
If an `LE α` instance is transitive and total, then it represents a linear preorder.
-/
public theorem IsLinearPreorder.of_le {α : Type u} [LE α]
    (le_trans : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·) := by exact inferInstance)
    (le_total : Total (α := α) (· ≤ ·) := by exact inferInstance) :
    IsLinearPreorder α where
  toIsPreorder := .of_le
  le_total := le_total.total

/--
If an `LE α` is reflexive, antisymmetric and transitive, then it represents a partial order.
-/
public theorem IsPartialOrder.of_le {α : Type u} [LE α]
    (le_refl : Std.Refl (α := α) (· ≤ ·) := by exact inferInstance)
    (le_antisymm : Std.Antisymm (α := α) (· ≤ ·) := by exact inferInstance)
    (le_trans : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·) := by exact inferInstance) :
    IsPartialOrder α where
  toIsPreorder := .of_le
  le_antisymm := le_antisymm.antisymm

/--
If an `LE α` instance is antisymmetric, transitive and total, then it represents a linear order.
-/
public theorem IsLinearOrder.of_le {α : Type u} [LE α]
    (le_antisymm : Std.Antisymm (α := α) (· ≤ ·) := by exact inferInstance)
    (le_trans : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·) := by exact inferInstance)
    (le_total : Total (α := α) (· ≤ ·) := by exact inferInstance) :
    IsLinearOrder α where
  toIsLinearPreorder := .of_le
  le_antisymm := le_antisymm.antisymm

/--
This lemma derives a `LawfulOrderLT α` instance from a property involving an `LE α` instance.
-/
public theorem LawfulOrderLT.of_le {α : Type u} [LT α] [LE α]
    (lt_iff : ∀ a b : α, a < b ↔ a ≤ b ∧ ¬ b ≤ a) : LawfulOrderLT α where
  lt_iff := lt_iff

/--
This lemma derives a `LawfulOrderBEq α` instance from a property involving an `LE α` instance.
-/
public theorem LawfulOrderBEq.of_le {α : Type u} [BEq α] [LE α]
    (beq_iff : ∀ a b : α, a == b ↔ a ≤ b ∧ b ≤ a) : LawfulOrderBEq α where
  beq_iff_le_and_ge := by simp [beq_iff]

/--
This lemma characterizes in terms of `LE α` when a `Min α` instance "behaves like an infimum
operator".
-/
public theorem LawfulOrderInf.of_le {α : Type u} [Min α] [LE α]
    (le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) : LawfulOrderInf α where
  le_min_iff := le_min_iff

/--
Returns a `LawfulOrderMin α` instance given certain properties.

This lemma derives a `LawfulOrderMin α` instance from two properties involving `LE α` and `Min α`
instances.

The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_le_min_iff {α : Type u} [Min α] [LE α]
    (le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c := by exact LawfulOrderInf.le_min_iff)
    (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b := by exact MinEqOr.min_eq_or) :
    LawfulOrderMin α where
  toLawfulOrderInf := .of_le le_min_iff
  toMinEqOr := ⟨min_eq_or⟩

/--
Returns a `LawfulOrderMin α` instance given that `max a b` returns either `a` or `b` and that
it is less than or equal to both of them. The `≤` relation needs to be transitive.

The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_min_le {α : Type u} [Min α] [LE α]
    [i : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·)]
    (min_le_left : ∀ a b : α, min a b ≤ a) (min_le_right : ∀ a b : α, min a b ≤ b)
    (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b := by exact MinEqOr.min_eq_or) :
    LawfulOrderMin α := by
  apply LawfulOrderMin.of_le_min_iff
  · simp [autoParam]
    intro a b c
    constructor
    · intro h
      exact ⟨i.trans h (min_le_left b c), i.trans h (min_le_right b c)⟩
    · intro h
      cases min_eq_or b c <;> simp [*]
  · exact min_eq_or

/--
This lemma characterizes in terms of `LE α` when a `Max α` instance "behaves like a supremum
operator".
-/
public def LawfulOrderSup.of_le {α : Type u} [Max α] [LE α]
    (max_le_iff : ∀ a b c : α, max a b ≤ c ↔ a ≤ c ∧ b ≤ c) : LawfulOrderSup α where
  max_le_iff := max_le_iff

/--
Returns a `LawfulOrderMax α` instance given certain properties.

This lemma derives a `LawfulOrderMax α` instance from two properties involving `LE α` and `Max α`
instances.

The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_max_le_iff {α : Type u} [Max α] [LE α]
    (max_le_iff : ∀ a b c : α, max a b ≤ c ↔ a ≤ c ∧ b ≤ c := by exact LawfulOrderInf.le_min_iff)
    (max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b := by exact MaxEqOr.max_eq_or) :
    LawfulOrderMax α where
  toLawfulOrderSup := .of_le max_le_iff
  toMaxEqOr := ⟨max_eq_or⟩

/--
Returns a `LawfulOrderMax α` instance given that `max a b` returns either `a` or `b` and that
it is larger than or equal to both of them. The `≤` relation needs to be transitive.

The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public theorem LawfulOrderMax.of_le_max {α : Type u} [Max α] [LE α]
    [i : Trans (α := α) (· ≤ ·) (· ≤ ·) (· ≤ ·)]
    (left_le_max : ∀ a b : α, a ≤ max a b) (right_le_max : ∀ a b : α, b ≤ max a b)
    (max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b := by exact MaxEqOr.max_eq_or) :
    LawfulOrderMax α := by
  apply LawfulOrderMax.of_max_le_iff
  · simp [autoParam]
    intro a b c
    constructor
    · intro h
      exact ⟨i.trans (left_le_max a b) h, i.trans (right_le_max a b) h⟩
    · intro h
      cases max_eq_or a b <;> simp [*]
  · exact max_eq_or

end OfLE

section OfLT

/--
Creates a *total* `LE α` instance from an `LT α` instance.

This only makes sense for asymmetric `LT α` instances (see `Std.Asymm`).
-/
@[inline]
public def _root_.LE.ofLT (α : Type u) [LT α] : LE α where
  le a b := ¬ b < a

/--
The `LE α` instance obtained from an asymmetric `LT α` instance is compatible with said
`LT α` instance.
-/
public instance LawfulOrderLT.of_lt {α : Type u} [LT α] [i : Asymm (α := α) (· < ·)] :
    haveI := LE.ofLT α
    LawfulOrderLT α :=
  letI := LE.ofLT α
  { lt_iff a b := by simpa [LE.ofLT, Classical.not_not] using i.asymm a b }

/--
If an `LT α` instance is asymmetric and its negation is transitive, then `LE.ofLT α` represents a
linear preorder.
-/
public theorem IsLinearPreorder.of_lt {α : Type u} [LT α]
    (lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
    (not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance) :
    haveI := LE.ofLT α
    IsLinearPreorder α :=
  letI := LE.ofLT α
  { le_trans := by simpa [LE.ofLT] using fun a b c hab hbc => not_lt_trans.trans hbc hab
    le_total a b := by
      apply Or.symm
      open Classical in simpa [LE.ofLT, Decidable.imp_iff_not_or] using lt_asymm.asymm a b
    le_refl a := by
      open Classical in simpa [LE.ofLT] using lt_asymm.asymm a a }

/--
If an `LT α` instance is asymmetric and its negation is transitive and antisymmetric, then
`LE.ofLT α` represents a linear order.
-/
public theorem IsLinearOrder.of_lt {α : Type u} [LT α]
    (lt_asymm : Asymm (α := α) (· < ·) := by exact inferInstance)
    (not_lt_trans : Trans (α := α) (¬ · < ·) (¬ · < ·) (¬ · < ·) := by exact inferInstance)
    (not_lt_antisymm : Antisymm (α := α) (¬ · < ·) := by exact inferInstance) :
    haveI := LE.ofLT α
    IsLinearOrder α :=
  letI := LE.ofLT α
  haveI : IsLinearPreorder α := .of_lt
  { le_antisymm := by
      simpa [LE.ofLT] using fun a b hab hba => not_lt_antisymm.antisymm a b hba hab }

/--
This lemma characterizes in terms of `LT α` when a `Min α` instance
"behaves like an infimum operator" with respect to `LE.ofLT α`.
-/
public theorem LawfulOrderInf.of_lt {α : Type u} [Min α] [LT α]
    (min_lt_iff : ∀ a b c : α, min b c < a ↔ b < a ∨ c < a) :
    haveI := LE.ofLT α
    LawfulOrderInf α :=
  letI := LE.ofLT α
  { le_min_iff a b c := by
      open Classical in
      simp only [LE.ofLT, ← Decidable.not_iff_not (a := ¬ min b c < a)]
      simpa [Decidable.imp_iff_not_or] using min_lt_iff a b c }

/--
Derives a `LawfulOrderMin α` instance for `LE.ofLT` from two properties involving
`LT α` and `Min α` instances.

The produced instance entails `LawfulOrderInf α` and `MinEqOr α`.
-/
public theorem LawfulOrderMin.of_lt {α : Type u} [Min α] [LT α]
    (min_lt_iff : ∀ a b c : α, min b c < a ↔ b < a ∨ c < a)
    (min_eq_or : ∀ a b : α, min a b = a ∨ min a b = b) :
    haveI := LE.ofLT α
    LawfulOrderMin α :=
  letI := LE.ofLT α
  { toLawfulOrderInf := .of_lt min_lt_iff
    toMinEqOr := ⟨min_eq_or⟩ }

/--
This lemma characterizes in terms of `LT α` when a `Max α` instance
"behaves like an supremum operator" with respect to `LE.ofLT α`.
-/
public def LawfulOrderSup.of_lt {α : Type u} [Max α] [LT α]
    (lt_max_iff : ∀ a b c : α, c < max a b ↔ c < a ∨ c < b) :
    haveI := LE.ofLT α
    LawfulOrderSup α :=
  letI := LE.ofLT α
  { max_le_iff a b c := by
      open Classical in
      simp only [LE.ofLT, ← Decidable.not_iff_not ( a := ¬ c < max a b)]
      simpa [Decidable.imp_iff_not_or] using lt_max_iff a b c }

/--
Derives a `LawfulOrderMax α` instance for `LE.ofLT` from two properties involving `LT α` and
`Max α` instances.

The produced instance entails `LawfulOrderSup α` and `MaxEqOr α`.
-/
public def LawfulOrderMax.of_lt {α : Type u} [Max α] [LT α]
    (lt_max_iff : ∀ a b c : α, c < max a b ↔ c < a ∨ c < b)
    (max_eq_or : ∀ a b : α, max a b = a ∨ max a b = b) :
    haveI := LE.ofLT α
    LawfulOrderMax α :=
  letI := LE.ofLT α
  { toLawfulOrderSup := .of_lt lt_max_iff
    toMaxEqOr := ⟨max_eq_or⟩ }

end OfLT

end Std
