-------------------------------------------------------------
- Three equivalent ordinal notation systems in Cubical Agda -
-------------------------------------------------------------

-- Remark 3.1: an equivalent definition of MutualOrd

The metatheory of inductive-recursive definitions (A, f), where the
recursive function f : A → A is an endofunction on the inductively
defined type A (e.g. (MutualOrd, fst) here) is in general not well
explored.  However, in this case fst is only used strictly positively
in MutualOrd and <, which means that its graph can be defined
inductive-inductively, and in turn used instead of the recursively
defined fst.  This reduces the inductive-inductive-recursive
construction to an inductive-inductive one, which is known to be
sound.

\begin{code}

{-# OPTIONS --cubical --safe #-}

module MutualOrdWithoutNestednessAndRecursion where

open import Preliminaries

import Agda.Builtin.Equality as P

\end{code}

■ Ordinal notations and their ordering

\begin{code}

infix 30 _<_ _>_ _>fst_ _≥fst_

data MutualOrd : Type₀
data _<_ : MutualOrd  MutualOrd  Type₀
data _≡fst_ : MutualOrd  MutualOrd  Type₀

_>_ _>fst_ _≥fst_ : MutualOrd  MutualOrd  Type₀

data MutualOrd where
 𝟎 : MutualOrd
 ω^_+_≡[_] : (a b : MutualOrd)  a ≡fst b  MutualOrd
 ω^_+_>[_] : (a b : MutualOrd)  a >fst b  MutualOrd

private
 variable
  a a' b b' c d : MutualOrd
  r : a ≡fst b
  s : c ≡fst d
  r' : a >fst b
  s' : c >fst d


data _<_ where
 <₁≡ : 𝟎 < ω^ a + b ≡[ r ]
 <₁> : 𝟎 < ω^ a + b >[ r' ]
 <₂≡≡ : a < c  ω^ a + b ≡[ r ] < ω^ c + d ≡[ s ]
 <₂≡> : a < c  ω^ a + b ≡[ r ] < ω^ c + d >[ s' ]
 <₂>≡ : a < c  ω^ a + b >[ r' ] < ω^ c + d ≡[ s ]
 <₂>> : a < c  ω^ a + b >[ r' ] < ω^ c + d >[ s' ]
 <₃≡≡ : a  c  b < d  ω^ a + b ≡[ r ] < ω^ c + d ≡[ s ]
 <₃≡> : a  c  b < d  ω^ a + b ≡[ r ] < ω^ c + d >[ s' ]
 <₃>≡ : a  c  b < d  ω^ a + b >[ r' ] < ω^ c + d ≡[ s ]
 <₃>> : a  c  b < d  ω^ a + b >[ r' ] < ω^ c + d >[ s' ]

data _≡fst_ where
  fst𝟎 : 𝟎 ≡fst 𝟎
  fst≡ : a  a'  a ≡fst (ω^ a' + b ≡[ r ])
  fst> : a  a'  a ≡fst (ω^ a' + b >[ r' ])
\end{code}


Note that the following definitions are abbreviations for convenience
only, since they make no use of recursion or pattern matching.

\begin{code}
a > b = b < a
a >fst b = Σ λ c  ((c ≡fst b) × a > c)
a ≥fst b = (a >fst b)  (a ≡fst b)
\end{code}


For convenience, we can define the old constructors as functions:

\begin{code}
ω^_+_[_] : (a b : MutualOrd)  a ≥fst b  MutualOrd
ω^ a + b [ inj₁ r ] = ω^ a + b >[ r ]
ω^ a + b [ inj₂ r ] = ω^ a + b ≡[ r ]

<₁ :  {r}  𝟎 < ω^ a + b [ r ]
<₁ {r = inj₁ r}  = <₁>
<₁ {r = inj₂ r'} = <₁≡

<₂ :  {r s}  a < c  ω^ a + b [ r ] < ω^ c + d [ s ]
<₂ {r = inj₁ r}  {inj₁ s}  = <₂>>
<₂ {r = inj₁ r}  {inj₂ s'} = <₂>≡
<₂ {r = inj₂ r'} {inj₁ s'} = <₂≡>
<₂ {r = inj₂ r'} {inj₂ s'} = <₂≡≡

<₃ :  {r s}  a  c  b < d  ω^ a + b [ r ] < ω^ c + d [ s ]
<₃ {r = inj₁ r}  {inj₁ s}  = <₃>>
<₃ {r = inj₁ r}  {inj₂ s'} = <₃>≡
<₃ {r = inj₂ r'} {inj₁ s'} = <₃≡>
<₃ {r = inj₂ r'} {inj₂ s'} = <₃≡≡

fstω :  {r}  a ≡fst (ω^ a + b [ r ])
fstω {r = inj₁ x} = fst> refl
fstω {r = inj₂ x} = fst≡ refl
\end{code}

We can now define fst as an ordinary function, after the definition of
𝒪, and show that the graph relation really represents the graph of
fst.

\begin{code}
fst : MutualOrd -> MutualOrd
fst 𝟎 = 𝟎
fst ω^ a + b ≡[ r ] = a
fst ω^ a + b >[ r' ] = a

sound-≡fst : a ≡fst b  a  fst b
sound-≡fst fst𝟎 = refl
sound-≡fst (fst≡ p) = p
sound-≡fst (fst> p) = p

complete-≡fst : a  fst b  a ≡fst b
complete-≡fst p with PropEqfromPath p
complete-≡fst {b = 𝟎} p | P.refl = fst𝟎
complete-≡fst {b = ω^ _ + _ ≡[ _ ]} p | P.refl = fst≡ p
complete-≡fst {b = ω^ _ + _ >[ _ ]} p | P.refl = fst> p

sound-complete-retract :  p  complete-≡fst (sound-≡fst {a} {b} p)  p
sound-complete-retract p with PropEqfromPath (sound-≡fst p)
sound-complete-retract fst𝟎 | P.refl = refl
sound-complete-retract (fst≡ x) | P.refl = refl
sound-complete-retract (fst> x) | P.refl = refl

sound-injective :  p q  (sound-≡fst {a} {b} p)  (sound-≡fst {a} {b} q)  p  q
sound-injective p q eq = (sound-complete-retract p) ⁻¹  cong complete-≡fst eq  sound-complete-retract q

sound->fst : a >fst b  a > fst b
sound->fst (b , x , p) with PropEqfromPath (sound-≡fst x)
... | P.refl = p

\end{code}

We can prove that MutualOrd defined simultaneously with fst is
isomorphic to MutualOrd defined simultaneously with the graph of
fst. Because of the simultaneous definition of MutualOrd and <, we
have to prove that the functions between the types preserve the
relation < at the same time.

\begin{code}
import MutualOrd as M

to : M.MutualOrd  MutualOrd
to-> : {a b : M.MutualOrd} -> a M.> b -> (to a) > (to b)
to-≥ : {a b : M.MutualOrd} -> a M.≥ M.fst b -> (to a) ≥fst (to b)

to M.𝟎 = 𝟎
to (M.ω^ a + b [ r ]) = ω^ (to a) + (to b) [ to-≥ {b = b} r ]

to-> M.<₁ = <₁
to-> (M.<₂ r) = <₂ (to-> r)
to-> (M.<₃ p r) = <₃ (cong to p) (to-> r)

to-≥ {b = M.𝟎} (inj₁ M.<₁) = inj₁ (_ , fst𝟎 , <₁)
to-≥ {b = M.ω^ b + c [ r ]} (inj₁ p) = inj₁ (_ , fstω , to-> p)
to-≥ {b = M.𝟎} (inj₂ r') with PropEqfromPath r'
... | P.refl = inj₂ fst𝟎
to-≥ {b = M.ω^ b + c [ r ]} (inj₂ p)  with PropEqfromPath p
... | P.refl = inj₂ fstω

from : MutualOrd  M.MutualOrd
from-> : a > b -> (from a) M.> (from b)
from-≥ : a ≥fst b -> (from a) M.≥ M.fst (from b)

from 𝟎 = M.𝟎
from (ω^ a + b ≡[ r ]) = M.ω^ (from a) + (from b) [ from-≥ {b = b} (inj₂ r) ]
from (ω^ a + b >[ r' ]) = M.ω^ (from a) + (from b) [ from-≥ {b = b} (inj₁ r') ]

from-> <₁≡ = M.<₁
from-> <₁> = M.<₁
from-> (<₂≡≡ r) = M.<₂ (from-> r)
from-> (<₂≡> r) = M.<₂ (from-> r)
from-> (<₂>≡ r) = M.<₂ (from-> r)
from-> (<₂>> r) = M.<₂ (from-> r)
from-> (<₃≡≡ p r) = M.<₃ (cong from p) (from-> r)
from-> (<₃≡> p r) = M.<₃ (cong from p) (from-> r)
from-> (<₃>≡ p r) = M.<₃ (cong from p) (from-> r)
from-> (<₃>> p r) = M.<₃ (cong from p) (from-> r)

from-≥ {b = 𝟎} (inj₁ (._ , fst𝟎 , p)) = inj₁ (from-> p)
from-≥ {a} {b = ω^ b + c ≡[ r ]} (inj₁ (b' , fst≡ q , p)) with PropEqfromPath q
... | P.refl = inj₁ (from-> p)
from-≥ {b = ω^ b + c >[ r ]} (inj₁ (b' , fst> q , p))  with PropEqfromPath q
...| P.refl = inj₁ (from-> p)
from-≥ {b = 𝟎} (inj₂ fst𝟎) = inj₂ refl
from-≥ {b = ω^ b + c ≡[ r ]} (inj₂ (fst≡ q)) = inj₂ (cong from q)
from-≥ {b = ω^ b + c >[ r ]} (inj₂ (fst> q)) = inj₂ (cong from q)
\end{code}

Finally, we want to check that we have an isomorphism. We first
establish that again _<_ is Prop-valued. The proof is the same as
before, and so not very interesting.

\begin{code}
rest : MutualOrd  MutualOrd
rest  𝟎               = 𝟎
rest (ω^ _ + b ≡[ _ ]) = b
rest (ω^ _ + b >[ _ ]) = b

caseMutualOrd : {A : Type } (x y z : A)  MutualOrd  A
caseMutualOrd x y z  𝟎               = x
caseMutualOrd x y z (ω^ _ + _ ≡[ _ ]) = y
caseMutualOrd x y z (ω^ _ + _ >[ _ ]) = z

𝟎≢ω : {r : a ≥fst b}  ¬ (𝟎  ω^ a + b [ r ])
𝟎≢ω {r = inj₁ r} e = subst (caseMutualOrd MutualOrd  ) e 𝟎
𝟎≢ω {r = inj₂ r} e = subst (caseMutualOrd MutualOrd  ) e 𝟎

ω≢𝟎 : {r : a ≥fst b}  ¬ (ω^ a + b [ r ]  𝟎)
ω≢𝟎 {r = inj₁ r} e = subst (caseMutualOrd  MutualOrd MutualOrd) e 𝟎
ω≢𝟎 {r = inj₂ r} e = subst (caseMutualOrd  MutualOrd MutualOrd) e 𝟎

ω≡≢ω> : {r : a ≡fst b}{r' : a' >fst b'}  ¬ (ω^ a + b ≡[ r ]  ω^ a' + b' >[ r' ])
ω≡≢ω> e = subst (caseMutualOrd MutualOrd MutualOrd ) e 𝟎

ω>≢ω≡ : {r : a ≡fst b}{r' : a' >fst b'}  ¬ (ω^ a' + b' >[ r' ]  ω^ a + b ≡[ r ])
ω>≢ω≡ e = subst (caseMutualOrd MutualOrd  MutualOrd) e 𝟎

<-irrefl : ¬ a < a
<-irrefl (<₂≡≡ r) = <-irrefl r
<-irrefl (<₂>> r) = <-irrefl r
<-irrefl (<₃≡≡ x r) = <-irrefl r
<-irrefl (<₃>> x r) = <-irrefl r

<-irreflexive : a  b  ¬ a < b
<-irreflexive {a} e = subst  x  ¬ a < x) e <-irrefl


<IsPropValued : isProp (a < b)
>fstIsPropValued : isProp (a >fst b)
≡fstIsPropValued : isProp (a ≡fst b)
≤IsPropValued : {a b : MutualOrd}  isProp (a ≥fst b)
MutualOrdIsDiscrete : Discrete MutualOrd
MutualOrdIsSet : isSet MutualOrd
MutualOrd⁼ : {r : a ≥fst b} {s : c ≥fst d}  a  c  b  d
            ω^ a + b [ r ]  ω^ c + d [ s ]

<IsPropValued <₁≡ <₁≡ = refl
<IsPropValued <₁> <₁> = refl
<IsPropValued (<₂≡≡ p) (<₂≡≡ q) = cong <₂≡≡ (<IsPropValued p q)
<IsPropValued (<₂≡> p) (<₂≡> q) = cong <₂≡> (<IsPropValued p q)
<IsPropValued (<₂>≡ p) (<₂>≡ q) = cong <₂>≡ (<IsPropValued p q)
<IsPropValued (<₂>> p) (<₂>> q) = cong <₂>> (<IsPropValued p q)
<IsPropValued (<₃≡≡ x p) (<₃≡≡ x' q) = cong₂ <₃≡≡ (MutualOrdIsSet x x') (<IsPropValued p q)
<IsPropValued (<₃≡> x p) (<₃≡> x' q) = cong₂ <₃≡> (MutualOrdIsSet x x') (<IsPropValued p q)
<IsPropValued (<₃>≡ x p) (<₃>≡ x' q) = cong₂ <₃>≡ (MutualOrdIsSet x x') (<IsPropValued p q)
<IsPropValued (<₃>> x p) (<₃>> x' q) = cong₂ <₃>> (MutualOrdIsSet x x') (<IsPropValued p q)
<IsPropValued (<₂≡≡ p) (<₃≡≡ x q) = ⊥-elim (<-irreflexive x p)
<IsPropValued (<₂≡> p) (<₃≡> x q) = ⊥-elim (<-irreflexive x p)
<IsPropValued (<₂>≡ p) (<₃>≡ x q) = ⊥-elim (<-irreflexive x p)
<IsPropValued (<₂>> p) (<₃>> x q) = ⊥-elim (<-irreflexive x p)
<IsPropValued (<₃≡≡ x p) (<₂≡≡ q) = ⊥-elim (<-irreflexive x q)
<IsPropValued (<₃>> x p) (<₂>> q) = ⊥-elim (<-irreflexive x q)
<IsPropValued (<₃>≡ x p) (<₂>≡ q) = ⊥-elim (<-irreflexive x q)
<IsPropValued (<₃≡> x p) (<₂≡> q) = ⊥-elim (<-irreflexive x q)


>fstIsPropValued (.𝟎 , fst𝟎 , p) (.𝟎 , fst𝟎 , p') = cong  z  (_ , _ , z)) (<IsPropValued p p')
>fstIsPropValued (b , fst≡ x , p) (b' , fst≡ x' , p')
  with PropEqfromPath x | PropEqfromPath x'
... | P.refl | P.refl = cong  z  (b' , z))
                             (cong₂ _,_ (cong fst≡ (MutualOrdIsSet x x'))
                                        (<IsPropValued p p'))
>fstIsPropValued (b , fst> x , p) (b' , fst> x' , p')
  with PropEqfromPath x | PropEqfromPath x'
... | P.refl | P.refl = cong  z  (b' , z))
                             (cong₂ _,_ (cong fst> (MutualOrdIsSet x x'))
                                        (<IsPropValued p p'))

≡fstIsPropValued p q = sound-injective p q (MutualOrdIsSet _ _)

≤IsPropValued (inj₁ p) (inj₁ q) = cong inj₁ (>fstIsPropValued p q)
≤IsPropValued (inj₁ p) (inj₂ q) = ⊥-elim (<-irreflexive ((sound-≡fst q) ⁻¹) (sound->fst p))
≤IsPropValued (inj₂ p) (inj₁ q) = ⊥-elim (<-irreflexive ((sound-≡fst p) ⁻¹) (sound->fst q))
≤IsPropValued (inj₂ p) (inj₂ q) = cong inj₂ (≡fstIsPropValued p q)

MutualOrdIsDiscrete 𝟎 𝟎 = yes refl
MutualOrdIsDiscrete (ω^ a + b ≡[ r ]) (ω^ a' + b' ≡[ r' ]) with MutualOrdIsDiscrete a a'
MutualOrdIsDiscrete ω^ a + b ≡[ r ] ω^ a' + b' ≡[ r' ] | yes a≡a' with MutualOrdIsDiscrete b b'
MutualOrdIsDiscrete ω^ a + b ≡[ r ] ω^ a' + b' ≡[ r' ] | yes a≡a' | yes b≡b' = yes (MutualOrd⁼ a≡a' b≡b')
MutualOrdIsDiscrete ω^ a + b ≡[ r ] ω^ a' + b' ≡[ r' ] | yes p | no b≠b' = no λ e  b≠b' (cong rest e)
MutualOrdIsDiscrete ω^ a + b ≡[ r ] ω^ a' + b' ≡[ r' ] | no a≠a' = no λ e  a≠a' (cong fst e)
MutualOrdIsDiscrete (ω^ a + b >[ r ]) (ω^ a' + b' >[ r' ]) with MutualOrdIsDiscrete a a'
MutualOrdIsDiscrete ω^ a + b >[ r ] ω^ a' + b' >[ r' ] | yes a≡a' with MutualOrdIsDiscrete b b'
MutualOrdIsDiscrete ω^ a + b >[ r ] ω^ a' + b' >[ r' ] | yes a≡a' | yes b≡b' = yes (MutualOrd⁼ a≡a' b≡b')
MutualOrdIsDiscrete ω^ a + b >[ r ] ω^ a' + b' >[ r' ] | yes p | no b≠b' = no λ e  b≠b' (cong rest e)
MutualOrdIsDiscrete ω^ a + b >[ r ] ω^ a' + b' >[ r' ] | no a≠a' = no λ e  a≠a' (cong fst e)
MutualOrdIsDiscrete (ω^ a + b ≡[ r ]) (ω^ a' + b' >[ r' ]) = no ω≡≢ω>
MutualOrdIsDiscrete (ω^ a + b >[ r ]) (ω^ a' + b' ≡[ r' ]) = no ω>≢ω≡
MutualOrdIsDiscrete 𝟎 (ω^ b + c ≡[ r ]) = no 𝟎≢ω
MutualOrdIsDiscrete 𝟎 (ω^ b + c >[ r ]) = no 𝟎≢ω
MutualOrdIsDiscrete (ω^ a + b ≡[ r ]) 𝟎 = no ω≢𝟎

MutualOrdIsDiscrete (ω^ a + b >[ r ]) 𝟎 = no ω≢𝟎

≡-normalise : a  b  a  b
≡-normalise {a} {b} a≡b with MutualOrdIsDiscrete a b
... | yes p = p
... | no ¬p = ⊥-elim (¬p a≡b)

≡-normalise-constant : (p q : a  b)  ≡-normalise p  ≡-normalise q
≡-normalise-constant {a} {b} p q with MutualOrdIsDiscrete a b
... | yes _ = refl
... | no ¬p = ⊥-elim (¬p p)

≡-canonical : (p : a  b)
             (≡-normalise refl) ⁻¹  (≡-normalise p)  p
≡-canonical = J  y p  (≡-normalise refl) ⁻¹  (≡-normalise p)  p)
                (lCancel (≡-normalise refl))

MutualOrdIsSet p q =
  ((≡-canonical p) ⁻¹) 
  cong ((≡-normalise refl) ⁻¹ ∙_) (≡-normalise-constant p q) 
  ≡-canonical q

MutualOrd⁼ {a} {b} a≡c b≡d with PropEqfromPath a≡c | PropEqfromPath b≡d
... | P.refl | P.refl = cong (ω^ a + b [_]) (≤IsPropValued _ _)

\end{code}

Using this, it is easy to check that the roundtrips are identities:

\begin{code}

from-to :  a  from (to a)  a
from-to M.𝟎 = refl
from-to (M.ω^ a + b [ r ]) with to-≥ {b = b} r
from-to M.ω^ a + b [ r ] | inj₁ x = M.MutualOrd⁼ (from-to a) (from-to b)
from-to M.ω^ a + b [ r ] | inj₂ x = M.MutualOrd⁼ (from-to a) (from-to b)

to-from :  a  to (from a)  a
to-from 𝟎 = refl
to-from ω^ a + b ≡[ r ] = MutualOrd⁼ (to-from a) (to-from b)
to-from ω^ a + b >[ r ] = MutualOrd⁼ (to-from a) (to-from b)
\end{code}