{-# OPTIONS --without-K --exact-split --safe #-}

open import Fragment.Algebra.Signature

module Fragment.Algebra.Free.Base (Σ : Signature) where

open import Fragment.Algebra.Algebra Σ
open import Fragment.Algebra.Free.Atoms public

open import Level using (Level; _⊔_)
open import Function using (_∘_)

open import Data.Empty using ()
open import Data.Nat using ()
open import Data.Fin using (Fin)
open import Data.Vec using (Vec; []; _∷_)
open import Data.Vec.Relation.Binary.Pointwise.Inductive
  using (Pointwise; []; _∷_)

open import Relation.Binary using (Setoid; IsEquivalence)
open import Relation.Binary.PropositionalEquality as PE using (_≡_)

private
  variable
    a  : Level

module _ (A : Set a) where

  data Term : Set a where
    atom : A  Term
    term :  {arity}  (f : ops Σ arity)  Vec Term arity  Term

module _ (S : Setoid a ) where

  open Setoid S renaming (Carrier to A)

  data _~_ : Term A  Term A  Set (a  ) where
      atom :  {x y}  x  y  atom x ~ atom y
      term :  {arity xs ys} {f : ops Σ arity}
              Pointwise _~_ xs ys
              term f xs ~ term f ys

  private

    mutual
      map-~-refl :  {n} {xs : Vec _ n}  Pointwise _~_ xs xs
      map-~-refl {xs = []}     = []
      map-~-refl {xs = x  xs} = ~-refl  map-~-refl

      ~-refl :  {x}  x ~ x
      ~-refl {atom _}   = atom refl
      ~-refl {term _ _} = term map-~-refl

    mutual
      map-~-sym :  {n} {xs ys : Vec _ n}
                    Pointwise _~_ xs ys
                    Pointwise _~_ ys xs
      map-~-sym [] = []
      map-~-sym (x≈y  xs≈ys) =
        ~-sym x≈y  map-~-sym xs≈ys

      ~-sym :  {x y}  x ~ y  y ~ x
      ~-sym (atom x≈y)   = atom (sym x≈y)
      ~-sym (term xs≈ys) = term (map-~-sym xs≈ys)

    mutual
      map-~-trans :  {n} {xs ys zs : Vec _ n}
                     Pointwise _~_ xs ys
                     Pointwise _~_ ys zs
                     Pointwise _~_ xs zs
      map-~-trans [] [] = []
      map-~-trans (x≈y  xs≈ys) (y≈z  ys≈zs) =
        ~-trans x≈y y≈z  map-~-trans xs≈ys ys≈zs

      ~-trans :  {x y z}  x ~ y  y ~ z  x ~ z
      ~-trans (atom x≈y) (atom y≈z)     =
        atom (trans x≈y y≈z)
      ~-trans (term xs≈ys) (term ys≈zs) =
        term (map-~-trans xs≈ys ys≈zs)

    ~-isEquivalence : IsEquivalence _~_
    ~-isEquivalence = record { refl  = ~-refl
                             ; sym   = ~-sym
                             ; trans = ~-trans
                             }

  Herbrand : Setoid _ _
  Herbrand = record { Carrier       = Term A
                    ; _≈_           = _~_
                    ; isEquivalence = ~-isEquivalence
                    }

  Free : Algebra
  Free = record { ∥_∥/≈           = Herbrand
                ; ∥_∥/≈-isAlgebra = Free-isAlgebra
                }
    where term-cong : Congruence Herbrand term
          term-cong f p = term p

          Free-isAlgebra : IsAlgebra Herbrand
          Free-isAlgebra = record { ⟦_⟧     = term
                                  ; ⟦⟧-cong = term-cong
                                  }

F :   Algebra
F = Free  Atoms (PE.setoid )