\import Algebra.Monoid
\import Algebra.Solver
\import Algebra.Solver.Semigroup
\import Arith.Fin.Order
\import Data.Fin
\import Data.List
\import Logic
\import Paths
\import Paths.Meta
\open SemigroupSolverModel
\open Sort
{- | Reflective solver model for commutative semigroups. The normal form is the
- (non-empty) multiset of variables; `interpretNF` interprets the sorted list,
- which is permutation-invariant by `*-comm`/`*-assoc` — no identity needed.
-}
\func CSemigroupSolverModel (S : CSemigroup) : SolverModel S \cowith
| Term => Term
| NF n => \Sigma (Fin n) (List (Fin n))
| normalize t => normalize t
| interpret => interpret
| interpretNF env nf => interpHead env (interpretNE env nf.1 nf.2) (RedBlack.sort (nf.1 :: nf.2))
| interpretNF-consistent {n} {env} {t} => sort-consistent *> normalize-consistent
\where {
-- | Interpret a list as a non-empty word; `fallback` is returned on `nil` (never reached here).
\func interpHead {n : Nat} {S : CSemigroup} (env : Fin n -> S) (fallback : S) (l : List (Fin n)) : S \elim l
| nil => fallback
| x :: l => interpretNE env x l
-- | Transpose the first two elements of a non-empty word.
\lemma swap-head {n : Nat} {S : CSemigroup} {env : Fin n -> S} {x y : Fin n} {l : List (Fin n)}
: interpretNE env x (y :: l) = interpretNE env y (x :: l) \elim l
| nil => *-comm
| z :: l => inv *-assoc *> pmap (* interpretNE env z l) *-comm *> *-assoc
-- | The interpretation of a non-empty word is invariant under permutations.
\lemma interpretNE-perm {n : Nat} {S : CSemigroup} {env : Fin n -> S} {x : Fin n} {l : List (Fin n)} {y : Fin n} {m : List (Fin n)}
(p : Perm (x :: l) (y :: m)) : interpretNE env x l = interpretNE env y m \elim l, m, p
| nil, nil, perm-:: idp q => idp
| a :: l, b :: m, perm-:: idp q => pmap (env x *) (interpretNE-perm q)
| nil, b :: m, perm-:: idp q => \case Perm.perm_length q \with {}
| a :: l, nil, perm-:: idp q => \case Perm.perm_length q \with {}
| a :: l, b :: m, perm-swap idp idp idp => swap-head
| l, m, perm-trans {nil} p1 p2 => \case Perm.perm_length p1 \with {}
| l, m, perm-trans {z :: w} p1 p2 => interpretNE-perm p1 *> interpretNE-perm p2
\lemma sort-consistent {n : Nat} {S : CSemigroup} {env : Fin n -> S} {x : Fin n} {l : List (Fin n)}
: interpHead env (interpretNE env x l) (RedBlack.sort (x :: l)) = interpretNE env x l
=> aux (transport (Perm (x :: l)) (inv (RedBlack.sort=insert (x :: l))) (Insertion.sort-perm (x :: l)))
\where {
\lemma aux {n : Nat} {S : CSemigroup} {env : Fin n -> S} {x : Fin n} {l : List (Fin n)} {srt : List (Fin n)}
(p : Perm (x :: l) srt) : interpHead env (interpretNE env x l) srt = interpretNE env x l \elim srt, p
| nil, p => \case Perm.perm_length p \with {}
| y :: m, p => inv (interpretNE-perm p)
}
}