open import Algebra.DecidableField

module MatrixFuncNormalization.NormAfter.Base {c ℓ₁ ℓ₂} (dField : DecidableField c ℓ₁ ℓ₂) where

open import Level using (Level; Lift; lift; _⊔_)
open import Function hiding (flip)
open import Data.Bool using (Bool; false; true; T)
open import Data.Unit renaming ( to ⊤₂) using ()
open import Data.Unit.Polymorphic using ()
open import Data.Product
open import Data.Maybe as Maybe
open import Data.Nat using ()
open import Data.Fin.Base hiding (_+_; lift)
open import Data.Fin.Properties as F hiding (_≟_)
open import Data.Vec.Functional as V
import Data.Vec.Functional.Relation.Binary.Equality.Setoid as EqSetoids
open import Algebra
open import Algebra.Apartness
import Relation.Binary.Reasoning.Setoid as ReasonSetoid
open import Relation.Nullary.Decidable
open import Relation.Nullary.Construct.Add.Infimum as 
import Algebra.Module.Instances.FunctionalVector as AMIF

open import Algebra.Matrix
open import Algebra.MatrixData renaming (Matrix to MatrixData)
import MatrixFuncNormalization.MatrixPropsAfter as MatrixPropsAfter
import MatrixFuncNormalization.normBef as NormBef
open import MatrixFuncNormalization.FinInduction
open import Vector

open DecidableField dField renaming (Carrier to F; heytingField to hField)
open HeytingField hField using (heytingCommutativeRing)
open NormBef dField using (normalizeMatrix; AllZeros; _-v_)
  renaming ( VecPivotPos to VecPivotPosLeft
           ; Lookup≢0 to Lookup≢0Left
           ; normTwoRows to normTwoRowsLeft
           )
open module M = AMIF ring hiding (_+ᴹ_)
open module PNormAfter {n} = MatrixPropsAfter (<-strictTotalOrder n) using (_<′_; AllRowsNormalizedRight; simpleFinProps)
module ≈-Reasoning = ReasonSetoid setoid
open FuncNormAllLines
open FuncNormAndZeros

private variable
   ℓ′ : Level
  A : Set 
  m n : 

private
  MaybeFin₋ : Fin n   Set   Set 
  MaybeFin₋ (just _) = id
  MaybeFin₋ ⊥₋ _ = 

PivValue : Fin m   Set _
PivValue p = MaybeFin₋ p (Σ[ x  F ] x # 0#)

PivWithValue :   Set _
PivWithValue m = Σ[ piv  Fin m  ] PivValue piv

findPivAndValue : (xs : Vector F n)  PivWithValue n
findPivAndValue {n = ℕ.zero} xs = ⊥₋ , lift _
findPivAndValue {n = ℕ.suc n} xs with xs (fromℕ _)  0#
... | yes x#0 = ₋.[ fromℕ _ ] , _ , x#0
... | no _  with findPivAndValue (xs  inject₁)
... | ⊥₋ , _ = ⊥₋ , _
... | ₋.[ p ] , x#0 = ₋.[ inject₁ p ] , x#0

normTwoRows′ : (xs ys : Vector F n) (px py : Fin n )  PivValue px  Vector F n
normTwoRows′ xs ys ⊥₋ py _ = ys
normTwoRows′ xs ys (just px) py (_ , vx#0) = ysn
  where
  open ≈-Reasoning

  vx⁻¹ = #⇒invertible vx#0 .proj₁
  vy = ys px

  -vx⁻¹vy = - (vy * vx⁻¹)
  xsn = -vx⁻¹vy *ₗ xs
  ysn = ys M.+ᴹ xsn

normTwoRows :  (xs ys : Vector F n) (px py : Fin n ) (vx : PivValue px)  Vector F n
normTwoRows xs ys ⊥₋ py _ = normTwoRows′ xs ys ⊥₋ py _
normTwoRows xs ys px@(just _) py (_ , vx#0) = normTwoRows′ xs ys px py (_ , vx#0)

normMatrixTwoRowsF′ :  (xs : Matrix F n m) (pivsXs : Vector (PivWithValue m) n)
  (i j : Fin n) (k : Fin n) (k≟j : Bool)  Vector F m
normMatrixTwoRowsF′ xs pivsXs i j k true
  = normTwoRows (xs i) (xs j) (pivsXs i .proj₁) (pivsXs j .proj₁) (pivsXs i .proj₂)
normMatrixTwoRowsF′ xs pivsXs i j k false = xs k

normMatrixTwoRowsF :  (xs : Matrix F n m) (pivsXs : Vector (PivWithValue m) n)
  (i j : Fin n)  Matrix F n m
normMatrixTwoRowsF xs pivsXs i j k = normMatrixTwoRowsF′ xs pivsXs i j k (does (k F.≟ j))

funcNorm : FuncNormAllLines m n (Matrix F (ℕ.suc n) m)
numberZeros funcNorm xs _ = ⊥₋
normProps funcNorm = simpleFinProps
f (fNormLines funcNorm i j i≢j) xs = normMatrixTwoRowsF xs (findPivAndValue  xs) i j
nZerosProp (fNormLines funcNorm i j i≢j) _ = _

normAfter : Op₁ $ Matrix F n m
normAfter {n = ℕ.zero} xs ()
normAfter {n = ℕ.suc n} = proj₁  normalizeA funcNorm

normalize : Op₁ $ Matrix F n m
normalize xs = let
  (ys₁ , _) , _ = normalizeMatrix xs
  ys₂ = flip ys₁
  ys₃ = normAfter ys₂
  ys₄ = flip ys₃
  in ys₄