This PR replaces `List.lt` with `List.Lex`, from Mathlib, and adds the new `Bool` valued lexicographic comparatory function `List.lex`. This subtly changes the definition of `<` on Lists in some situations. `List.lt` was a weaker relation: in particular if `l₁ < l₂`, then `a :: l₁ < b :: l₂` may hold according to `List.lt` even if `a` and `b` are merely incomparable (either neither `a < b` nor `b < a`), whereas according to `List.Lex` this would require `a = b`. When `<` is total, in the sense that `¬ · < ·` is antisymmetric, then the two relations coincide. Mathlib was already overriding the order instances for `List α`, so this change should not be noticed by anyone already using Mathlib. We simultaneously add the boolean valued `List.lex` function, parameterised by a `BEq` typeclass and an arbitrary `lt` function. This will support the flexibility previously provided for `List.lt`, via a `==` function which is weaker than strict equality.
259 lines
6.9 KiB
Text
259 lines
6.9 KiB
Text
/-
|
||
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Dany Fabian, Sebastian Ullrich
|
||
-/
|
||
|
||
prelude
|
||
import Init.Data.String
|
||
import Init.Data.Array.Basic
|
||
|
||
inductive Ordering where
|
||
| lt | eq | gt
|
||
deriving Inhabited, BEq
|
||
|
||
namespace Ordering
|
||
|
||
deriving instance DecidableEq for Ordering
|
||
|
||
/-- Swaps less and greater ordering results -/
|
||
def swap : Ordering → Ordering
|
||
| .lt => .gt
|
||
| .eq => .eq
|
||
| .gt => .lt
|
||
|
||
/--
|
||
If `o₁` and `o₂` are `Ordering`, then `o₁.then o₂` returns `o₁` unless it is `.eq`,
|
||
in which case it returns `o₂`. Additionally, it has "short-circuiting" semantics similar to
|
||
boolean `x && y`: if `o₁` is not `.eq` then the expression for `o₂` is not evaluated.
|
||
This is a useful primitive for constructing lexicographic comparator functions:
|
||
```
|
||
structure Person where
|
||
name : String
|
||
age : Nat
|
||
|
||
instance : Ord Person where
|
||
compare a b := (compare a.name b.name).then (compare b.age a.age)
|
||
```
|
||
This example will sort people first by name (in ascending order) and will sort people with
|
||
the same name by age (in descending order). (If all fields are sorted ascending and in the same
|
||
order as they are listed in the structure, you can also use `deriving Ord` on the structure
|
||
definition for the same effect.)
|
||
-/
|
||
@[macro_inline] def «then» : Ordering → Ordering → Ordering
|
||
| .eq, f => f
|
||
| o, _ => o
|
||
|
||
/--
|
||
Check whether the ordering is 'equal'.
|
||
-/
|
||
def isEq : Ordering → Bool
|
||
| eq => true
|
||
| _ => false
|
||
|
||
/--
|
||
Check whether the ordering is 'not equal'.
|
||
-/
|
||
def isNe : Ordering → Bool
|
||
| eq => false
|
||
| _ => true
|
||
|
||
/--
|
||
Check whether the ordering is 'less than or equal to'.
|
||
-/
|
||
def isLE : Ordering → Bool
|
||
| gt => false
|
||
| _ => true
|
||
|
||
/--
|
||
Check whether the ordering is 'less than'.
|
||
-/
|
||
def isLT : Ordering → Bool
|
||
| lt => true
|
||
| _ => false
|
||
|
||
/--
|
||
Check whether the ordering is 'greater than'.
|
||
-/
|
||
def isGT : Ordering → Bool
|
||
| gt => true
|
||
| _ => false
|
||
|
||
/--
|
||
Check whether the ordering is 'greater than or equal'.
|
||
-/
|
||
def isGE : Ordering → Bool
|
||
| lt => false
|
||
| _ => true
|
||
|
||
end Ordering
|
||
|
||
/--
|
||
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
|
||
`x = y` corresponds to `Ordering.eq`.
|
||
-/
|
||
@[inline] def compareOfLessAndEq {α} (x y : α) [LT α] [Decidable (x < y)] [DecidableEq α] : Ordering :=
|
||
if x < y then Ordering.lt
|
||
else if x = y then Ordering.eq
|
||
else Ordering.gt
|
||
|
||
/--
|
||
Yields an `Ordering` s.t. `x < y` corresponds to `Ordering.lt` / `Ordering.gt` and
|
||
`x == y` corresponds to `Ordering.eq`.
|
||
-/
|
||
@[inline] def compareOfLessAndBEq {α} (x y : α) [LT α] [Decidable (x < y)] [BEq α] : Ordering :=
|
||
if x < y then .lt
|
||
else if x == y then .eq
|
||
else .gt
|
||
|
||
/--
|
||
Compare `a` and `b` lexicographically by `cmp₁` and `cmp₂`. `a` and `b` are
|
||
first compared by `cmp₁`. If this returns 'equal', `a` and `b` are compared
|
||
by `cmp₂` to break the tie.
|
||
-/
|
||
@[inline] def compareLex (cmp₁ cmp₂ : α → β → Ordering) (a : α) (b : β) : Ordering :=
|
||
(cmp₁ a b).then (cmp₂ a b)
|
||
|
||
/--
|
||
`Ord α` provides a computable total order on `α`, in terms of the
|
||
`compare : α → α → Ordering` function.
|
||
|
||
Typically instances will be transitive, reflexive, and antisymmetric,
|
||
but this is not enforced by the typeclass.
|
||
|
||
There is a derive handler, so appending `deriving Ord` to an inductive type or structure
|
||
will attempt to create an `Ord` instance.
|
||
-/
|
||
class Ord (α : Type u) where
|
||
/-- Compare two elements in `α` using the comparator contained in an `[Ord α]` instance. -/
|
||
compare : α → α → Ordering
|
||
|
||
export Ord (compare)
|
||
|
||
set_option linter.unusedVariables false in -- allow specifying `ord` explicitly
|
||
/--
|
||
Compare `x` and `y` by comparing `f x` and `f y`.
|
||
-/
|
||
@[inline] def compareOn [ord : Ord β] (f : α → β) (x y : α) : Ordering :=
|
||
compare (f x) (f y)
|
||
|
||
instance : Ord Nat where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord Int where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord Bool where
|
||
compare
|
||
| false, true => Ordering.lt
|
||
| true, false => Ordering.gt
|
||
| _, _ => Ordering.eq
|
||
|
||
instance : Ord String where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance (n : Nat) : Ord (Fin n) where
|
||
compare x y := compare x.val y.val
|
||
|
||
instance : Ord UInt8 where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord UInt16 where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord UInt32 where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord UInt64 where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord USize where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance : Ord Char where
|
||
compare x y := compareOfLessAndEq x y
|
||
|
||
instance [Ord α] : Ord (Option α) where
|
||
compare
|
||
| none, none => .eq
|
||
| none, some _ => .lt
|
||
| some _, none => .gt
|
||
| some x, some y => compare x y
|
||
|
||
/-- The lexicographic order on pairs. -/
|
||
def lexOrd [Ord α] [Ord β] : Ord (α × β) where
|
||
compare := compareLex (compareOn (·.1)) (compareOn (·.2))
|
||
|
||
def ltOfOrd [Ord α] : LT α where
|
||
lt a b := compare a b = Ordering.lt
|
||
|
||
instance [Ord α] : DecidableRel (@LT.lt α ltOfOrd) :=
|
||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||
|
||
def leOfOrd [Ord α] : LE α where
|
||
le a b := (compare a b).isLE
|
||
|
||
instance [Ord α] : DecidableRel (@LE.le α leOfOrd) :=
|
||
inferInstanceAs (DecidableRel (fun a b => (compare a b).isLE))
|
||
|
||
namespace Ord
|
||
|
||
/--
|
||
Derive a `BEq` instance from an `Ord` instance.
|
||
-/
|
||
protected def toBEq (ord : Ord α) : BEq α where
|
||
beq x y := ord.compare x y == .eq
|
||
|
||
/--
|
||
Derive an `LT` instance from an `Ord` instance.
|
||
-/
|
||
protected def toLT (_ : Ord α) : LT α :=
|
||
ltOfOrd
|
||
|
||
instance [i : Ord α] : DecidableRel (@LT.lt _ (Ord.toLT i)) :=
|
||
inferInstanceAs (DecidableRel (fun a b => compare a b = Ordering.lt))
|
||
|
||
/--
|
||
Derive an `LE` instance from an `Ord` instance.
|
||
-/
|
||
protected def toLE (_ : Ord α) : LE α :=
|
||
leOfOrd
|
||
|
||
instance [i : Ord α] : DecidableRel (@LE.le _ (Ord.toLE i)) :=
|
||
inferInstanceAs (DecidableRel (fun a b => (compare a b).isLE))
|
||
|
||
/--
|
||
Invert the order of an `Ord` instance.
|
||
-/
|
||
protected def opposite (ord : Ord α) : Ord α where
|
||
compare x y := ord.compare y x
|
||
|
||
/--
|
||
`ord.on f` compares `x` and `y` by comparing `f x` and `f y` according to `ord`.
|
||
-/
|
||
protected def on (_ : Ord β) (f : α → β) : Ord α where
|
||
compare := compareOn f
|
||
|
||
/--
|
||
Derive the lexicographic order on products `α × β` from orders for `α` and `β`.
|
||
-/
|
||
protected def lex (_ : Ord α) (_ : Ord β) : Ord (α × β) :=
|
||
lexOrd
|
||
|
||
/--
|
||
Create an order which compares elements first by `ord₁` and then, if this
|
||
returns 'equal', by `ord₂`.
|
||
-/
|
||
protected def lex' (ord₁ ord₂ : Ord α) : Ord α where
|
||
compare := compareLex ord₁.compare ord₂.compare
|
||
|
||
/--
|
||
Creates an order which compares elements of an `Array` in lexicographic order.
|
||
-/
|
||
protected def arrayOrd [a : Ord α] : Ord (Array α) where
|
||
compare x y :=
|
||
let _ : LT α := a.toLT
|
||
let _ : BEq α := a.toBEq
|
||
if List.lex x.toList y.toList then .lt else if x == y then .eq else .gt
|
||
|
||
end Ord
|