/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro

! This file was ported from Lean 3 source module order.basic
! leanprover-community/mathlib commit de87d5053a9fe5cbde723172c0fb7e27e7436473
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Prod.Basic
import Mathlib.Data.Subtype
import Mathlib.Tactic.Classical
import Mathlib.Tactic.Convert
import Mathlib.Tactic.Inhabit
import Mathlib.Tactic.SimpRw
import Mathlib.Tactic.Spread
import Mathlib.Tactic.SplitIfs

/-!
# Basic definitions about `≤` and `<`

This file proves basic results about orders, provides extensive dot notation, defines useful order
classes and allows to transfer order instances.

## Type synonyms

* `OrderDual α` : A type synonym reversing the meaning of all inequalities, with notation `αᵒᵈ`.
* `AsLinearOrder α`: A type synonym to promote `PartialOrder α` to `LinearOrder α` using
  `IsTotal α (≤)`.

### Transfering orders

- `Order.Preimage`, `Preorder.lift`: Transfers a (pre)order on `β` to an order on `α`
  using a function `f : α → β`.
- `PartialOrder.lift`, `LinearOrder.lift`: Transfers a partial (resp., linear) order on `β` to a
  partial (resp., linear) order on `α` using an injective function `f`.

### Extra class

* `Sup`: type class for the `⊔` notation
* `Inf`: type class for the `⊓` notation
* `HasCompl`: type class for the `ᶜ` notation
* `DenselyOrdered`: An order with no gap, i.e. for any two elements `a < b` there exists `c` such
  that `a < c < b`.

## Notes

`≤` and `<` are highly favored over `≥` and `>` in mathlib. The reason is that we can formulate all
lemmas using `≤`/`<`, and `rw` has trouble unifying `≤` and `≥`. Hence choosing one direction spares
us useless duplication. This is enforced by a linter. See Note [nolint_ge] for more infos.

Dot notation is particularly useful on `≤` (`LE.le`) and `<` (`LT.lt`). To that end, we
provide many aliases to dot notation-less lemmas. For example, `le_trans` is aliased with
`LE.le.trans` and can be used to construct `hab.trans hbc : a ≤ c` when `hab : a ≤ b`,
`hbc : b ≤ c`, `lt_of_le_of_lt` is aliased as `LE.le.trans_lt` and can be used to construct
`hab.trans hbc : a < c` when `hab : a ≤ b`, `hbc : b < c`.

## TODO

- expand module docs
- automatic construction of dual definitions / theorems

## Tags

preorder, order, partial order, poset, linear order, chain
-/

open Function

universe u v w

variable {ι : Type _} {α : Type u} {β : Type v} {γ : Type w} {π : ι → Type _} {r : α → α → Prop}

section Preorder

variable [Preorder α] {a b c : α}

theorem le_trans' : b ≤ c → a ≤ b → a ≤ c :=
  flip le_trans
#align le_trans' le_trans'

theorem lt_trans' : b < c → a < b → a < c :=
  flip lt_trans
#align lt_trans' lt_trans'

theorem lt_of_le_of_lt' : b ≤ c → a < b → a < c :=
  flip lt_of_lt_of_le
#align lt_of_le_of_lt' lt_of_le_of_lt'

theorem lt_of_lt_of_le' : b < c → a ≤ b → a < c :=
  flip lt_of_le_of_lt
#align lt_of_lt_of_le' lt_of_lt_of_le'

end Preorder

section PartialOrder

variable [PartialOrder α] {a b : α}

theorem ge_antisymm : a ≤ b → b ≤ a → b = a :=
  flip le_antisymm
#align ge_antisymm ge_antisymm

theorem lt_of_le_of_ne' : a ≤ b → b ≠ a → a < b := fun h₁ h₂ ↦ lt_of_le_of_ne h₁ h₂.symm
#align lt_of_le_of_ne' lt_of_le_of_ne'

theorem Ne.lt_of_le : a ≠ b → a ≤ b → a < b :=
  flip lt_of_le_of_ne
#align ne.lt_of_le Ne.lt_of_le

theorem Ne.lt_of_le' : b ≠ a → a ≤ b → a < b :=
  flip lt_of_le_of_ne'
#align ne.lt_of_le' Ne.lt_of_le'

end PartialOrder

attribute [simp] le_refl

attribute [ext] LE

alias le_trans ← LE.le.trans

alias le_trans' ← LE.le.trans'

alias lt_of_le_of_lt ← LE.le.trans_lt

alias lt_of_le_of_lt' ← LE.le.trans_lt'

alias le_antisymm ← LE.le.antisymm

alias ge_antisymm ← LE.le.antisymm'

alias lt_of_le_of_ne ← LE.le.lt_of_ne

alias lt_of_le_of_ne' ← LE.le.lt_of_ne'

alias lt_of_le_not_le ← LE.le.lt_of_not_le

alias lt_or_eq_of_le ← LE.le.lt_or_eq

alias Decidable.lt_or_eq_of_le ← LE.le.lt_or_eq_dec

alias le_of_lt ← LT.lt.le

alias lt_trans ← LT.lt.trans

alias lt_trans' ← LT.lt.trans'

alias lt_of_lt_of_le ← LT.lt.trans_le

alias lt_of_lt_of_le' ← LT.lt.trans_le'

alias ne_of_lt ← LT.lt.ne
#align has_lt.lt.ne LT.lt.ne

alias lt_asymm ← LT.lt.asymm LT.lt.not_lt

alias le_of_eq ← Eq.le
#align eq.le Eq.le

-- Porting note: no `decidable_classical` linter
-- attribute [nolint decidable_classical] LE.le.lt_or_eq_dec

section

variable [Preorder α] {a b c : α}

/-- A version of `le_refl` where the argument is implicit -/
theorem le_rfl : a ≤ a :=
  le_refl a
#align le_rfl le_rfl

@[simp]
theorem lt_self_iff_false (x : α) : x < x ↔ False :=
  ⟨lt_irrefl x, False.elim⟩
#align lt_self_iff_false lt_self_iff_false

theorem le_of_le_of_eq (hab : a ≤ b) (hbc : b = c) : a ≤ c :=
  hab.trans hbc.le
#align le_of_le_of_eq le_of_le_of_eq

theorem le_of_eq_of_le (hab : a = b) (hbc : b ≤ c) : a ≤ c :=
  hab.le.trans hbc
#align le_of_eq_of_le le_of_eq_of_le

theorem lt_of_lt_of_eq (hab : a < b) (hbc : b = c) : a < c :=
  hab.trans_le hbc.le
#align lt_of_lt_of_eq lt_of_lt_of_eq

theorem lt_of_eq_of_lt (hab : a = b) (hbc : b < c) : a < c :=
  hab.le.trans_lt hbc
#align lt_of_eq_of_lt lt_of_eq_of_lt

theorem le_of_le_of_eq' : b ≤ c → a = b → a ≤ c :=
  flip le_of_eq_of_le
#align le_of_le_of_eq' le_of_le_of_eq'

theorem le_of_eq_of_le' : b = c → a ≤ b → a ≤ c :=
  flip le_of_le_of_eq
#align le_of_eq_of_le' le_of_eq_of_le'

theorem lt_of_lt_of_eq' : b < c → a = b → a < c :=
  flip lt_of_eq_of_lt
#align lt_of_lt_of_eq' lt_of_lt_of_eq'

theorem lt_of_eq_of_lt' : b = c → a < b → a < c :=
  flip lt_of_lt_of_eq
#align lt_of_eq_of_lt' lt_of_eq_of_lt'

alias le_of_le_of_eq ← LE.le.trans_eq

alias le_of_le_of_eq' ← LE.le.trans_eq'

alias lt_of_lt_of_eq ← LT.lt.trans_eq

alias lt_of_lt_of_eq' ← LT.lt.trans_eq'

alias le_of_eq_of_le ← Eq.trans_le
#align eq.trans_le Eq.trans_le

alias le_of_eq_of_le' ← Eq.trans_ge
#align eq.trans_ge Eq.trans_ge

alias lt_of_eq_of_lt ← Eq.trans_lt
#align eq.trans_lt Eq.trans_lt

alias lt_of_eq_of_lt' ← Eq.trans_gt
#align eq.trans_gt Eq.trans_gt

end

namespace Eq

variable [Preorder α] {x y z : α}

/-- If `x = y` then `y ≤ x`. Note: this lemma uses `y ≤ x` instead of `x ≥ y`, because `le` is used
almost exclusively in mathlib. -/
protected theorem ge (h : x = y) : y ≤ x :=
  h.symm.le
#align eq.ge Eq.ge

theorem not_lt (h : x = y) : ¬x < y := fun h' ↦ h'.ne h
#align eq.not_lt Eq.not_lt

theorem not_gt (h : x = y) : ¬y < x :=
  h.symm.not_lt
#align eq.not_gt Eq.not_gt

end Eq

namespace LE.le

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
protected theorem ge [LE α] {x y : α} (h : x ≤ y) : y ≥ x :=
  h
#align has_le.le.ge LE.le.ge

section PartialOrder

variable [PartialOrder α] {a b : α}

theorem lt_iff_ne (h : a ≤ b) : a < b ↔ a ≠ b :=
  ⟨fun h ↦ h.ne, h.lt_of_ne⟩
#align has_le.le.lt_iff_ne LE.le.lt_iff_ne

theorem gt_iff_ne (h : a ≤ b) : a < b ↔ b ≠ a :=
  ⟨fun h ↦ h.ne.symm, h.lt_of_ne'⟩
#align has_le.le.gt_iff_ne LE.le.gt_iff_ne

theorem not_lt_iff_eq (h : a ≤ b) : ¬a < b ↔ a = b :=
  h.lt_iff_ne.not_left
#align has_le.le.not_lt_iff_eq LE.le.not_lt_iff_eq

theorem not_gt_iff_eq (h : a ≤ b) : ¬a < b ↔ b = a :=
  h.gt_iff_ne.not_left
#align has_le.le.not_gt_iff_eq LE.le.not_gt_iff_eq

theorem le_iff_eq (h : a ≤ b) : b ≤ a ↔ b = a :=
  ⟨fun h' ↦ h'.antisymm h, Eq.le⟩
#align has_le.le.le_iff_eq LE.le.le_iff_eq

theorem ge_iff_eq (h : a ≤ b) : b ≤ a ↔ a = b :=
  ⟨h.antisymm, Eq.ge⟩
#align has_le.le.ge_iff_eq LE.le.ge_iff_eq

end PartialOrder

theorem lt_or_le [LinearOrder α] {a b : α} (h : a ≤ b) (c : α) : a < c ∨ c ≤ b :=
  ((lt_or_ge a c).imp id) fun hc ↦ le_trans hc h
#align has_le.le.lt_or_le LE.le.lt_or_le

theorem le_or_lt [LinearOrder α] {a b : α} (h : a ≤ b) (c : α) : a ≤ c ∨ c < b :=
  ((le_or_gt a c).imp id) fun hc ↦ lt_of_lt_of_le hc h
#align has_le.le.le_or_lt LE.le.le_or_lt

theorem le_or_le [LinearOrder α] {a b : α} (h : a ≤ b) (c : α) : a ≤ c ∨ c ≤ b :=
  (h.le_or_lt c).elim Or.inl fun h ↦ Or.inr <| le_of_lt h
#align has_le.le.le_or_le LE.le.le_or_le

end LE.le

namespace LT.lt

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
protected theorem gt [LT α] {x y : α} (h : x < y) : y > x :=
  h
#align has_lt.lt.gt LT.lt.gt

protected theorem false [Preorder α] {x : α} : x < x → False :=
  lt_irrefl x
#align has_lt.lt.false LT.lt.false

theorem ne' [Preorder α] {x y : α} (h : x < y) : y ≠ x :=
  h.ne.symm
#align has_lt.lt.ne' LT.lt.ne'

theorem lt_or_lt [LinearOrder α] {x y : α} (h : x < y) (z : α) : x < z ∨ z < y :=
  (lt_or_ge z y).elim Or.inr fun hz ↦ Or.inl <| h.trans_le hz
#align has_lt.lt.lt_or_lt LT.lt.lt_or_lt

end LT.lt

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
protected theorem GE.ge.le [LE α] {x y : α} (h : x ≥ y) : y ≤ x :=
  h
#align ge.le GE.ge.le

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
protected theorem GT.gt.lt [LT α] {x y : α} (h : x > y) : y < x :=
  h
#align gt.lt GT.gt.lt

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
theorem ge_of_eq [Preorder α] {a b : α} (h : a = b) : a ≥ b :=
  h.ge
#align ge_of_eq ge_of_eq

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
@[simp]
theorem ge_iff_le [LE α] {a b : α} : a ≥ b ↔ b ≤ a :=
  Iff.rfl
#align ge_iff_le ge_iff_le

-- see Note [nolint_ge]
-- Porting note: linter not found @[nolint ge_or_gt]
@[simp]
theorem gt_iff_lt [LT α] {a b : α} : a > b ↔ b < a :=
  Iff.rfl
#align gt_iff_lt gt_iff_lt

theorem not_le_of_lt [Preorder α] {a b : α} (h : a < b) : ¬b ≤ a :=
  (le_not_le_of_lt h).right
#align not_le_of_lt not_le_of_lt

alias not_le_of_lt ← LT.lt.not_le

theorem not_lt_of_le [Preorder α] {a b : α} (h : a ≤ b) : ¬b < a := fun hba ↦ hba.not_le h
#align not_lt_of_le not_lt_of_le

alias not_lt_of_le ← LE.le.not_lt

theorem ne_of_not_le [Preorder α] {a b : α} (h : ¬a ≤ b) : a ≠ b := fun hab ↦ h (le_of_eq hab)
#align ne_of_not_le ne_of_not_le

-- See Note [decidable namespace]
protected theorem Decidable.le_iff_eq_or_lt [PartialOrder α] [@DecidableRel α (· ≤ ·)] {a b : α} :
    a ≤ b ↔ a = b ∨ a < b :=
  Decidable.le_iff_lt_or_eq.trans or_comm
#align decidable.le_iff_eq_or_lt Decidable.le_iff_eq_or_lt

theorem le_iff_eq_or_lt [PartialOrder α] {a b : α} : a ≤ b ↔ a = b ∨ a < b :=
  le_iff_lt_or_eq.trans or_comm
#align le_iff_eq_or_lt le_iff_eq_or_lt

theorem lt_iff_le_and_ne [PartialOrder α] {a b : α} : a < b ↔ a ≤ b ∧ a ≠ b :=
  ⟨fun h ↦ ⟨le_of_lt h, ne_of_lt h⟩, fun ⟨h1, h2⟩ ↦ h1.lt_of_ne h2⟩
#align lt_iff_le_and_ne lt_iff_le_and_ne

-- See Note [decidable namespace]
protected theorem Decidable.eq_iff_le_not_lt [PartialOrder α] [@DecidableRel α (· ≤ ·)] {a b : α} :
    a = b ↔ a ≤ b ∧ ¬a < b :=
  ⟨fun h ↦ ⟨h.le, h ▸ lt_irrefl _⟩, fun ⟨h₁, h₂⟩ ↦
    h₁.antisymm <| Decidable.by_contradiction fun h₃ ↦ h₂ (h₁.lt_of_not_le h₃)⟩
#align decidable.eq_iff_le_not_lt Decidable.eq_iff_le_not_lt

theorem eq_iff_le_not_lt [PartialOrder α] {a b : α} : a = b ↔ a ≤ b ∧ ¬a < b :=
  haveI := Classical.dec
  Decidable.eq_iff_le_not_lt
#align eq_iff_le_not_lt eq_iff_le_not_lt

theorem eq_or_lt_of_le [PartialOrder α] {a b : α} (h : a ≤ b) : a = b ∨ a < b :=
  h.lt_or_eq.symm
#align eq_or_lt_of_le eq_or_lt_of_le

theorem eq_or_gt_of_le [PartialOrder α] {a b : α} (h : a ≤ b) : b = a ∨ a < b :=
  h.lt_or_eq.symm.imp Eq.symm id
#align eq_or_gt_of_le eq_or_gt_of_le

theorem gt_or_eq_of_le [PartialOrder α] {a b : α} (h : a ≤ b) : a < b ∨ b = a :=
  (eq_or_gt_of_le h).symm
#align gt_or_eq_of_le gt_or_eq_of_le

alias Decidable.eq_or_lt_of_le ← LE.le.eq_or_lt_dec

alias eq_or_lt_of_le ← LE.le.eq_or_lt

alias eq_or_gt_of_le ← LE.le.eq_or_gt

alias gt_or_eq_of_le ← LE.le.gt_or_eq

-- Porting note: no `decidable_classical` linter
-- attribute [nolint decidable_classical] LE.le.eq_or_lt_dec

theorem eq_of_le_of_not_lt [PartialOrder α] {a b : α} (hab : a ≤ b) (hba : ¬a < b) : a = b :=
  hab.eq_or_lt.resolve_right hba
#align eq_of_le_of_not_lt eq_of_le_of_not_lt

theorem eq_of_ge_of_not_gt [PartialOrder α] {a b : α} (hab : a ≤ b) (hba : ¬a < b) : b = a :=
  (hab.eq_or_lt.resolve_right hba).symm
#align eq_of_ge_of_not_gt eq_of_ge_of_not_gt

alias eq_of_le_of_not_lt ← LE.le.eq_of_not_lt

alias eq_of_ge_of_not_gt ← LE.le.eq_of_not_gt

theorem Ne.le_iff_lt [PartialOrder α] {a b : α} (h : a ≠ b) : a ≤ b ↔ a < b :=
  ⟨fun h' ↦ lt_of_le_of_ne h' h, fun h ↦ h.le⟩
#align ne.le_iff_lt Ne.le_iff_lt

theorem Ne.not_le_or_not_le [PartialOrder α] {a b : α} (h : a ≠ b) : ¬a ≤ b ∨ ¬b ≤ a :=
  not_and_or.1 <| le_antisymm_iff.not.1 h
#align ne.not_le_or_not_le Ne.not_le_or_not_le

-- See Note [decidable namespace]
protected theorem Decidable.ne_iff_lt_iff_le [PartialOrder α] [DecidableEq α] {a b : α} :
    (a ≠ b ↔ a < b) ↔ a ≤ b :=
  ⟨fun h ↦ Decidable.byCases le_of_eq (le_of_lt ∘ h.mp), fun h ↦ ⟨lt_of_le_of_ne h, ne_of_lt⟩⟩
#align decidable.ne_iff_lt_iff_le Decidable.ne_iff_lt_iff_le

@[simp]
theorem ne_iff_lt_iff_le [PartialOrder α] {a b : α} : (a ≠ b ↔ a < b) ↔ a ≤ b :=
  haveI := Classical.dec
  Decidable.ne_iff_lt_iff_le
#align ne_iff_lt_iff_le ne_iff_lt_iff_le

-- Variant of `min_def` with the branches reversed.
theorem min_def' [LinearOrder α] (a b : α) : min a b = if b ≤ a then b else a := by
  rw [min_def]
  rcases lt_trichotomy a b with (lt | eq | gt)
  · rw [if_pos lt.le, if_neg (not_le.mpr lt)]
  · rw [if_pos eq.le, if_pos eq.ge, eq]
  · rw [if_neg (not_le.mpr gt.gt), if_pos gt.le]
#align min_def' min_def'

-- Variant of `min_def` with the branches reversed.
-- This is sometimes useful as it used to be the default.
theorem max_def' [LinearOrder α] (a b : α) : max a b = if b ≤ a then a else b := by
  rw [max_def]
  rcases lt_trichotomy a b with (lt | eq | gt)
  · rw [if_pos lt.le, if_neg (not_le.mpr lt)]
  · rw [if_pos eq.le, if_pos eq.ge, eq]
  · rw [if_neg (not_le.mpr gt.gt), if_pos gt.le]
#align max_def' max_def'

theorem lt_of_not_le [LinearOrder α] {a b : α} (h : ¬b ≤ a) : a < b :=
  ((le_total _ _).resolve_right h).lt_of_not_le h
#align lt_of_not_le lt_of_not_le

theorem lt_iff_not_le [LinearOrder α] {x y : α} : x < y ↔ ¬y ≤ x :=
  ⟨not_le_of_lt, lt_of_not_le⟩
#align lt_iff_not_le lt_iff_not_le

theorem Ne.lt_or_lt [LinearOrder α] {x y : α} (h : x ≠ y) : x < y ∨ y < x :=
  lt_or_gt_of_ne h
#align ne.lt_or_lt Ne.lt_or_lt

/-- A version of `ne_iff_lt_or_gt` with LHS and RHS reversed. -/
@[simp]
theorem lt_or_lt_iff_ne [LinearOrder α] {x y : α} : x < y ∨ y < x ↔ x ≠ y :=
  ne_iff_lt_or_gt.symm
#align lt_or_lt_iff_ne lt_or_lt_iff_ne

theorem not_lt_iff_eq_or_lt [LinearOrder α] {a b : α} : ¬a < b ↔ a = b ∨ b < a :=
  not_lt.trans <| Decidable.le_iff_eq_or_lt.trans <| or_congr eq_comm Iff.rfl
#align not_lt_iff_eq_or_lt not_lt_iff_eq_or_lt

theorem exists_ge_of_linear [LinearOrder α] (a b : α) : ∃ c, a ≤ c ∧ b ≤ c :=
  match le_total a b with
  | Or.inl h => ⟨_, h, le_rfl⟩
  | Or.inr h => ⟨_, le_rfl, h⟩
#align exists_ge_of_linear exists_ge_of_linear

theorem lt_imp_lt_of_le_imp_le {β} [LinearOrder α] [Preorder β] {a b : α} {c d : β}
    (H : a ≤ b → c ≤ d) (h : d < c) : b < a :=
  lt_of_not_le fun h' ↦ (H h').not_lt h
#align lt_imp_lt_of_le_imp_le lt_imp_lt_of_le_imp_le

theorem le_imp_le_iff_lt_imp_lt {β} [LinearOrder α] [LinearOrder β] {a b : α} {c d : β} :
    a ≤ b → c ≤ d ↔ d < c → b < a :=
  ⟨lt_imp_lt_of_le_imp_le, le_imp_le_of_lt_imp_lt⟩
#align le_imp_le_iff_lt_imp_lt le_imp_le_iff_lt_imp_lt

theorem lt_iff_lt_of_le_iff_le' {β} [Preorder α] [Preorder β] {a b : α} {c d : β}
    (H : a ≤ b ↔ c ≤ d) (H' : b ≤ a ↔ d ≤ c) : b < a ↔ d < c :=
  lt_iff_le_not_le.trans <| (and_congr H' (not_congr H)).trans lt_iff_le_not_le.symm
#align lt_iff_lt_of_le_iff_le' lt_iff_lt_of_le_iff_le'

theorem lt_iff_lt_of_le_iff_le {β} [LinearOrder α] [LinearOrder β] {a b : α} {c d : β}
    (H : a ≤ b ↔ c ≤ d) : b < a ↔ d < c :=
  not_le.symm.trans <| (not_congr H).trans <| not_le
#align lt_iff_lt_of_le_iff_le lt_iff_lt_of_le_iff_le

theorem le_iff_le_iff_lt_iff_lt {β} [LinearOrder α] [LinearOrder β] {a b : α} {c d : β} :
    (a ≤ b ↔ c ≤ d) ↔ (b < a ↔ d < c) :=
  ⟨lt_iff_lt_of_le_iff_le, fun H ↦ not_lt.symm.trans <| (not_congr H).trans <| not_lt⟩
#align le_iff_le_iff_lt_iff_lt le_iff_le_iff_lt_iff_lt

theorem eq_of_forall_le_iff [PartialOrder α] {a b : α} (H : ∀ c, c ≤ a ↔ c ≤ b) : a = b :=
  ((H _).1 le_rfl).antisymm ((H _).2 le_rfl)
#align eq_of_forall_le_iff eq_of_forall_le_iff

theorem le_of_forall_le [Preorder α] {a b : α} (H : ∀ c, c ≤ a → c ≤ b) : a ≤ b :=
  H _ le_rfl
#align le_of_forall_le le_of_forall_le

theorem le_of_forall_le' [Preorder α] {a b : α} (H : ∀ c, a ≤ c → b ≤ c) : b ≤ a :=
  H _ le_rfl
#align le_of_forall_le' le_of_forall_le'

theorem le_of_forall_lt [LinearOrder α] {a b : α} (H : ∀ c, c < a → c < b) : a ≤ b :=
  le_of_not_lt fun h ↦ lt_irrefl _ (H _ h)
#align le_of_forall_lt le_of_forall_lt

theorem forall_lt_iff_le [LinearOrder α] {a b : α} : (∀ ⦃c⦄, c < a → c < b) ↔ a ≤ b :=
  ⟨le_of_forall_lt, fun h _ hca ↦ lt_of_lt_of_le hca h⟩
#align forall_lt_iff_le forall_lt_iff_le

theorem le_of_forall_lt' [LinearOrder α] {a b : α} (H : ∀ c, a < c → b < c) : b ≤ a :=
  le_of_not_lt fun h ↦ lt_irrefl _ (H _ h)
#align le_of_forall_lt' le_of_forall_lt'

theorem forall_lt_iff_le' [LinearOrder α] {a b : α} : (∀ ⦃c⦄, a < c → b < c) ↔ b ≤ a :=
  ⟨le_of_forall_lt', fun h _ hac ↦ lt_of_le_of_lt h hac⟩
#align forall_lt_iff_le' forall_lt_iff_le'

theorem eq_of_forall_ge_iff [PartialOrder α] {a b : α} (H : ∀ c, a ≤ c ↔ b ≤ c) : a = b :=
  ((H _).2 le_rfl).antisymm ((H _).1 le_rfl)
#align eq_of_forall_ge_iff eq_of_forall_ge_iff

theorem eq_of_forall_lt_iff [LinearOrder α] {a b : α} (h : ∀ c, c < a ↔ c < b) : a = b :=
  (le_of_forall_lt fun _ ↦ (h _).1).antisymm <| le_of_forall_lt fun _ ↦ (h _).2
#align eq_of_forall_lt_iff eq_of_forall_lt_iff

theorem eq_of_forall_gt_iff [LinearOrder α] {a b : α} (h : ∀ c, a < c ↔ b < c) : a = b :=
  (le_of_forall_lt' fun _ ↦ (h _).2).antisymm <| le_of_forall_lt' fun _ ↦ (h _).1
#align eq_of_forall_gt_iff eq_of_forall_gt_iff

/-- A symmetric relation implies two values are equal, when it implies they're less-equal.  -/
theorem rel_imp_eq_of_rel_imp_le [PartialOrder β] (r : α → α → Prop) [IsSymm α r] {f : α → β}
    (h : ∀ a b, r a b → f a ≤ f b) {a b : α} : r a b → f a = f b := fun hab ↦
  le_antisymm (h a b hab) (h b a <| symm hab)
#align rel_imp_eq_of_rel_imp_le rel_imp_eq_of_rel_imp_le

/-- monotonicity of `≤` with respect to `→` -/
theorem le_implies_le_of_le_of_le {a b c d : α} [Preorder α] (hca : c ≤ a) (hbd : b ≤ d) :
    a ≤ b → c ≤ d :=
  fun hab ↦ (hca.trans hab).trans hbd
#align le_implies_le_of_le_of_le le_implies_le_of_le_of_le

section PartialOrder

variable [PartialOrder α]

/-- To prove commutativity of a binary operation `○`, we only to check `a ○ b ≤ b ○ a` for all `a`,
`b`. -/
theorem commutative_of_le {f : β → β → α} (comm : ∀ a b, f a b ≤ f b a) : ∀ a b, f a b = f b a :=
  fun _ _ ↦ (comm _ _).antisymm <| comm _ _
#align commutative_of_le commutative_of_le

/-- To prove associativity of a commutative binary operation `○`, we only to check
`(a ○ b) ○ c ≤ a ○ (b ○ c)` for all `a`, `b`, `c`. -/
theorem associative_of_commutative_of_le {f : α → α → α} (comm : Commutative f)
    (assoc : ∀ a b c, f (f a b) c ≤ f a (f b c)) : Associative f := fun a b c ↦
  le_antisymm (assoc _ _ _) <| by
    rw [comm, comm b, comm _ c, comm a]
    exact assoc _ _ _
#align associative_of_commutative_of_le associative_of_commutative_of_le

end PartialOrder

@[ext]
theorem Preorder.toLE_injective {α : Type _} : Function.Injective (@Preorder.toLE α) :=
  fun A B h ↦ match A, B with
  | { lt := A_lt, lt_iff_le_not_le := A_iff, .. },
    { lt := B_lt, lt_iff_le_not_le := B_iff, .. } => by
    cases h
    have : A_lt = B_lt := by
      funext a b
      show (LT.mk A_lt).lt a b = (LT.mk B_lt).lt a b
      rw [A_iff, B_iff]
    cases this
    congr
#align preorder.to_has_le_injective Preorder.toLE_injective

@[ext]
theorem PartialOrder.toPreorder_injective {α : Type _} :
    Function.Injective (@PartialOrder.toPreorder α) := fun A B h ↦ by
  cases A
  cases B
  cases h
  congr
#align partial_order.to_preorder_injective PartialOrder.toPreorder_injective

@[ext]
theorem LinearOrder.toPartialOrder_injective {α : Type _} :
    Function.Injective (@LinearOrder.toPartialOrder α) :=
  fun A B h ↦ match A, B with
  | { le := A_le, lt := A_lt,
      decidable_le := A_decidable_le, decidable_eq := A_decidable_eq, decidable_lt := A_decidable_lt
      min := A_min, max := A_max, min_def := A_min_def, max_def := A_max_def,
      compare := A_compare, compare_eq_compareOfLessAndEq := A_compare_canonical,  .. },
    { le := B_le, lt := B_lt,
      decidable_le := B_decidable_le, decidable_eq := B_decidable_eq, decidable_lt := B_decidable_lt
      min := B_min, max := B_max, min_def := B_min_def, max_def := B_max_def,
      compare := B_compare, compare_eq_compareOfLessAndEq := B_compare_canonical, .. } => by
    cases h
    obtain rfl : A_decidable_le = B_decidable_le := Subsingleton.elim _ _
    obtain rfl : A_decidable_eq = B_decidable_eq := Subsingleton.elim _ _
    obtain rfl : A_decidable_lt = B_decidable_lt := Subsingleton.elim _ _
    have : A_min = B_min := by
      funext a b
      exact (A_min_def _ _).trans (B_min_def _ _).symm
    cases this
    have : A_max = B_max := by
      funext a b
      exact (A_max_def _ _).trans (B_max_def _ _).symm
    cases this
    have : A_compare = B_compare := by
      funext a b
      exact (A_compare_canonical _ _).trans (B_compare_canonical _ _).symm
    congr
#align linear_order.to_partial_order_injective LinearOrder.toPartialOrder_injective

theorem Preorder.ext {α} {A B : Preorder α}
    (H : ∀ x y : α, (haveI := A; x ≤ y) ↔ x ≤ y) : A = B := by
  ext x y
  exact H x y
#align preorder.ext Preorder.ext

theorem PartialOrder.ext {α} {A B : PartialOrder α}
    (H : ∀ x y : α, (haveI := A; x ≤ y) ↔ x ≤ y) : A = B := by
  ext x y
  exact H x y
#align partial_order.ext PartialOrder.ext

theorem LinearOrder.ext {α} {A B : LinearOrder α}
    (H : ∀ x y : α, (haveI := A; x ≤ y) ↔ x ≤ y) : A = B := by
  ext x y
  exact H x y
#align linear_order.ext LinearOrder.ext

/-- Given a relation `R` on `β` and a function `f : α → β`, the preimage relation on `α` is defined
by `x ≤ y ↔ f x ≤ f y`. It is the unique relation on `α` making `f` a `RelEmbedding` (assuming `f`
is injective). -/
@[simp]
def Order.Preimage {α β} (f : α → β) (s : β → β → Prop) (x y : α) : Prop :=
  s (f x) (f y)
#align order.preimage Order.Preimage

@[inherit_doc]
infixl:80 " ⁻¹'o " => Order.Preimage

/-- The preimage of a decidable order is decidable. -/
instance Order.Preimage.decidable {α β} (f : α → β) (s : β → β → Prop) [H : DecidableRel s] :
    DecidableRel (f ⁻¹'o s) := fun _ _ ↦ H _ _
#align order.preimage.decidable Order.Preimage.decidable

/-! ### Order dual -/

/-- Type synonym to equip a type with the dual order: `≤` means `≥` and `<` means `>`. `αᵒᵈ` is
notation for `OrderDual α`. -/
def OrderDual (α : Type _) : Type _ :=
  α
#align order_dual OrderDual

@[inherit_doc]
notation:max α "ᵒᵈ" => OrderDual α

namespace OrderDual

instance (α : Type _) [h : Nonempty α] : Nonempty αᵒᵈ :=
  h

instance (α : Type _) [h : Subsingleton α] : Subsingleton αᵒᵈ :=
  h

instance (α : Type _) [LE α] : LE αᵒᵈ :=
  ⟨fun x y : α ↦ y ≤ x⟩

instance (α : Type _) [LT α] : LT αᵒᵈ :=
  ⟨fun x y : α ↦ y < x⟩

instance preorder (α : Type _) [Preorder α] : Preorder αᵒᵈ where
  le_refl := fun _ ↦ le_refl _
  le_trans := fun _ _ _ hab hbc ↦ hbc.trans hab
  lt_iff_le_not_le := fun _ _ ↦ lt_iff_le_not_le

instance partialOrder (α : Type _) [PartialOrder α] : PartialOrder αᵒᵈ where
  __ := inferInstanceAs (Preorder αᵒᵈ)
  le_antisymm := fun a b hab hba ↦ @le_antisymm α _ a b hba hab

instance linearOrder (α : Type _) [LinearOrder α] : LinearOrder αᵒᵈ where
  __ := inferInstanceAs (PartialOrder αᵒᵈ)
  le_total     := λ a b : α => le_total b a
  max := fun a b ↦ (min a b : α)
  min := fun a b ↦ (max a b : α)
  min_def := fun a b ↦ show (max .. : α) = _ by rw [max_comm, max_def]; rfl
  max_def := fun a b ↦ show (min .. : α) = _ by rw [min_comm, min_def]; rfl
  decidable_le := (inferInstance : DecidableRel (λ a b : α => b ≤ a))
  decidable_lt := (inferInstance : DecidableRel (λ a b : α => b < a))
#align order_dual.linear_order OrderDual.linearOrder

instance : ∀ [Inhabited α], Inhabited αᵒᵈ := λ [x: Inhabited α] => x

theorem Preorder.dual_dual (α : Type _) [H : Preorder α] : OrderDual.preorder αᵒᵈ = H :=
  Preorder.ext fun _ _ ↦ Iff.rfl
#align order_dual.preorder.dual_dual OrderDual.Preorder.dual_dual

theorem partialOrder.dual_dual (α : Type _) [H : PartialOrder α] : OrderDual.partialOrder αᵒᵈ = H :=
  PartialOrder.ext fun _ _ ↦ Iff.rfl
#align order_dual.partial_order.dual_dual OrderDual.partialOrder.dual_dual

theorem linearOrder.dual_dual (α : Type _) [H : LinearOrder α] : OrderDual.linearOrder αᵒᵈ = H :=
  LinearOrder.ext fun _ _ ↦ Iff.rfl
#align order_dual.linear_order.dual_dual OrderDual.linearOrder.dual_dual

end OrderDual

/-! ### `HasCompl` -/

/-- Set / lattice complement -/
@[notation_class]
class HasCompl (α : Type _) where
  /-- Set / lattice complement -/
  compl : α → α
#align has_compl HasCompl

export HasCompl (compl)

@[inherit_doc]
postfix:999 "ᶜ" => compl

instance Prop.hasCompl : HasCompl Prop :=
  ⟨Not⟩
#align Prop.has_compl Prop.hasCompl

instance Pi.hasCompl {ι : Type u} {α : ι → Type v} [∀ i, HasCompl (α i)] : HasCompl (∀ i, α i) :=
  ⟨fun x i ↦ x iᶜ⟩
#align pi.has_compl Pi.hasCompl

theorem Pi.compl_def {ι : Type u} {α : ι → Type v} [∀ i, HasCompl (α i)] (x : ∀ i, α i) :
    xᶜ = fun i ↦ x iᶜ :=
  rfl
#align pi.compl_def Pi.compl_def

@[simp]
theorem Pi.compl_apply {ι : Type u} {α : ι → Type v} [∀ i, HasCompl (α i)] (x : ∀ i, α i) (i : ι) :
    (xᶜ) i = x iᶜ :=
  rfl
#align pi.compl_apply Pi.compl_apply

instance IsIrrefl.compl (r) [IsIrrefl α r] : IsRefl α (rᶜ) :=
  ⟨@irrefl α r _⟩
#align is_irrefl.compl IsIrrefl.compl

instance IsRefl.compl (r) [IsRefl α r] : IsIrrefl α (rᶜ) :=
  ⟨fun a ↦ not_not_intro (refl a)⟩
#align is_refl.compl IsRefl.compl

/-! ### Order instances on the function space -/

instance Pi.hasLe {ι : Type u} {α : ι → Type v} [∀ i, LE (α i)] :
    LE (∀ i, α i) where le x y := ∀ i, x i ≤ y i
#align pi.has_le Pi.hasLe

theorem Pi.le_def {ι : Type u} {α : ι → Type v} [∀ i, LE (α i)] {x y : ∀ i, α i} :
    x ≤ y ↔ ∀ i, x i ≤ y i :=
  Iff.rfl
#align pi.le_def Pi.le_def

instance Pi.preorder {ι : Type u} {α : ι → Type v} [∀ i, Preorder (α i)] : Preorder (∀ i, α i) where
  __ := inferInstanceAs (LE (∀ i, α i))
  le_refl := fun a i ↦ le_refl (a i)
  le_trans := fun a b c h₁ h₂ i ↦ le_trans (h₁ i) (h₂ i)
#align pi.preorder Pi.preorder

theorem Pi.lt_def {ι : Type u} {α : ι → Type v} [∀ i, Preorder (α i)] {x y : ∀ i, α i} :
    x < y ↔ x ≤ y ∧ ∃ i, x i < y i := by
  simp (config := { contextual := true }) [lt_iff_le_not_le, Pi.le_def]
#align pi.lt_def Pi.lt_def

instance Pi.partialOrder [∀ i, PartialOrder (π i)] : PartialOrder (∀ i, π i) where
  __ := Pi.preorder
  le_antisymm := fun _ _ h1 h2 ↦ funext fun b ↦ (h1 b).antisymm (h2 b)
#align pi.partial_order Pi.partialOrder

section Pi

/-- A function `a` is strongly less than a function `b`  if `a i < b i` for all `i`. -/
def StrongLT [∀ i, LT (π i)] (a b : ∀ i, π i) : Prop :=
  ∀ i, a i < b i
#align strong_lt StrongLT

@[inherit_doc]
local infixl:50 " ≺ " => StrongLT

variable [∀ i, Preorder (π i)] {a b c : ∀ i, π i}

theorem le_of_strongLT (h : a ≺ b) : a ≤ b := fun _ ↦ (h _).le
#align le_of_strong_lt le_of_strongLT

theorem lt_of_strongLT [Nonempty ι] (h : a ≺ b) : a < b := by
  inhabit ι
  exact Pi.lt_def.2 ⟨le_of_strongLT h, default, h _⟩
#align lt_of_strong_lt lt_of_strongLT

theorem strongLT_of_strongLT_of_le (hab : a ≺ b) (hbc : b ≤ c) : a ≺ c := fun _ ↦
  (hab _).trans_le <| hbc _
#align strong_lt_of_strong_lt_of_le strongLT_of_strongLT_of_le

theorem strongLT_of_le_of_strongLT (hab : a ≤ b) (hbc : b ≺ c) : a ≺ c := fun _ ↦
  (hab _).trans_lt <| hbc _
#align strong_lt_of_le_of_strong_lt strongLT_of_le_of_strongLT

alias le_of_strongLT ← StrongLT.le
#align strong_lt.le StrongLT.le

alias lt_of_strongLT ← StrongLT.lt
#align strong_lt.lt StrongLT.lt

alias strongLT_of_strongLT_of_le ← StrongLT.trans_le
#align strong_lt.trans_le StrongLT.trans_le

alias strongLT_of_le_of_strongLT ← LE.le.trans_strongLT

end Pi

section Function

variable [DecidableEq ι] [∀ i, Preorder (π i)] {x y : ∀ i, π i} {i : ι} {a b : π i}

theorem le_update_iff : x ≤ Function.update y i a ↔ x i ≤ a ∧ ∀ (j) (_ : j ≠ i), x j ≤ y j :=
  Function.forall_update_iff _ fun j z ↦ x j ≤ z
#align le_update_iff le_update_iff

theorem update_le_iff : Function.update x i a ≤ y ↔ a ≤ y i ∧ ∀ (j) (_ : j ≠ i), x j ≤ y j :=
  Function.forall_update_iff _ fun j z ↦ z ≤ y j
#align update_le_iff update_le_iff

theorem update_le_update_iff :
    Function.update x i a ≤ Function.update y i b ↔ a ≤ b ∧ ∀ (j) (_ : j ≠ i), x j ≤ y j := by
  simp (config := { contextual := true }) [update_le_iff]
#align update_le_update_iff update_le_update_iff

@[simp]
theorem le_update_self_iff : x ≤ update x i a ↔ x i ≤ a := by simp [le_update_iff]
#align le_update_self_iff le_update_self_iff

@[simp]
theorem update_le_self_iff : update x i a ≤ x ↔ a ≤ x i := by simp [update_le_iff]
#align update_le_self_iff update_le_self_iff

@[simp]
theorem lt_update_self_iff : x < update x i a ↔ x i < a := by simp [lt_iff_le_not_le]
#align lt_update_self_iff lt_update_self_iff

@[simp]
theorem update_lt_self_iff : update x i a < x ↔ a < x i := by simp [lt_iff_le_not_le]
#align update_lt_self_iff update_lt_self_iff

end Function

instance Pi.sdiff {ι : Type u} {α : ι → Type v} [∀ i, SDiff (α i)] : SDiff (∀ i, α i) :=
  ⟨fun x y i ↦ x i \ y i⟩
#align pi.has_sdiff Pi.sdiff

theorem Pi.sdiff_def {ι : Type u} {α : ι → Type v} [∀ i, SDiff (α i)] (x y : ∀ i, α i) :
    x \ y = fun i ↦ x i \ y i :=
  rfl
#align pi.sdiff_def Pi.sdiff_def

@[simp]
theorem Pi.sdiff_apply {ι : Type u} {α : ι → Type v} [∀ i, SDiff (α i)] (x y : ∀ i, α i) (i : ι) :
    (x \ y) i = x i \ y i :=
  rfl
#align pi.sdiff_apply Pi.sdiff_apply

namespace Function

variable [Preorder α] [Nonempty β] {a b : α}

@[simp]

@[simp]
theorem const_lt_const : const β a < const β b ↔ a < b := by simpa [Pi.lt_def] using le_of_lt
#align function.const_lt_const Function.const_lt_const

end Function

/-! ### `min`/`max` recursors -/

section MinMaxRec

variable [LinearOrder α] {p : α → Prop} {x y : α}

theorem min_rec (hx : x ≤ y → p x) (hy : y ≤ x → p y) : p (min x y) :=
  (le_total x y).rec (fun h ↦ (min_eq_left h).symm.subst (hx h)) fun h ↦
    (min_eq_right h).symm.subst (hy h)
#align min_rec min_rec

theorem max_rec (hx : y ≤ x → p x) (hy : x ≤ y → p y) : p (max x y) :=
  @min_rec αᵒᵈ _ _ _ _ hx hy
#align max_rec max_rec

theorem min_rec' (p : α → Prop) (hx : p x) (hy : p y) : p (min x y) :=
  min_rec (fun _ ↦ hx) fun _ ↦ hy
#align min_rec' min_rec'

theorem max_rec' (p : α → Prop) (hx : p x) (hy : p y) : p (max x y) :=
  max_rec (fun _ ↦ hx) fun _ ↦ hy
#align max_rec' max_rec'

theorem min_def_lt (x y : α) : min x y = if x < y then x else y := by
  rw [min_comm, min_def, ← ite_not]
  simp only [not_le]
#align min_def_lt min_def_lt

theorem max_def_lt (x y : α) : max x y = if x < y then y else x := by
  rw [max_comm, max_def, ← ite_not]
  simp only [not_le]
#align max_def_lt max_def_lt

end MinMaxRec

/-! ### `Sup` and `Inf` -/

/-- Typeclass for the `⊔` (`\lub`) notation -/
@[notation_class, ext]
class Sup (α : Type u) where
  /-- Least upper bound (`\lub` notation) -/
  sup : α → α → α
#align has_sup Sup

/-- Typeclass for the `⊓` (`\glb`) notation -/
@[notation_class, ext]
class Inf (α : Type u) where
  /-- Greatest lower bound (`\glb` notation) -/
  inf : α → α → α
#align has_inf Inf

@[inherit_doc]
infixl:68 " ⊔ " => Sup.sup

@[inherit_doc]
infixl:69 " ⊓ " => Inf.inf

/-! ### Lifts of order instances -/

/-- Transfer a `Preorder` on `β` to a `Preorder` on `α` using a function `f : α → β`.
See note [reducible non-instances]. -/
@[reducible]
def Preorder.lift {α β} [Preorder β] (f : α → β) : Preorder α where
  le x y := f x ≤ f y
  le_refl _ := le_rfl
  le_trans _ _ _ := _root_.le_trans
  lt x y := f x < f y
  lt_iff_le_not_le _ _ := _root_.lt_iff_le_not_le
#align preorder.lift Preorder.lift

/-- Transfer a `PartialOrder` on `β` to a `PartialOrder` on `α` using an injective
function `f : α → β`. See note [reducible non-instances]. -/
@[reducible]
def PartialOrder.lift {α β} [PartialOrder β] (f : α → β) (inj : Injective f) : PartialOrder α :=
  { Preorder.lift f with le_antisymm := fun _ _ h₁ h₂ ↦ inj (h₁.antisymm h₂) }
#align partial_order.lift PartialOrder.lift

theorem compare_of_injective_eq_compareOfLessAndEq (a b : α) [LinearOrder β]
    [DecidableEq α] (f : α → β) (inj : Injective f)
    [Decidable (LT.lt (self := PartialOrder.lift f inj |>.toLT) a b)] :
    compare (f a) (f b) =
      @compareOfLessAndEq _ a b (PartialOrder.lift f inj |>.toLT) _ _ := by
  have h := LinearOrder.compare_eq_compareOfLessAndEq (f a) (f b)
  simp only [h, compareOfLessAndEq]
  split_ifs <;> try (first | rfl | contradiction)
  · have : ¬ f a = f b := by rename_i h; exact inj.ne h
    contradiction
  · have : f a = f b := by rename_i h; exact congrArg f h
    contradiction

/-- Transfer a `LinearOrder` on `β` to a `LinearOrder` on `α` using an injective
function `f : α → β`. This version takes `[Sup α]` and `[Inf α]` as arguments, then uses
them for `max` and `min` fields. See `LinearOrder.lift'` for a version that autogenerates `min` and
`max` fields, and `LinearOrder.liftWithOrd` for one that does not auto-generate `compare`
fields. See note [reducible non-instances]. -/
@[reducible]
def LinearOrder.lift {α β} [LinearOrder β] [Sup α] [Inf α] (f : α → β) (inj : Injective f)
    (hsup : ∀ x y, f (x ⊔ y) = max (f x) (f y)) (hinf : ∀ x y, f (x ⊓ y) = min (f x) (f y)) :
    LinearOrder α :=
  letI instOrdα : Ord α := ⟨fun a b ↦ compare (f a) (f b)⟩
  letI decidable_le := fun x y ↦ (inferInstance : Decidable (f x ≤ f y))
  letI decidable_lt := fun x y ↦ (inferInstance : Decidable (f x < f y))
  letI decidable_eq := fun x y ↦ decidable_of_iff (f x = f y) inj.eq_iff
  { PartialOrder.lift f inj, instOrdα with
    le_total := fun x y ↦ le_total (f x) (f y)
    decidable_le := decidable_le
    decidable_lt := decidable_lt
    decidable_eq := decidable_eq
    min := (· ⊓ ·)
    max := (· ⊔ ·)
    min_def := by
      intros x y
      apply inj
      rw [apply_ite f]
      exact (hinf _ _).trans (min_def _ _)
    max_def := by
      intros x y
      apply inj
      rw [apply_ite f]
      exact (hsup _ _).trans (max_def _ _)
    compare_eq_compareOfLessAndEq := fun a b ↦
      compare_of_injective_eq_compareOfLessAndEq a b f inj }

/-- Transfer a `LinearOrder` on `β` to a `LinearOrder` on `α` using an injective
function `f : α → β`. This version autogenerates `min` and `max` fields. See `LinearOrder.lift`
for a version that takes `[Sup α]` and `[Inf α]`, then uses them as `max` and `min`. See
`LinearOrder.liftWithOrd'` for a version which does not auto-generate `compare` fields.
See note [reducible non-instances]. -/
@[reducible]
def LinearOrder.lift' {α β} [LinearOrder β] (f : α → β) (inj : Injective f) : LinearOrder α :=
  @LinearOrder.lift α β _ ⟨fun x y ↦ if f x ≤ f y then y else x⟩
    ⟨fun x y ↦ if f x ≤ f y then x else y⟩ f inj
    (fun _ _ ↦ (apply_ite f _ _ _).trans (max_def _ _).symm) fun _ _ ↦
    (apply_ite f _ _ _).trans (min_def _ _).symm
#align linear_order.lift' LinearOrder.lift'

/-- Transfer a `LinearOrder` on `β` to a `LinearOrder` on `α` using an injective
function `f : α → β`. This version takes `[Sup α]` and `[Inf α]` as arguments, then uses
them for `max` and `min` fields. It also takes `[Ord α]` as an argument and uses them for `compare`
fields. See `LinearOrder.lift` for a version that autogenerates `compare` fields, and
`LinearOrder.liftWithOrd'` for one that auto-generates `min` and `max` fields.
fields. See note [reducible non-instances]. -/
@[reducible]
def LinearOrder.liftWithOrd {α β} [LinearOrder β] [Sup α] [Inf α] [Ord α] (f : α → β)
    (inj : Injective f) (hsup : ∀ x y, f (x ⊔ y) = max (f x) (f y))
    (hinf : ∀ x y, f (x ⊓ y) = min (f x) (f y))
    (compare_f : ∀ a b : α, compare a b = compare (f a) (f b)) : LinearOrder α :=
  letI decidable_le := fun x y ↦ (inferInstance : Decidable (f x ≤ f y))
  letI decidable_lt := fun x y ↦ (inferInstance : Decidable (f x < f y))
  letI decidable_eq := fun x y ↦ decidable_of_iff (f x = f y) inj.eq_iff
  { PartialOrder.lift f inj with
    le_total := fun x y ↦ le_total (f x) (f y)
    decidable_le := decidable_le
    decidable_lt := decidable_lt
    decidable_eq := decidable_eq
    min := (· ⊓ ·)
    max := (· ⊔ ·)
    min_def := by
      intros x y
      apply inj
      rw [apply_ite f]
      exact (hinf _ _).trans (min_def _ _)
    max_def := by
      intros x y
      apply inj
      rw [apply_ite f]
      exact (hsup _ _).trans (max_def _ _)
    compare_eq_compareOfLessAndEq := fun a b ↦
      (compare_f a b).trans <| compare_of_injective_eq_compareOfLessAndEq a b f inj }

/-- Transfer a `LinearOrder` on `β` to a `LinearOrder` on `α` using an injective
function `f : α → β`. This version auto-generates `min` and `max` fields. It also takes `[Ord α]`
as an argument and uses them for `compare` fields. See `LinearOrder.lift` for a version that
autogenerates `compare` fields, and `LinearOrder.liftWithOrd` for one that doesn't auto-generate
`min` and `max` fields. fields. See note [reducible non-instances]. -/
@[reducible]
def LinearOrder.liftWithOrd' {α β} [LinearOrder β] [Ord α] (f : α → β)
    (inj : Injective f)
    (compare_f : ∀ a b : α, compare a b = compare (f a) (f b)) : LinearOrder α :=
  @LinearOrder.liftWithOrd α β _ ⟨fun x y ↦ if f x ≤ f y then y else x⟩
    ⟨fun x y ↦ if f x ≤ f y then x else y⟩ _ f inj
    (fun _ _ ↦ (apply_ite f _ _ _).trans (max_def _ _).symm)
    (fun _ _ ↦ (apply_ite f _ _ _).trans (min_def _ _).symm)
    compare_f

/-! ### Subtype of an order -/

namespace Subtype

instance le [LE α] {p : α → Prop} : LE (Subtype p) :=
  ⟨fun x y ↦ (x : α) ≤ y⟩

instance lt [LT α] {p : α → Prop} : LT (Subtype p) :=
  ⟨fun x y ↦ (x : α) < y⟩

@[simp]
theorem mk_le_mk [LE α] {p : α → Prop} {x y : α} {hx : p x} {hy : p y} :
    (⟨x, hx⟩ : Subtype p) ≤ ⟨y, hy⟩ ↔ x ≤ y :=
  Iff.rfl
#align subtype.mk_le_mk Subtype.mk_le_mk

@[simp]
theorem mk_lt_mk [LT α] {p : α → Prop} {x y : α} {hx : p x} {hy : p y} :
    (⟨x, hx⟩ : Subtype p) < ⟨y, hy⟩ ↔ x < y :=
  Iff.rfl
#align subtype.mk_lt_mk Subtype.mk_lt_mk

@[simp, norm_cast]
theorem coe_le_coe [LE α] {p : α → Prop} {x y : Subtype p} : (x : α) ≤ y ↔ x ≤ y :=
  Iff.rfl
#align subtype.coe_le_coe Subtype.coe_le_coe

@[simp, norm_cast]
theorem coe_lt_coe [LT α] {p : α → Prop} {x y : Subtype p} : (x : α) < y ↔ x < y :=
  Iff.rfl
#align subtype.coe_lt_coe Subtype.coe_lt_coe

instance preorder [Preorder α] (p : α → Prop) : Preorder (Subtype p) :=
  Preorder.lift (fun (a : Subtype p) ↦ (a : α))

instance partialOrder [PartialOrder α] (p : α → Prop) : PartialOrder (Subtype p) :=
  PartialOrder.lift (fun (a : Subtype p) ↦ (a : α)) Subtype.coe_injective
#align subtype.partial_order Subtype.partialOrder

instance decidableLE [Preorder α] [h : @DecidableRel α (· ≤ ·)] {p : α → Prop} :
    @DecidableRel (Subtype p) (· ≤ ·) := fun a b ↦ h a b
#align subtype.decidable_le Subtype.decidableLE

instance decidableLT [Preorder α] [h : @DecidableRel α (· < ·)] {p : α → Prop} :
    @DecidableRel (Subtype p) (· < ·) := fun a b ↦ h a b
#align subtype.decidable_lt Subtype.decidableLT

/-- A subtype of a linear order is a linear order. We explicitly give the proofs of decidable
equality and decidable order in order to ensure the decidability instances are all definitionally
equal. -/
instance linearOrder [LinearOrder α] (p : α → Prop) : LinearOrder (Subtype p) :=
  @LinearOrder.lift (Subtype p) _ _ ⟨fun x y ↦ ⟨max x y, max_rec' _ x.2 y.2⟩⟩
    ⟨fun x y ↦ ⟨min x y, min_rec' _ x.2 y.2⟩⟩ (fun (a : Subtype p) ↦ (a : α))
    Subtype.coe_injective (fun _ _ ↦ rfl) fun _ _ ↦
    rfl

end Subtype

/-!
### Pointwise order on `α × β`

The lexicographic order is defined in `Data.Prod.Lex`, and the instances are available via the
type synonym `α ×ₗ β = α × β`.
-/

namespace Prod

instance (α : Type u) (β : Type v) [LE α] [LE β] : LE (α × β) :=
  ⟨fun p q ↦ p.1 ≤ q.1 ∧ p.2 ≤ q.2⟩

theorem le_def [LE α] [LE β] {x y : α × β} : x ≤ y ↔ x.1 ≤ y.1 ∧ x.2 ≤ y.2 :=
  Iff.rfl
#align prod.le_def Prod.le_def

@[simp]
theorem mk_le_mk [LE α] [LE β] {x₁ x₂ : α} {y₁ y₂ : β} : (x₁, y₁) ≤ (x₂, y₂) ↔ x₁ ≤ x₂ ∧ y₁ ≤ y₂ :=
  Iff.rfl
#align prod.mk_le_mk Prod.mk_le_mk

@[simp]
theorem swap_le_swap [LE α] [LE β] {x y : α × β} : x.swap ≤ y.swap ↔ x ≤ y :=
  and_comm
#align prod.swap_le_swap Prod.swap_le_swap

section Preorder

variable [Preorder α] [Preorder β] {a a₁ a₂ : α} {b b₁ b₂ : β} {x y : α × β}

instance (α : Type u) (β : Type v) [Preorder α] [Preorder β] : Preorder (α × β) where
  __ := inferInstanceAs (LE (α × β))
  le_refl := fun ⟨a, b⟩ ↦ ⟨le_refl a, le_refl b⟩
  le_trans := fun ⟨a, b⟩ ⟨c, d⟩ ⟨e, f⟩ ⟨hac, hbd⟩ ⟨hce, hdf⟩ ↦ ⟨le_trans hac hce, le_trans hbd hdf⟩

@[simp]
theorem swap_lt_swap : x.swap < y.swap ↔ x < y :=
  and_congr swap_le_swap (not_congr swap_le_swap)
#align prod.swap_lt_swap Prod.swap_lt_swap

theorem mk_le_mk_iff_left : (a₁, b) ≤ (a₂, b) ↔ a₁ ≤ a₂ :=
  and_iff_left le_rfl
#align prod.mk_le_mk_iff_left Prod.mk_le_mk_iff_left

theorem mk_le_mk_iff_right : (a, b₁) ≤ (a, b₂) ↔ b₁ ≤ b₂ :=
  and_iff_right le_rfl
#align prod.mk_le_mk_iff_right Prod.mk_le_mk_iff_right

theorem mk_lt_mk_iff_left : (a₁, b) < (a₂, b) ↔ a₁ < a₂ :=
  lt_iff_lt_of_le_iff_le' mk_le_mk_iff_left mk_le_mk_iff_left
#align prod.mk_lt_mk_iff_left Prod.mk_lt_mk_iff_left

theorem mk_lt_mk_iff_right : (a, b₁) < (a, b₂) ↔ b₁ < b₂ :=
  lt_iff_lt_of_le_iff_le' mk_le_mk_iff_right mk_le_mk_iff_right
#align prod.mk_lt_mk_iff_right Prod.mk_lt_mk_iff_right

theorem lt_iff : x < y ↔ x.1 < y.1 ∧ x.2 ≤ y.2 ∨ x.1 ≤ y.1 ∧ x.2 < y.2 := by
  refine' ⟨fun h ↦ _, _⟩
  · by_cases h₁ : y.1 ≤ x.1
    · exact Or.inr ⟨h.1.1, LE.le.lt_of_not_le h.1.2 fun h₂ ↦ h.2 ⟨h₁, h₂⟩⟩
    · exact Or.inl ⟨LE.le.lt_of_not_le h.1.1 h₁, h.1.2⟩
  · rintro (⟨h₁, h₂⟩ | ⟨h₁, h₂⟩)
    · exact ⟨⟨h₁.le, h₂⟩, fun h ↦ h₁.not_le h.1⟩
    · exact ⟨⟨h₁, h₂.le⟩, fun h ↦ h₂.not_le h.2⟩
#align prod.lt_iff Prod.lt_iff

@[simp]
theorem mk_lt_mk : (a₁, b₁) < (a₂, b₂) ↔ a₁ < a₂ ∧ b₁ ≤ b₂ ∨ a₁ ≤ a₂ ∧ b₁ < b₂ :=
  lt_iff
#align prod.mk_lt_mk Prod.mk_lt_mk

end Preorder

/-- The pointwise partial order on a product.
    (The lexicographic ordering is defined in `Order.Lexicographic`, and the instances are
    available via the type synonym `α ×ₗ β = α × β`.) -/
instance (α : Type u) (β : Type v) [PartialOrder α] [PartialOrder β] : PartialOrder (α × β) where
  __ := inferInstanceAs (Preorder (α × β))
  le_antisymm := fun _ _ ⟨hac, hbd⟩ ⟨hca, hdb⟩ ↦ Prod.ext (hac.antisymm hca) (hbd.antisymm hdb)

end Prod

/-! ### Additional order classes -/

/-- An order is dense if there is an element between any pair of distinct comparable elements. -/
class DenselyOrdered (α : Type u) [LT α] : Prop where
  /-- An order is dense if there is an element between any pair of distinct elements. -/
  dense : ∀ a₁ a₂ : α, a₁ < a₂ → ∃ a, a₁ < a ∧ a < a₂
#align densely_ordered DenselyOrdered

theorem exists_between [LT α] [DenselyOrdered α] : ∀ {a₁ a₂ : α}, a₁ < a₂ → ∃ a, a₁ < a ∧ a < a₂ :=
  DenselyOrdered.dense _ _
#align exists_between exists_between

instance OrderDual.denselyOrdered (α : Type u) [LT α] [h : DenselyOrdered α] :
    DenselyOrdered αᵒᵈ :=
  ⟨fun _ _ ha ↦ (@exists_between α _ h _ _ ha).imp fun _ ↦ And.symm⟩
#align order_dual.densely_ordered OrderDual.denselyOrdered

@[simp]
theorem denselyOrdered_orderDual [LT α] : DenselyOrdered αᵒᵈ ↔ DenselyOrdered α :=
  ⟨by convert @OrderDual.denselyOrdered αᵒᵈ _, @OrderDual.denselyOrdered α _⟩
#align densely_ordered_order_dual denselyOrdered_orderDual

instance [Preorder α] [Preorder β] [DenselyOrdered α] [DenselyOrdered β] : DenselyOrdered (α × β) :=
  ⟨fun a b ↦ by
    simp_rw [Prod.lt_iff]
    rintro (⟨h₁, h₂⟩ | ⟨h₁, h₂⟩)
    · obtain ⟨c, ha, hb⟩ := exists_between h₁
      exact ⟨(c, _), Or.inl ⟨ha, h₂⟩, Or.inl ⟨hb, le_rfl⟩⟩
    · obtain ⟨c, ha, hb⟩ := exists_between h₂
      exact ⟨(_, c), Or.inr ⟨h₁, ha⟩, Or.inr ⟨le_rfl, hb⟩⟩⟩

instance {α : ι → Type _} [∀ i, Preorder (α i)] [∀ i, DenselyOrdered (α i)] :
    DenselyOrdered (∀ i, α i) :=
  ⟨fun a b ↦ by
    classical
      simp_rw [Pi.lt_def]
      rintro ⟨hab, i, hi⟩
      obtain ⟨c, ha, hb⟩ := exists_between hi
      exact
        ⟨Function.update a i c,
          ⟨le_update_iff.2 ⟨ha.le, fun _ _ ↦ le_rfl⟩, i, by rwa [update_same]⟩,
          update_le_iff.2 ⟨hb.le, fun _ _ ↦ hab _⟩, i, by rwa [update_same]⟩⟩

theorem le_of_forall_le_of_dense [LinearOrder α] [DenselyOrdered α] {a₁ a₂ : α}
    (h : ∀ a, a₂ < a → a₁ ≤ a) : a₁ ≤ a₂ :=
  le_of_not_gt fun ha ↦
    let ⟨a, ha₁, ha₂⟩ := exists_between ha
    lt_irrefl a <| lt_of_lt_of_le ‹a < a₁› (h _ ‹a₂ < a›)
#align le_of_forall_le_of_dense le_of_forall_le_of_dense

theorem eq_of_le_of_forall_le_of_dense [LinearOrder α] [DenselyOrdered α] {a₁ a₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : ∀ a, a₂ < a → a₁ ≤ a) : a₁ = a₂ :=
  le_antisymm (le_of_forall_le_of_dense h₂) h₁
#align eq_of_le_of_forall_le_of_dense eq_of_le_of_forall_le_of_dense

theorem le_of_forall_ge_of_dense [LinearOrder α] [DenselyOrdered α] {a₁ a₂ : α}
    (h : ∀ a₃ < a₁, a₃ ≤ a₂) : a₁ ≤ a₂ :=
  le_of_not_gt fun ha ↦
    let ⟨a, ha₁, ha₂⟩ := exists_between ha
    lt_irrefl a <| lt_of_le_of_lt (h _ ‹a < a₁›) ‹a₂ < a›
#align le_of_forall_ge_of_dense le_of_forall_ge_of_dense

theorem eq_of_le_of_forall_ge_of_dense [LinearOrder α] [DenselyOrdered α] {a₁ a₂ : α} (h₁ : a₂ ≤ a₁)
    (h₂ : ∀ a₃ < a₁, a₃ ≤ a₂) : a₁ = a₂ :=
  (le_of_forall_ge_of_dense h₂).antisymm h₁
#align eq_of_le_of_forall_ge_of_dense eq_of_le_of_forall_ge_of_dense

theorem dense_or_discrete [LinearOrder α] (a₁ a₂ : α) :
    (∃ a, a₁ < a ∧ a < a₂) ∨ (∀ a, a₁ < a → a₂ ≤ a) ∧ ∀ a < a₂, a ≤ a₁ :=
  or_iff_not_imp_left.2 fun h ↦
    ⟨fun a ha₁ ↦ le_of_not_gt fun ha₂ ↦ h ⟨a, ha₁, ha₂⟩,
     fun a ha₂ ↦ le_of_not_gt fun ha₁ ↦ h ⟨a, ha₁, ha₂⟩⟩
#align dense_or_discrete dense_or_discrete

/-- If a linear order has no elements `x < y < z`, then it has at most two elements. -/
lemma eq_or_eq_or_eq_of_forall_not_lt_lt [LinearOrder α]
    (h : ∀ ⦃x y z : α⦄, x < y → y < z → False) (x y z : α) : x = y ∨ y = z ∨ x = z := by
  by_contra hne
  simp only [not_or, ← Ne.def] at hne
  cases' hne.1.lt_or_lt with h₁ h₁ <;> cases' hne.2.1.lt_or_lt with h₂ h₂ <;>
    cases' hne.2.2.lt_or_lt with h₃ h₃
  exacts [h h₁ h₂, h h₂ h₃, h h₃ h₂, h h₃ h₁, h h₁ h₃, h h₂ h₃, h h₁ h₃, h h₂ h₁]
#align eq_or_eq_or_eq_of_forall_not_lt_lt eq_or_eq_or_eq_of_forall_not_lt_lt

namespace PUnit

variable (a b : PUnit.{u + 1})

-- Porting note: no `refine_struct` at time of port
instance linearOrder: LinearOrder PUnit where
  le  := fun _ _ ↦ True
  lt  := fun _ _ ↦ False
  max := fun _ _ ↦ unit
  min := fun _ _ ↦ unit
  decidable_eq := inferInstance
  decidable_le := fun _ _ ↦ Decidable.isTrue trivial
  decidable_lt := fun _ _ ↦ Decidable.isFalse id
  le_refl     := by intros; trivial
  le_trans    := by intros; trivial
  le_total    := by intros; exact Or.inl trivial
  le_antisymm := by intros; rfl
  lt_iff_le_not_le := by simp only [not_true, and_false, forall_const]

theorem max_eq : max a b = star :=
  rfl
#align punit.max_eq PUnit.max_eq

theorem min_eq : min a b = star :=
  rfl
#align punit.min_eq PUnit.min_eq

-- Porting note: simp can prove this @[simp]
protected theorem le : a ≤ b :=
  trivial
#align punit.le PUnit.le

-- Porting note: simp can prove this @[simp]
theorem not_lt : ¬a < b :=
  not_false
#align punit.not_lt PUnit.not_lt

instance : DenselyOrdered PUnit :=
  ⟨fun _ _ ↦ False.elim⟩

end PUnit

section «Prop»

/-- Propositions form a complete boolean algebra, where the `≤` relation is given by implication. -/
instance Prop.le : LE Prop :=
  ⟨(· → ·)⟩
#align Prop.has_le Prop.le

@[simp]
theorem le_Prop_eq : ((· ≤ ·) : Prop → Prop → Prop) = (· → ·) :=
  rfl
#align le_Prop_eq le_Prop_eq

theorem subrelation_iff_le {r s : α → α → Prop} : Subrelation r s ↔ r ≤ s :=
  Iff.rfl
#align subrelation_iff_le subrelation_iff_le

instance Prop.partialOrder : PartialOrder Prop where
  __ := Prop.le
  le_refl _ := id
  le_trans _ _ _ f g := g ∘ f
  le_antisymm _ _ Hab Hba := propext ⟨Hab, Hba⟩
#align Prop.partial_order Prop.partialOrder

end «Prop»

variable {s : β → β → Prop} {t : γ → γ → Prop}

/-! ### Linear order from a total partial order -/

/-- Type synonym to create an instance of `LinearOrder` from a `PartialOrder` and `IsTotal α (≤)` -/
def AsLinearOrder (α : Type u) :=
  α
#align as_linear_order AsLinearOrder

instance {α} [Inhabited α] : Inhabited (AsLinearOrder α) :=
  ⟨(default : α)⟩

noncomputable instance AsLinearOrder.linearOrder {α} [PartialOrder α] [IsTotal α (· ≤ ·)] :
    LinearOrder (AsLinearOrder α) where
  __ := inferInstanceAs (PartialOrder α)
  le_total := @total_of α (· ≤ ·) _
  decidable_le := Classical.decRel _
#align as_linear_order.linear_order AsLinearOrder.linearOrder


/-
Copyright (c) 2022 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import category_theory.generator
import category_theory.preadditive.yoneda.basic

/-!
# Separators in preadditive categories

This file contains characterizations of separating sets and objects that are valid in all
preadditive categories.

-/

universes v u

open category_theory opposite

namespace category_theory
variables {C : Type u} [category.{v} C] [preadditive C]

lemma preadditive.is_separating_iff (𝒢 : set C) :
  is_separating 𝒢 ↔ ∀ ⦃X Y : C⦄ (f : X ⟶ Y), (∀ (G ∈ 𝒢) (h : G ⟶ X), h ≫ f = 0) → f = 0 :=
⟨λ h𝒢 X Y f hf, h𝒢 _ _ (by simpa only [limits.comp_zero] using hf),
 λ h𝒢 X Y f g hfg, sub_eq_zero.1 $ h𝒢 _
  (by simpa only [preadditive.comp_sub, sub_eq_zero] using hfg)⟩

lemma preadditive.is_coseparating_iff (𝒢 : set C) :
  is_coseparating 𝒢 ↔ ∀ ⦃X Y : C⦄ (f : X ⟶ Y), (∀ (G ∈ 𝒢) (h : Y ⟶ G), f ≫ h = 0) → f = 0 :=
⟨λ h𝒢 X Y f hf, h𝒢 _ _ (by simpa only [limits.zero_comp] using hf),
 λ h𝒢 X Y f g hfg, sub_eq_zero.1 $ h𝒢 _
  (by simpa only [preadditive.sub_comp, sub_eq_zero] using hfg)⟩

lemma preadditive.is_separator_iff (G : C) :
  is_separator G ↔ ∀ ⦃X Y : C⦄ (f : X ⟶ Y), (∀ h : G ⟶ X, h ≫ f = 0) → f = 0 :=
⟨λ hG X Y f hf, hG.def _ _ (by simpa only [limits.comp_zero] using hf),
 λ hG, (is_separator_def _).2 $ λ X Y f g hfg, sub_eq_zero.1 $ hG _
  (by simpa only [preadditive.comp_sub, sub_eq_zero] using hfg)⟩

lemma preadditive.is_coseparator_iff (G : C) :
  is_coseparator G ↔ ∀ ⦃X Y : C⦄ (f : X ⟶ Y), (∀ h : Y ⟶ G, f ≫ h = 0) → f = 0 :=
⟨λ hG X Y f hf, hG.def _ _ (by simpa only [limits.zero_comp] using hf),
 λ hG, (is_coseparator_def _).2 $ λ X Y f g hfg, sub_eq_zero.1 $ hG _
  (by simpa only [preadditive.sub_comp, sub_eq_zero] using hfg)⟩

lemma is_separator_iff_faithful_preadditive_coyoneda_obj (G : C) :
  is_separator G ↔ faithful (preadditive_coyoneda_obj (op G)) :=
begin
  rw [is_separator_iff_faithful_preadditive_coyoneda, preadditive_coyoneda_obj_2],
  exact ⟨λ h, by exactI faithful.of_comp _ (forget₂ _ AddCommGroup.{v}),
         λ h, by exactI faithful.comp _ _⟩
end

lemma is_coseparator_iff_faithful_preadditive_yoneda (G : C) :
  is_coseparator G ↔ faithful (preadditive_yoneda.obj G) :=
begin
  rw [is_coseparator_iff_faithful_yoneda_obj, ← whiskering_preadditive_yoneda, functor.comp_obj,
    whiskering_right_obj_obj],
  exact ⟨λ h, by exactI faithful.of_comp _ (forget AddCommGroup), λ h, by exactI faithful.comp _ _⟩
end

lemma is_coseparator_iff_faithful_preadditive_yoneda_obj (G : C) :
  is_coseparator G ↔ faithful (preadditive_yoneda_obj G) :=
begin
  rw [is_coseparator_iff_faithful_preadditive_yoneda, preadditive_yoneda_obj_2],
  exact ⟨λ h, by exactI faithful.of_comp _ (forget₂ _ AddCommGroup.{v}),
         λ h, by exactI faithful.comp _ _⟩
end

end category_theory


/-
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, Mario Carneiro

! This file was ported from Lean 3 source module data.rat.order
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Field.Defs
import Mathbin.Data.Rat.Basic
import Mathbin.Data.Int.Cast.Lemmas
import Mathbin.Tactic.AssertExists

/-!
# Order for Rational Numbers

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

## Summary

We define the order on `ℚ`, prove that `ℚ` is a discrete, linearly ordered field, and define
functions such as `abs` and `sqrt` that depend on this order.

## Notations

- `/.` is infix notation for `rat.mk`.

## Tags

rat, rationals, field, ℚ, numerator, denominator, num, denom, order, ordering, sqrt, abs
-/

namespace Rat

variable (a b c : ℚ)

open Rat

#print Rat.Nonneg /-
/-- A rational number is called nonnegative if its numerator is nonnegative. -/
protected def Nonneg (r : ℚ) : Prop :=
  0 ≤ r.num
#align rat.nonneg Rat.Nonneg
-/

/- warning: rat.mk_nonneg -> Rat.divInt_nonneg is a dubious translation:
lean 3 declaration is
  forall (a : Int) {b : Int}, (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) b) -> (Iff (Rat.Nonneg (Rat.mk a b)) (LE.le.{0} Int Int.hasLe (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) a))
but is expected to have type
  forall (a : Int) {b : Int}, (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) b) -> (Iff (Rat.Nonneg (Rat.divInt a b)) (LE.le.{0} Int Int.instLEInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) a))
Case conversion may be inaccurate. Consider using '#align rat.mk_nonneg Rat.divInt_nonnegₓ'. -/
@[simp]
theorem divInt_nonneg (a : ℤ) {b : ℤ} (h : 0 < b) : (a /. b).NonNeg ↔ 0 ≤ a :=
  by
  generalize ha : a /. b = x; cases' x with n₁ d₁ h₁ c₁; rw [num_denom'] at ha
  simp [Rat.Nonneg]
  have d0 := Int.ofNat_lt.2 h₁
  have := (mk_eq (ne_of_gt h) (ne_of_gt d0)).1 ha
  constructor <;> intro h₂
  · apply nonneg_of_mul_nonneg_left _ d0
    rw [this]
    exact mul_nonneg h₂ (le_of_lt h)
  · apply nonneg_of_mul_nonneg_left _ h
    rw [← this]
    exact mul_nonneg h₂ (Int.ofNat_zero_le _)
#align rat.mk_nonneg Rat.divInt_nonneg

#print Rat.nonneg_add /-
protected theorem nonneg_add {a b} : Rat.Nonneg a → Rat.Nonneg b → Rat.Nonneg (a + b) :=
  numDenCasesOn' a fun n₁ d₁ h₁ =>
    numDenCasesOn' b fun n₂ d₂ h₂ =>
      by
      have d₁0 : 0 < (d₁ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₁)
      have d₂0 : 0 < (d₂ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₂)
      simp [d₁0, d₂0, h₁, h₂, mul_pos d₁0 d₂0]
      intro n₁0 n₂0
      apply add_nonneg <;> apply mul_nonneg <;> · first |assumption|apply Int.ofNat_zero_le
#align rat.nonneg_add Rat.nonneg_add
-/

#print Rat.nonneg_mul /-
protected theorem nonneg_mul {a b} : Rat.Nonneg a → Rat.Nonneg b → Rat.Nonneg (a * b) :=
  numDenCasesOn' a fun n₁ d₁ h₁ =>
    numDenCasesOn' b fun n₂ d₂ h₂ =>
      by
      have d₁0 : 0 < (d₁ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₁)
      have d₂0 : 0 < (d₂ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₂)
      simp (config := { contextual := true }) [d₁0, d₂0, h₁, h₂, mul_pos d₁0 d₂0, mul_nonneg]
#align rat.nonneg_mul Rat.nonneg_mul
-/

#print Rat.nonneg_antisymm /-
protected theorem nonneg_antisymm {a} : Rat.Nonneg a → Rat.Nonneg (-a) → a = 0 :=
  numDenCasesOn' a fun n d h =>
    by
    have d0 : 0 < (d : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h)
    simp [d0, h]
    exact fun h₁ h₂ => le_antisymm h₂ h₁
#align rat.nonneg_antisymm Rat.nonneg_antisymm
-/

#print Rat.nonneg_total /-
protected theorem nonneg_total : Rat.Nonneg a ∨ Rat.Nonneg (-a) := by
  cases' a with n <;> exact Or.imp_right neg_nonneg_of_nonpos (le_total 0 n)
#align rat.nonneg_total Rat.nonneg_total
-/

#print Rat.decidableNonneg /-
instance decidableNonneg : Decidable (Rat.Nonneg a) := by
  cases a <;> unfold Rat.Nonneg <;> infer_instance
#align rat.decidable_nonneg Rat.decidableNonneg
-/

#print Rat.le' /-
/-- Relation `a ≤ b` on `ℚ` defined as `a ≤ b ↔ rat.nonneg (b - a)`. Use `a ≤ b` instead of
`rat.le a b`. -/
protected def le' (a b : ℚ) :=
  Rat.Nonneg (b - a)
#align rat.le Rat.le'
-/

instance : LE ℚ :=
  ⟨Rat.le'⟩

instance decidableLe : DecidableRel ((· ≤ ·) : ℚ → ℚ → Prop)
  | a, b => show Decidable (Rat.Nonneg (b - a)) by infer_instance
#align rat.decidable_le Rat.decidableLe

/- warning: rat.le_def -> Rat.le_def is a dubious translation:
lean 3 declaration is
  forall {a : Int} {b : Int} {c : Int} {d : Int}, (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) b) -> (LT.lt.{0} Int Int.hasLt (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) d) -> (Iff (LE.le.{0} Rat Rat.hasLe (Rat.mk a b) (Rat.mk c d)) (LE.le.{0} Int Int.hasLe (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) a d) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) c b)))
but is expected to have type
  forall {a : Int} {b : Int} {c : Int} {d : Int}, (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) b) -> (LT.lt.{0} Int Int.instLTInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) d) -> (Iff (LE.le.{0} Rat Rat.instLERat (Rat.divInt a b) (Rat.divInt c d)) (LE.le.{0} Int Int.instLEInt (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) a d) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) c b)))
Case conversion may be inaccurate. Consider using '#align rat.le_def Rat.le_defₓ'. -/
protected theorem le_def {a b c d : ℤ} (b0 : 0 < b) (d0 : 0 < d) :
    a /. b ≤ c /. d ↔ a * d ≤ c * b := by
  show Rat.Nonneg _ ↔ _
  rw [← sub_nonneg]
  simp [sub_eq_add_neg, ne_of_gt b0, ne_of_gt d0, mul_pos d0 b0]
#align rat.le_def Rat.le_def

/- warning: rat.le_refl -> Rat.le_refl is a dubious translation:
lean 3 declaration is
  forall (a : Rat), LE.le.{0} Rat Rat.hasLe a a
but is expected to have type
  forall (a : Rat), LE.le.{0} Rat Rat.instLERat a a
Case conversion may be inaccurate. Consider using '#align rat.le_refl Rat.le_reflₓ'. -/
protected theorem le_refl : a ≤ a :=
  show Rat.Nonneg (a - a) by rw [sub_self] <;> exact le_refl (0 : ℤ)
#align rat.le_refl Rat.le_refl

/- warning: rat.le_total -> Rat.le_total is a dubious translation:
lean 3 declaration is
  forall (a : Rat) (b : Rat), Or (LE.le.{0} Rat Rat.hasLe a b) (LE.le.{0} Rat Rat.hasLe b a)
but is expected to have type
  forall (a : Rat) (b : Rat), Or (LE.le.{0} Rat Rat.instLERat a b) (LE.le.{0} Rat Rat.instLERat b a)
Case conversion may be inaccurate. Consider using '#align rat.le_total Rat.le_totalₓ'. -/
protected theorem le_total : a ≤ b ∨ b ≤ a := by
  have := Rat.nonneg_total (b - a) <;> rwa [neg_sub] at this
#align rat.le_total Rat.le_total

/- warning: rat.le_antisymm -> Rat.le_antisymm is a dubious translation:
lean 3 declaration is
  forall {a : Rat} {b : Rat}, (LE.le.{0} Rat Rat.hasLe a b) -> (LE.le.{0} Rat Rat.hasLe b a) -> (Eq.{1} Rat a b)
but is expected to have type
  forall {a : Rat} {b : Rat}, (LE.le.{0} Rat Rat.instLERat a b) -> (LE.le.{0} Rat Rat.instLERat b a) -> (Eq.{1} Rat a b)
Case conversion may be inaccurate. Consider using '#align rat.le_antisymm Rat.le_antisymmₓ'. -/
protected theorem le_antisymm {a b : ℚ} (hab : a ≤ b) (hba : b ≤ a) : a = b :=
  by
  have := eq_neg_of_add_eq_zero_left (Rat.nonneg_antisymm hba <| by rwa [← sub_eq_add_neg, neg_sub])
  rwa [neg_neg] at this
#align rat.le_antisymm Rat.le_antisymm

/- warning: rat.le_trans -> Rat.le_trans is a dubious translation:
lean 3 declaration is
  forall {a : Rat} {b : Rat} {c : Rat}, (LE.le.{0} Rat Rat.hasLe a b) -> (LE.le.{0} Rat Rat.hasLe b c) -> (LE.le.{0} Rat Rat.hasLe a c)
but is expected to have type
  forall {a : Rat} {b : Rat} {c : Rat}, (LE.le.{0} Rat Rat.instLERat a b) -> (LE.le.{0} Rat Rat.instLERat b c) -> (LE.le.{0} Rat Rat.instLERat a c)
Case conversion may be inaccurate. Consider using '#align rat.le_trans Rat.le_transₓ'. -/
protected theorem le_trans {a b c : ℚ} (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c :=
  by
  have : Rat.Nonneg (b - a + (c - b)) := Rat.nonneg_add hab hbc
  simpa [sub_eq_add_neg, add_comm, add_left_comm]
#align rat.le_trans Rat.le_trans

instance : LinearOrder ℚ where
  le := Rat.le'
  le_refl := Rat.le_refl
  le_trans := @Rat.le_trans
  le_antisymm := @Rat.le_antisymm
  le_total := Rat.le_total
  DecidableEq := by infer_instance
  decidableLe a b := Rat.decidableNonneg (b - a)

-- Extra instances to short-circuit type class resolution
instance : LT ℚ := by infer_instance

instance : DistribLattice ℚ := by infer_instance

instance : Lattice ℚ := by infer_instance

instance : SemilatticeInf ℚ := by infer_instance

instance : SemilatticeSup ℚ := by infer_instance

instance : Inf ℚ := by infer_instance

instance : Sup ℚ := by infer_instance

instance : PartialOrder ℚ := by infer_instance

instance : Preorder ℚ := by infer_instance

/- warning: rat.le_def' -> Rat.le_def' is a dubious translation:
lean 3 declaration is
  forall {p : Rat} {q : Rat}, Iff (LE.le.{0} Rat Rat.hasLe p q) (LE.le.{0} Int Int.hasLe (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) (Rat.num p) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Rat.den q))) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) (Rat.num q) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Rat.den p))))
but is expected to have type
  forall {p : Rat} {q : Rat}, Iff (LE.le.{0} Rat Rat.instLERat p q) (LE.le.{0} Int Int.instLEInt (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (Rat.num p) (Nat.cast.{0} Int instNatCastInt (Rat.den q))) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (Rat.num q) (Nat.cast.{0} Int instNatCastInt (Rat.den p))))
Case conversion may be inaccurate. Consider using '#align rat.le_def' Rat.le_def'ₓ'. -/
protected theorem le_def' {p q : ℚ} : p ≤ q ↔ p.num * q.den ≤ q.num * p.den :=
  by
  rw [← @num_denom q, ← @num_denom p]
  conv_rhs => simp only [num_denom]
  exact Rat.le_def (by exact_mod_cast p.pos) (by exact_mod_cast q.pos)
#align rat.le_def' Rat.le_def'

#print Rat.lt_def /-
protected theorem lt_def {p q : ℚ} : p < q ↔ p.num * q.den < q.num * p.den :=
  by
  rw [lt_iff_le_and_ne, Rat.le_def']
  suffices p ≠ q ↔ p.num * q.denom ≠ q.num * p.denom
    by
    constructor <;> intro h
    · exact lt_iff_le_and_ne.elim_right ⟨h.left, this.elim_left h.right⟩
    · have tmp := lt_iff_le_and_ne.elim_left h
      exact ⟨tmp.left, this.elim_right tmp.right⟩
  exact not_iff_not.elim_right eq_iff_mul_eq_mul
#align rat.lt_def Rat.lt_def
-/

/- warning: rat.nonneg_iff_zero_le -> Rat.nonneg_iff_zero_le is a dubious translation:
lean 3 declaration is
  forall {a : Rat}, Iff (Rat.Nonneg a) (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) a)
but is expected to have type
  forall {a : Rat}, Iff (Rat.Nonneg a) (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) a)
Case conversion may be inaccurate. Consider using '#align rat.nonneg_iff_zero_le Rat.nonneg_iff_zero_leₓ'. -/
theorem nonneg_iff_zero_le {a} : Rat.Nonneg a ↔ 0 ≤ a :=
  show Rat.Nonneg a ↔ Rat.Nonneg (a - 0) by simp
#align rat.nonneg_iff_zero_le Rat.nonneg_iff_zero_le

/- warning: rat.num_nonneg_iff_zero_le -> Rat.num_nonneg_iff_zero_le is a dubious translation:
lean 3 declaration is
  forall {a : Rat}, Iff (LE.le.{0} Int Int.hasLe (OfNat.ofNat.{0} Int 0 (OfNat.mk.{0} Int 0 (Zero.zero.{0} Int Int.hasZero))) (Rat.num a)) (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) a)
but is expected to have type
  forall {a : Rat}, Iff (LE.le.{0} Int Int.instLEInt (OfNat.ofNat.{0} Int 0 (instOfNatInt 0)) (Rat.num a)) (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) a)
Case conversion may be inaccurate. Consider using '#align rat.num_nonneg_iff_zero_le Rat.num_nonneg_iff_zero_leₓ'. -/
theorem num_nonneg_iff_zero_le : ∀ {a : ℚ}, 0 ≤ a.num ↔ 0 ≤ a
  | ⟨n, d, h, c⟩ => @nonneg_iff_zero_le ⟨n, d, h, c⟩
#align rat.num_nonneg_iff_zero_le Rat.num_nonneg_iff_zero_le

/- warning: rat.add_le_add_left -> Rat.add_le_add_left is a dubious translation:
lean 3 declaration is
  forall {a : Rat} {b : Rat} {c : Rat}, Iff (LE.le.{0} Rat Rat.hasLe (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) c a) (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.hasAdd) c b)) (LE.le.{0} Rat Rat.hasLe a b)
but is expected to have type
  forall {a : Rat} {b : Rat} {c : Rat}, Iff (LE.le.{0} Rat Rat.instLERat (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) c a) (HAdd.hAdd.{0, 0, 0} Rat Rat Rat (instHAdd.{0} Rat Rat.instAddRat) c b)) (LE.le.{0} Rat Rat.instLERat a b)
Case conversion may be inaccurate. Consider using '#align rat.add_le_add_left Rat.add_le_add_leftₓ'. -/
protected theorem add_le_add_left {a b c : ℚ} : c + a ≤ c + b ↔ a ≤ b := by
  unfold LE.le Rat.le' <;> rw [add_sub_add_left_eq_sub]
#align rat.add_le_add_left Rat.add_le_add_left

/- warning: rat.mul_nonneg -> Rat.mul_nonneg is a dubious translation:
lean 3 declaration is
  forall {a : Rat} {b : Rat}, (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) a) -> (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) b) -> (LE.le.{0} Rat Rat.hasLe (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.hasMul) a b))
but is expected to have type
  forall {a : Rat} {b : Rat}, (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) a) -> (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) b) -> (LE.le.{0} Rat Rat.instLERat (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) (HMul.hMul.{0, 0, 0} Rat Rat Rat (instHMul.{0} Rat Rat.instMulRat) a b))
Case conversion may be inaccurate. Consider using '#align rat.mul_nonneg Rat.mul_nonnegₓ'. -/
protected theorem mul_nonneg {a b : ℚ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := by
  rw [← nonneg_iff_zero_le] at ha hb⊢ <;> exact Rat.nonneg_mul ha hb
#align rat.mul_nonneg Rat.mul_nonneg

instance : LinearOrderedField ℚ :=
  { Rat.field, Rat.linearOrder,
    Rat.semiring with
    zero_le_one := by decide
    add_le_add_left := fun a b ab c => Rat.add_le_add_left.2 ab
    mul_pos := fun a b ha hb =>
      lt_of_le_of_ne (Rat.mul_nonneg (le_of_lt ha) (le_of_lt hb))
        (mul_ne_zero (ne_of_lt ha).symm (ne_of_lt hb).symm).symm }

-- Extra instances to short-circuit type class resolution
instance : LinearOrderedCommRing ℚ := by infer_instance

instance : LinearOrderedRing ℚ := by infer_instance

instance : OrderedRing ℚ := by infer_instance

instance : LinearOrderedSemiring ℚ := by infer_instance

instance : OrderedSemiring ℚ := by infer_instance

instance : LinearOrderedAddCommGroup ℚ := by infer_instance

instance : OrderedAddCommGroup ℚ := by infer_instance

instance : OrderedCancelAddCommMonoid ℚ := by infer_instance

instance : OrderedAddCommMonoid ℚ := by infer_instance

#print Rat.num_pos_iff_pos /-
theorem num_pos_iff_pos {a : ℚ} : 0 < a.num ↔ 0 < a :=
  lt_iff_lt_of_le_iff_le <| by
    simpa [(by cases a <;> rfl : (-a).num = -a.num)] using @num_nonneg_iff_zero_le (-a)
#align rat.num_pos_iff_pos Rat.num_pos_iff_pos
-/

#print Rat.div_lt_div_iff_mul_lt_mul /-
theorem div_lt_div_iff_mul_lt_mul {a b c d : ℤ} (b_pos : 0 < b) (d_pos : 0 < d) :
    (a : ℚ) / b < c / d ↔ a * d < c * b :=
  by
  simp only [lt_iff_le_not_le]
  apply and_congr
  · simp [div_num_denom, Rat.le_def b_pos d_pos]
  · apply not_congr
    simp [div_num_denom, Rat.le_def d_pos b_pos]
#align rat.div_lt_div_iff_mul_lt_mul Rat.div_lt_div_iff_mul_lt_mul
-/

#print Rat.lt_one_iff_num_lt_denom /-
theorem lt_one_iff_num_lt_denom {q : ℚ} : q < 1 ↔ q.num < q.den := by simp [Rat.lt_def]
#align rat.lt_one_iff_num_lt_denom Rat.lt_one_iff_num_lt_denom
-/

/- warning: rat.abs_def -> Rat.abs_def is a dubious translation:
lean 3 declaration is
  forall (q : Rat), Eq.{1} Rat (Abs.abs.{0} Rat (Neg.toHasAbs.{0} Rat Rat.hasNeg Rat.hasSup) q) (Rat.mk ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Int.natAbs (Rat.num q))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Rat.den q)))
but is expected to have type
  forall (q : Rat), Eq.{1} Rat (Abs.abs.{0} Rat (Neg.toHasAbs.{0} Rat Rat.instNegRat Rat.instSupRat) q) (Rat.divInt (Nat.cast.{0} Int instNatCastInt (Int.natAbs (Rat.num q))) (Nat.cast.{0} Int instNatCastInt (Rat.den q)))
Case conversion may be inaccurate. Consider using '#align rat.abs_def Rat.abs_defₓ'. -/
theorem abs_def (q : ℚ) : |q| = q.num.natAbs /. q.den :=
  by
  cases' le_total q 0 with hq hq
  · rw [abs_of_nonpos hq]
    rw [← @num_denom q, ← mk_zero_one, Rat.le_def (Int.coe_nat_pos.2 q.pos) zero_lt_one, mul_one,
      MulZeroClass.zero_mul] at hq
    rw [Int.ofNat_natAbs_of_nonpos hq, ← neg_def, num_denom]
  · rw [abs_of_nonneg hq]
    rw [← @num_denom q, ← mk_zero_one, Rat.le_def zero_lt_one (Int.coe_nat_pos.2 q.pos), mul_one,
      MulZeroClass.zero_mul] at hq
    rw [Int.natAbs_of_nonneg hq, num_denom]
#align rat.abs_def Rat.abs_def

end Rat

-- We make some assertions here about declarations that do not need to be in the import dependencies
-- for this file, but have been in the past.
assert_not_exists Fintype

assert_not_exists Set.Icc

assert_not_exists GaloisConnection

-- These are less significant, but should not be relaxed until at least after port to Lean 4.
assert_not_exists LinearOrderedCommGroupWithZero

-- This one doesn't exist anywhere!
-- assert_not_exists positive.add_comm_semigroup


import data.set.lattice topology.basic order.bounded_lattice
open set lattice
set_option old_structure_cmd true
universe u

class has_operator (α : Type u) :=
    (operator : α → α)

notation `#` := has_operator.operator

class order_retract (α : Type u) extends has_operator α, order_top α, order_bot α :=
    (monotone : ∀ x y : α, x ≤ y → # x ≤ # y) -- Axiom K
    (idempotent : ∀ x : α, (# ∘ #) x = # x) -- Axiom 4

class necessity (α : Type u) extends order_retract α :=
    (decreasing :  ∀ x : α, # x ≤ x) -- Axiom T
    (top_open : # top = top) -- Necessitation rule

class possibility (α : Type u) extends order_retract α :=
    (increasing :  ∀ x : α, x ≤ # x)
    (bot_closed : # bot = bot)

class interior_algebra (α : Type u) extends necessity α, boolean_algebra α
class closure_algebra (α : Type u) extends possibility α, boolean_algebra α

notation `□` := necessity.operator
notation `⋄` := possibility.operator

variable {α : Type u}

@[simp, reducible]
def pbot [c : closure_algebra α] :  ⋄ ⊥ = (⊥:α)  := c.bot_closed

lemma possibility_top_open [closure_algebra α] : -⋄(-⊤) = (⊤:α) :=
    by simp --rw [neg_top, pbot, neg_bot]

instance classical_possibility [necessity α] [Ω : boolean_algebra α] : possibility α :=
{ operator := - (□ ∘ has_neg.neg),
  monotone := sorry,
  idempotent := sorry,
  increasing := sorry,
  bot_closed := sorry,
  ..Ω }

instance classical_necessity [possibility α] [Ω : boolean_algebra α] : necessity α :=
{ operator := - (⋄ ∘ has_neg.neg),
  monotone := sorry,
  idempotent := sorry,
  decreasing := sorry,
  top_open := sorry,
  ..Ω }

instance set_dualize₁ [necessity (set α)] : possibility (set α) := by apply_instance
instance set_dualize₂ [possibility (set α)] : necessity (set α) := by apply_instance

open topological_space
instance topological_interior [topological_space α ] : necessity (set α) :=
{
  operator := interior,
  monotone := @interior_mono α _ ,
  idempotent := @interior_interior α _,
  decreasing := @interior_subset α _,
  top_open := @interior_univ α _,
  ..set.lattice_set  }

instance topological_closure [topological_space α ] : possibility (set α) := by apply_instance

instance closure_from_possibility [p : possibility (set α)] : topological_space α :=
{ is_open := λ s, - ⋄(-s) = s,
  is_open_univ := by {!!),--by rwa p.bot_closed,
  is_open_inter := _,
  is_open_sUnion := _ }

-- instance topological_closure [topological_space α ] : possibility (set α) :=
-- {
--   operator := closure,
--   monotone := @closure_mono α _ ,
--   idempotent := @closure_closure α _,
--   increasing := @subset_closure α _,
--   bot_closed := @closure_empty α _,
--   ..set.lattice_set  }


import combinatorics.simple_graph.basic
import combinatorics.simple_graph.subgraph
import data.finset.basic
import data.finset.powerset
import data.fintype.basic
import logic.basic
import simple_graph_aux
import finset_fintype_aux

universe u

variable {V : Type u}

variable G : simple_graph V
variable R : set V
variables R₁ R₂ : set V

namespace simple_graph

def induced_subgraph : G.subgraph :=
{ verts := R,
  adj := λ v w, v ∈ R ∧ w ∈ R ∧ G.adj v w,
  adj_sub := λ _ _ ⟨_, _, G_adj⟩, G_adj,
  edge_vert := λ _ _ ⟨v_in,_ , _⟩, v_in, }

lemma induced_subgraph_adj_eq_of_verts_eq :
  (induced_subgraph G R₁).verts = (induced_subgraph G R₂).verts →
    (induced_subgraph G R₁).adj = (induced_subgraph G R₂).adj :=
begin
  intro h_verts,
  change R₁ = R₂ at h_verts,
  rw h_verts,
end

lemma induced_subgraph_injective : function.injective (induced_subgraph G) :=
begin
  unfold function.injective,
  intros R₁ R₂,
  rw subgraph.ext_iff,
  rintro ⟨h_verts, _⟩,
  exact h_verts,
end

namespace induced_subgraph_size

variable (k : ℕ)

def set_univ : set G.subgraph := set.image
  (induced_subgraph G)
  {Rₖ : set V | ∃ fin_Rₖ : fintype ↥Rₖ, (@set.to_finset _ Rₖ (by assumption)).card = k}

section finite

variable [fintype V]

def finset_univ : finset G.subgraph := finset.map
⟨ (induced_subgraph G) ∘ coe,
  function.injective.comp (induced_subgraph_injective G) finset.coe_injective ⟩
  (finset.powerset_len k (finset.univ : finset V))

lemma set_univ_eq_coe : (set_univ G k) = ↑(finset_univ G k) :=
begin
  apply set.ext,
  intro G',
  rw finset.mem_coe,
  unfold set_univ,
  unfold finset_univ,
  rw set.mem_image,
  rw finset.mem_map,
  split,
  { rintro ⟨Rₖ, h_Rₖ, h_G'⟩,
    rw set.mem_set_of_eq at h_Rₖ,
    cases h_Rₖ with fin_Rₖ card_Rₖ,
    use @set.to_finset _ Rₖ (by assumption),
    split,
    { rw finset.mem_powerset_len,
      exact ⟨finset.subset_univ _, card_Rₖ⟩, },
    { rw function.embedding.coe_fn_mk,
      rw function.comp_app,
      rw set.coe_to_finset,
      exact h_G', }, },
  { rintro ⟨Rₖ, Rₖ_in, h_G'⟩,
    rw function.embedding.coe_fn_mk at h_G',
    use ↑Rₖ,
    split,
    { rw set.mem_set_of_eq,
      use (finset_coe.fintype Rₖ),
      rw set.to_finset_card,
      simp_rw [finset.coe_sort_coe],
      rw fintype.card_coe,
      rw finset.mem_powerset_len at Rₖ_in,
      exact Rₖ_in.right, },
    { rw function.comp_app at h_G',
      exact h_G', }, },
end

lemma card_set_univ :
(finset_univ G k).card = (fintype.card V).choose k :=
begin
  unfold finset_univ,
  rw finset.card_map,
  rw finset.card_powerset_len,
  rw finset.card_univ,
end

end finite

end induced_subgraph_size

namespace induced_subgraph

lemma is_induced : subgraph.is_induced (induced_subgraph G R) :=
begin
  unfold subgraph.is_induced,
  unfold induced_subgraph,
  dsimp [induced_subgraph],
  intros v w v_in w_in G_adj,
  exact ⟨v_in, w_in, G_adj⟩,
end

namespace decidable

variable [decidable_eq V]
variable [decidable_rel G.adj]
variable [decidable_pred (∈ R)]

instance adj : decidable_rel (G.induced_subgraph R).adj :=
begin
  unfold decidable_rel,
  intros v w,
  unfold induced_subgraph,
  simp only [],
  apply_instance,
end

instance coe_adj : decidable_rel (G.induced_subgraph R).coe.adj :=
begin
  unfold decidable_rel,
  intros v w,
  unfold subgraph.coe,
  simp only [],
  apply_instance,
end

instance verts_mem : decidable_pred (∈ (G.induced_subgraph R).verts) :=
begin
  unfold induced_subgraph,
  simp only [],
  apply_instance,
end

end decidable

section finite

variables [fintype V]
variables [decidable_pred (∈ R)]

instance verts_fintype : fintype ↥(induced_subgraph G R).verts :=
begin
  unfold induced_subgraph,
  change fintype ↥R,
  apply_instance,
end

variable [decidable_eq V]
variable [decidable_rel G.adj]

def edge_subtype_finset (s : finset (sym2 V)) :
finset ↥((G.induced_subgraph R).edge_finset) :=
finset.subtype _ s

def edge_complement_subtype_finset (s : finset (sym2 V)) :
finset ↥(G.edge_finset \ (G.induced_subgraph R).edge_finset) :=
finset.subtype _ s

end finite

end induced_subgraph

end simple_graph

namespace complete_graph

open simple_graph

namespace induced_subgraph

lemma coe_eq_complete_graph : ((complete_graph V).induced_subgraph R).coe = complete_graph ↥R :=
begin
  rw simple_graph.ext_iff,
  funext,
  rw subgraph.coe_adj,
  unfold induced_subgraph,
  simp only [],
  ext,
  change ↥R at v,
  change ↥R at w,
  rw ← subtype.val_eq_coe,
  rw ← subtype.val_eq_coe,
  rw eq_true_intro v.property,
  rw eq_true_intro w.property,
  rw true_and,
  rw true_and,
  unfold complete_graph,
  simp only [],
  rw ne.def,
  rw subtype.val_inj,
end

section finite

variables [decidable_eq V] [fintype V] [decidable_pred (∈ R)]

def edge_to_finset_powerset_len_two_surj  :
∀ s,
  ∃ e, ∃ e_in,
    ((complete_graph V).induced_subgraph R).edge_to_finset_powerset_len_two e e_in = s :=
begin
  rintro ⟨s, s_in⟩,
  rw finset.mem_powerset_len at s_in,
  cases s_in with s_subset card_s,
  rw finset.card_eq_two at card_s,
  rcases card_s with ⟨v₁, v₂, v₁_ne_v₂, hs⟩,
  use ⟦(v₁, v₂)⟧,
  unfold induced_subgraph,
  simp only [subgraph.mem_edge_set, complete_graph_eq_top, top_adj],
  unfold induced_subgraph at s_subset,
  change s ⊆ R.to_finset at s_subset,
  have v₁_in : v₁ ∈ R,
  { rw ← set.mem_to_finset,
    apply finset.mem_of_subset s_subset,
    rw hs,
    simp only [finset.mem_insert, eq_self_iff_true, true_or], },
  have v₂_in : v₂ ∈ R,
  { rw ← set.mem_to_finset,
    apply finset.mem_of_subset s_subset,
    rw hs,
    simp only [finset.mem_insert, finset.mem_singleton, eq_self_iff_true, or_true], },
  use ⟨v₁_in, v₂_in, v₁_ne_v₂⟩,
  unfold subgraph.edge_to_finset_powerset_len_two,
  simp only [sym2.lift_mk, subtype.coe_mk, subtype.mk_eq_mk],
  exact hs.symm,
end

lemma card_edge_finset : ((complete_graph V).induced_subgraph R).edge_finset.card = (fintype.card ↥R).choose 2 :=
begin
  suffices : (((complete_graph V).induced_subgraph R).edge_finset).card = (finset.powerset_len 2 R.to_finset).card,
  { rw this,
    rw ← set.to_finset_card R,
    exact finset.card_powerset_len _ _, },
  unfold subgraph.edge_finset,
  refine finset.card_congr
    (λ e, λ e_in,
      (((complete_graph V).induced_subgraph R).edge_to_finset_powerset_len_two
        e (by rwa ← set.mem_to_finset)).val) _ _ _,
  { intros e e_in,
    rw subtype.val_eq_coe,
    have h := (((complete_graph V).induced_subgraph R).edge_to_finset_powerset_len_two e _).prop,
    unfold induced_subgraph at h,
    exact h, },
  { simp_rw set.mem_to_finset,
    simp_rw subtype.val_inj,
    exact ((complete_graph V).induced_subgraph R).edge_to_finset_powerset_len_two_inj, },
  { intros s s_in,
    have h_surj := edge_to_finset_powerset_len_two_surj R
      ⟨s, by {unfold induced_subgraph, exact s_in}⟩,
    simp_rw ← subtype.val_inj at h_surj,
    simp_rw set.mem_to_finset,
    exact h_surj },
end

end finite

end induced_subgraph

namespace induced_subgraph_size

variables [fintype V] [decidable_eq V]
variable {k : ℕ}
variable Rₖ : ↥(finset.powerset_len k (finset.univ : finset V))

lemma card_Rₖ : (Rₖ : finset V).card = k :=
begin
  cases Rₖ with Rₖ Rₖ_in,
  change Rₖ.card = k,
  rw finset.mem_powerset_len at Rₖ_in,
  rw Rₖ_in.right,
end

instance Rₖ_mem_decidable : decidable_pred (∈ (↑Rₖ : set V)) :=
λ a, finset.decidable_mem a Rₖ

theorem edge_set_nonempty (hk : k ≥ 2) :
(((complete_graph V).induced_subgraph Rₖ).edge_set).nonempty :=
begin
  unfold set.nonempty,
  unfold subgraph.edge_set,
  rw sym2.exists,
  simp_rw [sym2.from_rel_prop],
  unfold induced_subgraph,
  change ∃ (v₁ v₂ : V),
    v₁ ∈ ↑Rₖ ∧ v₂ ∈ ↑Rₖ ∧ (complete_graph V).adj v₁ v₂,
  unfold complete_graph,
  simp only [],
  have hRₖ_card : (Rₖ : finset V).card ≥ 2,
  { rw card_Rₖ,
    exact hk, },
  rcases finset.ex_x_ne_y_of_card_ge_two hRₖ_card with
    ⟨v₁, v₁_in, v₂, v₂_in, v₁_ne_v₂⟩,
  exact ⟨v₁, v₂, v₁_in, v₂_in, v₁_ne_v₂⟩,
end

theorem edge_finset_nonempty (hk : k ≥ 2) :
(((complete_graph V).induced_subgraph Rₖ).edge_finset).nonempty :=
begin
  unfold subgraph.edge_finset,
  rw ← finset.coe_nonempty,
  rw set.coe_to_finset,
  exact edge_set_nonempty Rₖ hk,
end

end induced_subgraph_size

end complete_graph


import data.real.basic

theorem exo (f: real -> real):
  (forall x y, f(x^2)-f(y^2)=(x+y)*(f(x)-f(y)))
  -> (exists m l, forall x, f(x) = m * x + l)
:=
  sorry


/- -----------------------------------------------------------------------
Facts about algebraic structures.
----------------------------------------------------------------------- -/

namespace qp
namespace stdaux

universe variables ℓ₁ ℓ₂ ℓ₃

/-! #brief A semigroup homomorphism.
-/
structure semigroup.hom
    {A : Type ℓ₁} [A_semigroup : semigroup A]
    {B : Type ℓ₂} [B_semigroup : semigroup B]
    (f : A → B)
    : Prop
:= (dist : ∀ (a₁ a₂ : A), f (a₁ * a₂) = f a₁ * f a₂)

/-! #brief A commutative semigroup homomorphism.
-/
structure comm_semigroup.hom
    {A : Type ℓ₁} [A_comm_semigroup : comm_semigroup A]
    {B : Type ℓ₂} [B_comm_semigroup : comm_semigroup B]
    (f : A → B)
    extends semigroup.hom f
    : Prop

/-! #brief Semigroup homomorphisms are closed under composition.
-/
definition semigroup.hom_comp
    {A : Type ℓ₁} [A_semigroup : semigroup A]
    {B : Type ℓ₂} [B_semigroup : semigroup B]
    {C : Type ℓ₃} [C_semigroup : semigroup C]
    {g : B → C} (g_hom : semigroup.hom g)
    {f : A → B} (f_hom : semigroup.hom f)
    : semigroup.hom (function.comp g f)
:= { dist := λ a₁ a₂, by rw [-g_hom^.dist, -f_hom^.dist]
   }

/-! #brief The identity function is a semigroup homomorphism.
-/
definition semigroup.hom_id
    (A : Type ℓ₁) [A_semigroup : semigroup A]
    : semigroup.hom (@id A)
:= { dist := λ a₁ a₂, rfl }

/-! #brief A monoid homomorphism.
-/
structure monoid.hom
    {A : Type ℓ₁} [A_monoid : monoid A]
    {B : Type ℓ₂} [B_monoid : monoid B]
    (f : A → B)
    extends semigroup.hom f
    : Prop
:= (id : f 1 = 1)

/-! #brief A commutative monoid homomorphism.
-/
structure comm_monoid.hom
    {A : Type ℓ₁} [A_comm_monoid : comm_monoid A]
    {B : Type ℓ₂} [B_comm_monoid : comm_monoid B]
    (f : A → B)
    extends monoid.hom f
    : Prop

/-! #brief Monoid homomorphisms are closed under composition.
-/
definition monoid.hom_comp
    {A : Type ℓ₁} [A_monoid : monoid A]
    {B : Type ℓ₂} [B_monoid : monoid B]
    {C : Type ℓ₃} [C_monoid : monoid C]
    {g : B → C} (g_hom : monoid.hom g)
    {f : A → B} (f_hom : monoid.hom f)
    : monoid.hom (function.comp g f)
:= { dist := λ a₁ a₂, by rw [-g_hom^.dist, -f_hom^.dist]
   , id := by rw [-g_hom^.id, -f_hom^.id]
   }

/-! #brief The identity function is a monoid homomorphism.
-/
definition monoid.hom_id
    (A : Type ℓ₁) [A_monoid : monoid A]
    : monoid.hom (@id A)
:= { dist := (semigroup.hom_id A)^.dist
   , id := rfl
   }

/-! #brief A group homomorphism.
-/
structure group.hom
    {A : Type ℓ₁} [A_group : group A]
    {B : Type ℓ₂} [B_group : group B]
    (f : A → B)
    extends semigroup.hom f
    : Prop

/-! #brief A commutative group homomorphism.
-/
structure comm_group.hom
    {A : Type ℓ₁} [A_comm_group : comm_group A]
    {B : Type ℓ₂} [B_comm_group : comm_group B]
    (f : A → B)
    extends group.hom f
    : Prop

/-! #brief Group homomorphisms are closed under composition.
-/
definition group.hom_comp
    {A : Type ℓ₁} [A_group : group A]
    {B : Type ℓ₂} [B_group : group B]
    {C : Type ℓ₃} [C_group : group C]
    {g : B → C} (g_hom : group.hom g)
    {f : A → B} (f_hom : group.hom f)
    : group.hom (function.comp g f)
:= { dist := (semigroup.hom_comp (group.hom.to_hom g_hom) (group.hom.to_hom f_hom))^.dist
   }

/-! #brief The identity function is a group homomorphism.
-/
definition group.hom_id
    (A : Type ℓ₁) [A_group : group A]
    : group.hom (@id A)
:= { dist := (semigroup.hom_id A)^.dist
   }

/-! #brief Idempotents in groups are trivial.
-/
theorem group.idempotent {A : Type ℓ₁} [A_group : group A]
    {a : A}
    (ω : a = a * a)
    : a = 1
:= by calc a   = a * 1         : by rw mul_one
           ... = a * (a * a⁻¹) : by rw mul_right_inv
           ... = (a * a) * a⁻¹ : by rw mul_assoc
           ... = a * a⁻¹       : by rw -ω
           ... = 1             : by rw mul_right_inv

/-! #brief Inverses in a group are unique.
-/
theorem group.inv_uniq
    {A : Type ℓ₁} [A_group : group A]
    {a b : A}
    (ω : a * b = 1)
    : b = a⁻¹
:= by calc b   = 1 * b : by rw one_mul
           ... = (a⁻¹ * a) * b : by rw mul_left_inv
           ... = a⁻¹ * (a * b) : by rw mul_assoc
           ... = a⁻¹ * 1       : by rw ω
           ... = a⁻¹           : by rw mul_one

/-! #brief Group homomorphisms preserve identities.
-/
theorem group.hom_one
    {A : Type ℓ₁} [A_group : group A]
    {B : Type ℓ₂} [B_group : group B]
    {f : A → B}
    (f_hom : group.hom f)
    : f 1 = 1
:= group.idempotent
    (by calc f 1 = f (1 * 1) : by rw mul_one
             ... = f 1 * f 1 : by rw f_hom^.dist)

/-! #brief Group homomorphisms preserve inverses.
-/
theorem group.hom_inv
    {A : Type ℓ₁} [A_group : group A]
    {B : Type ℓ₂} [B_group : group B]
    {f : A → B}
    (f_hom : group.hom f)
    (a : A)
    : f (a⁻¹) = (f a)⁻¹
:= group.inv_uniq
    (by calc f a * f (a⁻¹)
                 = f (a * a⁻¹)   : by rw f_hom^.dist
             ... = f 1           : by rw mul_right_inv
             ... = 1             : by rw group.hom_one f_hom)

/-! #brief Every group homomorphism is a monoid homomorphism.
-/
definition group.hom.to_monoid_hom
    {A : Type ℓ₁} [A_group : group A]
    {B : Type ℓ₂} [B_group : group B]
    {f : A → B}
    (f_hom : group.hom f)
    : monoid.hom f
:= { dist := f_hom^.dist
   , id := group.hom_one f_hom
   }

/-! #brief A homomorphism of commutative rings.
-/
structure comm_ring.hom
    {A : Type ℓ₁} [A_comm_ring : comm_ring A]
    {B : Type ℓ₂} [B_comm_ring : comm_ring B]
    (f : A → B)
    : Prop
:= (add : ∀ (a₁ a₂ : A), f (a₁ + a₂) = f a₁ + f a₂)
   (mul : ∀ (a₁ a₂ : A), f (a₁ * a₂) = f a₁ * f a₂)
   (one : f 1 = 1)

/-! #brief The identity function is a commutative ring homomorphism.
-/
definition comm_ring.hom_id
    {A : Type ℓ₁} [A_comm_ring : comm_ring A]
    : comm_ring.hom (@id A)
:= { add := λ a₁ a₂, rfl
   , mul := λ a₁ a₂, rfl
   , one := rfl
   }

/-! #brief Commutative ring homomorphisms are closed under composition.
-/
definition comm_ring.hom_comp
    {A : Type ℓ₁} [A_comm_ring : comm_ring A]
    {B : Type ℓ₂} [B_comm_ring : comm_ring B]
    {C : Type ℓ₃} [C_comm_ring : comm_ring C]
    {g : B → C} (g_hom : comm_ring.hom g)
    {f : A → B} (f_hom : comm_ring.hom f)
    : comm_ring.hom (function.comp g f)
:= { add := λ a₁ a₂, by rw [-g_hom^.add, -f_hom^.add]
   , mul := λ a₁ a₂, by rw [-g_hom^.mul, -f_hom^.mul]
   , one :=  by rw [-g_hom^.one, -f_hom^.one]
   }

end stdaux
end qp