open import Level using (Level; _⊔_)
open import Function
open import Algebra
open import Algebra.Module
open import Data.Empty
open import Data.Bool hiding (_≟_)
open import Data.Maybe using (Maybe; map)
open import Data.Product
open import Data.Sum renaming ([_,_] to [_⊕_])
open import Data.Nat as  using (; _∸_)
open import Data.Nat.Properties as  hiding (_≟_)
open import Data.Vec as V renaming (_∷_ to _::_) using (Vec; [])
open import Data.Fin hiding (_+_; _-_)
open import Data.Fin.Properties
open import Data.Fin.Patterns
open import Data.Fin.Permutation as  hiding (remove; _≈_)
open import Data.Fin.Permutation.Components renaming (transpose to transposeC)
open import Data.List as L using (List)
open import Data.Vec.Functional hiding (transpose)
open import Data.Vec.Functional.Properties hiding (++-cong)
import Data.Vec.Functional.Relation.Binary.Equality.Setoid as ≋-props
open import Relation.Binary
import Relation.Binary.Reasoning.Setoid as ≈-Reasoning
open import Relation.Binary.PropositionalEquality as  hiding (sym)
open import Relation.Nullary
open import Relation.Nullary.Decidable
import Algebra.Solver.CommutativeMonoid as CMonoidSolver

open import Vector.Base
import Vector.Setoid.Properties as VecProps
open import Vector.Properties
open import Vector.Permutation
import Algebra.Module.Base as MBase
import Algebra.Module.Definition as MDefinitions
import Algebra.Module.DefsField as DField
open import Algebra.BigOps

open import Vec.Updates.Base renaming (_∷_ to _::_)
open import Vec.Relation.FirstOrNot

module Algebra.Module.VecSpace {rr ℓr mr ℓm}
  {ring : Ring rr ℓr}
  (leftModule : LeftModule ring mr ℓm)
  where

infixl 4 _≈ⱽ_

open MBase ring
open module R = Ring ring renaming (Carrier to R)
open LeftModule leftModule renaming (Carrierᴹ to M)
open ≋-props ≈ᴹ-setoid
open SetoidProps ≈ᴹ-setoid
open VecProps ≈ᴹ-setoid
module ≈ᴹ-Reasoning = ≈-Reasoning ≈ᴹ-setoid
module ≈ᴹ = Setoid ≈ᴹ-setoid
module ≋-Reasoning {n} = ≈-Reasoning (≋-setoid n)
open MDefinitions leftModule
open SumCommMonoid +ᴹ-commutativeMonoid

private variable
  m n : 
  x y : M
  i j p q : Fin n
  xs xs‵ ys ys‵ zs : Vector M n
  r : R

_[_]←_*[_] : Vector M n  Fin n  R  Fin n  Vector M n
(M [ q ]← r *[ p ]) i with does (q  i)
... | true = M i +ᴹ r *ₗ M p
... | false = M i

matOps→func : VecOp n  Vector M n  Vector M n
matOps→func (swapOp p q p≢q)    xs = swapV xs p q
matOps→func (addCons p q p≢q r) xs = xs [ q ]← r *[ p ]

matOps→func-cong : (xs ys : Vector M n) (mOps : VecOp n)  xs  ys  matOps→func mOps xs  matOps→func mOps ys
matOps→func-cong xs ys (swapOp p q p≢q) xs≈ys i = []≔-cong (xs≈ys _) ([]≔-cong (xs≈ys _) xs≈ys _) q i
matOps→func-cong xs ys (addCons p q p≢q r) xs≈ys i with does $ q  i
... | false = xs≈ys i
... | true  = +ᴹ-cong (xs≈ys i) (*ₗ-cong R.refl (xs≈ys p))

private
  opposite-injective : Injective _≡_ _≡_ (opposite {n})
  opposite-injective {ℕ.suc n} {i} {j} eq = toℕ-injective $ ∸-cancelˡ-≡ {m = n} (toℕ≤pred[n] i) (toℕ≤pred[n] j) (begin
    n  toℕ i       ≡˘⟨ opposite-prop i 
    toℕ (opposite i) ≡⟨ cong toℕ eq 
    toℕ (opposite j) ≡⟨ opposite-prop j 
    n  toℕ j )
    where open ≡-Reasoning

opVecOps : Op₁ $ VecOp n
opVecOps (swapOp p q p≢q) = swapOp (opposite p) (opposite q) $ p≢q  opposite-injective
opVecOps (addCons p q p≢q r) = addCons (opposite p) (opposite q) (p≢q  opposite-injective) r

invVecOp : Op₁ $ VecOp n
invVecOp (swapOp p q p≢q) = swapOp q p (p≢q  ≡.sym)
invVecOp (addCons p q p≢q r) = addCons q p (p≢q  ≡.sym) r

data _≈ⱽ_ : Rel (Vector M n) (rr  mr  ℓm) where
  idR : xs  ys  xs ≈ⱽ ys
  rec : (mOps : VecOp n)
     xs ≈ⱽ ys
     matOps→func mOps ys  zs
     xs ≈ⱽ zs

≈ⱽ⇒listVops : {xs : Vector M n}  xs ≈ⱽ ys  List (VecOp n)
≈ⱽ⇒listVops (idR _) = L.[]
≈ⱽ⇒listVops (rec mOps xs≈ⱽys _) = mOps L.∷ ≈ⱽ⇒listVops xs≈ⱽys

∷≈≋→≈ⱽ : x ≈ᴹ y  xs  ys  (x  xs) ≈ⱽ (y  ys)
∷≈≋→≈ⱽ = idR ∘₂ ≋-cons

[]←suc :  x xs r (p q : Fin n)
   ((x  xs) [ suc q ]← r *[ suc p ])  (x  xs [ q ]← r *[ p ])
[]←suc x xs r p q zero    = ≈ᴹ-refl
[]←suc x xs r p q (suc i) with q  i
... | yes _ = ≈ᴹ-refl
... | no  _ = ≈ᴹ-refl

≡r*[] :  (xs : Vector M n) r p q 
  (xs [ q ]← r *[ p ]) q  xs q +ᴹ r *ₗ xs p
≡r*[] xs r p q with q  q
... | yes _ = ≡.refl
... | no ¬q = ⊥-elim (¬q ≡.refl)

≢r*[] :  (xs : Vector M n) r p q {i}  q  i 
  (xs [ q ]← r *[ p ]) i  xs i
≢r*[] xs r p q {i} q≢i with q  i
... | yes q≡i = ⊥-elim (q≢i q≡i)
... | no    _ = ≡.refl

[]←++ :  (xs : Vector M m) (ys : Vector M n) r p q
   ((xs ++ ys) [ q ↑ˡ n ]← r *[ p ↑ˡ n ])
       (xs [ q ]← r *[ p ] ++ ys)
[]←++ {m = m} {n = n} xs ys r p q i with q ↑ˡ n  i | toℕ i ℕ.<? m
... | no q↑n≢i | yes i<m = α where
  α : _
  α with q  fromℕ< i<m
  ... | yes refl = ⊥-elim (q↑n≢i (toℕ-injective (begin
    toℕ (fromℕ< i<m ↑ˡ n) ≡⟨ toℕ-↑ˡ _ _ 
    toℕ (fromℕ< i<m)      ≡⟨ toℕ-fromℕ< _ 
    toℕ i ))) where open ≡-Reasoning
  ... | no   q≢i = begin
   [ xs  ys ] (splitAt m i)         ≡⟨ lookup-++-< xs ys i i<m  
   xs (fromℕ< i<m)                   ≡˘⟨ ≢r*[] xs r p q q≢i 
   (xs [ q ]← r *[ p ]) (fromℕ< i<m) ≡˘⟨ lookup-++-< (xs [ q ]← r *[ p ]) ys i i<m 
   (xs [ q ]← r *[ p ] ++ ys) i  where open ≈ᴹ-Reasoning
... | yes q↑n≡i | no ¬i<m = ⊥-elim (¬i<m (begin-strict
  toℕ i       ≡˘⟨ cong toℕ q↑n≡i 
  toℕ (q ↑ˡ n) ≡⟨ toℕ-↑ˡ _ _ 
  toℕ q        <⟨ toℕ<n _ 
  m )) where open ≤-Reasoning
... | no  _ | no ¬i<m rewrite splitAt-≥ m i (≮⇒≥ ¬i<m) = ≈ᴹ-refl
... | yes refl | yes i<m rewrite splitAt-< m i i<m with q  fromℕ< i<m
...    | yes _ rewrite splitAt-< m (p ↑ˡ n)
  (≤-<-trans (ℕ.≤-reflexive (toℕ-↑ˡ _ _)) (toℕ<n _)) = +ᴹ-congˡ
    (*ₗ-congˡ (≈ᴹ-reflexive (cong xs (toℕ-injective α)))) where
    open ≡-Reasoning
    α = begin
      toℕ (fromℕ< (≤-<-trans (ℕ.≤-reflexive (toℕ-↑ˡ p n)) (toℕ<n p))) ≡⟨ toℕ-fromℕ< _ 
      toℕ (p ↑ˡ n) ≡⟨ toℕ-↑ˡ _ _ 
      toℕ p 
...    | no  q≠i = ⊥-elim (q≠i (≡.sym (toℕ-injective
  (≡.trans (toℕ-fromℕ< _) (toℕ-↑ˡ _ _)))))

∷≈ⱽ→≈ⱽ : x ≈ᴹ y  xs ≈ⱽ ys  (x  xs) ≈ⱽ (y  ys)
∷≈ⱽ→≈ⱽ x≈y (idR xs≋ys) = ∷≈≋→≈ⱽ x≈y xs≋ys
∷≈ⱽ→≈ⱽ x≈y (rec (swapOp p q p≢q) xs≈ys mopsM) =
  rec (swapOp (suc p) (suc q) (p≢q  λ where refl  ≡.refl)) (∷≈ⱽ→≈ⱽ x≈y xs≈ys)
  (≋-trans (≈ᴹ-reflexive  swapCons _ _ _ _) (≋-consˡ mopsM))
∷≈ⱽ→≈ⱽ x≈y (rec (addCons p q p≢q r) xs≈ys mopsM) =
  rec (addCons (suc p) (suc q) (p≢q  λ where refl  ≡.refl) r) (∷≈ⱽ→≈ⱽ x≈y xs≈ys)
  (≋-trans ([]←suc _ _ _ _ _) (≋-consˡ mopsM))

≈ⱽ++ : xs ≈ⱽ xs‵  ys  ys‵  (xs ++ ys) ≈ⱽ (xs‵ ++ ys‵)
≈ⱽ++ (idR xs≋xs‵) = idR  ++⁺ _≈ᴹ_ xs≋xs‵
≈ⱽ++ {xs‵ = xs‵} {ys‵ = ys‵} (rec {ys = ys} (swapOp p q p≢q) xs≈xs‵ mOpsZ) ys≋ys‵ =
  rec (swapOp (p ↑ˡ _) (q ↑ˡ _) (p≢q  ↑ˡ-injective _ _ _))
    (≈ⱽ++ xs≈xs‵ ys≋ys‵) (begin
      swapV (ys ++ ys‵) (p ↑ˡ _) (q ↑ˡ _) ≈⟨  i  ≈ᴹ-reflexive (swapV-++ ys ys‵ p q i)) 
      swapV ys p q ++ ys‵ ≈⟨ ++-congʳ _ xs‵ mOpsZ 
      xs‵ ++ ys‵ ) where open ≋-Reasoning
≈ⱽ++ {xs‵ = xs‵} {ys‵ = ys‵} (rec {ys = ys} (addCons p q p≢q r) xs≈xs‵ mOpsZ) ys≋ys‵ =
  rec (addCons (p ↑ˡ _) (q ↑ˡ _) (p≢q  ↑ˡ-injective _ _ _) r)
    (≈ⱽ++ xs≈xs‵ ys≋ys‵) (begin
      (ys ++ ys‵) [ q ↑ˡ _ ]← r *[ p ↑ˡ _ ] ≈⟨ []←++ _ _ _ p q 
      ys [ q ]← r *[ p ] ++ ys‵ ≈⟨ ++-congʳ (ys [ q ]← r *[ p ]) xs‵ mOpsZ 
      xs‵ ++ ys‵ ) where open ≋-Reasoning

≈ⱽ-refl : Reflexive (_≈ⱽ_ {n = n})
≈ⱽ-refl = idR ≋-refl

≈ⱽ-trans : Transitive (_≈ⱽ_ {n = n})
≈ⱽ-trans (idR xs≋ys) (idR ys≋zs) = idR (≋-trans xs≋ys ys≋zs)
≈ⱽ-trans (rec mOps xs≈ⱽys mOpsXs) (idR ys≋zs) = rec mOps xs≈ⱽys (≋-trans mOpsXs ys≋zs)
≈ⱽ-trans xs≈ⱽys (rec {ys = ws} mOpsYsZs ys≈ⱽzs mOpsYs) = rec mOpsYsZs (≈ⱽ-trans xs≈ⱽys ys≈ⱽzs) mOpsYs

[_,,_] : M  M  Vector M 2
[ x ,, y ] zero = x
[ x ,, y ] (suc i) = y

private
  v→ij : (v : Fin 2) (i j : Fin n)  Fin n
  v→ij 0F i j = i
  v→ij 1F i j = j

stepVecSpace :  {n}  (let n′ = ℕ.suc (ℕ.suc n)) 
  (xs : Vector M n′) (ys : Vector M 2)
  (i j : Fin n′) (let x = xs i) (let y = xs j)
   i  j
   [ x ,, y ] ≈ⱽ ys
   (let x′ = ys 0F) (let y′ = ys 1F)
   xs ≈ⱽ xs [ i ]≔ x′ [ j ]≔ y′
stepVecSpace xs ys i j i≢j (idR xsIJ≈ys) = idR xs≈zs
  where
  xs≈zs : xs  (xs [ i ]≔ ys 0F [ j ]≔ ys 1F)
  xs≈zs k with k  j
  ... | yes refl = ≈ᴹ.sym $ begin
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k ≡⟨ updateAt-updates j _ 
    ys 1F                           ≈˘⟨ xsIJ≈ys 1F 
    xs k  where open ≈ᴹ-Reasoning
  ... | no k≢j with k  i
  ...    | yes ≡.refl = ≈ᴹ.sym $ begin
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k ≡⟨ updateAt-minimal _ _ _ k≢j 
    (xs [ i ]≔ ys 0F) k              ≡⟨ updateAt-updates i _ 
    ys 0F ≈˘⟨ xsIJ≈ys 0F 
    xs k  where open ≈ᴹ-Reasoning
  ...    | no     k≢i = ≈ᴹ.sym $ begin
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k ≡⟨ updateAt-minimal _ _ _ k≢j 
    (xs [ i ]≔ ys 0F) k              ≡⟨ updateAt-minimal _ _ _ k≢i 
    xs k  where open ≈ᴹ-Reasoning
stepVecSpace xs ys i j i≢j (rec {ys = zs} (swapOp p q p≢q) xy≈ⱽys swapZsYs) =
  rec (swapOp i′ j′ (proj₁ (helper p q p≢q swapZsYs))) (stepVecSpace _ _ _ _ i≢j xy≈ⱽys) (proj₂ (helper p q p≢q swapZsYs))
  where
  open ≈ᴹ-Reasoning

  i′ = v→ij p i j
  j′ = v→ij q i j

  helper : (p q : Fin 2) (p≢q : p  q)
    (let i′ = v→ij p i j)
    (let j′ = v→ij q i j)
    (let ws = xs [ i ]≔ zs 0F [ j ]≔ zs 1F)
     swapV zs p q  ys
      i′  j′ × swapV ws i′ j′  xs [ i ]≔ ys 0F [ j ]≔ ys 1F
  helper 0F 0F p≢q _ = contradiction ≡.refl p≢q
  helper 1F 1F p≢q _ = contradiction ≡.refl p≢q
  proj₁ (helper 0F 1F p≢q _) = i≢j
  proj₁ (helper 1F 0F p≢q _) = i≢j  ≡.sym
  proj₂ (helper 0F 1F p≢q sZs≋ys) k with k  i | k  j
  ... | yes refl | yes refl = contradiction ≡.refl i≢j
  ... | yes refl | no k≢j = begin
    (_ [ j ]≔ _) k       ≡⟨ updateAt-minimal _ j _ k≢j 
    (_ [ k ]≔ _) k       ≡⟨ updateAt-updates k _ 
    (_ [ j ]≔ _) j       ≡⟨ updateAt-updates j _ 
    zs 1F                ≈⟨ sZs≋ys 0F 
    ys 0F               ≡˘⟨ updateAt-updates i _ 
    (xs [ i ]≔ ys 0F) k ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 
  ... | no k≢i | yes refl = begin
    (_ [ k ]≔ _) k ≡⟨ updateAt-updates k _ 
    (_ [ k ]≔ _) i ≡⟨ updateAt-minimal _ k _ (k≢i  ≡.sym) 
    (_ [ i ]≔ _) i ≡⟨ updateAt-updates i _ 
    zs 0F ≈⟨ sZs≋ys _ 
    ys 1F ≡˘⟨ updateAt-updates k _ 
    (_ [ k ]≔ _) k 
  ... | no k≢i | no k≢j = begin
    (_ [ j ]≔ _) k  ≡⟨ updateAt-minimal _ j _ k≢j 
    (_ [ i ]≔ _) k  ≡⟨ updateAt-minimal _ i _ k≢i 
    (_ [ j ]≔ _) k  ≡⟨ updateAt-minimal _ j _ k≢j 
    (_ [ i ]≔ _) k  ≡⟨ updateAt-minimal _ i _ k≢i 
    xs k           ≡˘⟨ updateAt-minimal _ i _ k≢i 
    (_ [ i ]≔ _) k ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (_ [ j ]≔ _) k 
  proj₂ (helper 1F 0F p≢q sZs≋ys) k with k  i | k  j
  ... | yes refl | yes refl = contradiction ≡.refl i≢j
  ... | yes refl | no k≢j = begin
    (_ [ k ]≔ _) k  ≡⟨ updateAt-updates k _ 
    (_ [ j ]≔ _) j  ≡⟨ updateAt-updates j _ 
    zs 1F           ≈⟨ sZs≋ys _ 
    ys 0F          ≡˘⟨ updateAt-updates k _ 
    (_ [ k ]≔ _) k ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (_ [ j ]≔ _) k 
  ... | no k≢i | yes refl = begin
    (_ [ i ]≔ _) k ≡⟨ updateAt-minimal _ i _ k≢i 
    (_ [ k ]≔ _) k ≡⟨ updateAt-updates k _ 
    (_ [ k ]≔ _) i ≡⟨ updateAt-minimal _ k _ (k≢i  ≡.sym) 
    (_ [ i ]≔ _) i ≡⟨ updateAt-updates i _ 
    zs 0F          ≈⟨ sZs≋ys _ 
    ys 1F         ≡˘⟨ updateAt-updates k _ 
    (_ [ k ]≔ _) k 
  ... | no k≢i | no k≢j = begin
    (_ [ i ]≔ _) k  ≡⟨ updateAt-minimal _ i _ k≢i 
    (_ [ j ]≔ _) k  ≡⟨ updateAt-minimal _ j _ k≢j 
    (_ [ j ]≔ _) k  ≡⟨ updateAt-minimal _ j _ k≢j 
    (_ [ i ]≔ _) k  ≡⟨ updateAt-minimal _ i _ k≢i 
    xs k           ≡˘⟨ updateAt-minimal _ i _ k≢i 
    (_ [ i ]≔ _) k ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (_ [ j ]≔ _) k 

stepVecSpace {n} xs ys i j i≢j (rec {ys = zs} (addCons p q p≢q r) xy≈ⱽys addZsYs) =
  rec (addCons i′ j′ (helper p q p≢q addZsYs .proj₁) r) (stepVecSpace _ _ _ _ i≢j xy≈ⱽys) (helper p q p≢q addZsYs .proj₂)

  where
  open ≈ᴹ-Reasoning

  i′ = v→ij p i j
  j′ = v→ij q i j

  helper : (p q : Fin 2) (p≢q : p  q)
    (let i′ = v→ij p i j)
    (let j′ = v→ij q i j)
    (let ws = xs [ i ]≔ zs 0F [ j ]≔ zs 1F)
     zs [ q ]← r *[ p ]  ys
      i′  j′ ×  ws [ j′ ]← r *[ i′ ]  xs [ i ]≔ ys 0F [ j ]≔ ys 1F
  helper 0F 0F p≢q = contradiction ≡.refl p≢q
  helper 1F 1F p≢q = contradiction ≡.refl p≢q
  proj₁ (helper 0F 1F p≢q _) = i≢j
  proj₁ (helper 1F 0F p≢q _) = i≢j  ≡.sym
  proj₂ (helper 0F 1F _ zs≋ys) k  with j  k | k  i
  ... | yes refl | yes refl = contradiction ≡.refl i≢j
  ... | yes refl | no k≢i = begin
    (xs [ i ]≔ zs 0F [ k ]≔ zs 1F) k +ᴹ r *ₗ (xs [ i ]≔ zs 0F [ k ]≔ zs 1F) i
      ≈⟨ +ᴹ-cong (≈ᴹ-reflexive (updateAt-updates k _)) (*ₗ-congˡ (≈ᴹ-reflexive
        (≡.trans (updateAt-minimal _ k _ (k≢i  ≡.sym)) (updateAt-updates i xs)))) 
     zs 1F +ᴹ r *ₗ zs 0F            ≈⟨ zs≋ys 1F 
     ys 1F            ≡˘⟨ updateAt-updates k _ 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 
  ... | no  j≢k | yes refl = begin
    (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) k ≡⟨ updateAt-minimal _ j _ (j≢k  ≡.sym) 
    (xs [ i ]≔ zs 0F) k              ≡⟨ updateAt-updates k xs 
    zs 0F                            ≈⟨ zs≋ys 0F 
    ys 0F                            ≡˘⟨ updateAt-updates k xs 
    (xs [ i ]≔ ys 0F) k             ≡˘⟨ updateAt-minimal _ j _ (j≢k  ≡.sym) 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 
  ... | no j≢k | no k≢i = begin
    (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) k ≡⟨ updateAt-minimal _ j _ (j≢k  ≡.sym) 
    (xs [ i ]≔ zs 0F) k              ≡⟨ updateAt-minimal _ i _ k≢i 
    xs k                            ≡˘⟨ updateAt-minimal _ i _ k≢i 
    (xs [ i ]≔ ys 0F) k             ≡˘⟨ updateAt-minimal _ j _ (j≢k  ≡.sym) 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 
  proj₂ (helper 1F 0F p≢q zs≋ys) k with i  k | k  j
  ... | yes refl | yes refl = contradiction ≡.refl i≢j
  ... | yes refl | no k≢j = begin
    (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) i +ᴹ r *ₗ (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) j
      ≈⟨ +ᴹ-cong (≈ᴹ-reflexive (≡.trans (updateAt-minimal _ j _ k≢j) (updateAt-updates i _)))
        (*ₗ-congˡ (≈ᴹ-reflexive (updateAt-updates j _))) 
     zs 0F +ᴹ r *ₗ zs 1F            ≈⟨ zs≋ys 0F 
     ys 0F            ≡˘⟨ updateAt-updates i _ 
     (xs [ i ]≔ ys 0F) i            ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) i 
  ... | no i≢k | yes refl = begin
    (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) k ≡⟨ updateAt-updates k _ 
     zs 1F                           ≈⟨ zs≋ys 1F 
     ys 1F                          ≡˘⟨ updateAt-updates k _ 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 
  ... | no i≢k | no k≢j = begin
    (xs [ i ]≔ zs 0F [ j ]≔ zs 1F) k ≡⟨ updateAt-minimal _ j _ k≢j 
    (xs [ i ]≔ zs 0F) k              ≡⟨ updateAt-minimal _ i _ (i≢k  ≡.sym) 
    xs k                            ≡˘⟨ updateAt-minimal _ i _ (i≢k  ≡.sym) 
    (xs [ i ]≔ ys 0F) k             ≡˘⟨ updateAt-minimal _ j _ k≢j 
    (xs [ i ]≔ ys 0F [ j ]≔ ys 1F) k 

open _reaches_ renaming (ys to ws; xs*ys≈x to xs*ws≈x)
open _≋ⱽ_
open CMonoidSolver +ᴹ-commutativeMonoid hiding (_≟_)


≈ⱽ⇒⊆ⱽ : xs ≈ⱽ ys  xs ⊆ⱽ ys
ws (≈ⱽ⇒⊆ⱽ (idR x) record { ys = ys ; xs*ys≈x = xs*ys≈x }) = ys
xs*ws≈x (≈ⱽ⇒⊆ⱽ (idR {xs = xs} {ys = zs} xs≈zs) {x} record { ys = ys ; xs*ys≈x = xs*zs≈x }) = begin
   (ys *ᵣ zs) ≈˘⟨ ∑Ext (*ₗ-congˡ  xs≈zs) 
   (ys *ᵣ xs) ≈⟨ xs*zs≈x 
  x  where open ≈ᴹ-Reasoning
ws (≈ⱽ⇒⊆ⱽ (rec (swapOp p q p≢q) xs≈ⱽys swap≈ys) {x} reach@record { ys = ys ; xs*ys≈x = xs*ys≈x }) = swapV (≈ⱽ⇒⊆ⱽ xs≈ⱽys reach .ws) p q
xs*ws≈x (≈ⱽ⇒⊆ⱽ {n} {ys = yss} (rec {xs = xs} {zs} (swapOp p q p≢q) xs≈ⱽys swap≈ys) {x} reach@record { ys = ys ; xs*ys≈x = xs*ys≈x }) = begin
   (sws *ᵣ yss) ≈˘⟨ ∑Ext (*ₗ-congˡ  swap≈ys) 
   (sws *ᵣ swapV zs p q) ≈⟨ ∑Ext {n} sameness 
   (swapV (wss *ᵣ zs) p q) ≈⟨ ∑Swap (wss *ᵣ zs) p q 
   (wss *ᵣ zs) ≈⟨ help .xs*ws≈x 
  x  where
  open ≈ᴹ-Reasoning

  eqn = ≈ⱽ⇒⊆ⱽ xs≈ⱽys
  help = eqn reach
  wss = ws help
  sws = swapV wss p q
  wssZs = wss *ᵣ zs


  sameness : sws *ᵣ swapV zs p q  swapV (wss *ᵣ zs) p q
  sameness k = begin
    swapV wss p q k *ₗ swapV zs p q k ≈⟨ *ₗ-congʳ (reflexive
      (vecUpdates≡reflectBool-theo2 wss indices valuesWss k)) 
    evalFromPosition valuesWss (wss k) evaluatedWss *ₗ swapV zs p q k ≈⟨ *ₗ-congˡ
      (≈ᴹ-reflexive (vecUpdates≡reflectBool-theo2 zs indices val2 k)) 
    _ *ₗ _ ≈⟨ helper _ (vBoolFromIndices indices k .proj₂) 
    _ ≈˘⟨ ≈ᴹ-reflexive (vecUpdates≡reflectBool-theo2 wssZs indices val3 k) 
    swapV wssZs p q k 
    where

    indices = q :: p :: V.[]
    valuesWss = wss p :: wss q :: V.[]
    evaluatedWss = firstTrue $ proj₁ $ vBoolFromIndices indices k

    val2 = zs p :: zs q :: V.[]
    val3 = wssZs p :: wssZs q :: V.[]

    helper :  vBool (vType : VecWithType  (ind , b)  Reflects (k  ind) b) $ V.zip indices vBool) 
      evalFromPosition valuesWss (wss k) (firstTrue vBool) *ₗ evalFromPosition val2 (zs k) (firstTrue vBool) ≈ᴹ
      evalFromPosition val3 (wssZs k) (firstTrue vBool)
    helper (true :: _) (ofʸ ≡.refl :: _) = ≈ᴹ-refl
    helper (false :: true :: V.[]) (ofⁿ _ :: ofʸ ≡.refl :: []) = ≈ᴹ-refl
    helper (false :: false :: V.[]) (ofⁿ _ :: ofⁿ _ :: []) = ≈ᴹ-refl
ws (≈ⱽ⇒⊆ⱽ {ys = yss} (rec {xs = xs} {zs} (addCons p q p≢q r) xs≈ⱽys zs≋ys) x∈xs) =
  let wss = ws (≈ⱽ⇒⊆ⱽ xs≈ⱽys x∈xs) in wss [ p ]≔ (wss p - wss q * r)
xs*ws≈x (≈ⱽ⇒⊆ⱽ {n} {ys = yss} (rec {xs = xs} {zs} (addCons p q p≢q r) xs≈ⱽys zs≋ys) {x} x∈xs) = begin
   (sws *ᵣ yss) ≈˘⟨ ∑Ext (*ₗ-congˡ  zs≋ys) 
   (sws *ᵣ (zs [ q ]← r *[ p ])) ≈⟨ sameness 
   (wss *ᵣ zs) ≈⟨ help .xs*ws≈x 
  x 
  where
  open ≈ᴹ-Reasoning
  module  = ≈-Reasoning R.setoid

  help = ≈ⱽ⇒⊆ⱽ xs≈ⱽys x∈xs
  wss = ws help
  sws : Vector R n
  sws = wss [ p ]≔ (wss p - wss q * r)

  q≢p = p≢q  ≡.sym

  wss*zs = wss *ᵣ zs
  sws*zs = sws *ᵣ (zs [ q ]← r *[ p ])

  zsPq≡ : (zs [ q ]← r *[ p ]) q  zs q +ᴹ r *ₗ zs p
  zsPq≡ rewrite dec-yes (q  q) ≡.refl .proj₂ = ≡.refl

  sws*zsP≡ : sws*zs p  sws p *ₗ zs p
  sws*zsP≡ rewrite dec-no (q  p) (p≢q  ≡.sym) = ≡.refl

  sws*zsK≡ :  {k}  k  q  sws*zs k  sws k *ₗ zs k
  sws*zsK≡ {k} k≢q rewrite dec-no (q  k) (k≢q  ≡.sym) = ≡.refl

  sws*zsQ : sws*zs q ≈ᴹ sws q *ₗ zs q +ᴹ (sws q * r) *ₗ zs p
  sws*zsQ = begin
    _ ≈⟨ *ₗ-congˡ (≈ᴹ-reflexive zsPq≡) 
    sws q *ₗ (zs q +ᴹ r *ₗ zs p) ≈⟨ *ₗ-distribˡ _ _ _ 
    sws q *ₗ zs q +ᴹ sws q *ₗ r *ₗ zs p ≈˘⟨ +ᴹ-congˡ (*ₗ-assoc _ _ _) 
    _ 

  swsP : sws p + sws q * r  wss p
  swsP = ∼.begin
    sws p + sws q * r ∼.≈⟨ +-cong (reflexive (updateAt-updates p wss))
      (*-congʳ (reflexive (updateAt-minimal q p _ q≢p))) 
    wss p - wss q * r + wss q * r ∼.≈⟨ R.+-assoc (wss p) _ _ 
    wss p + (- (wss q * r) + wss q * r) ∼.≈⟨ +-congˡ (-‿inverseˡ _) 
    wss p + 0# ∼.≈⟨ R.+-identityʳ _ 
    wss p ∼.∎

  swsQ : sws q  wss q
  swsQ = reflexive (updateAt-minimal q p wss q≢p)

  lemma₁ : (sws p + sws q * r) *ₗ zs p +ᴹ sws q *ₗ zs q ≈ᴹ wss p *ₗ zs p +ᴹ wss q *ₗ zs q
  lemma₁ = +ᴹ-cong (*ₗ-congʳ swsP) (*ₗ-congʳ swsQ)

  theo₀ : sws*zs p +ᴹ sws*zs q ≈ᴹ wss*zs p +ᴹ wss*zs q
  theo₀ = begin
    _ ≈⟨ +ᴹ-cong (≈ᴹ-reflexive sws*zsP≡) sws*zsQ 
    sws p *ₗ zs p +ᴹ (sws q *ₗ zs q +ᴹ (sws q * r) *ₗ zs p) ≈⟨
      solve 3  a b c  a  b  c  (a  c)  b) ≈ᴹ-refl (sws p *ₗ zs p) (sws q *ₗ zs q) ((sws q * r) *ₗ zs p) 
    sws p *ₗ zs p +ᴹ (sws q * r) *ₗ zs p +ᴹ sws q *ₗ zs q ≈˘⟨ +ᴹ-congʳ (*ₗ-distribʳ (zs p) _ _) 
    (sws p + sws q * r) *ₗ zs p +ᴹ sws q *ₗ zs q ≈⟨ lemma₁ 
    _ 

  theo₁ : sws*zs p +ᴹ (sws*zs [ p ]≔ 0ᴹ) q ≈ᴹ wss*zs p +ᴹ (wss*zs [ p ]≔ 0ᴹ) q
  theo₁ = begin
    _ +ᴹ _ ≈⟨ +ᴹ-congˡ (≈ᴹ-reflexive (updateAt-minimal q p sws*zs q≢p)) 
    _ +ᴹ sws*zs q ≈⟨ theo₀ 
    _ +ᴹ wss*zs q ≈˘⟨ +ᴹ-congˡ (≈ᴹ-reflexive (updateAt-minimal q p wss*zs q≢p)) 
    _ +ᴹ _ 

  theo₂ : sws*zs [ p ]≔ 0ᴹ [ q ]≔ 0ᴹ  wss*zs [ p ]≔ 0ᴹ [ q ]≔ 0ᴹ
  theo₂ k = helper _ $ vBoolFromIndices indices k .proj₂
    where
    indices = q :: p :: V.[]
    v0 = 0ᴹ :: 0ᴹ :: V.[]


    helper :  vBool (vType : VecIndBool indices vBool k)
       (sws*zs [ p ]≔ 0ᴹ [ q ]≔ 0ᴹ) k ≈ᴹ (wss*zs [ p ]≔ 0ᴹ [ q ]≔ 0ᴹ) k
    helper (true :: _) vt@(ofʸ ≡.refl :: _) = begin
      _   ≡⟨ vecUpdates≡reflectBool-theo sws*zs v0 k vt 
      0ᴹ ≡˘⟨ vecUpdates≡reflectBool-theo wss*zs v0 k vt 
      _ 
    helper (false :: true :: _) vt@(_ :: _ :: _) = ≈ᴹ-reflexive (≡.trans
      (vecUpdates≡reflectBool-theo sws*zs v0 k vt) (≡.sym $ vecUpdates≡reflectBool-theo wss*zs v0 k vt))
    helper (false :: false :: V.[]) vt@(ofⁿ k≢q :: ofⁿ k≢p :: []) = begin
      _              ≡⟨ vecUpdates≡reflectBool-theo sws*zs v0 k vt 
      sws*zs k       ≡⟨ sws*zsK≡ k≢q 
      _ *ₗ zs k      ≈⟨ *ₗ-congʳ (reflexive (updateAt-minimal k p _ k≢p)) 
      wss k *ₗ zs k ≡˘⟨ vecUpdates≡reflectBool-theo wss*zs v0 k vt 
      _ 


  sameness :  (sws *ᵣ (zs [ q ]← r *[ p ])) ≈ᴹ  (wss *ᵣ zs)
  sameness = begin
     sws*zs ≈⟨ ∑Remove₂ sws*zs p 
    sws*zs p +ᴹ _ ≈⟨ +ᴹ-congˡ (∑Remove₂ {n} _ q) 
    sws*zs p +ᴹ (_ +ᴹ _) ≈˘⟨ +ᴹ-assoc (sws*zs p) _ _ 
    sws*zs p +ᴹ (sws*zs [ p ]≔ 0ᴹ) q +ᴹ _ ≈⟨ +ᴹ-cong theo₁ (∑Ext theo₂) 
    wss*zs p +ᴹ (wss*zs [ p ]≔ 0ᴹ) q  +ᴹ _ ≈⟨ +ᴹ-assoc _ _ _ 
    wss*zs p +ᴹ (_ +ᴹ _) ≈˘⟨ +ᴹ-congˡ (∑Remove₂ {n} _ q) 
    wss*zs p +ᴹ _ ≈˘⟨ ∑Remove₂ wss*zs p 
     wss*zs 

≈ⱽ-sym : Symmetric (_≈ⱽ_ {n = n})
≈ⱽ-sym (idR xs≈ys) = idR (≋-sym xs≈ys)
≈ⱽ-sym {x = xs} {zs} (rec {ys = ys} (MBase.swapOp p q p≢q) xs≈ⱽys fMops) =
  ≈ⱽ-trans (rec (swapOp p q p≢q) (idR (≋-sym fMops)) λ i  ≈ᴹ-reflexive (swap-involute ys p q i)) ys≈ⱽxs
  where
  ys≈ⱽxs = ≈ⱽ-sym xs≈ⱽys
≈ⱽ-sym {x = xs} {zs} (rec {ys = ys} (MBase.addCons p q p≢q r) xs≈ⱽys fMops) =
  ≈ⱽ-trans (rec (addCons p q p≢q (- r)) (idR (≋-sym fMops)) sameVec) ys≈ⱽxs
  where
  ys≈ⱽxs = ≈ⱽ-sym xs≈ⱽys

  sameVec : ((ys [ q ]← r *[ p ]) [ q ]← - r *[ p ])  ys
  sameVec i with q  i
  ... | no q≢i rewrite dec-no (q  i) q≢i | dec-no (q  i) q≢i = ≈ᴹ-refl
  ... | yes ≡.refl rewrite dec-yes (i  i) ≡.refl .proj₂ | dec-no (i  p) (p≢q  ≡.sym) = begin
    ys i +ᴹ r *ₗ ys p +ᴹ - r *ₗ ys p ≈⟨ +ᴹ-assoc _ _ _ 
    ys i +ᴹ (r *ₗ ys p +ᴹ - r *ₗ ys p) ≈⟨ +ᴹ-congˡ (begin
       _ ≈˘⟨ *ₗ-distribʳ (ys p) r (- r) 
       (r - r) *ₗ ys p ≈⟨ *ₗ-congʳ (-‿inverseʳ _) 
       0# *ₗ ys p ≈⟨ *ₗ-zeroˡ _ 
       _ 
       ) 
    ys i +ᴹ 0ᴹ ≈⟨ +ᴹ-identityʳ _ 
    ys i 
    where open ≈ᴹ-Reasoning

≈ⱽ⇒⊇ⱽ : xs ≈ⱽ ys  xs ⊇ⱽ ys
≈ⱽ⇒⊇ⱽ = ≈ⱽ⇒⊆ⱽ  ≈ⱽ-sym

≈ⱽ⇒≋ⱽ : xs ≈ⱽ ys  xs ≋ⱽ ys
≈ⱽ⇒≋ⱽ xs≈ⱽys = record { fwd = ≈ⱽ⇒⊆ⱽ xs≈ⱽys ; bwd = ≈ⱽ⇒⊇ⱽ xs≈ⱽys }