{-# OPTIONS --safe #-}
module Cubical.Tactics.CommRingSolver.EvalHom where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat using ()
open import Cubical.Data.Int.Base hiding (_+_ ; _·_ ; -_)
open import Cubical.Data.FinData
open import Cubical.Data.Vec
open import Cubical.Data.Bool
open import Cubical.Relation.Nullary.Base

open import Cubical.Tactics.CommRingSolver.Utility
open import Cubical.Tactics.CommRingSolver.RawAlgebra
open import Cubical.Tactics.CommRingSolver.IntAsRawRing
open import Cubical.Tactics.CommRingSolver.HornerForms
open import Cubical.Tactics.CommRingSolver.HornerEval
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.Ring

private
  variable
     : Level

module HomomorphismProperties (R : CommRing ) where
  private
    νR = CommRing→RawℤAlgebra R
  open CommRingStr (snd R)
  open RingTheory (CommRing→Ring R)
  open IteratedHornerOperations νR

  EvalHom+0 : {n : } (P : IteratedHornerForms νR n) (xs : Vec  νR  n)
       eval (0ₕ +ₕ P) xs  eval P xs
  EvalHom+0 {n = ℕ.zero} (const x) [] = cong (scalar R) (+Ridℤ x)
  EvalHom+0 {n = ℕ.suc _} P xs = refl

  Eval0H : {n : } (xs : Vec  νR  n)
          eval {A = νR} 0ₕ xs  0r
  Eval0H  [] = refl
  Eval0H (x  xs) = refl

  Eval1ₕ : {n : } (xs : Vec  νR  n)
          eval {A = νR} 1ₕ xs  1r
  Eval1ₕ [] = refl
  Eval1ₕ (x  xs) =
    eval 1ₕ (x  xs)                             ≡⟨ refl 
    eval (0H ·X+ 1ₕ) (x  xs)                    ≡⟨ combineCasesEval R 0H 1ₕ x xs 
    eval {A = νR} 0H (x  xs) · x + eval 1ₕ xs   ≡⟨ cong  u  u · x + eval 1ₕ xs)
                                                                   (Eval0H (x  xs)) 
    0r · x + eval 1ₕ xs                          ≡⟨ cong  u  0r · x + u)
                                                        (Eval1ₕ xs) 
    0r · x + 1r                                  ≡⟨ cong  u  u + 1r)
                                                         (0LeftAnnihilates _) 
    0r + 1r                                      ≡⟨ +IdL _ 
    1r 

  -EvalDist :
    {n : } (P : IteratedHornerForms νR n) (xs : Vec  νR  n)
     eval (-ₕ P) xs  - eval P xs
  -EvalDist (const x) []   = -DistScalar R x
  -EvalDist 0H  xs =
    eval (-ₕ 0H) xs  ≡⟨ Eval0H xs 
    0r               ≡⟨ sym 0Selfinverse 
    - 0r             ≡⟨ cong -_ (sym (Eval0H xs)) 
    - eval 0H xs     
  -EvalDist (P ·X+ Q) (x  xs) =
      eval (-ₕ (P ·X+ Q)) (x  xs)
    ≡⟨ refl 
      eval ((-ₕ P) ·X+ (-ₕ Q)) (x  xs)
    ≡⟨ combineCasesEval R (-ₕ P) (-ₕ Q) x xs 
      (eval (-ₕ P) (x  xs)) · x + eval (-ₕ Q) xs
    ≡⟨ cong  u  u · x + eval (-ₕ Q) xs) (-EvalDist P _) 
      (- eval P (x  xs)) · x + eval (-ₕ Q) xs
    ≡⟨ cong  u  (- eval P (x  xs)) · x + u) (-EvalDist Q _) 
      (- eval P (x  xs)) · x + - eval Q xs
    ≡[ i ]⟨ -DistL· (eval P (x  xs)) x i +  - eval Q xs 
      - ((eval P (x  xs)) · x) + (- eval Q xs)
    ≡⟨ -Dist _ _ 
      - ((eval P (x  xs)) · x + eval Q xs)
    ≡[ i ]⟨ - combineCasesEval R P Q x xs (~ i) 
      - eval (P ·X+ Q) (x  xs) 

  combineCases+ : {n : } (P Q : IteratedHornerForms νR (ℕ.suc n))
                  (r s : IteratedHornerForms νR n)
                  (x : fst R) (xs : Vec (fst R) n)
                   eval ((P ·X+ r) +ₕ (Q ·X+ s)) (x  xs)
                   eval ((P +ₕ Q) ·X+ (r +ₕ s)) (x  xs)
  combineCases+ {n = n} P Q r s x xs with (isZero νR (P +ₕ Q) and isZero νR (r +ₕ s))  true
  ... | yes p = compute+ₕEvalBothZero R n P Q r s x xs p
  ... | no p = compute+ₕEvalNotBothZero R n P Q r s x xs (¬true→false _ p)

  +Homeval :
    {n : } (P Q : IteratedHornerForms νR n) (xs : Vec  νR  n)
     eval (P +ₕ Q) xs  (eval P xs) + (eval Q xs)
  +Homeval (const x) (const y) [] = +HomScalar R x y
  +Homeval 0H Q xs =
    eval (0H +ₕ Q) xs            ≡⟨ refl 
    eval Q xs                    ≡⟨ sym (+IdL _) 
    0r + eval Q xs               ≡⟨ cong  u  u + eval Q xs) (sym (Eval0H xs)) 
    eval 0H xs + eval Q xs 
  +Homeval (P ·X+ Q) 0H xs =
    eval ((P ·X+ Q) +ₕ 0H) xs                    ≡⟨ refl 
    eval (P ·X+ Q) xs                            ≡⟨ sym (+IdR _) 
    eval (P ·X+ Q) xs + 0r
   ≡⟨ cong  u  eval (P ·X+ Q) xs + u) (sym (Eval0H xs)) 
    eval (P ·X+ Q) xs + eval 0H xs 
  +Homeval (P ·X+ Q) (S ·X+ T) (x  xs) =
    eval ((P ·X+ Q) +ₕ (S ·X+ T)) (x  xs)
   ≡⟨ combineCases+ P S Q T x xs 
    eval ((P +ₕ S) ·X+ (Q +ₕ T)) (x  xs)
   ≡⟨ combineCasesEval R (P +ₕ S) (Q +ₕ T) x xs 
    (eval (P +ₕ S) (x  xs)) · x + eval (Q +ₕ T) xs
   ≡⟨ cong  u  (eval (P +ₕ S) (x  xs)) · x + u) (+Homeval Q T xs) 
    (eval (P +ₕ S) (x  xs)) · x + (eval Q xs + eval T xs)
   ≡⟨ cong  u  u · x + (eval Q xs + eval T xs)) (+Homeval P S (x  xs)) 
    (eval P (x  xs) + eval S (x  xs)) · x
    + (eval Q xs + eval T xs)
   ≡⟨ cong  u  u + (eval Q xs + eval T xs)) (·DistL+ _ _ _) 
    (eval P (x  xs)) · x + (eval S (x  xs)) · x
    + (eval Q xs + eval T xs)
   ≡⟨ +ShufflePairs _ _ _ _ 
    ((eval P (x  xs)) · x + eval Q xs)
    + ((eval S (x  xs)) · x + eval T xs)
   ≡[ i ]⟨ combineCasesEval R P Q x xs (~ i) + combineCasesEval R S T x xs (~ i) 
    eval (P ·X+ Q) (x  xs)
    + eval (S ·X+ T) (x  xs) 

  ⋆Homeval : {n : }
             (r : IteratedHornerForms νR n)
             (P : IteratedHornerForms νR (ℕ.suc n)) (x :  νR ) (xs : Vec  νR  n)
            eval (r  P) (x  xs)  eval r xs · eval P (x  xs)

  ⋆0LeftAnnihilates :
    {n : } (P : IteratedHornerForms νR (ℕ.suc n)) (xs : Vec  νR  (ℕ.suc n))
     eval (0ₕ  P) xs  0r
  ⋆0LeftAnnihilates 0H xs = Eval0H xs
  ⋆0LeftAnnihilates {n = ℕ.zero} (P ·X+ Q) (x  xs) = refl
  ⋆0LeftAnnihilates {n = ℕ.suc _} (P ·X+ Q) (x  xs) = refl

  ⋆isZeroLeftAnnihilates :
    {n : } (r : IteratedHornerForms νR n)
            (P : IteratedHornerForms νR (ℕ.suc n))
    (xs : Vec  νR  (ℕ.suc n))
     isZero νR r  true
     eval (r  P) xs  0r
  ⋆isZeroLeftAnnihilates r P xs isZero-r = evalIsZero R (r  P) xs (isZeroPresLeft⋆ r P isZero-r)

  ·0LeftAnnihilates :
    {n : } (P : IteratedHornerForms νR n) (xs : Vec  νR  n)
     eval (0ₕ ·ₕ P) xs  0r
  ·0LeftAnnihilates (const x) xs =
    eval (const _) xs ≡⟨ Eval0H xs  0r 
  ·0LeftAnnihilates 0H xs = Eval0H xs
  ·0LeftAnnihilates (P ·X+ P₁) xs = Eval0H xs

  ·isZeroLeftAnnihilates :
    {n : } (P Q : IteratedHornerForms νR n)
    (xs : Vec (fst R) n)
     isZero νR P  true
     eval (P ·ₕ Q) xs  0r
  ·isZeroLeftAnnihilates P Q xs isZeroP = evalIsZero R (P ·ₕ Q) xs (isZeroPresLeft·ₕ P Q isZeroP)

  ·Homeval : {n : } (P Q : IteratedHornerForms νR n) (xs : Vec  νR  n)
     eval (P ·ₕ Q) xs  (eval P xs) · (eval Q xs)

  combineCases⋆ : {n : } (x : fst R) (xs : Vec (fst R) n)
                 (r : IteratedHornerForms νR n)
                 (P : IteratedHornerForms νR (ℕ.suc n))
                 (Q : IteratedHornerForms νR n)
                 eval (r  (P ·X+ Q)) (x  xs)  eval ((r  P) ·X+ (r ·ₕ Q)) (x  xs)
  combineCases⋆ x xs r P Q with isZero νR r  true
  ... | yes p =
    eval (r  (P ·X+ Q)) (x  xs)                ≡⟨ ⋆isZeroLeftAnnihilates r (P ·X+ Q) (x  xs) p 
    0r                                           ≡⟨ someCalculation R 
    0r · x + 0r                                  ≡⟨ step1 
    eval (r  P) (x  xs) · x + eval (r ·ₕ Q) xs  ≡⟨ sym (combineCasesEval R (r  P) (r ·ₕ Q) x xs) 
    eval ((r  P) ·X+ (r ·ₕ Q)) (x  xs) 
    where
      step1 : 0r · x + 0r  eval (r  P) (x  xs) · x + eval (r ·ₕ Q) xs
      step1 i = ⋆isZeroLeftAnnihilates r P (x  xs) p (~ i) · x + ·isZeroLeftAnnihilates r Q xs p (~ i)
  ... | no p with isZero νR r
  ...           | true = byAbsurdity (p refl)
  ...           | false = refl

  ⋆Homeval r 0H x xs =
    eval (r  0H) (x  xs)                ≡⟨ refl 
    0r                                    ≡⟨ sym (0RightAnnihilates _) 
    eval r xs · 0r                        ≡⟨ refl 
    eval r xs · eval {A = νR} 0H (x  xs) 
  ⋆Homeval r (P ·X+ Q) x xs =
      eval (r  (P ·X+ Q)) (x  xs)                ≡⟨ combineCases⋆ x xs r P Q 
      eval ((r  P) ·X+ (r ·ₕ Q)) (x  xs)
    ≡⟨ combineCasesEval R (r  P) (r ·ₕ Q) x xs 
      (eval (r  P) (x  xs)) · x + eval (r ·ₕ Q) xs
    ≡⟨ cong  u  u · x + eval (r ·ₕ Q) xs) (⋆Homeval r P x xs) 
      (eval r xs · eval P (x  xs)) · x + eval (r ·ₕ Q) xs
    ≡⟨ cong  u  (eval r xs · eval P (x  xs)) · x + u) (·Homeval r Q xs) 
      (eval r xs · eval P (x  xs)) · x + eval r xs · eval Q xs
    ≡⟨ cong  u  u  + eval r xs · eval Q xs) (sym (·Assoc _ _ _)) 
      eval r xs · (eval P (x  xs) · x) + eval r xs · eval Q xs
    ≡⟨ sym (·DistR+ _ _ _) 
      eval r xs · ((eval P (x  xs) · x) + eval Q xs)
    ≡[ i ]⟨ eval r xs · combineCasesEval R P Q x xs (~ i) 
      eval r xs · eval (P ·X+ Q) (x  xs) 

  lemmaForCombineCases· :
    {n : } (Q : IteratedHornerForms νR n) (P S : IteratedHornerForms νR (ℕ.suc n))
    (xs : Vec (fst R) (ℕ.suc n))
      isZero νR (P ·ₕ S)  true
     eval ((P ·X+ Q) ·ₕ S) xs  eval (Q  S) xs
  lemmaForCombineCases· Q P S xs isZeroProd with isZero νR (P ·ₕ S)
  ... | true = refl
  ... | false = byBoolAbsurdity isZeroProd

  combineCases· :
    {n : } (Q : IteratedHornerForms νR n) (P S : IteratedHornerForms νR (ℕ.suc n))
    (xs : Vec (fst R) (ℕ.suc n))
     eval ((P ·X+ Q) ·ₕ S) xs  eval (((P ·ₕ S) ·X+ 0ₕ) +ₕ (Q  S)) xs
  combineCases· Q P S (x  xs) with isZero νR (P ·ₕ S)  true
  ... | yes p =
        eval ((P ·X+ Q) ·ₕ S) (x  xs)                          ≡⟨ lemmaForCombineCases· Q P S (x  xs) p 
        eval (Q  S) (x  xs)                                   ≡⟨ sym (+IdL _) 
        0r + eval (Q  S) (x  xs)                              ≡⟨ step1 
        eval ((P ·ₕ S) ·X+ 0ₕ) (x  xs) + eval (Q  S) (x  xs)  ≡⟨ step2 
        eval (((P ·ₕ S) ·X+ 0ₕ) +ₕ (Q  S)) (x  xs)             
        where
          lemma =
            eval ((P ·ₕ S) ·X+ 0ₕ) (x  xs)          ≡⟨ combineCasesEval R (P ·ₕ S) 0ₕ x xs 
            eval (P ·ₕ S) (x  xs) · x + eval 0ₕ xs  ≡[ i ]⟨ evalIsZero R (P ·ₕ S) (x  xs) p i · x + Eval0H xs i 
            0r · x + 0r                             ≡⟨ sym (someCalculation R) 
            0r                                      
          step1 : _  _
          step1 i = lemma (~ i) + eval (Q  S) (x  xs)
          step2 = sym (+Homeval ((P ·ₕ S) ·X+ 0ₕ) (Q  S) (x  xs))
  ... | no p with isZero νR (P ·ₕ S)
  ...           | true = byAbsurdity (p refl)
  ...           | false = refl

  ·Homeval (const x) (const y) [] = ·HomScalar R x y
  ·Homeval 0H Q xs =
    eval (0H ·ₕ Q) xs       ≡⟨ Eval0H xs 
    0r                     ≡⟨ sym (0LeftAnnihilates _) 
    0r · eval Q xs         ≡⟨ cong  u  u · eval Q xs) (sym (Eval0H xs)) 
    eval 0H xs · eval Q xs 
  ·Homeval (P ·X+ Q) S (x  xs) =
      eval ((P ·X+ Q) ·ₕ S) (x  xs)
    ≡⟨ combineCases· Q P S (x  xs) 
      eval (((P ·ₕ S) ·X+ 0ₕ) +ₕ (Q  S)) (x  xs)
    ≡⟨ +Homeval ((P ·ₕ S) ·X+ 0ₕ) (Q  S) (x  xs) 
      eval ((P ·ₕ S) ·X+ 0ₕ) (x  xs) + eval (Q  S) (x  xs)
    ≡⟨ cong  u  u + eval (Q  S) (x  xs)) (combineCasesEval R (P ·ₕ S) 0ₕ x xs) 
      (eval (P ·ₕ S) (x  xs) · x + eval 0ₕ xs) + eval (Q  S) (x  xs)
    ≡⟨ cong  u  u + eval (Q  S) (x  xs))
          ((eval (P ·ₕ S) (x  xs) · x + eval 0ₕ xs)
         ≡⟨ cong  u  eval (P ·ₕ S) (x  xs) · x + u) (Eval0H xs) 
           (eval (P ·ₕ S) (x  xs) · x + 0r)
         ≡⟨ +IdR _ 
           (eval (P ·ₕ S) (x  xs) · x)
         ≡⟨ cong  u  u · x) (·Homeval P S (x  xs)) 
           ((eval P (x  xs) · eval S (x  xs)) · x)
         ≡⟨ sym (·Assoc _ _ _) 
           (eval P (x  xs) · (eval S (x  xs) · x))
         ≡⟨ cong  u  eval P (x  xs) · u) (·Comm _ _) 
           (eval P (x  xs) · (x · eval S (x  xs)))
         ≡⟨ ·Assoc _ _ _ 
           (eval P (x  xs) · x) · eval S (x  xs)
          ) 
      (eval P (x  xs) · x) · eval S (x  xs) + eval (Q  S) (x  xs)
    ≡⟨ cong  u  (eval P (x  xs) · x) · eval S (x  xs) + u)
            (⋆Homeval Q S x xs) 
      (eval P (x  xs) · x) · eval S (x  xs) + eval Q xs · eval S (x  xs)
    ≡⟨ sym (·DistL+ _ _ _) 
      ((eval P (x  xs) · x) + eval Q xs) · eval S (x  xs)
    ≡⟨ cong  u  u · eval S (x  xs)) (sym (combineCasesEval R P Q x xs)) 
      eval (P ·X+ Q) (x  xs) · eval S (x  xs)