\import Category
\import Function.Meta
\import Paths
\import Paths.Meta

\data CatTerm {V : \Type} (a b : V) (H : V -> V -> \Type)
  | var (H a b)
  | :id (a = b)
  | :o (c : V) (CatTerm c b H) (CatTerm a c H)

\data CatNF {V : \Type} (a b : V) (H : V -> V -> \Type)
  | :nil (a = b)
  | \infixl 7 :cons {c : V} (H c b) (CatNF a c H)

\func normalize {V : \Type} {a b : V} {H : V -> V -> \Type} (t : CatTerm a b H) : CatNF a b H
  => aux t (:nil idp)
  \where
    \func aux {V : \Type} {a b c : V} {H : V -> V -> \Type} (t : CatTerm b c H) (acc : CatNF a b H) : CatNF a c H \elim t
      | var h => h :cons acc
      | :id idp => acc
      | :o _ t s => aux t (aux s acc)

\class HData \noclassifying {C : Precat} {V : \Set} (f : V -> C) (H : V -> V -> \Set) (g : \Pi {x y : V} -> H x y -> Hom (f x) (f y)) {
  \func interpret {a b : V} (t : CatTerm a b H) : Hom (f a) (f b) \elim t
    | var h => g h
    | :id idp => id (f a)
    | :o _ t s => interpret t  interpret s

  \func interpretNF {a b : V} (t : CatNF a b H) : Hom (f a) (f b) \elim t
    | :nil idp => id (f a)
    | :cons h (:nil idp) => g h
    | :cons h t => g h  interpretNF t
    \where
      \lemma cons {a b c : V} (h : H b c) (l : CatNF a b H) : interpretNF (h :cons l) = g h  interpretNF l \elim l
        | :nil idp => inv id-right
        | :cons a l => idp

  \func \infixr 5 :++ {a b c : V} (h : CatNF b c H) (h' : CatNF a b H) : CatNF a c H \elim h
    | :nil idp => h'
    | :cons h t => h :cons (t :++ h')

  \lemma interpretNF_++ {a b c : V} (h : CatNF b c H) (h' : CatNF a b H) : interpretNF (h :++ h') = interpretNF h  interpretNF h' \elim h
    | :nil idp => inv id-left
    | :cons h t => repeat {2} (rewrite interpretNF.cons) $ rewrite interpretNF_++ (inv o-assoc)

  \lemma normalize-consistent {a b : V} (t : CatTerm a b H) : interpret t = interpretNF (normalize t)
    => inv id-right *> aux t (:nil idp)
    \where
      \lemma aux {a b c : V} (t : CatTerm b c H) (acc : CatNF a b H) : interpret t  interpretNF acc = interpretNF (normalize.aux t acc) \elim t
        | var h => inv (interpretNF.cons h acc)
        | :id idp => id-left
        | :o _ t s => o-assoc *> pmap (interpret t ) (aux s acc) *> aux t (normalize.aux s acc)

  \lemma terms-equality {a b : V} (t s : CatTerm a b H) (p : interpretNF (normalize t) = interpretNF (normalize s)) : interpret t = interpret s
    => normalize-consistent t *> p *> inv (normalize-consistent s)
}