{-# OPTIONS --safe #-}
module Cubical.Tactics.NatSolver.Solver where

open import Cubical.Foundations.Prelude

open import Cubical.Data.FinData
open import Cubical.Data.Nat
open import Cubical.Data.Nat.Order using (zero-≤)
open import Cubical.Data.Vec.Base
open import Cubical.Tactics.NatSolver.NatExpression
open import Cubical.Tactics.NatSolver.HornerForms
open import Cubical.Tactics.NatSolver.EvalHom

private
  variable
     : Level

module EqualityToNormalform where
  open Eval
  open IteratedHornerOperations
  open HomomorphismProperties

  normalize : {n : }  Expr n  IteratedHornerForms n
  normalize {n = n} (K r) = Constant n r
  normalize {n = n} ( k) = Variable n k
  normalize (x +' y) =
    (normalize x) +ₕ (normalize y)
  normalize (x ·' y) =
    (normalize x) ·ₕ (normalize y)

  isEqualToNormalform :
            {n : }
            (e : Expr n) (xs : Vec  n)
           eval (normalize e) xs   e  xs
  isEqualToNormalform (K r) [] = refl
  isEqualToNormalform {n = ℕ.suc n} (K r) (x  xs) =
     eval (Constant (ℕ.suc n) r) (x  xs)           ≡⟨ refl 
     eval (0ₕ ·X+ Constant n r) (x  xs)             ≡⟨ refl 
     eval 0ₕ (x  xs) · x + eval (Constant n r) xs
    ≡⟨ cong  u  u · x + eval (Constant n r) xs) (eval0H (x  xs)) 
     0 · x + eval (Constant n r) xs
    ≡⟨ refl 
     eval (Constant n r) xs
    ≡⟨ isEqualToNormalform (K r) xs 
     r 

  isEqualToNormalform ( zero) (x  xs) =
    eval (1ₕ ·X+ 0ₕ) (x  xs)           ≡⟨ refl 
    eval 1ₕ (x  xs) · x + eval 0ₕ xs   ≡⟨ cong  u  u · x + eval 0ₕ xs)
                                               (eval1ₕ (x  xs)) 
    1 · x + eval 0ₕ xs                  ≡⟨ cong  u  1 · x + u ) (eval0H xs) 
    1 · x + 0                          ≡⟨ +-zero _ 
    1 · x                               ≡⟨ ·-identityˡ _ 
    x 
  isEqualToNormalform {n = ℕ.suc n} ( (suc k)) (x  xs) =
      eval (0ₕ ·X+ Variable n k) (x  xs)             ≡⟨ refl 
      eval 0ₕ (x  xs) · x + eval (Variable n k) xs
    ≡⟨ cong  u  u · x + eval (Variable n k) xs) (eval0H (x  xs)) 
      0 · x + eval (Variable n k) xs
    ≡⟨ refl 
      eval (Variable n k) xs
    ≡⟨ isEqualToNormalform ( k) xs 
        (suc k)  (x  xs) 

  isEqualToNormalform (e +' e₁) [] =
        eval (normalize e +ₕ normalize e₁) []
      ≡⟨ +Homeval (normalize e) _ [] 
        eval (normalize e) []
        + eval (normalize e₁) []
      ≡⟨ cong  u  u + eval (normalize e₁) [])
              (isEqualToNormalform e []) 
         e  []
        + eval (normalize e₁) []
      ≡⟨ cong  u   e  [] + u) (isEqualToNormalform e₁ []) 
         e  [] +  e₁  [] 
  isEqualToNormalform (e +' e₁) (x  xs) =
        eval (normalize e
              +ₕ normalize e₁) (x  xs)
      ≡⟨ +Homeval (normalize e) _ (x  xs) 
        eval (normalize e) (x  xs)
        + eval (normalize e₁) (x  xs)
      ≡⟨ cong  u  u + eval (normalize e₁) (x  xs))
              (isEqualToNormalform e (x  xs)) 
         e  (x  xs)
        + eval (normalize e₁) (x  xs)
      ≡⟨ cong  u   e  (x  xs) + u)
              (isEqualToNormalform e₁ (x  xs)) 
         e  (x  xs) +  e₁  (x  xs) 

  isEqualToNormalform (e ·' e₁) [] =
        eval (normalize e ·ₕ normalize e₁) []
      ≡⟨ ·Homeval (normalize e) _ [] 
        eval (normalize e) []
        · eval (normalize e₁) []
      ≡⟨ cong  u  u · eval (normalize e₁) [])
              (isEqualToNormalform e []) 
         e  []
        · eval (normalize e₁) []
      ≡⟨ cong  u   e  [] · u) (isEqualToNormalform e₁ []) 
         e  [] ·  e₁  [] 

  isEqualToNormalform (e ·' e₁) (x  xs) =
        eval (normalize e ·ₕ normalize e₁) (x  xs)
      ≡⟨ ·Homeval (normalize e) _ (x  xs) 
        eval (normalize e) (x  xs)
        · eval (normalize e₁) (x  xs)
      ≡⟨ cong  u  u · eval (normalize e₁) (x  xs))
              (isEqualToNormalform e (x  xs)) 
         e  (x  xs)
        · eval (normalize e₁) (x  xs)
      ≡⟨ cong  u   e  (x  xs) · u)
              (isEqualToNormalform e₁ (x  xs)) 
         e  (x  xs) ·  e₁  (x  xs) 

  solve :
    {n : } (e₁ e₂ : Expr n) (xs : Vec  n)
    (p : eval (normalize e₁) xs  eval (normalize e₂) xs)
      e₁  xs   e₂  xs
  solve e₁ e₂ xs p =
     e₁  xs                ≡⟨ sym (isEqualToNormalform e₁ xs) 
    eval (normalize e₁) xs ≡⟨ p 
    eval (normalize e₂) xs ≡⟨ isEqualToNormalform e₂ xs 
     e₂  xs