open import Algebra.DecidableField

module SystemEquations.UniqueSolution {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.Apartness.Properties.HeytingCommutativeRing heytingCommutativeRing
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 SystemEquations.Solving dField

open import lbry

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

private variable
  m n p q : 

private
  lemma-a :  {a b}  a * (- b) + b * a  0#
  lemma-a = trans (+-congʳ (trans (*-comm _ _) (sym (-‿distribˡ-* _ _)))) (-‿inverseˡ _)

module _ where
  open SystemEquations
  open _≋ⱽ_

  sameSolutions≈ : {sx : SystemEquations n m} {sy : SystemEquations p m}
     A++b sy ≋ⱽ A++b sx  Solution sx  Solution sy
  sameSolutions≈ sy≋ⱽsx (noSol f) = noSol (f  sameSolutionsS (sy≋ⱽsx .bwd) _)
  sameSolutions≈ {sx = sx} {sy} sy≋ⱽsx (sol _ _ (f , g)) = sol
    _ _ (sameSolutionsS (sy≋ⱽsx .fwd) _  f , g  sameSolutionsS (sy≋ⱽsx .bwd) _)

solveNormedEquationUnique :  (sx : SystemEquations n m) (open SystemEquations sx)
   MatrixIsNormed≁0≈1 A   IsSolutionAndUnique
solveNormedEquationUnique {n} {m} sx ANormed = vAffine , vAffFamily , sameSolution
  where
  open SolvingNormedEquation sx ANormed

  sameSolution : SameSolution vAffine
  sameSolution {vSol} isSol = vecSol , vecSol≈vSol
    where

    vecSol :  i  F
    vecSol = vSol  pivRes

    vPivSame :  i  Affine.eval (vAffine (pivs i)) vecSol  vSol (pivs i)
    vPivSame i rewrite vSplit′-same pivs i pivsCrescent = begin
       {m  n}  j  vSol _ * - A i (pivRes j)) + b i ≈˘⟨ +-congˡ (isSol i) 
      _ +  {m}  j  A i j * vSol j) ≈˘⟨ +-congˡ (∑-pivs′-same pivs _ pivsCrescent) 
      _ + (  j  A i (pivs j) * vSol (pivs j)) +  {m  n}   j  A i (pivRes j) * vSol (pivRes j)))
        ≈⟨ +-congˡ (+-comm _ _) 
      _ + (_ + _) ≈˘⟨ +-assoc _ _ _ 
      _ + _ + _ ≈˘⟨ +-congʳ (∑Split {m  n} _ _) 
       {m  n}  j  vSol (pivRes j) * - A i (pivRes j) + A i (pivRes j) * vSol (pivRes j)) + _
        ≈⟨ +-congʳ (∑Ext {m  n} λ _  lemma-a ) 
      _ + _ ≈⟨ +-congʳ (∑0r (m  n)) 
      0# + _ ≈⟨ +-identityˡ _ 
        j  A i (pivs j) * vSol (pivs j)) ≈⟨ ∑Ext  j  *-congʳ (AiPj≈δij i j)) 
        j  δ i j * vSol (pivs j)) ≈⟨ ∑Mul1r _ i 
      vSol _ 

    vRPivSame :  i  Affine.eval (vAffine (pivRes i)) vecSol  vSol (pivRes i)
    vRPivSame i rewrite vSplit′-rPivs pivs i pivsCrescent = begin
       {m  n}  j  _ * _) + 0# ≈⟨ +-identityʳ _ 
       {m  n}  j  _ * δ i j)  ≈⟨ ∑Ext  j  *-comm _ (δ i j)) 
       {m  n}  j  δ i j * _)  ≈⟨ ∑Mul1r _ i 
      vSol _ 


    vecSol≈vSol : (i : Fin m)  Affine.eval (vAffine i) vecSol  vSol i
    vecSol≈vSol i with ∃-piv⊎pivRes′ pivs pivsCrescent i
    ... | inj₁ (x , x≡piv) rewrite ≡.sym x≡piv = vPivSame _
    ... | inj₂ (x , x≡rPiv) rewrite ≡.sym x≡rPiv = vRPivSame _

solveNormedEquationNormUnique :  (sx : SystemEquations n m) (open SystemEquations sx)  MatrixIsNormed≁0≈1 A++b
   Solution
solveNormedEquationNormUnique sx norm with
  systemNormedSplit sx norm |
  systemUnsolvable {sx = sx} |
  solveNormedEquationUnique sx
... | inj₁ x | b | _ = SystemEquations.noSol $ b x
... | inj₂ y | _ | c = SystemEquations.sol _ _ $ proj₂ $ c y

solveUniqueSystemEquations : (sx : SystemEquations n m) (open SystemEquations sx)  Solution
solveUniqueSystemEquations sx = sameSolutions≈ A++b≋ⱽs sol-prob
  where
  open SystemEquations sx
  open FromNormalization≁0≈1 (normalize≈1≁0 A++b)

  sYs = A++b⇒systemEquations ys

  open SystemEquations sYs using ()
    renaming (Solution to SYs; A++b to A++b-ys)

  ys≋A++b-ys :  i j  ys i j  A++b-ys i j
  ys≋A++b-ys i j = reflexive (≡.sym (same-take ys i j))

  A++b≋ⱽs : A++b ≋ⱽ A++b-ys
  A++b≋ⱽs = ≋ⱽ-trans xs≋ⱽys $ ≋ⱽ-reflexive ys≋A++b-ys

  sol-prob = solveNormedEquationNormUnique sYs $ ≈-norm ys≋A++b-ys ysNormed