-------------------------------------------------------------
- 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.ω^ _ + c [ r ]} (inj₁ M.<₁) = inj₁ (_ , fstω , <₁)
to-≥ {b = M.ω^ _ + c [ r ]} (inj₁ (M.<₂ p)) = inj₁ (_ , fstω , <₂ (to-> p))
to-≥ {b = M.ω^ _ + c [ r ]} (inj₁ (M.<₃ e p)) = inj₁ (_ , fstω , <₃ (cong to e) (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'
{--- Pattern match on b, to please Agda's termination checker ---}
MutualOrdIsDiscrete ω^ a + 𝟎 ≡[ r ] ω^ a' + b' ≡[ r' ] | yes a≡a' | yes 𝟎≡b'
  with PropEqfromPath a≡a' | PropEqfromPath 𝟎≡b'
... | P.refl | P.refl = yes (cong (ω^ a + 𝟎 ≡[_]) (≡fstIsPropValued r r'))
MutualOrdIsDiscrete ω^ a + b@(ω^ _ + _ ≡[ _ ]) ≡[ r ] ω^ a' + b' ≡[ r' ] | yes a≡a' | yes b≡b'
  with PropEqfromPath a≡a' | PropEqfromPath b≡b'
... | P.refl | P.refl = yes (cong (ω^ a + b ≡[_]) (≡fstIsPropValued r r'))
MutualOrdIsDiscrete ω^ a + b@(ω^ _ + _ >[ _ ]) ≡[ r ] ω^ a' + b' ≡[ r' ] | yes a≡a' | yes b≡b'
  with PropEqfromPath a≡a' | PropEqfromPath b≡b'
... | P.refl | P.refl = yes (cong (ω^ a + b ≡[_]) (≡fstIsPropValued r r'))
{------------------- End of pattern matching --------------------}
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'
{--- Pattern match on b, to please Agda's termination checker ---}
MutualOrdIsDiscrete ω^ a + 𝟎 >[ r ] ω^ a' + b' >[ r' ] | yes a≡a' | yes 𝟎≡b'
  with PropEqfromPath a≡a' | PropEqfromPath 𝟎≡b'
... | P.refl | P.refl = yes (cong (ω^ a + 𝟎 >[_]) (>fstIsPropValued r r'))
MutualOrdIsDiscrete ω^ a + b@(ω^ _ + _ ≡[ _ ]) >[ r ] ω^ a' + b' >[ r' ] | yes a≡a' | yes b≡b'
  with PropEqfromPath a≡a' | PropEqfromPath b≡b'
... | P.refl | P.refl = yes (cong (ω^ a + b >[_]) (>fstIsPropValued r r'))
MutualOrdIsDiscrete ω^ a + b@(ω^ _ + _ >[ _ ]) >[ r ] ω^ a' + b' >[ r' ] | yes a≡a' | yes b≡b'
  with PropEqfromPath a≡a' | PropEqfromPath b≡b'
... | P.refl | P.refl = yes (cong (ω^ a + b >[_]) (>fstIsPropValued r r'))
{------------------- End of pattern matching --------------------}
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:



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}
