module sets.nat.solver where

open import decidable
open import equality
open import function.core
  hiding (const)
open import sets.nat.core
open import sets.nat.properties
open import sets.nat.ordering
open import sets.fin.core
  hiding (_≟_)
open import sets.vec.core
open import sets.vec.dependent

data Exp (n : ) : Set where
  var : Fin n  Exp n
  const :   Exp n
  _:+_ : Exp n  Exp n  Exp n
infixl 6 _:+_

eval :  {n}  (Fin n  )  Exp n  
eval env (var i) = env i
eval env (const x) = x
eval env (e₁ :+ e₂) = eval env e₁ + eval env e₂

NF :   Set
NF n = Vec  n

evalNF' :  {n}  (Fin n  )  NF n  
evalNF' _ [] = 0
evalNF' env (x  xs) = x * env zero + evalNF' (env ∘' suc) xs

evalNF :  {n}  (Fin n  )  NF (suc n)  
evalNF env xs = evalNF' (1 ∷∷ env) xs

decNF :  {n}(xs ys : NF n)  Dec (xs  ys)
decNF [] [] = yes refl
decNF (x  xs) (y  ys) with x  y | decNF xs ys
decNF (x  xs) (y  ys) | _ | no u = no λ p  u (ap tail p)
decNF (x  xs) (y  ys) | no u | _ = no λ p  u (ap head p)
decNF (x  xs) (y  ys) | yes p | yes q = yes (ap₂ _∷_ p q)

emptyNF :  {n}  NF n
emptyNF {zero} = []
emptyNF {suc n} = 0  emptyNF

eval-emptyNF :  {n}(env : Fin n  )  evalNF' env emptyNF  0
eval-emptyNF {zero} env = refl
eval-emptyNF {suc n} env = eval-emptyNF {n} (env ∘' suc)

δ :  {n}  Fin n    NF n
δ zero val = val  emptyNF
δ (suc i) val = 0  δ i val

eval-δ :  {n}(i : Fin n)(val : )
        (env : Fin n  )
        evalNF' env (δ i val)  val * env i
eval-δ zero val env = ap (_+_ (val * env zero)) (eval-emptyNF env)
                    · +-right-unit _
eval-δ (suc i) val env = eval-δ i val (env ∘' suc)

_+NF_ :  {n}  NF n  NF n  NF n
[] +NF [] = []
(x  xs) +NF (y  ys) = (x + y)  (xs +NF ys)

eval+NF :  {n}(xs ys : NF n)(env : Fin n  )
         evalNF' env (xs +NF ys)
         evalNF' env xs + evalNF' env ys
eval+NF [] [] env = refl
eval+NF (x  xs) (y  ys) env
  = ap₂ _+_ (right-distr x y (env zero)) (eval+NF xs ys (env ∘' suc))
  · lem (x * env zero) (y * env zero) (evalNF' (env ∘' suc) xs) (evalNF' (env ∘' suc) ys)
  where
    lem :  a b c d  (a + b) + (c + d)  (a + c) + (b + d)
    lem a b c d
      = +-associativity a b (c + d)
      · ap (_+_ a) (sym (+-associativity b c d))
      · ap  z  a + (z + d)) (+-commutativity b c)
      · ap (_+_ a) (+-associativity c b d)
      · sym (+-associativity a c (b + d))

nf :  {n}  Exp n  NF (suc n)
nf (var i) = δ (suc i) 1
nf (const k) = δ zero k
nf (e₁ :+ e₂) = nf e₁ +NF nf e₂

nf-sound :  {n}(env : Fin n  )(e : Exp n)
          evalNF env (nf e)  eval env e
nf-sound env (var i) = eval-δ i 1 env · +-right-unit (env i)
nf-sound env (const x) = ap (_+_ (x * 1)) (eval-emptyNF env)
                       · +-right-unit (x * 1)
                       · *-right-unit x
nf-sound env (e₁ :+ e₂) = eval+NF (nf e₁) (nf e₂) (1 ∷∷ env)
                        · ap₂ _+_ (nf-sound env e₁) (nf-sound env e₂)

HOTerm :     Set
HOTerm n zero = Exp n
HOTerm n (suc i) = Exp n  HOTerm n i

private
  exp' : (n i : )  i  n  HOTerm n i  (Fin i  Exp n)  Exp n
  exp' _ zero _ e _ = e
  exp' zero (suc i) () e f
  exp' (suc n) (suc i) p e f = exp' (suc n) i (trans≤ suc≤ p) (e (f zero)) (f ∘' suc)

exp :  {n}  HOTerm n n  Exp n
exp {n} e = exp' n n refl≤ e var

solve :  n (e₁ e₂ : Exp n){t : True (decNF (nf e₁) (nf e₂))}
       (env : Fin n  )
       eval env e₁  eval env e₂
solve n e₁ e₂ {t} env = sym (nf-sound env e₁)
                      · ap (evalNF env) (witness t)
                      · nf-sound env e₂