/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.logic init.data.ordering.basic
universes u v

/-!
# Unbundled algebra classes

These classes and the `@[algebra]` attribute are part of an incomplete refactor described
[here on the github Wiki](https://github.com/leanprover/lean/wiki/Refactoring-structures#encoding-the-algebraic-hierarchy-1).

By themselves, these classes are not good replacements for the `monoid` / `group` etc structures
provided by mathlib, as they are not discoverable by `simp` unlike the current lemmas due to there
being little to index on. The Wiki page linked above describes an algebraic normalizer, but it is not
implemented.
-/

@[algebra] class is_symm_op (α : Type u) (β : out_param (Type v)) (op : α → α → β) : Prop :=
(symm_op : ∀ a b, op a b = op b a)

@[algebra] class is_commutative (α : Type u) (op : α → α → α) : Prop :=
(comm : ∀ a b, op a b = op b a)

@[priority 100]
instance is_symm_op_of_is_commutative (α : Type u) (op : α → α → α) [is_commutative α op] :
  is_symm_op α α op :=
{symm_op := is_commutative.comm}

@[algebra] class is_associative (α : Type u) (op : α → α → α) : Prop :=
(assoc : ∀ a b c, op (op a b) c = op a (op b c))

@[algebra] class is_left_id (α : Type u) (op : α → α → α) (o : out_param α) : Prop :=
(left_id : ∀ a, op o a = a)

@[algebra] class is_right_id (α : Type u) (op : α → α → α) (o : out_param α) : Prop :=
(right_id : ∀ a, op a o = a)

@[algebra] class is_left_null (α : Type u) (op : α → α → α) (o : out_param α) : Prop :=
(left_null : ∀ a, op o a = o)

@[algebra] class is_right_null (α : Type u) (op : α → α → α) (o : out_param α) : Prop :=
(right_null : ∀ a, op a o = o)

@[algebra] class is_left_cancel (α : Type u) (op : α → α → α) : Prop :=
(left_cancel : ∀ a b c, op a b = op a c → b = c)

@[algebra] class is_right_cancel (α : Type u) (op : α → α → α) : Prop :=
(right_cancel : ∀ a b c, op a b = op c b → a = c)

@[algebra] class is_idempotent (α : Type u) (op : α → α → α) : Prop :=
(idempotent : ∀ a, op a a = a)

@[algebra] class is_left_distrib (α : Type u) (op₁ : α → α → α) (op₂ : out_param $ α → α → α) : Prop :=
(left_distrib : ∀ a b c, op₁ a (op₂ b c) = op₂ (op₁ a b) (op₁ a c))

@[algebra] class is_right_distrib (α : Type u) (op₁ : α → α → α) (op₂ : out_param $ α → α → α) : Prop :=
(right_distrib : ∀ a b c, op₁ (op₂ a b) c = op₂ (op₁ a c) (op₁ b c))

@[algebra] class is_left_inv (α : Type u) (op : α → α → α) (inv : out_param $ α → α) (o : out_param α) : Prop :=
(left_inv : ∀ a, op (inv a) a = o)

@[algebra] class is_right_inv (α : Type u) (op : α → α → α) (inv : out_param $ α → α) (o : out_param α) : Prop :=
(right_inv : ∀ a, op a (inv a) = o)

@[algebra] class is_cond_left_inv (α : Type u) (op : α → α → α) (inv : out_param $ α → α) (o : out_param α) (p : out_param $ α → Prop) : Prop :=
(left_inv : ∀ a, p a → op (inv a) a = o)

@[algebra] class is_cond_right_inv (α : Type u) (op : α → α → α) (inv : out_param $ α → α) (o : out_param α) (p : out_param $ α → Prop) : Prop :=
(right_inv : ∀ a, p a → op a (inv a) = o)

@[algebra] class is_distinct (α : Type u) (a : α) (b : α) : Prop :=
(distinct : a ≠ b)

/-
-- The following type class doesn't seem very useful, a regular simp lemma should work for this.
class is_inv (α : Type u) (β : Type v) (f : α → β) (g : out β → α) : Prop :=
(inv : ∀ a, g (f a) = a)

-- The following one can also be handled using a regular simp lemma
class is_idempotent (α : Type u) (f : α → α) : Prop :=
(idempotent : ∀ a, f (f a) = f a)
-/

/-- `is_irrefl X r` means the binary relation `r` on `X` is irreflexive (that is, `r x x` never
holds). -/
@[algebra] class is_irrefl (α : Type u) (r : α → α → Prop) : Prop :=
(irrefl : ∀ a, ¬ r a a)

/-- `is_refl X r` means the binary relation `r` on `X` is reflexive. -/
@[algebra] class is_refl (α : Type u) (r : α → α → Prop) : Prop :=
(refl : ∀ a, r a a)

/-- `is_symm X r` means the binary relation `r` on `X` is symmetric. -/
@[algebra] class is_symm (α : Type u) (r : α → α → Prop) : Prop :=
(symm : ∀ a b, r a b → r b a)

/-- The opposite of a symmetric relation is symmetric. -/
@[priority 100] instance is_symm_op_of_is_symm (α : Type u) (r : α → α → Prop) [is_symm α r] :
  is_symm_op α Prop r :=
{symm_op := λ a b, propext $ iff.intro (is_symm.symm a b) (is_symm.symm b a)}

/-- `is_asymm X r` means that the binary relation `r` on `X` is asymmetric, that is,
`r a b → ¬ r b a`. -/
@[algebra] class is_asymm (α : Type u) (r : α → α → Prop) : Prop :=
(asymm : ∀ a b, r a b → ¬ r b a)

/-- `is_antisymm X r` means the binary relation `r` on `X` is antisymmetric. -/
@[algebra] class is_antisymm (α : Type u) (r : α → α → Prop) : Prop :=
(antisymm : ∀ a b, r a b → r b a → a = b)

/-- `is_trans X r` means the binary relation `r` on `X` is transitive. -/
@[algebra] class is_trans (α : Type u) (r : α → α → Prop) : Prop :=
(trans  : ∀ a b c, r a b → r b c → r a c)

/-- `is_total X r` means that the binary relation `r` on `X` is total, that is, that for any
`x y : X` we have `r x y` or `r y x`.-/
@[algebra] class is_total (α : Type u) (r : α → α → Prop) : Prop :=
(total : ∀ a b, r a b ∨ r b a)

/-- `is_preorder X r` means that the binary relation `r` on `X` is a pre-order, that is, reflexive
and transitive. -/
@[algebra] class is_preorder (α : Type u) (r : α → α → Prop) extends
  is_refl α r, is_trans α r : Prop.

/-- `is_total_preorder X r` means that the binary relation `r` on `X` is total and a preorder. -/
@[algebra] class is_total_preorder (α : Type u) (r : α → α → Prop) extends
  is_trans α r, is_total α r : Prop.

/-- Every total pre-order is a pre-order. -/
instance is_total_preorder_is_preorder (α : Type u) (r : α → α → Prop) [s : is_total_preorder α r] :
  is_preorder α r :=
{trans := s.trans,
 refl  := λ a, or.elim (@is_total.total _ r _ a a) id id}

/-- `is_partial_order X r` means that the binary relation `r` on `X` is a partial order, that is,
`is_preorder X r` and `is_antisymm X r`. -/
@[algebra] class is_partial_order (α : Type u) (r : α → α → Prop) extends
  is_preorder α r, is_antisymm α r : Prop.

/-- `is_linear_order X r` means that the binary relation `r` on `X` is a linear order, that is,
`is_partial_order X r` and `is_total X r`. -/
@[algebra] class is_linear_order (α : Type u) (r : α → α → Prop) extends
  is_partial_order α r, is_total α r : Prop.

/-- `is_equiv X r` means that the binary relation `r` on `X` is an equivalence relation, that
is, `is_preorder X r` and `is_symm X r`. -/
@[algebra] class is_equiv (α : Type u) (r : α → α → Prop) extends
  is_preorder α r, is_symm α r : Prop.

/-- `is_per X r` means that the binary relation `r` on `X` is a partial equivalence relation, that
is, `is_symm X r` and `is_trans X r`. -/
@[algebra] class is_per (α : Type u) (r : α → α → Prop) extends is_symm α r, is_trans α r : Prop.

/-- `is_strict_order X r` means that the binary relation `r` on `X` is a strict order, that is,
`is_irrefl X r` and `is_trans X r`. -/
@[algebra] class is_strict_order (α : Type u) (r : α → α → Prop) extends
  is_irrefl α r, is_trans α r : Prop.

/-- `is_incomp_trans X lt` means that for `lt` a binary relation on `X`, the incomparable relation
`λ a b, ¬ lt a b ∧ ¬ lt b a` is transitive. -/
@[algebra] class is_incomp_trans (α : Type u) (lt : α → α → Prop) : Prop :=
(incomp_trans : ∀ a b c, (¬ lt a b ∧ ¬ lt b a) → (¬ lt b c ∧ ¬ lt c b) → (¬ lt a c ∧ ¬ lt c a))

/-- `is_strict_weak_order X lt` means that the binary relation `lt` on `X` is a strict weak order,
that is, `is_strict_order X lt` and `is_incomp_trans X lt`. -/
@[algebra] class is_strict_weak_order (α : Type u) (lt : α → α → Prop) extends
  is_strict_order α lt, is_incomp_trans α lt : Prop.

/-- `is_trichotomous X lt` means that the binary relation `lt` on `X` is trichotomous, that is,
either `lt a b` or `a = b` or `lt b a` for any `a` and `b`. -/
@[algebra] class is_trichotomous (α : Type u) (lt : α → α → Prop) : Prop :=
(trichotomous : ∀ a b, lt a b ∨ a = b ∨ lt b a)

/-- `is_strict_total_order X lt` means that the binary relation `lt` on `X` is a strict total order,
that is, `is_trichotomous X lt` and `is_strict_order X lt`. -/
@[algebra] class is_strict_total_order (α : Type u) (lt : α → α → Prop)
  extends is_trichotomous α lt, is_strict_order α lt : Prop.

/-- Equality is an equivalence relation. -/
instance eq_is_equiv (α : Type u) : is_equiv α (=) :=
{symm := @eq.symm _, trans := @eq.trans _, refl := eq.refl}

section
variables {α : Type u} {r : α → α → Prop}
local infix `≺`:50 := r

lemma irrefl [is_irrefl α r] (a : α) : ¬ a ≺ a :=
is_irrefl.irrefl a

lemma refl [is_refl α r] (a : α) : a ≺ a :=
is_refl.refl a

lemma trans [is_trans α r] {a b c : α} : a ≺ b → b ≺ c → a ≺ c :=
is_trans.trans _ _ _

lemma symm [is_symm α r] {a b : α} : a ≺ b → b ≺ a :=
is_symm.symm _ _

lemma antisymm [is_antisymm α r] {a b : α} : a ≺ b → b ≺ a → a = b :=
is_antisymm.antisymm _ _

lemma asymm [is_asymm α r] {a b : α} : a ≺ b → ¬ b ≺ a :=
is_asymm.asymm _ _

lemma trichotomous [is_trichotomous α r] : ∀ (a b : α), a ≺ b ∨ a = b ∨ b ≺ a :=
is_trichotomous.trichotomous

lemma incomp_trans [is_incomp_trans α r] {a b c : α} : (¬ a ≺ b ∧ ¬ b ≺ a) → (¬ b ≺ c ∧ ¬ c ≺ b) → (¬ a ≺ c ∧ ¬ c ≺ a) :=
is_incomp_trans.incomp_trans _ _ _

@[priority 90]
instance is_asymm_of_is_trans_of_is_irrefl [is_trans α r] [is_irrefl α r] : is_asymm α r :=
⟨λ a b h₁ h₂, absurd (trans h₁ h₂) (irrefl a)⟩

section explicit_relation_variants
variable (r)

@[elab_simple]
lemma irrefl_of [is_irrefl α r] (a : α) : ¬ a ≺ a := irrefl a

@[elab_simple]
lemma refl_of [is_refl α r] (a : α) : a ≺ a := refl a

@[elab_simple]
lemma trans_of [is_trans α r] {a b c : α} : a ≺ b → b ≺ c → a ≺ c := trans

@[elab_simple]
lemma symm_of [is_symm α r] {a b : α} : a ≺ b → b ≺ a := symm

@[elab_simple]
lemma asymm_of [is_asymm α r] {a b : α} : a ≺ b → ¬ b ≺ a := asymm

@[elab_simple]
lemma total_of [is_total α r] (a b : α) : a ≺ b ∨ b ≺ a :=
is_total.total _ _

@[elab_simple]
lemma trichotomous_of [is_trichotomous α r] : ∀ (a b : α), a ≺ b ∨ a = b ∨ b ≺ a := trichotomous

@[elab_simple]
lemma incomp_trans_of [is_incomp_trans α r] {a b c : α} : (¬ a ≺ b ∧ ¬ b ≺ a) → (¬ b ≺ c ∧ ¬ c ≺ b) → (¬ a ≺ c ∧ ¬ c ≺ a) := incomp_trans

end explicit_relation_variants

end

namespace strict_weak_order
section
parameters {α : Type u} {r : α → α → Prop}
local infix `≺`:50 := r

def equiv (a b : α) : Prop :=
¬ a ≺ b ∧ ¬ b ≺ a

parameter [is_strict_weak_order α r]

local infix (name := equiv) ` ≈ `:50 := equiv

lemma erefl (a : α) : a ≈ a :=
⟨irrefl a, irrefl a⟩

lemma esymm {a b : α} : a ≈ b → b ≈ a :=
λ ⟨h₁, h₂⟩, ⟨h₂, h₁⟩

lemma etrans {a b c : α} : a ≈ b → b ≈ c → a ≈ c :=
incomp_trans

lemma not_lt_of_equiv {a b : α} : a ≈ b → ¬ a ≺ b :=
λ h, h.1

lemma not_lt_of_equiv' {a b : α} : a ≈ b → ¬ b ≺ a :=
λ h, h.2

instance is_equiv : is_equiv α equiv :=
{refl := erefl, trans := @etrans, symm := @esymm}
end

/- Notation for the equivalence relation induced by lt -/
notation a ` ≈[`:50 lt `]` b:50 := @equiv _ lt a b
end strict_weak_order

lemma is_strict_weak_order_of_is_total_preorder {α : Type u} {le : α → α → Prop} {lt : α → α → Prop} [decidable_rel le] [s : is_total_preorder α le]
                                                (h : ∀ a b, lt a b ↔ ¬ le b a) : is_strict_weak_order α lt :=
{
 trans        :=
  λ a b c hab hbc,
    have nba : ¬ le b a, from iff.mp (h _ _) hab,
    have ncb : ¬ le c b, from iff.mp (h _ _) hbc,
    have hab : le a b,   from or.resolve_left (total_of le b a) nba,
    have nca : ¬ le c a, from λ hca : le c a,
       have hcb : le c b, from trans_of le hca hab,
       absurd hcb ncb,
    iff.mpr (h _ _) nca,

 irrefl       := λ a hlt, absurd (refl_of le a) (iff.mp (h _ _) hlt),

 incomp_trans := λ a b c ⟨nab, nba⟩ ⟨nbc, ncb⟩,
    have hba : le b a, from decidable.of_not_not (iff.mp (not_iff_not_of_iff (h _ _)) nab),
    have hab : le a b, from decidable.of_not_not (iff.mp (not_iff_not_of_iff (h _ _)) nba),
    have hcb : le c b, from decidable.of_not_not (iff.mp (not_iff_not_of_iff (h _ _)) nbc),
    have hbc : le b c, from decidable.of_not_not (iff.mp (not_iff_not_of_iff (h _ _)) ncb),
    have hac : le a c, from trans_of le hab hbc,
    have hca : le c a, from trans_of le hcb hba,
    and.intro
      (λ n, absurd hca (iff.mp (h _ _) n))
      (λ n, absurd hac (iff.mp (h _ _) n))
}

lemma lt_of_lt_of_incomp {α : Type u} {lt : α → α → Prop} [is_strict_weak_order α lt] [decidable_rel lt]
                         : ∀ {a b c}, lt a b → (¬ lt b c ∧ ¬ lt c b) → lt a c :=
λ a b c hab ⟨nbc, ncb⟩,
  have nca : ¬ lt c a, from λ hca, absurd (trans_of lt hca hab) ncb,
  decidable.by_contradiction $
    λ nac : ¬ lt a c,
      have ¬ lt a b ∧ ¬ lt b a, from incomp_trans_of lt ⟨nac, nca⟩ ⟨ncb, nbc⟩,
      absurd hab this.1

lemma lt_of_incomp_of_lt {α : Type u} {lt : α → α → Prop} [is_strict_weak_order α lt] [decidable_rel lt]
                         : ∀ {a b c}, (¬ lt a b ∧ ¬ lt b a) → lt b c → lt a c :=
λ a b c ⟨nab, nba⟩ hbc,
  have nca : ¬ lt c a, from λ hca, absurd (trans_of lt hbc hca) nba,
  decidable.by_contradiction $
    λ nac : ¬ lt a c,
      have ¬ lt b c ∧ ¬ lt c b, from incomp_trans_of lt ⟨nba, nab⟩ ⟨nac, nca⟩,
      absurd hbc this.1

lemma eq_of_incomp {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] {a b} : (¬ lt a b ∧ ¬ lt b a) → a = b :=
λ ⟨nab, nba⟩,
  match trichotomous_of lt a b with
  | or.inl hab          := absurd hab nab
  | or.inr (or.inl hab) := hab
  | or.inr (or.inr hba) := absurd hba nba
  end

lemma eq_of_eqv_lt {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] {a b} : a ≈[lt] b → a = b :=
eq_of_incomp

lemma incomp_iff_eq {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] [is_irrefl α lt] (a b) : (¬ lt a b ∧ ¬ lt b a) ↔ a = b :=
iff.intro eq_of_incomp (λ hab, eq.subst hab (and.intro (irrefl_of lt a) (irrefl_of lt a)))

lemma eqv_lt_iff_eq {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] [is_irrefl α lt] (a b) : a ≈[lt] b ↔ a = b :=
incomp_iff_eq a b

lemma not_lt_of_lt {α : Type u} {lt : α → α → Prop} [is_strict_order α lt] {a b} : lt a b → ¬ lt b a :=
λ h₁ h₂, absurd (trans_of lt h₁ h₂) (irrefl_of lt _)
