module Algebra.Instances.VecAbGroup where

open import Function
open import Data.Unit.Polymorphic
open import Data.Nat
open import Data.Vec
open import Data.Product using (_×_; _,_; proj₁; proj₂)
open import Relation.Binary.Core
open import Relation.Binary.Structures
open import Algebra

module _ { ℓ'} (GAb : AbelianGroup  ℓ') where

  module AG = AbelianGroup GAb renaming (Carrier to G)
  open AG

  private
    variable
      n : 

    _∙v_ : Op₂ $ Vec G n
    _∙v_ = zipWith _∙_

    εv : Vec G n
    εv = replicate _ ε

    _⁻¹v : Op₁ $ Vec G n
    _⁻¹v = map _⁻¹

    _≈v_ : Rel (Vec G n) ℓ'
    [] ≈v [] = 
    (x  xs) ≈v (y  ys) = (x  y) × (xs ≈v ys)


  open IsAbelianGroup hiding (refl)
  open IsEquivalence
  open IsGroup
  open IsMonoid
  open IsSemigroup
  open IsMagma


  IsVecAbGroup : IsAbelianGroup {A = Vec G n} _≈v_ _∙v_ εv _⁻¹v
  comm IsVecAbGroup [] [] = _
  comm IsVecAbGroup (x  xs) (y  ys) = AG.comm _ _ , comm IsVecAbGroup xs ys
  refl (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {[]} = _
  refl (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {x  xs} =
    AG.refl , (refl (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))))
  sym (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {[]} {[]} x≡y = _
  sym (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {x  xs} {y  ys} (x≡y , xs≡ys) =
    AG.sym x≡y , sym (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) xs≡ys
  trans (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {[]} {[]} {[]} i≡j j≡k = _
  trans (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) {x  xs} {y  ys} {z  zs} (x≡y , xs≡ys) (y≡z , ys≡zs) =
    (AG.trans x≡y y≡z) , trans (isEquivalence (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup))))) xs≡ys ys≡zs
  ∙-cong (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup)))) {[]} {[]} {[]} {[]} eq1 eq2 = _
  ∙-cong (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup)))) {x  xs} {y  ys} {u  us} {v  vs} (x≡y , xs≡ys) (u≡v , us≡vs) =
    (AG.∙-cong x≡y u≡v) , ∙-cong (isMagma (isSemigroup (isMonoid (isGroup IsVecAbGroup)))) xs≡ys us≡vs
  assoc (isSemigroup (isMonoid (isGroup IsVecAbGroup))) [] [] [] = _
  assoc (isSemigroup (isMonoid (isGroup IsVecAbGroup))) (x  xs) (y  ys) (z  zs) =
    AG.assoc x y z , assoc (isSemigroup (isMonoid (isGroup IsVecAbGroup))) xs ys zs
  proj₁ (identity (isMonoid (isGroup IsVecAbGroup))) [] = _
  proj₁ (identity (isMonoid (isGroup IsVecAbGroup))) (x  xs) =
    proj₁ AG.identity x , proj₁ (identity (isMonoid (isGroup IsVecAbGroup))) xs
  proj₂ (identity (isMonoid (isGroup IsVecAbGroup))) [] = _
  proj₂ (identity (isMonoid (isGroup IsVecAbGroup))) (x  xs) =
    proj₂ AG.identity x , proj₂ (identity (isMonoid (isGroup IsVecAbGroup))) xs
  proj₁ (inverse (isGroup IsVecAbGroup)) [] = _
  proj₁ (inverse (isGroup IsVecAbGroup)) (x  xs) =
    proj₁ AG.inverse _ , proj₁ (inverse (isGroup IsVecAbGroup)) xs
  proj₂ (inverse (isGroup IsVecAbGroup)) [] = _
  proj₂ (inverse (isGroup IsVecAbGroup)) (x  xs) =
    proj₂ AG.inverse _ , proj₂ (inverse (isGroup IsVecAbGroup)) xs
  ⁻¹-cong (isGroup IsVecAbGroup) {[]} {[]} eq = _
  ⁻¹-cong (isGroup IsVecAbGroup) {x  xs} {y  ys} (eqx , eqxs) =
    (AG.⁻¹-cong eqx) , ⁻¹-cong (isGroup IsVecAbGroup) eqxs

  VecAbGroup :  {n}  AbelianGroup  ℓ'
  VecAbGroup {n} = record
                 { Carrier = _
                 ; _≈_ = _
                 ; _∙_ = _
                 ; ε = _
                 ; _⁻¹ = _
                 ; isAbelianGroup = IsVecAbGroup {n = n}
                 }