\import Algebra.Monoid
\import Algebra.Solver
\import Data.List
\import Function.Meta
\import Paths
\import Paths.Meta
\func MonoidSolverModel (M : Monoid) : SubstSolverModel M \cowith
| Term => Term
| NF n => List (Fin n)
| normalize t => normalize-aux t nil
| interpret {n : Nat} (env : Fin n -> M) (t : Term n) : M \elim t {
| var x => env x
| :ide => M.ide
| :* t s => interpret env t * interpret env s
}
| interpretNF => interpretNF
| interpretNF-consistent => interpretNF-consistent-aux *> ide-right
| nfVar v => v :: nil
| >>= => >>=
| >>=-consistent => >>=-consistent
\where {
\data Term (n : Nat)
| var (Fin n)
| :ide
| \infixl 7 :* (t s : Term n)
\private \func normalize-aux {n : Nat} (t : Term n) (acc : List (Fin n)) : List (Fin n) \elim t
| var v => v :: acc
| :ide => acc
| :* t s => normalize-aux t (normalize-aux s acc)
\protected \func interpretNF {V : \Set} (env : V -> M) (l : List V) : M \elim l
| nil => M.ide
| x :: nil => env x
| x :: l => env x * interpretNF env l
\protected \lemma interpretNF_:: {V : \Set} {env : V -> M} {x : V} {l : List V} : interpretNF env (x :: l) = env x * interpretNF env l \elim l
| nil => inv ide-right
| :: a l => idp
\protected \lemma interpretNF_++ {V : \Set} {env : V -> M} {t s : List V} : interpretNF env (t ++ s) = interpretNF env t * interpretNF env s \elim t
| nil => inv ide-left
| v :: l => rewrite (interpretNF_::,interpretNF_::) $ pmap (_ *) interpretNF_++ *> inv *-assoc
\private \lemma interpretNF-consistent-aux {n : Nat} {env : Fin n -> M} {t : Term n} {acc : List (Fin n)} : interpretNF env (normalize-aux t acc) = interpret env t * interpretNF env acc \elim t
| var x => interpretNF_::
| :ide => inv ide-left
| :* t s => interpretNF-consistent-aux *> pmap (_ *) interpretNF-consistent-aux *> inv *-assoc
\func \infixl 2 >>= {U V : \Set} (l : List U) (k : U -> List V) : List V \elim l
| nil => nil
| u :: l => k u ++ (l >>= k)
\private \lemma >>=-consistent {U V : \Set} {env : V -> M} {l : List U} {k : U -> List V}
: interpretNF env (l >>= k) = interpretNF (\lam u => interpretNF env (k u)) l \elim l
| nil => idp
| u :: l => interpretNF_++ *> pmap (_ *) >>=-consistent *> inv interpretNF_::
}
\func AddMonoidSolverModel (A : AddMonoid) : SubstSolverModel A
=> MonoidSolverModel (AddMonoid.toMonoid A)