module Data.Rational.Order where

Ordering on rationals🔗

The field of rational numbers inherits a partial order from the ring of integers defining the relation to be As usual, we define this first at the level of fractions, then prove that it extends to the quotient

_≤ᶠ_ : Fraction  Fraction  Type
(x / s [ _ ]) ≤ᶠ (y / t [ _ ]) = (x *ℤ t) ℤ.≤ (y *ℤ s)

The easiest way to show this is transitivity at the level of fractions, since it will follow directly from the definition that implies We can then prove that is invariant under on either side, by appealing to transitivity. The proof is not complicated, just annoying:

≤ᶠ-refl' :  {x y}  x  y  x ≤ᶠ y
≤ᶠ-refl' {x@record{}} {y@record{}} p = ℤ.≤-refl' (from-same-rational p)

≤ᶠ-trans :  x y z  x ≤ᶠ y  y ≤ᶠ z  x ≤ᶠ z
≤ᶠ-trans (x / s [ pos s' ]) (y / t [ pos t' ]) (z / u [ pos u' ]) α β =
  (ℤ.*ℤ-cancel-≤r {t} $
    x *ℤ u *ℤ t   =⟨ solve 3  x u t  x :* u :* t  x :* t :* u) x u t refl =
    x *ℤ t *ℤ u   ≤⟨ ℤ.*ℤ-preserves-≤r u α 
    y *ℤ s *ℤ u   =⟨ solve 3  y s u  y :* s :* u  y :* u :* s) y s u refl =
    y *ℤ u *ℤ s   ≤⟨ ℤ.*ℤ-preserves-≤r s β 
    z *ℤ t *ℤ s   =⟨ solve 3  z t s  z :* t :* s  z :* s :* t) z t s refl =
    z *ℤ s *ℤ t   ≤∎)
We can then lift this to a family of propositions over The strategy for showing it respects the quotient is as outlined above, so we’ll leave this in a <details> block.
private
  leℚ :     Prop lzero
  leℚ (inc x) (inc y) =
    Coeq-rec₂ (hlevel 2) work
       a (x , y , r)  r2 x y a r)
       a (x , y , r)  r1 a x y r) x y
    where
    work : Fraction  Fraction  Prop lzero
     work x y  = x ≤ᶠ y
    work record{} record{} .is-tr = hlevel 1

    r1 :  u x y  x  y  work u x  work u y
    r1 u@record{} x@record{} y@record{} p' = n-ua (prop-ext!
       α  ≤ᶠ-trans u x y α (≤ᶠ-refl' p'))
       α  ≤ᶠ-trans u y x α (≤ᶠ-refl' (≈.symᶜ p'))))

    r2 :  x y u  x  y  work x u  work y u
    r2 u@record{} x@record{} y@record{} p' = n-ua (prop-ext!
       α  ≤ᶠ-trans x u y (≤ᶠ-refl' (≈.symᶜ p')) α)
       α  ≤ᶠ-trans u x y (≤ᶠ-refl' p') α))

We define the type family _≤_ as a record type, wrapping orderℚ, to help out type inference: orderℚ is a pretty nasty definition, whereas an application of a record name is always a normal form.

record _≤_ (x y : ) : Type where
  constructor inc
  field
    lower :  leℚ x y 

By lifting our proofs about _≤ᶠ_ through this record type, we can prove that the ordering on the rational numbers is decidable, reflexive, transitive, and antisymmetric.

instance
  Dec-≤ :  {x y}  Dec (x  y)
  Dec-≤ {inc x} {inc y} = elim! {P = λ x   y  Dec (ℚ.inc x  inc y)} go x y where
    go :  x y  Dec (toℚ x  toℚ y)
    go x@record{} y@record{} with holds? (( x *ℤ  y) ℤ.≤ ( y *ℤ  x))
    ... | yes p = yes (inc p)
    ... | no ¬p = no (¬p  lower)

  ≤-from :  {x y}  p : ( x *ℤ  y) ℤ.≤ ( y *ℤ  x)   toℚ x  toℚ y
  ≤-from {record{}} {record{}}  p  = inc p

abstract
  toℚ≤ :  {x y}  ( x *ℤ  y) ℤ.≤ ( y *ℤ  x)  toℚ x  toℚ y
  toℚ≤ {record{}} {record{}} p = inc p

  ≤-refl    :  {x}  x  x
  ≤-trans   :  {x y z}  x  y  y  z  x  z
  ≤-antisym :  {x y}  x  y  y  x  x  y

  unquoteDef ≤-refl ≤-trans ≤-antisym = do
    by-elim-ℚ ≤-refl λ d x  inc ℤ.≤-refl

    by-elim-ℚ ≤-trans λ d x y z (inc p) (inc q)  inc
      (≤ᶠ-trans (x / d [ auto ]) (y / d [ auto ]) (z / d [ auto ]) p q)

    by-elim-ℚ ≤-antisym λ d x y (inc α) (inc β) 
      quotℚ (to-same-rational (ℤ.≤-antisym α β))

  ≤-refl' :  {x y} (p : x  y)  x  y
  ≤-refl' {x} p = transport  i  x  p i) ≤-refl

Algebraic properties🔗

We can also show that the ordering on behaves well with respect to its algebraic structure. For example, the addition function is monotone in both arguments, and division by a positive integer preserves the order.

  common-denominator-≤ :  {x y d} (p : ℤ.Positive d)  x ℤ.≤ y  toℚ (x / d [ p ])  toℚ (y / d [ p ])
  common-denominator-≤ {d = d} (pos _) p = inc (ℤ.*ℤ-preserves-≤r d p)

  +ℚ-preserves-≤   :  {x y a b}  x  y  a  b  (x +ℚ a)  (y +ℚ b)
  *ℚ-nonnegative   :  {x y}  0  x  0  y  0  (x *ℚ y)
  invℚ-nonnegative :  {x}  p : Nonzero x   0  x  0  (invℚ x  p ⦄)

  unquoteDef +ℚ-preserves-≤ *ℚ-nonnegative invℚ-nonnegative = do

    by-elim-ℚ +ℚ-preserves-≤ λ d x y a b (inc p) (inc q) 
      common-denominator-≤ (ℤ.*ℤ-positive auto auto) $
        x *ℤ d +ℤ a *ℤ d ≤⟨ ℤ.+ℤ-preserves-≤r (a *ℤ d) _ _ p 
        y *ℤ d +ℤ a *ℤ d ≤⟨ ℤ.+ℤ-preserves-≤l (y *ℤ d) _ _ q 
        y *ℤ d +ℤ b *ℤ d ≤∎

    by-elim-ℚ *ℚ-nonnegative λ d x y (inc p) (inc q)  inc
      (ℤ.≤-trans
        (ℤ.*ℤ-nonnegative
          (ℤ.≤-trans p (ℤ.≤-refl' (ℤ.*ℤ-oner x)))
          (ℤ.≤-trans q (ℤ.≤-refl' (ℤ.*ℤ-oner y))))
        (ℤ.≤-refl' (sym (ℤ.*ℤ-oner (x *ℤ y)))))

    by-elim-ℚ invℚ-nonnegative λ where
      (possuc x) posz  inc nz  z  absurd (nz (quotℚ (to-same-rational refl)))
      (possuc x) (possuc y) z  inc (ℤ.pos≤pos 0≤x)

  /ℚ-nonnegative :  {x y}  p : Nonzero y   0  x  0  y  0  (x /ℚ y)
  /ℚ-nonnegative {inc x} {inc y} a b = *ℚ-nonnegative a (invℚ-nonnegative {inc y} b)

Positivity🔗

We can also lift the notion of positivity from the integers to the rational numbers. A fraction is positive if its numerator is positive.

private
  positiveℚ :   Prop lzero
  positiveℚ (inc x) = Coeq-rec  f  el! (ℤ.Positive ( f)))  (x , y , p)  r x y p) x where
    r :  x y  x  y  el! (ℤ.Positive ( x))  el! (ℤ.Positive ( y))
    r (x / s [ ps ]) (y / t [ pt ]) r with rfrom-same-rational r = n-ua (prop-ext!
       px  ℤ.*ℤ-positive-cancel ps (subst ℤ.Positive (r  ℤ.*ℤ-commutative y s) (ℤ.*ℤ-positive px pt)))
      λ py  ℤ.*ℤ-positive-cancel pt (subst ℤ.Positive (sym r  ℤ.*ℤ-commutative x t) (ℤ.*ℤ-positive py ps)))

record Positive (q : ) : Type where
  constructor inc
  field
    lower :  positiveℚ q 

This has the expected interaction with the ordering and the algebraic operations.

nonnegative-nonzero→positive :  {x}  0  x  x  0  Positive x
positive→nonzero :  {x}  Positive x  x  0
positive→nonnegative :  {x}  Positive x  0  x

unquoteDef nonnegative-nonzero→positive positive→nonzero positive→nonnegative = do
  by-elim-ℚ nonnegative-nonzero→positive λ where
    d posz (inc q) r  absurd (r (ext refl))
    d (possuc x) (inc q) r  inc (pos x)

  by-elim-ℚ positive→nonzero λ where
    d a (inc α) β  ℤ.positive→nonzero α (sym (ℤ.*ℤ-oner a)  from-same-rational (unquotℚ β))

  by-elim-ℚ positive→nonnegative λ where
    d a (inc α)  inc (ℤ.≤-trans (ℤ.positive→nonnegative α) (ℤ.≤-refl' (sym (ℤ.*ℤ-oner a))))

The strict order on rationals🔗

With a bit more effort, we can also lift the strict ordering on the integers to the rationals. The definitions end up being pretty much the same as for lifting the partial order! We start by defining it on fractions, then showing that it respects the quotient on both sides — we can’t use transitivity and reflexivity like we did above, unfortunately.

_<ᶠ_ : Fraction  Fraction  Type
(x / s [ _ ]) <ᶠ (y / t [ _ ]) = (x *ℤ t) ℤ.< (y *ℤ s)

<ᶠ-respr :  {x y f}  x  y  f <ᶠ y  f <ᶠ x
<ᶠ-respr {x / s [ p ]} {y / t [ q ]} {z / u [ r ]} xt~ys α = ℤ.*ℤ-cancel-<r {t}  q  $
  ℤ.≤-<-trans
    (ℤ.≤-refl' (solve 3  z s t  z :* s :* t  (z :* t) :* s) z s t refl))
    (ℤ.<-≤-trans (ℤ.*ℤ-preserves-<r s  p  α) (ℤ.≤-refl'
      ( solve 3  y u s  y :* u :* s  y :* s :* u) y u s refl
       ap (_*ℤ u) (sym (from-same-rational xt~ys))
       solve 3  x t u  x :* t :* u  x :* u :* t) x t u refl)))

<ᶠ-respl :  {x y f}  x  y  x <ᶠ f  y <ᶠ f
<ᶠ-respl {x / s [ p ]} {y / t [ q ]} {z / u [ r ]} xt~ys α = ℤ.*ℤ-cancel-<l {s}  p  $
  ℤ.≤-<-trans
    (ℤ.≤-refl'
      ( solve 3  s y u  s :* (y :* u)  (y :* s) :* u) s y u refl
       ap (_*ℤ u) (sym (from-same-rational xt~ys))
       solve 3  x t u  x :* t :* u  x :* u :* t) x t u refl))
    (ℤ.<-≤-trans (ℤ.*ℤ-preserves-<r t  q  α)
      (ℤ.≤-refl' (solve 3  z s t  (z :* s) :* t  s :* (z :* t)) z s t refl)))
Having gotten the bulk of the construction out of the way, we can lift it to a type family over pairs of rationals, and do the same dance as to make this into something definitionally injective.
private
  ltℚ :     Prop lzero
  ltℚ (inc x) (inc y) = Coeq-rec₂ (hlevel 2) work  u (x , y , r)  r2 x y u r)  u (x , y , r)  r1 u x y r) x y where
    work : Fraction  Fraction  Prop lzero
     work x y  = x <ᶠ y
    work record{} record{} .is-tr = hlevel 1

    r1 :  u x y  x  y  work u x  work u y
    r1 u@record{} x@record{} y@record{} p' = n-ua (prop-ext!
       α  <ᶠ-respr {y} {x} {u} (≈.symᶜ p') α)
       α  <ᶠ-respr {x} {y} {u} p' α))

    r2 :  x y u  x  y  work x u  work y u
    r2 x@record{} y@record{} u@record{} p' = n-ua (prop-ext!
       α  <ᶠ-respl {x} {y} {u} p' α)
       α  <ᶠ-respl {y} {x} {u} (≈.symᶜ p') α))

record _<_ (x y : ) : Type where
  constructor inc
  field
    lower :  ltℚ x y 

open _<_
unquoteDecl H-Level-< = declare-record-hlevel 1 H-Level-< (quote _<_)

As before, everything we want to show about strict inequality on the rationals follows by lifting the analogous facts from the integers, where we’re free to assume any set of related rationals has the same denominator.

instance
  Dec-< :  {x y}  Dec (x < y)
  Dec-< {inc x} {inc y} = elim! {P = λ x   y  Dec (inc x < inc y)} go x y where
    go :  x y  Dec (toℚ x < toℚ y)
    go x@record{} y@record{} with holds? (( x *ℤ  y) ℤ.< ( y *ℤ  x))
    ... | yes p = yes (inc p)
    ... | no ¬p = no (¬p  lower)

abstract
  toℚ< :  {x y}  ( x *ℤ  y) ℤ.< ( y *ℤ  x)  toℚ x < toℚ y
  toℚ< {record{}} {record{}} p = inc p

  common-denom-< :  {x y d}  _ : ℤ.Positive d   x ℤ.< y  (x / d) < (y / d)
  common-denom-< {x} {y} {d} p = inc (ℤ.*ℤ-preserves-<r {x} {y} d p)

  <-common-denom :  {x y d}  _ : ℤ.Positive d   (x / d) < (y / d)  x ℤ.< y
  <-common-denom {x} {y} {d} (inc p) = ℤ.*ℤ-cancel-<r {d} {x} {y} p

  <-irrefl     :  {x y}  x  y  ¬ x < y
  <-trans      :  {x y z}  x < y  y < z  x < z
  <-weaken     :  {x y}  x < y  x  y
  ≤-strengthen :  {x y}  x  y  (x  y)  (x < y)

  private instance
    hl-str :  {x y}  H-Level ((x  y)  (x < y)) 1
    hl-str = prop-instance (disjoint-⊎-is-prop (hlevel 1) (hlevel 1) λ (x , y)  <-irrefl x y)
    {-# OVERLAPPING hl-str #-}

  unquoteDef <-irrefl <-trans <-weaken ≤-strengthen = do
    by-elim-ℚ <-irrefl λ d x y p a 
      ℤ.<-irrefl (from-same-denom p) (<-common-denom a)

    by-elim-ℚ <-trans λ d x y z p q 
      common-denom-< (ℤ.<-trans (<-common-denom p) (<-common-denom q))

    by-elim-ℚ <-weaken λ d x y (inc p)  inc (ℤ.<-weaken p)

    by-elim-ℚ ≤-strengthen λ d x y (inc p)  case ℤ.≤-strengthen p of λ where
      (inl dx=dy)  inl (quotℚ (to-same-rational dx=dy))
      (inr dx<dy)  inr (inc dx<dy)

  <-≤-trans :  {x y z}  x < y  y  z  x < z
  <-≤-trans p q with ≤-strengthen q
  ... | inl y=z = subst₂ _<_ refl y=z p
  ... | inr y<z = <-trans p y<z

  ≤-<-trans :  {x y z}  x  y  y < z  x < z
  ≤-<-trans p q with ≤-strengthen p
  ... | inl x=y = subst₂ _<_ (sym x=y) refl q
  ... | inr x<z = <-trans x<z q

  <-from-≤ :  {x y}  x  y  x  y  x < y
  <-from-≤ x≤y x≠y with ≤-strengthen x≤y
  ... | inl x=y = absurd (x≠y x=y)
  ... | inr x<y = x<y

absℚ :   
absℚ (inc x) = ℚ.inc $ ℚ-rec absᶠ absᶠ-resp (inc x) where
  absᶠ : Fraction  _
  absᶠ (x / s [ p ]) = inc (pos (abs x) / s [ p ])

  absᶠ-resp :  x y  x  y  absᶠ x  absᶠ y
  absᶠ-resp (pos x / possuc s [ p ]) (pos y / possuc t [ q ]) α = quot α
  absᶠ-resp (pos x / possuc s [ p ]) (negsuc y / possuc t [ q ]) α = absurd (pos≠negsuc (sym (ℤ.assign-pos (x * suc t))  from-same-rational α))
  absᶠ-resp (negsuc x / possuc s [ p ]) (pos y / possuc t [ q ]) α = absurd (negsuc≠pos (from-same-rational α  ℤ.assign-pos (y * suc s)))
  absᶠ-resp (negsuc x / possuc s [ p ]) (negsuc y / possuc t [ q ]) α = quot (to-same-rational (ap possuc (negsuc-injective (from-same-rational α))))