open import Algebra.DecidableField

module SystemEquations.Solving {c ℓ₁ ℓ₂} (dField : DecidableField c ℓ₁ ℓ₂) where

open import Algebra
open import Algebra.Apartness
open import Algebra.Module
open import Function
open import Data.Bool using (Bool; true; false; if_then_else_)
open import Data.Product
open import Data.Maybe using (Is-just; Maybe; just; nothing)
open import Data.Sum renaming ([_,_] to [_∙_])
open import Data.Empty
open import Data.Nat as  using (; _∸_)
open import Data.Nat.Properties as  using (module ≤-Reasoning)
open import Data.Fin as F using (Fin; suc; splitAt; fromℕ; toℕ; inject₁)
open import Data.Fin.Properties as F
open import Data.Fin.Patterns
open import Data.Vec.Functional
open import Relation.Nullary

open import Fin.Base
open import Fin.Properties
open import Vector.Structures
open import Vector.Properties
open import Algebra.Matrix.Structures
open import SystemEquations.Definitions dField
open import MatrixFuncNormalization.Definitions dField
import Algebra.Module.Definition as MDefinition
import Algebra.Module.Props as MProps′
open import Algebra.BigOps

open DecidableField dField renaming (Carrier to F)
open import Algebra.Properties.Ring ring
open VRing rawRing
open import Algebra.Module.Instances.AllVecLeftModule ring using (leftModule)
open MRing rawRing using (Matrix; _++v_)
open import Algebra.Module.Instances.CommutativeRing commutativeRing
open import Data.Vec.Functional.Relation.Binary.Equality.Setoid setoid
open import Relation.Binary.PropositionalEquality as  using (_≡_; _≢_; subst; subst₂; cong)
open import Relation.Binary.Reasoning.Setoid setoid
open import Algebra.Solver.CommutativeMonoid +-commutativeMonoid hiding (id)
open import Algebra.Module.PropsVec commutativeRing
open import SystemEquations.VecPiv dField
open import MatrixFuncNormalization.MainTheo dField

open import lbry

open SumRing ring using (∑Ext; ∑0r; δ; ∑Mul1r; ∑Split)
open MDef′
open MProps

private variable
  m n p q : 

lastFin : (n : )  Fin (ℕ.suc n)
lastFin ℕ.zero = 0F
lastFin (ℕ.suc n) = suc (lastFin n)

injF : Fin n  Fin (n ℕ.+ 1)
injF 0F = 0F
injF (suc i) = suc (injF i)

add-1 : Vector F n  Vector F (ℕ.suc n)
add-1 xs = appendLast xs (- 1#)

same-take :  (xs : Matrix F n (ℕ.suc m)) i j
   ((λ i j  xs i (inject₁ j)) ++v λ w  xs w (lastFin _)) i j  xs i j
same-take {n} {ℕ.zero} xs i 0F = ≡.refl
same-take {n} {ℕ.suc m} xs i 0F = ≡.refl
same-take {n} {ℕ.suc m} xs i (suc j) rewrite same-take  i j  xs i (suc j)) i j = ≡.refl

A++b⇒systemEquations : Matrix F n (ℕ.suc m)  SystemEquations n m
A++b⇒systemEquations xs = record { A = λ i j  xs i (inject₁ j) ; b = λ i  xs i (lastFin _) }

module _ where
  open SystemEquations

  sameSolutionsSE : {sx : SystemEquations n m} {sy : SystemEquations p m}  A++b sy ⊆ⱽ A++b sx
      v  IsSolutionA++b sx v  IsSolutionA++b sy v
  sameSolutionsSE sy⊆ⱽsx i = sameSolutions {α = i} sy⊆ⱽsx

tail-lemma :  (u : Vector F (ℕ.suc n)) b k  tail (appendLast u b) k  (appendLast (tail u) b) k
tail-lemma u b k = refl

add-1∑ :  (v : Vector F n) b (u : Vector F n)  (add-1 v ∙ⱽ appendLast u b)  u ∙ⱽ v - b
add-1∑ {ℕ.zero} v b u = begin
  - 1# * b + 0# ≈⟨ +-identityʳ _ 
  - 1# * b      ≈⟨ -1*x≈-x _ 
  - b          ≈˘⟨ +-identityˡ (- b) 
  0# + - b 
add-1∑ {ℕ.suc n} v b u = begin
  v 0F * u 0F + add-1 (tail v) ∙ⱽ tail (appendLast u b) ≈⟨ +-congʳ (*-comm _ _)
  u 0F * v 0F + add-1 (tail v) ∙ⱽ appendLast (tail u) b ≈⟨ +-congˡ (add-1∑ _ b (tail u)) 
  u 0F * v 0F + (tail u ∙ⱽ tail v - b)             ≈˘⟨ +-assoc _ _ (- b) 
  u 0F * v 0F + tail u ∙ⱽ tail v - b 

sameSolutionsA++b :  {sx : SystemEquations n m} {v} (open SystemEquations sx)
   IsSolutionA++b $ add-1 v  IsSolution v
sameSolutionsA++b {n = n} {m = m} {sx = system A b} {v} sv i = begin
  A i ∙ⱽ v ≈⟨ +-inverseˡ-unique (A i ∙ⱽ v) (- b i) sv-lemma 
  - - b i  ≈⟨ -‿involutive _ 
  b i 
  where

  sv-lemma = begin
    A i ∙ⱽ v - b i             ≈˘⟨ add-1∑ v (b i) (A i) 
    add-1 v ∙ⱽ appendLast (A i) (b i) ≈⟨ sv i 
    0# 

sameSolutionsA++b-inv :  {sx : SystemEquations n m} {v} (open SystemEquations sx)
   IsSolution v  IsSolutionA++b $ add-1 v
sameSolutionsA++b-inv {m = m} {system A b} {v} sv i = begin
  add-1 v ∙ⱽ appendLast (A i) (b i) ≈⟨ add-1∑ v _ _ 
  A i ∙ⱽ v - b i                    ≈⟨ +-congʳ (sv i) 
  b i - b i                         ≈⟨ -‿inverseʳ (b i) 
  0# 

module _ where
  open SystemEquations

  sameSolutionsS : {sx : SystemEquations n m} {sy : SystemEquations p m}   A++b sy ⊆ⱽ A++b sx
      v  IsSolution sx v  IsSolution sy v
  sameSolutionsS {sx = sx} {sy} sy⊆ⱽsx v isSol = sameSolutionsA++b {sx = sy}
    (sameSolutionsSE {sx = sx} {sy = sy} sy⊆ⱽsx (add-1 v) (sameSolutionsA++b-inv {sx = sx} isSol))

  open _≋ⱽ_

systemUnsolvable :  {sx : SystemEquations n m} (open SystemEquations sx)  A≈0∧b#0   {v}  IsSolution v  
systemUnsolvable {n = n} {m} {system A b} (i , A0 , b#0) {v} sv = tight _ _ .proj₂
  (begin
    b i             ≈˘⟨ sv i 
    A i ∙ⱽ v         ≈⟨ ∑Ext  j  trans (*-congʳ (A0 j)) (zeroˡ _)) 
     {m} 0ⱽ ≈⟨ ∑0r m 
    0# ) b#0

module _ where
  open MatrixIsNormed≁0≈1
  open MatrixIsNormed≁0

  emptyNormed : (A : Matrix F 0 n)  MatrixIsNormed≁0≈1 A
  pivs (isNormed (emptyNormed A)) ()
  mPivots (isNormed (emptyNormed A)) ()
  pivsCrescent (isNormed (emptyNormed A)) {y = ()} x
  columnsZero (isNormed (emptyNormed A)) () j x
  pivsOne (emptyNormed A) ()

systemNormedSplit :  (sx : SystemEquations n m) (open SystemEquations sx)  MatrixIsNormed≁0≈1 A++b
   A≈0∧b#0  MatrixIsNormed≁0≈1 A
systemNormedSplit {ℕ.zero} sx normed = inj₂ (emptyNormed _)
systemNormedSplit {ℕ.suc n} {m} sx (cIsNorm≁0≈1 (cIsNorm≁0 pivs mPivots pivsCrescent columnsZero) pivsOne)
  with pivs (fromℕ n) F.≟ fromℕ m
... | yes p = inj₁ (fromℕ _ ,  i  A≈0 (mPivots (fromℕ n) .proj₂ (inject₁ i) (inj₁< i)))  , (b#0 $ mPivots (fromℕ _) .proj₁))
  where
  open SystemEquations sx

  b#0 : appendLast (A $ fromℕ n) (b $ fromℕ n) (pivs $ fromℕ n) # 0#  b (fromℕ n) # 0#
  b#0 rewrite p | appendLastFromℕ (A $ fromℕ n) (b $ fromℕ n) = id

  A≈0 : {i : Fin _}  appendLast (A (fromℕ n)) (b (fromℕ n)) (inject₁ i)  0#  A (fromℕ n) i  0#
  A≈0 {i} rewrite appendLastInj (A (fromℕ n)) (b (fromℕ n)) i = id

  inj₁< :  i  inject₁ i F.< pivs (fromℕ n)
  inj₁< i = ≡.subst (inject₁ i F.<_) (≡.sym p) (≤∧≢⇒< (≤fromℕ _) (fromℕ≢inject₁  ≡.sym))
... | no pn≢fromℕ = inj₂ (cIsNorm≁0≈1 (cIsNorm≁0 pivsR mPivotsR allRowsR cols0R) pivs≁0)
  where
  open SystemEquations sx

  pn<n : pivs (fromℕ n) F.< fromℕ _
  pn<n = ≤∧≢⇒< (≤fromℕ _) pn≢fromℕ

  pi≢n :  i  pivs i  fromℕ _
  pi≢n i peq with i F.≟ fromℕ _
  ... | yes ≡.refl = pn≢fromℕ peq
  ... | no i≢fn = <⇒≢ (<-trans (pivsCrescent (≤∧≢⇒< (≤fromℕ _) i≢fn)) pn<n) peq

  toℕ-pi≢n :  i  m  F.toℕ (pivs i)
  toℕ-pi≢n i peq = pi≢n i (toℕ-injective (≡.sym (≡.trans (toℕ-fromℕ _) peq)))

  pivsR : Vector (Fin m) (ℕ.suc n)
  pivsR i = F.lower₁ (pivs i) (toℕ-pi≢n i)

  toℕ-pivR-≡ :  i  toℕ (pivsR i)  toℕ (pivs i)
  toℕ-pivR-≡ i = toℕ-lower₁ _ (toℕ-pi≢n _)

  A++b≡piv :  i  A++b i (pivs i)  A i (pivsR i)
  A++b≡piv i = appendLastLower (A i) (b i) (pivs i) (toℕ-pi≢n i)

  mPivotsR : MatrixPivots≁0 A pivsR
  proj₁ (mPivotsR i) = help $ mPivots i .proj₁
    where
    help : A++b i (pivs i) # 0#  _
    help rewrite A++b≡piv i = id
  proj₂ (mPivotsR i) j j<pI = help $ mPivots i .proj₂ (inject₁ j) (subst₂ ℕ._<_ (≡.sym (toℕ-inject₁ _)) (toℕ-pivR-≡ _) j<pI)
    where
    help : appendLast (A i) (b i) (inject₁ j)  0#  _
    help rewrite appendLastInj (A i) (b i) j = id

  allRowsR : AllRowsNormalized≁0 pivsR
  allRowsR = subst₂ ℕ._<_ (≡.sym $ toℕ-lower₁ _ $ toℕ-pi≢n _) (≡.sym $ toℕ-lower₁ _ $ toℕ-pi≢n _)  pivsCrescent

  cols0R : ColumnsZero≁0 A pivsR
  cols0R i j i≢j = trans (sym (reflexive (appendLastLower (A _) (b _) _ (toℕ-pi≢n _)))) (columnsZero i j i≢j)

  pivs≁0 : PivsOne≁0 A pivsR
  pivs≁0 i = trans (sym (reflexive (A++b≡piv _))) (pivsOne i)

module SolvingNormedEquation (sx : SystemEquations n m)
  (ANormed : MatrixIsNormed≁0≈1 (SystemEquations.A sx)) where

  open SystemEquations sx public
  open MatrixIsNormed≁0≈1 ANormed public

  vBool : Vector Bool m
  vBool = vecIn→vecBool pivs

  pivRes : Vector (Fin m) (m  n)
  pivRes = rPivs′ _ pivsCrescent

  vSplit' : Vector (Fin (m  n)  Fin n) m
  vSplit' = vSplit′ _ pivsCrescent

  vAffRPiv : Vector (Affine $ m  n) $ m  n
  vAffRPiv j = vAff (δ j) 0#

  vAffPiv : Vector (Affine $ m  n) n
  vAffPiv j = vAff  k  - A j (pivRes k)) (b j)

  vAffine : VecAffine m (m  n)
  vAffine = [ vAffRPiv  vAffPiv ]  vSplit'

  open Affine

  AiPj≈δij : (i j : Fin n)  A i (pivs j)  δ i j
  AiPj≈δij i j with i F.≟ j
  ... | yes ≡.refl = pivsOne _
  ... | no i≢j = columnsZero _ _ (i≢j  ≡.sym)

  vAffFamily : IsFamilySolution vAffine
  vAffFamily vecs i = begin
      j  A i j * (  k  vecs k * coeff (vAffine j) k) + constant (vAffine j))) ≈˘⟨ ∑-pivs′-same _
       j  A i j * (  k  vecs k * coeff (vAffine j) k) + constant (vAffine j))) pivsCrescent 
      j  A i (pivs j) * (  k  vecs k * coeff (vAffine $ pivs j) k) + constant (vAffine $ pivs j))) +
      j  A i (pivRes j) * (  k  vecs k * coeff (vAffine $ pivRes j) k) + constant (vAffine $ pivRes j)))
      ≈⟨ +-cong ∑Piv ∑NPiv 
    b i +   k  - (A i (pivRes k) * vecs k)) +   k  A i (pivRes k) * vecs k) ≈⟨ +-assoc (b i) _ _ 
    b i + ( {m  n}  k  - (A i (pivRes k) * vecs k)) +  λ k  A i (pivRes k) * vecs k) ≈⟨ +-congˡ
      (begin
        _ ≈˘⟨ ∑Split _  k  A i (pivRes k) * vecs k) 
        _ ≈⟨ ∑Ext  k  -‿inverseˡ (A i (pivRes k) * vecs k)) 
         {m  n} (const 0#) ≈⟨ ∑0r (m  n) 
        _ ) 
    b i + 0# ≈⟨ +-identityʳ _ 
    b i 
    where


    coeffVAffine≈ :  k  coeff (vAffine (pivs i)) k  - A i (pivRes k)
    coeffVAffine≈ k rewrite vSplit′-same pivs i pivsCrescent = refl

    constVAffine≈ : constant (vAffine (pivs i))  b i
    constVAffine≈ rewrite vSplit′-same pivs i pivsCrescent = refl

    sameInSum :  k  vecs k * coeff (vAffine (pivs i)) k  - (A i (pivRes k) * vecs k)
    sameInSum k = begin
      vecs k * coeff (vAffine (pivs i)) k ≈⟨ *-comm _ _ 
      coeff (vAffine (pivs i)) k * vecs k ≈⟨ *-congʳ (coeffVAffine≈ k) 
      - A i (pivRes k) * vecs k ≈˘⟨ -‿distribˡ-* _ _ 
      - (A i (pivRes k) * vecs k) 

    ∑Piv = begin
       {n}  j  A i (pivs j) * (  k  vecs k * coeff (vAffine (pivs j)) k) + constant (vAffine (pivs j))))
        ≈⟨ ∑Ext {n}  j  *-congʳ (AiPj≈δij i j)) 
        j  δ i j *  p    k  vecs k * coeff (vAffine (pivs p)) k) + constant (vAffine (pivs p))) j )
        ≈⟨ ∑Mul1r {n} _ i 
        k  vecs k * coeff (vAffine (pivs i)) k) + constant (vAffine (pivs i))
        ≈⟨ +-cong (∑Ext sameInSum) constVAffine≈ 
        k  - (A i (pivRes k) * vecs k)) + b i ≈⟨ +-comm _ _ 
      b i +   k  - (A i (pivRes k) * vecs k)) 

    ∑CoeffConst₁ :  j k  coeff (vAffine (pivRes j)) k  δ j k
    ∑CoeffConst₁ j k rewrite vSplit′-rPivs _ j pivsCrescent =  refl

    ∑CoeffConst₂ :  j  constant (vAffine (pivRes j))  0#
    ∑CoeffConst₂ j rewrite vSplit′-rPivs _ j pivsCrescent = refl

    ∑Eq = λ j  begin
      _ ≈⟨ +-identityʳ _ 
        k  vecs k * δ j k) ≈⟨ ∑Ext  k  *-comm (vecs k) _) 
        k  δ j k * vecs k) ≈⟨ ∑Mul1r _ j 
      vecs j 

    ∑NPiv = begin
        j  A i (pivRes j) * (  k  vecs k * coeff (vAffine (pivRes j)) k) + constant (vAffine (pivRes j))))
        ≈⟨ ∑Ext  j  *-congˡ (+-cong (∑Ext  k  *-congˡ (∑CoeffConst₁ j k))) (∑CoeffConst₂ j))) 
        j  A i (pivRes j) * (  k  vecs k * δ j k) + 0#)) ≈⟨ ∑Ext  j  *-congˡ (∑Eq j)) 
        j  A i (pivRes j) * vecs j)