open import Data.Empty
open import Data.Nat using (; _+_)
open import Data.Fin hiding (_+_)
open import Data.Fin.Properties
open import Data.Vec
open import Algebra
import Algebra.Morphism.Definitions as MorphDefs
open import Relation.Binary
open import Relation.Binary.Morphism.Structures
open import Relation.Binary.PropositionalEquality as  using (_≢_)
import Data.Vec.Relation.Binary.Equality.Setoid as VecSetoid

open import Vec.Permutation

module Algebra.Morphism.vecCommMonoid {c} {} {a} {ℓ′}
  (S : Setoid a ℓ′)
  (let open module S = Setoid S renaming (Carrier to A; _≈_ to _≈₁_))
  (open VecSetoid S)
  (cMonoid : CommutativeMonoid c )
  (let open module CM = CommutativeMonoid cMonoid renaming (Carrier to B; _≈_ to _≈₂_))
  (let open module MD {n} = MorphDefs (Vec A n) B _≈₂_)
  (⟦_⟧ :  {n}  Vec A n  B)
  (ε-homo : Homomorphic₀ ⟦_⟧ [] ε)
  (isRelHomomorphism :  {n}  IsRelHomomorphism (_≋_ {n}) _≈₂_ ⟦_⟧)
  (homo :  {m n}  (xs : Vec A m) (ys : Vec A n)   xs ++ ys  ≈₂  xs    ys )
  where

open module IRL {n} = IsRelHomomorphism (isRelHomomorphism {n})

open import Vec.View S
open import Relation.Binary.Reasoning.Setoid setoid
open import Algebra.Solver.CommutativeMonoid cMonoid hiding (⟦_⟧)

private variable
  m n : 
  xs ys ws zs vs : Vec A n
  p q : A
  x y w z v : B

⦅_⦆ : A  B
 x  =  [ x ] 

↭⇒≈ : (_↭_ {n = n}) =[ ⟦_⟧ ]⇒ _≈₂_
↭⇒≈ _↭_.refl = CM.refl
↭⇒≈ (prep {n} {xs} {ys} x xs↔ys) = begin
   [ x ] ++  xs  ≈⟨ homo _ _ 
   x    xs    ≈⟨ ∙-congˡ (↭⇒≈ xs↔ys) 
   x    ys   ≈˘⟨ homo _ _ 
   [ x ] ++ ys    
↭⇒≈ (swap {n} {xs} {ys} x y xs↔ys) = begin
   [ x ] ++ [ y ] ++ xs   ≈⟨ homo _ _ 
   x    [ y ] ++ xs    ≈⟨ ∙-congˡ (homo _ _) 
   x   ( y    xs ) ≈˘⟨ assoc _ _ _ 
   x    y    xs     ≈⟨ ∙-cong (comm _ _) (↭⇒≈ xs↔ys) 
   y    x    ys     ≈⟨ assoc _ _ _ 
   y   ( x    ys ) ≈˘⟨ ∙-congˡ (homo _ _) 
   y    [ x ] ++ ys   ≈˘⟨ homo _ _ 
   [ y ] ++ [ x ] ++ ys    
↭⇒≈ (_↭_.trans {n} {xs} {ys} {zs} xs↔ys ys↔zs) = begin
   xs  ≈⟨ ↭⇒≈ xs↔ys 
   ys  ≈⟨ ↭⇒≈ ys↔zs 
   zs  

cong-vec : {xs : Vec A m} {ys : Vec A n}  xs  ys   xs  ≈₂  ys 
cong-vec eqn with length-equal eqn
... | ≡.refl = cong eqn

cong-⦅⦆ : p ≈₁ q   p  ≈₂  q 
cong-⦅⦆ eqn = cong (eqn VecSetoid.∷ VecSetoid.[])

homo5 :  xs ++ ys ++ ws ++ zs ++ vs  ≈₂  xs   ( ys   ( ws   ( zs    vs )))
homo5 {xs = xs} {ys = ys} {ws = ws} {zs = zs} {vs = vs} = begin
  xs ++ ys ++ ws ++ zs ++ vs  ≈⟨ homo xs _ 
  xs     ys ++ ws ++ zs ++ vs  ≈⟨ ∙-congˡ (homo ys _) 
  xs   ( ys    ws ++ zs ++ vs ) ≈⟨ ∙-congˡ (∙-congˡ (homo ws _)) 
  xs   ( ys   ( ws    zs ++ vs )) ≈⟨ ∙-congˡ (∙-congˡ (∙-congˡ (homo _ _))) 
  xs   ( ys   ( ws   ( zs    vs ))) 


swap5 : x  (y  (w  (z  v))) ≈₂ (y  z)  (x  (w  v))
swap5 = solve 5  x y w z v  x  (y  (w  (z  v)))  (y  z)  (x  (w  v))) CM.refl _ _ _ _ _

swap5' : x  (y  (w  (z  v))) ≈₂ (z  y)  (x  (w  v))
swap5' = solve 5  x y w z v  x  (y  (w  (z  v)))  (z  y)  (x  (w  v))) CM.refl _ _ _ _ _

swapSameVecI<j :  {xs : Vec A (2 + n)} {i j p q}
   i < j
    lookup xs i    lookup xs j  ≈₂  p    q 
    xs  ≈₂  xs [ i ]≔ p [ j ]≔ q 
swapSameVecI<j {xs = xs} {i} {j} {p} {q} i<j eqn with vec→VecView2 xs i<j
... | view2 {ys = ys} {x} {zs} {w} {ws} vec≡xs = begin
   xs  ≈˘⟨ cong-vec vec≡xs 
   ys ++ [ x ] ++ zs ++ [ w ] ++ ws  ≈⟨ homo5 
   ys   ( x   ( zs   ( w    ws ))) ≈⟨ swap5 
   x    w   ( ys   ( zs    ws )) ≈⟨ ∙-congʳ xw≡pq 
   p    q   ( ys   ( zs    ws )) ≈˘⟨ swap5 
   ys   ( p   ( zs   ( q    ws ))) ≈˘⟨ homo5 
   ys ++ [ p ] ++ zs ++ [ q ] ++ ws 
    ≈⟨ cong-vec (changeVecView2ʳ i<j q (changeVecView2ˡ i<j p vec≡xs)) 
   xs [ i ]≔ p [ j ]≔ q   where

  lXsI : lookup xs i ≈₁ x
  lXsI = lookupVecView2ˡ i<j vec≡xs

  lXsJ : lookup xs j ≈₁ w
  lXsJ = lookupVecView2ʳ i<j vec≡xs

  xw≡pq = begin
     x    w  ≈˘⟨ ∙-cong (cong-⦅⦆ lXsI) (cong-⦅⦆ lXsJ) 
     lookup xs i    lookup xs j  ≈⟨ eqn 
     p    q  

swapSameVecI>j :  {xs : Vec A (2 + n)} {i j p q}
   i > j
    lookup xs i    lookup xs j  ≈₂  p    q 
    xs  ≈₂  xs [ i ]≔ p [ j ]≔ q 
swapSameVecI>j {xs = xs} {i} {j} {p} {q} i>j eqn with vec→VecView2 xs i>j
... | view2 {ys = ys} {x} {zs} {w} {ws} vec≡xs = begin
   xs  ≈˘⟨ cong-vec vec≡xs 
   ys ++ [ x ] ++ zs ++ [ w ] ++ ws  ≈⟨ homo5 
   ys   ( x   ( zs   ( w    ws ))) ≈⟨ swap5' 
   w    x   ( ys   ( zs    ws )) ≈⟨ ∙-congʳ xw≡pq 
   p    q   ( ys   ( zs    ws )) ≈˘⟨ swap5' 
   ys   ( q   ( zs   ( p    ws ))) ≈˘⟨ homo5 
   ys ++ [ q ] ++ zs ++ [ p ] ++ ws 
    ≈⟨ cong-vec (changeVecView2ˡ i>j q (changeVecView2ʳ i>j p vec≡xs)) 
   xs [ i ]≔ p [ j ]≔ q   where

  lXsI : lookup xs i ≈₁ w
  lXsI = lookupVecView2ʳ i>j vec≡xs

  lXsJ : lookup xs j ≈₁ x
  lXsJ = lookupVecView2ˡ i>j vec≡xs

  xw≡pq = begin
     w    x  ≈˘⟨ ∙-cong (cong-⦅⦆ lXsI) (cong-⦅⦆ lXsJ) 
     lookup xs i    lookup xs j  ≈⟨ eqn 
     p    q  


swapSameVecLookup :  {xs : Vec A n} {i j p q}
   i  j
    lookup xs i    lookup xs j  ≈₂  p    q 
    xs  ≈₂  xs [ i ]≔ p [ j ]≔ q 
swapSameVecLookup {n = ℕ.suc ℕ.zero} {xs} {zero} {zero} i≢j eqn = ⊥-elim (i≢j ≡.refl)
swapSameVecLookup {n = ℕ.suc (ℕ.suc n)} {xs = xs} {i} {j} i≢j eqn with <-cmp i j
... | tri≈ _ i≡j _ = ⊥-elim (i≢j i≡j)
... | tri< i<j _ _ = swapSameVecI<j i<j eqn
... | tri> _ _ i>j = swapSameVecI>j i>j eqn