open import Algebra.DecidableField

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

open import Algebra
open import Function
open import Data.Product
open import Data.Fin as F using (Fin; zero; suc; toℕ)
open import Data.Maybe as Maybe
open import Data.Bool using (Bool; false; true; T)
open import Data.Nat using ()
open import Data.Vec.Functional as V
open import Relation.Nullary
open import Relation.Nullary.Construct.Add.Supremum

open import Vector
open import Algebra.Matrix
open import Algebra.MatrixData renaming (Matrix to MatrixData)
open import MatrixFuncNormalization.normBef dField using (findNonZeroPos)
open import MatrixFuncNormalization.NormAfter.Base dField

open DecidableField dField renaming (Carrier to F; heytingField to hField)

private variable
  m n : 

is≠0 : F  Bool
is≠0 x = does $ x  0#

divideVec : Op₁ $ Vector F n
divideVec {n = n} xs = fromMaybe xs (Maybe.map  (_ , inv) i  inv * xs i) (findFirstWithProp xs fMaybe))
  where
  fMaybe : (x : F)  Maybe F
  fMaybe x = Maybe.map (proj₁  #⇒invertible) (dec⇒maybe $ x  0#)

divideByCoeff : Op₁ $ Matrix F n m
divideByCoeff = V.map divideVec

normalizeAndDivide : Op₁ $ Matrix F n m
normalizeAndDivide = divideByCoeff  normalize

func⇒op₁⇒data : Op₁ $ Matrix F n m  Op₁ $ MatrixData F n m
func⇒op₁⇒data f = matrixFunc→Data  f  matrixData→Fun

normalizeData : Op₁ $ MatrixData F n m
normalizeData = func⇒op₁⇒data normalize

normalizeAndDivideData : Op₁ $ MatrixData F n m
normalizeAndDivideData = func⇒op₁⇒data normalizeAndDivide

findZeroEnd :  (pivs : Vector (Fin m ) n)  Fin $ ℕ.suc n
findZeroEnd {n = ℕ.zero} pivs = F.zero
findZeroEnd {n = ℕ.suc n} pivs with pivs F.zero
... | just _  = F.zero
... | ⊥₋  = F.suc $ findZeroEnd (tail pivs)

eliminateZerosEnd :  (xs : Matrix F n m) (pivs : Vector (Fin m ) n)  Matrix F (toℕ (findZeroEnd pivs)) m
eliminateZerosEnd xs _ = xs  F.inject!

normalizeAndEliminateZeros : (xs : Matrix F n m)  Σ[ n   ] (Matrix F n m)
normalizeAndEliminateZeros xs = _ , eliminateZerosEnd (normalizeAndDivide xs) (V.map findNonZeroPos xs)