\import Algebra.Monoid
\import Algebra.Solver
\import Data.Fin
\import Data.List
\import Paths
\import Paths.Meta

{- | Reflective solver model for (non-commutative) semigroups. The normal form of a
 -   word is the non-empty list of its variables (head + tail), flattened by
 -   associativity. No identity is involved, so this works on bare `Semigroup`s.
 -}
\func SemigroupSolverModel (S : Semigroup) : SolverModel S \cowith
  | Term => Term
  | NF n => \Sigma (Fin n) (List (Fin n))
  | normalize t => normalize t
  | interpret => interpret
  | interpretNF env nf => interpretNE env nf.1 nf.2
  | interpretNF-consistent {n} {env} {t} => normalize-consistent
  \where {
    \data Term (n : Nat)
      | var (Fin n)
      | \infixl 7 :* (t s : Term n)

    \func interpret {n : Nat} (env : Fin n -> S) (t : Term n) : S \elim t
      | var x => env x
      | :* t s => interpret env t * interpret env s

    -- | Interpret a non-empty list given as head `x` and tail `l`.
    \func interpretNE {n : Nat} (env : Fin n -> S) (x : Fin n) (l : List (Fin n)) : S \elim l
      | nil => env x
      | y :: l => env x * interpretNE env y l

    \func normalize {n : Nat} (t : Term n) : \Sigma (Fin n) (List (Fin n)) \elim t
      | var x => (x, nil)
      | :* t s => ((normalize t).1, (normalize t).2 ++ ((normalize s).1 :: (normalize s).2))

    \lemma interpretNE_++ {n : Nat} {env : Fin n -> S} {x : Fin n} {l : List (Fin n)} {y : Fin n} {m : List (Fin n)}
      : interpretNE env x (l ++ (y :: m)) = interpretNE env x l * interpretNE env y m \elim l
      | nil => idp
      | z :: l => pmap (env x *) interpretNE_++ *> inv *-assoc

    \lemma normalize-consistent {n : Nat} {env : Fin n -> S} {t : Term n}
      : interpretNE env (normalize t).1 (normalize t).2 = interpret env t \elim t
      | var x => idp
      | :* t s => interpretNE_++ *> pmap2 (*) normalize-consistent normalize-consistent
  }