----------------------------------------------------------------------------------------------------
-- Multiplication of Cantor Normal Forms
----------------------------------------------------------------------------------------------------

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

module CantorNormalForm.Multiplication where

open import Cubical.Foundations.Prelude
open import Cubical.Data.Sum
open import Cubical.Data.Empty
  renaming (rec to ⊥-rec)
open import Cubical.Data.Sigma
open import Cubical.Data.Nat
  using ( ; zero ; suc)
open import Cubical.Data.Nat.Order.Recursive
  renaming (_<_ to _<ᴺ_)
  using ()

open import CantorNormalForm.Base
open import CantorNormalForm.Addition

open import Abstract.ZerSucLim _<_ _≤_


{- Multiplication -}

infixl 36 _·_ _•_

mutual

 _·_ : Cnf  Cnf  Cnf
 𝟎 · b = 𝟎
 a · 𝟎 = 𝟎
 a · (ω^ 𝟎 + d [ r ]) = a + a · d
 (ω^ a + c [ r ]) · (ω^ b@(ω^ _ + _ [ _ ]) + d [ s ]) =
     ω^ (a + b) + (ω^ a + c [ r ] · d) [ ≥left· (ω^ a + c [ r ]) b d <₁ s ]

 ≥left· :  x b d  b > 𝟎  b  left d  left x + b  left (x · d)
 ≥left· 𝟎 b d b>𝟎 _ = 𝟎≤
 ≥left· x@(ω^ a + c [ r ]) b@(ω^ _ + _ [ _ ]) 𝟎 <₁ _ = 𝟎≤
 ≥left· x@(ω^ a + c [ r ]) b@(ω^ _ + _ [ _ ]) (ω^ 𝟎 + d' [ t ]) <₁ _ = goal
  where
   claim : a + b  a
   claim = ≤+r a b
   IH : a + b  left (x · d')
   IH = ≥left· x b d' <₁ (≤-trans t 𝟎≤)
   goal : a + b  left (x + x · d')
   goal = ≥left+ x (x · d') claim IH
 ≥left· x@(ω^ a + c [ r ]) b@(ω^ _ + _ [ _ ]) (ω^ d@(ω^ _ + _ [ _ ]) + d' [ _ ]) <₁ d≤b = goal
  where
   goal : a + b  a + d
   goal = +r-is-≤-monotone a d b d≤b

{- Some properties of multiplication -}

·𝟎-is-𝟎 :  a  a · 𝟎  𝟎
·𝟎-is-𝟎  𝟎               = refl
·𝟎-is-𝟎 (ω^ a + b [ r ]) = refl

·𝟏-is-identity :  a  a · 𝟏  a
·𝟏-is-identity  𝟎               = refl
·𝟏-is-identity (ω^ a + b [ r ]) = refl

·+𝟏-spec : (a b : Cnf)  a · (b + 𝟏)  a · b + a
·+𝟏-spec 𝟎 b = refl
·+𝟏-spec (ω^ a + c [ r ]) 𝟎 = refl
·+𝟏-spec (ω^ a + c [ r ]) (ω^ b + d [ s ]) with <-tri b 𝟎
·+𝟏-spec x@(ω^ a + c [ r ]) (ω^ 𝟎 + d [ s ]) | inr _ = goal
 where
  IH : x · (d + 𝟏)  x · d + x
  IH = ·+𝟏-spec x d
  claim₀ : x + x · (d + 𝟏)  x + (x · d + x)
  claim₀ = cong (x +_) IH
  claim₁ : x + (x · d + x)  x + x · d + x
  claim₁ = +-is-assoc x (x · d) x
  goal : x + x · (d + 𝟏)  (x + x · d) + x
  goal = claim₀  claim₁
·+𝟏-spec x@(ω^ a + c [ r ]) (ω^ y@(ω^ _ + _ [ _ ]) + d [ s ]) | inr _ =
   ω^ (a + y) + x · (d + 𝟏) [ _ ]
  ≡⟨ ω^+≡ω^⟨⟩+ _ _ 
   ω^⟨ a + y  + x · (d + 𝟏)
  ≡⟨ cong (ω^⟨ a + y  +_) (·+𝟏-spec x d) 
   ω^⟨ a + y  + (x · d + x)
  ≡⟨ +-is-assoc ω^⟨ a + y  (x · d) x 
   ω^⟨ a + y  + x · d + x
  ≡⟨ cong (_+ x) (sym (ω^+≡ω^⟨⟩+ _ _)) 
   ω^ (a + y) + x · d [ _ ] + x 

ω^·ω^>𝟎 :  a b  a > 𝟎  b > 𝟎  a · b > 𝟎
ω^·ω^>𝟎 a@(ω^ _ + _ [ _ ]) (ω^ 𝟎 + b' [ s ]) <₁ <₁ = <∘≤-in-< <₁ (≤+r a (a · b'))
ω^·ω^>𝟎 a@(ω^ _ + _ [ _ ]) (ω^ b@(ω^ _ + _ [ _ ]) + b' [ s ]) <₁ <₁ = <₁

left⟨·nat⟩ :  a b  left b  𝟎  left a  left (a + a · b)
left⟨·nat⟩ 𝟎 b _ = refl
left⟨·nat⟩ (ω^ a + c [ r ]) 𝟎 e = refl
left⟨·nat⟩ x@(ω^ a + c [ r ]) (ω^ 𝟎 + d [ s ]) _ = goal
 where
  IH : a  left (x + x · d)
  IH = left⟨·nat⟩ x d (≤𝟎→≡𝟎 s)
  goal : a  left (x + (x + x · d))
  goal = left⟨+⟩ x (x + x · d) (inr IH)
left⟨·nat⟩ (ω^ a + c [ r ]) (ω^ b@(ω^ _ + _ [ _ ]) + d [ s ]) b≡𝟎 = ⊥-rec (ω≢𝟎 b≡𝟎)

left⟨·nat⟩⁻¹ :  a b  a > 𝟎  left (a · b)  left a  left b  𝟎
left⟨·nat⟩⁻¹ _ 𝟎 <₁ _ = refl
left⟨·nat⟩⁻¹ _ (ω^ 𝟎 + _ [ _ ]) <₁ _ = refl
left⟨·nat⟩⁻¹ _ (ω^ (ω^ _ + _ [ _ ]) + _ [ _ ]) <₁ v = ⊥-rec (≤→≯ v (<+r _ _ <₁))

·nat<ω^ :  a b c {d u}  left b  𝟎  c > 𝟎  a · b < ω^ (left a + c) + d [ u ]
·nat<ω^ a 𝟎 (ω^ c + c' [ t ]) e <₁ = subst (_< ω^ (left a + _) + _ [ _ ]) (sym (·𝟎-is-𝟎 a)) <₁
·nat<ω^ 𝟎 (ω^ 𝟎 + b' [ s ]) (ω^ c + c' [ t ]) e <₁ = <₁
·nat<ω^ x@(ω^ a + a' [ r ]) (ω^ 𝟎 + b' [ s ]) z@(ω^ c + c' [ t ]) {d} {u} e <₁ = goal
 where
  IH : x · b' < ω^ (a + z) + d [ u ]
  IH = ·nat<ω^ x b' z (≤𝟎→≡𝟎 s) <₁
  claim₀ : x + x · b' < x + ω^ (a + z) + d [ u ]
  claim₀ = +r-is-<-monotone x (x · b') (ω^ (a + z) + d [ u ]) IH
  claim₁ : x + ω^ (a + z) + d [ u ]  ω^ (a + z) + d [ u ]
  claim₁ with <-tri a (a + z)
  claim₁ | inl a<a+z = refl
  claim₁ | inr a≥a+z = ⊥-rec (≤→≯ a≥a+z (<+r a z <₁))
  goal : x + x · b' < ω^ (a + z) + d [ u ]
  goal = subst (_ <_) claim₁ claim₀
·nat<ω^ a (ω^ (ω^ _ + _ [ _ ]) + b' [ s ]) (ω^ c + c' [ t ]) e <₁ = ⊥-rec (ω≢𝟎 e)

ω^·-is-<-monotone :  x y z  x > 𝟎  y < z  x · y < x · z
ω^·-is-<-monotone x@(ω^ _ + _ [ _ ]) 𝟎 y@(ω^ _ + _ [ _ ]) <₁ <₁ = ω^·ω^>𝟎 x y <₁ <₁
ω^·-is-<-monotone x@(ω^ _ + _ [ _ ])
                  y@(ω^ 𝟎 + b' [ s ])
                  z@(ω^ 𝟎 + c' [ t ]) <₁ (<₃ e b'<c') = goal
 where
  IH : x · b' < x · c'
  IH = ω^·-is-<-monotone x b' c' <₁ b'<c'
  goal : x + x · b' < x + x · c'
  goal = +r-is-<-monotone x _ _ IH
ω^·-is-<-monotone x@(ω^ a + _ [ _ ])
                  y@(ω^ 𝟎 + _ [ _ ])
                  z@(ω^ c@(ω^ _ + _ [ _ ]) + c' [ _ ]) <₁ _ = goal
 where
  goal : x · y < ω^ (a + c) + x · c' [ _ ]
  goal = ·nat<ω^ x y c refl <₁
ω^·-is-<-monotone x@(ω^ _ + _ [ _ ])
                  y@(ω^ (ω^ _ + _ [ _ ]) + _ [ _ ])
                  z@(ω^ 𝟎 + _ [ _ ]) <₁ y<z = ⊥-rec (<→≯ y<z (<₂ <₁))
ω^·-is-<-monotone x@(ω^ a + _ [ _ ])
                  y@(ω^ b@(ω^ _ + _ [ _ ]) + b' [ _ ])
                  z@(ω^ c@(ω^ _ + _ [ _ ]) + c' [ _ ]) <₁ (<₂ b<c) = goal
 where
  goal : ω^ (a + b) + x · b' [ _ ] < ω^ (a + c) + x · c' [ _ ]
  goal = <₂ (+r-is-<-monotone a b c b<c)
ω^·-is-<-monotone x@(ω^ a + a' [ r ])
                  y@(ω^ b@(ω^ _ + _ [ _ ]) + b' [ s ])
                  z@(ω^ c@(ω^ _ + _ [ _ ]) + c' [ t ]) <₁ (<₃ b≡c b'<c') = goal
 where
  IH : x · b' < x · c'
  IH = ω^·-is-<-monotone x b' c' <₁ b'<c'
  goal : ω^ (a + b) + x · b' [ _ ] < ω^ (a + c) + x · c' [ _ ]
  goal = <₃ (cong (a +_) b≡c) IH

·r-is-≤-monotone :  a b c  b  c  a · b  a · c
·r-is-≤-monotone 𝟎 b c b≤c = inr refl
·r-is-≤-monotone (ω^ a + a' [ r ]) b c (inl b<c) = inl (ω^·-is-<-monotone _ _ _ <₁ b<c)
·r-is-≤-monotone (ω^ a + a' [ r ]) b c (inr c≡b) = inr (cong ((ω^ a + a' [ r ]) ·_) c≡b)

·-is-left-distributive :  x y z  x · (y + z)  x · y + x · z
·-is-left-distributive 𝟎 y z = refl
·-is-left-distributive (ω^ a + a' [ r ]) 𝟎 z = refl
·-is-left-distributive (ω^ a + a' [ r ]) (ω^ b + b' [ s ]) 𝟎 = sym +𝟎-is-identity
·-is-left-distributive x@(ω^ a + a' [ r ]) y@(ω^ 𝟎 + b' [ s ]) z@(ω^ 𝟎 + c' [ t ]) = goal
 where
  IH : x · (b' + z)  x · b' + x · z -- ≡ x · b' + (x + x · c')
  IH = ·-is-left-distributive x b' z
  claim₀ : x + x · (b' + z)  x + (x · b' + x · z)
  claim₀ = cong (x +_) IH
  claim₁ : x + (x · b' + x · z)  (x + x · b') + (x + x · c')
  claim₁ = +-is-assoc x (x · b') (x · z)
  goal : x + x · (b' + z)  (x + x · b') + (x + x · c')
  goal = claim₀  claim₁
·-is-left-distributive x@(ω^ a + a' [ r ])
                       y@(ω^ 𝟎 + b' [ s ])
                       z@(ω^ c@(ω^ v + v' [ _ ]) + c' [ t ]) = goal
 where
  claim₀ : a  left (x + x · b')
  claim₀ = left⟨·nat⟩ x b' (≤𝟎→≡𝟎 s)
  a<a+c : a < a + c
  a<a+c = <+r a c <₁
  claim₁ : left (x + x · b') < a + c
  claim₁ = subst (_< a + c) claim₀ a<a+c
  goal : ω^ (a + c) + x · c' [ _ ]  (x + x · b') + (ω^ (a + c) + x · c' [ _ ])
  goal = sym (+-left-cancel (x + x · b') _ claim₁)
·-is-left-distributive x@(ω^ a + a' [ r ])
                       y@(ω^ b@(ω^ u + u' [ _ ]) + b' [ s ])
                       z@(ω^ 𝟎 + c' [ t ]) =
    ω^ (a + b) + x · (b' + z) [ _ ]
   ≡⟨ ω^+≡ω^⟨⟩+ _ _ 
    ω^⟨ a + b  + x · (b' + z)
   ≡⟨ cong (ω^⟨ a + b  +_) IH 
    ω^⟨ a + b  + (x · b' + x · z)
   ≡⟨ +-is-assoc ω^⟨ a + b  (x · b') (x · z) 
    (ω^⟨ a + b  + x · b') + x · z
   ≡⟨ cong (_+ x · z) (sym (ω^+≡ω^⟨⟩+ _ _)) 
    (ω^ (a + b) + x · b' [ _ ]) + x · z 
 where
  IH : x · (b' + z)  x · b' + x · z
  IH = ·-is-left-distributive x b' z
·-is-left-distributive x@(ω^ a + a' [ r ])
                       y@(ω^ b@(ω^ u + u' [ _ ]) + b' [ s ])
                       z@(ω^ c@(ω^ v + v' [ _ ]) + c' [ t ]) = goal
 where
  goal : x · (y + z)  x · y + x · z
  goal with <-tri b c with <-tri u v with <-tri (a + b) (a + c)
  goal | inl b<c | inl u<v | inl a+b<a+c = refl
  goal | inl b<c | inl u<v | inr a+b≥a+c = ⊥-rec (≤→≯ a+b≥a+c (+r-is-<-monotone a b c b<c))
  goal | inl b<c | inr (inl u>v) | inl a+b<a+c = ⊥-rec (<→≯ b<c (<₂ u>v))
  goal | inl b<c | inr (inr u≡v) | inl a+b<a+c with <-tri u' v'
  goal | inl b<c | inr (inr u≡v) | inl a+b<a+c | inl u'<v' = refl
  goal | inl b<c | inr (inr u≡v) | inl a+b<a+c | inr u'≥v' = ⊥-rec (≤→≯ u'≥v' (<₃⁻¹ u≡v b<c))
  goal | inl b<c | inr u≥v | inr a+b≥a+c = ⊥-rec (≤→≯ a+b≥a+c (+r-is-<-monotone a b c b<c))
  goal | inr b≥c | inl u<v | inl a+b<a+c = ⊥-rec (≤→≯ (+r-is-≤-monotone a c b b≥c) a+b<a+c)
  goal | inr b≥c | inl u<v | inr a+b≥a+c = ⊥-rec (≤→≯ b≥c (<₂ u<v))
  goal | inr b≥c | inr u≥v | inl a+b<a+c = ⊥-rec (≤→≯ (+r-is-≤-monotone a c b b≥c) a+b<a+c)
  goal | inr b≥c | inr (inl u>v) | inr a+b≥a+c = Goal
   where
    IH : x · (b' + z)  x · b' + x · z
    IH = ·-is-left-distributive x b' z
    Goal : ω^ (a + b) + x · (b' + z) [ _ ]   ω^ (a + b) + (x · b' + x · z) [ _ ]
    Goal = Cnf⁼ refl IH
  goal | inr b≥c | inr (inr u≡v) | inr a+b≥a+c with <-tri u' v'
  goal | inr b≥c | inr (inr u≡v) | inr a+b≥a+c | inl u'<v' = ⊥-rec (≤→≯ b≥c (<₃ u≡v u'<v'))
  goal | inr b≥c | inr (inr u≡v) | inr a+b≥a+c | inr (inl u'>v') = Goal
   where
    IH : x · (b' + z)  x · b' + x · z
    IH = ·-is-left-distributive x b' z
    Goal : ω^ (a + b) + x · (b' + z) [ _ ]  ω^ (a + b) + (x · b' + x · z) [ _ ]
    Goal = Cnf⁼ refl IH
  goal | inr b≥c | inr (inr u≡v) | inr a+b≥a+c | inr (inr u'≡v') = Goal
   where
    IH : x · (b' + z)  x · b' + x · z
    IH = ·-is-left-distributive x b' z
    Goal : ω^ (a + b) + x · (b' + z) [ _ ]  ω^ (a + b) + (x · b' + x · z) [ _ ]
    Goal = Cnf⁼ refl IH

·ω^⟨⟩≡ω^⟨left+⟩ :  a b  a > 𝟎  b > 𝟎  a · ω^⟨ b   ω^⟨ left a + b 
·ω^⟨⟩≡ω^⟨left+⟩ x@(ω^ a + c [ r ]) y@(ω^ b + d [ s ]) <₁ <₁ = refl


-- Multiplication with natural numbers

_•_ : Cnf    Cnf
a  n = a · NtoC n

•suc-spec :  a n  a  suc n  a  n + a
•suc-spec a n = claim₀  claim₁
 where
  claim₀ : a  suc n  a · (NtoC n + 𝟏)
  claim₀ = cong (a ·_) (NtoC⟨suc⟩≡NtoC+𝟏 n)
  claim₁ : a · (NtoC n + 𝟏)  a  n + a
  claim₁ = ·+𝟏-spec a (NtoC n)

•suc-spec' :  a n  a  suc n  a + a  n
•suc-spec' 𝟎 n = refl
•suc-spec' (ω^ _ + _ [ _ ]) n = refl

ω^•-is-<-increasing :  a  a > 𝟎  is-<-increasing (a •_)
ω^•-is-<-increasing a@(ω^ _ + _ [ _ ]) <₁ n = subst (a  n <_) claim₁ claim₀
 where
  claim₀ : a  n < a  n + a
  claim₀ = <+r _ a <₁
  claim₁ : a  n + a  a  suc n
  claim₁ = sym (•suc-spec a n)

left•≤ :  a n  left (a  n)  left a
left•≤ 𝟎 n = inr refl
left•≤ x@(ω^ a + b [ r ]) 0 = 𝟎≤
left•≤ x@(ω^ a + b [ r ]) (suc n) = goal
 where
  claim₀ : x  suc n  x  n + x
  claim₀ = •suc-spec x n
  claim₁ : left (x  n + x)  a
  claim₁ = ≥left+ (x  n) x (left•≤ x n) (inr refl)
  goal : left (x  suc n)  a
  goal = subst  z  left z  a) (sym claim₀) claim₁

Σn<•sn :  {a} b  a  left b  Σ[ n   ] b < ω^⟨ a   suc n
Σn<•sn {a} 𝟎 _ = 0 , <₁
Σn<•sn {a} (ω^ b + c [ r ]) (inl a>b) = 0 , <₂ a>b
Σn<•sn {a} (ω^ b + c [ r ]) (inr a≡b) = suc n , goal
 where
  r' : a  left c
  r' = subst (_≥ left c) (sym a≡b) r
  IH : Σ[ n   ] c < ω^⟨ a   suc n
  IH = Σn<•sn {a} c r'
  n : 
  n = fst IH
  claim₁ : ω^ b + c [ r ] < ω^ a + ω^⟨ a   suc n [ left•≤ ω^⟨ a  (suc n) ]
  claim₁ = <₃ (sym a≡b) (snd IH)
  claim₂ : ω^ a + ω^⟨ a   suc n [ _ ]  ω^⟨ a  + ω^⟨ a   suc n
  claim₂ = ω^+≡ω^⟨⟩+ _ _
  claim₃ : ω^⟨ a  + ω^⟨ a   suc n  ω^⟨ a   suc (suc n)
  claim₃ = sym (•suc-spec' ω^⟨ a  (suc n))
  goal : ω^ b + c [ r ] < ω^⟨ a   suc (suc n)
  goal = subst (ω^ b + c [ r ] <_) (claim₂  claim₃) claim₁


{- Converting to CNF's of the form "ω ^ a • n + d" with n ∈ ℕ and d < a -}

to-CNF-with-coefficients :  a b r 
  Σ[ n   ] Σ[ d  Cnf ] (ω^ a + b [ r ]  ω^⟨ a   n + d) × (left d < ω^⟨ a )
to-CNF-with-coefficients a 𝟎 _ = 1 , 𝟎 , Cnf⁼ refl refl , <₁
to-CNF-with-coefficients a y@(ω^ b + _ [ _ ]) (inl a>b) = 1 , y , ω^+≡ω^⟨⟩+ _ _ , <-trans a>b left<
to-CNF-with-coefficients a y@(ω^ b + b' [ s ]) (inr a≡b) = suc n , d , goal₀ , goal₁
 where
  IH : Σ[ n   ] Σ[ d  Cnf ] (ω^ b + b' [ s ]  ω^⟨ b   n + d) × (left d < ω^⟨ b )
  IH = to-CNF-with-coefficients b b' s
  n : 
  n = fst IH
  d : Cnf
  d = fst (snd IH)
  goal₀ : ω^ a + y [ _ ]  ω^⟨ a   suc n + d
  goal₀ =
    ω^ a + y [ _ ]
   ≡⟨ ω^+≡ω^⟨⟩+ _ _ 
    ω^⟨ a  + y
   ≡⟨ cong (ω^⟨ a  +_) (fst (snd (snd IH))) 
    ω^⟨ a  + (ω^⟨ b   n + d)
   ≡⟨ cong  z  ω^⟨ a  + (ω^⟨ z   n + d)) (sym a≡b) 
    ω^⟨ a  + (ω^⟨ a   n + d)
   ≡⟨ +-is-assoc ω^⟨ a  (ω^⟨ a   n) d 
    (ω^⟨ a  + ω^⟨ a   n) + d
   ≡⟨ cong (_+ d) (•suc-spec' ω^⟨ a  n) 
    ω^⟨ a   suc n + d 
  goal₁ : left d < ω^⟨ a 
  goal₁ = subst  z  left d < ω^⟨ z ) (sym a≡b) (snd (snd (snd IH)))