{-# OPTIONS --cubical-compatible -WnoUnsupportedIndexedMatch --safe #-}

open import Level
open import Algebra.Bundles
open import Data.Product
open import Data.Sum
import Data.Nat as Nat
import Data.Nat.Properties
import Data.Fin as Fin
open import Relation.Unary hiding ()
import Relation.Binary.PropositionalEquality as PE

open import Krull.WildRing

module Krull.LinearAlgebra (R… : CommutativeRing 0ℓ 0ℓ) where

open CommutativeRing R… renaming (Carrier to R)
open import Relation.Binary.Reasoning.Setoid setoid
open import Krull.Base (R…)
import Krull.QuotientRing as QR
open import Krull.WildLinearAlgebra (forget R…)


-- Auxiliary lemmas for reduce-surjective

finSum-congr : {n : Nat.ℕ} {f g : Fin.Fin n  R}
   (∀ j  f j  g j)  finSum f  finSum g
finSum-congr {Nat.zero}  h = refl
finSum-congr {Nat.suc n} h = +-cong (h Fin.zero) (finSum-congr  j  h (Fin.suc j)))

finSum-add : {n : Nat.ℕ} (f g : Fin.Fin n  R)
   finSum  j  f j + g j)  finSum f + finSum g
finSum-add {Nat.zero}  f g = sym (+-identityˡ 0#)
finSum-add {Nat.suc n} f g = begin
  (a + b) + finSum  j  f (Fin.suc j) + g (Fin.suc j))
    ≈⟨ +-congˡ (finSum-add  j  f (Fin.suc j))  j  g (Fin.suc j))) 
  (a + b) + (sf + sg)
    ≈⟨ +-assoc _ _ _ 
  a + (b + (sf + sg))
    ≈˘⟨ +-congˡ (+-assoc b sf sg) 
  a + ((b + sf) + sg)
    ≈⟨ +-congˡ (+-congʳ (+-comm b sf)) 
  a + ((sf + b) + sg)
    ≈⟨ +-congˡ (+-assoc sf b sg) 
  a + (sf + (b + sg))
    ≈˘⟨ +-assoc a sf _ 
  (a + sf) + (b + sg)
    
  where
  a  = f Fin.zero
  b  = g Fin.zero
  sf = finSum  j  f (Fin.suc j))
  sg = finSum  j  g (Fin.suc j))

finSum-scale : {n : Nat.ℕ} (c : R) (f : Fin.Fin n  R)
   finSum  j  c * f j)  c * finSum f
finSum-scale {Nat.zero}  c f = sym (zeroʳ c)
finSum-scale {Nat.suc n} c f = begin
  c * f Fin.zero + finSum  j  c * f (Fin.suc j))
    ≈⟨ +-congˡ (finSum-scale c  j  f (Fin.suc j))) 
  c * f Fin.zero + c * finSum  j  f (Fin.suc j))
    ≈˘⟨ distribˡ c _ _ 
  c * (f Fin.zero + finSum  j  f (Fin.suc j)))
    

-- finSum over Fin (suc m) equals f(j) plus the sum over the complement of j
finSum-punchIn : {m : Nat.ℕ} (j : Fin.Fin (Nat.suc m)) (f : Fin.Fin (Nat.suc m)  R)
   finSum f  f j + finSum  j'  f (Fin.punchIn j j'))
finSum-punchIn           Fin.zero    f = refl
finSum-punchIn {Nat.suc _} (Fin.suc j) f = begin
  f Fin.zero + finSum  j'  f (Fin.suc j'))
    ≈⟨ +-congˡ (finSum-punchIn j  j'  f (Fin.suc j'))) 
  f Fin.zero + (f (Fin.suc j) + s')
    ≈˘⟨ +-assoc (f Fin.zero) _ _ 
  (f Fin.zero + f (Fin.suc j)) + s'
    ≈⟨ +-congʳ (+-comm (f Fin.zero) _) 
  (f (Fin.suc j) + f Fin.zero) + s'
    ≈⟨ +-assoc _ _ _ 
  f (Fin.suc j) + (f Fin.zero + s')
    
  where s' = finSum  j'  f (Fin.suc (Fin.punchIn j j')))

finSum-neg : {n : Nat.ℕ} (f : Fin.Fin n  R)
   finSum  j  - f j)  - finSum f
finSum-neg f = begin
  finSum  j  - f j)
    ≈⟨ finSum-congr  j  sym (neg-one-times (f j))) 
  finSum  j  - 1# * f j)
    ≈⟨ finSum-scale (- 1#) f 
  - 1# * finSum f
    ≈⟨ neg-one-times (finSum f) 
  - finSum f
    

finSum-sub : {n : Nat.ℕ} (f g : Fin.Fin n  R)
   finSum  j  f j - g j)  finSum f - finSum g
finSum-sub f g = begin
  finSum  j  f j - g j)
    ≈⟨ finSum-add f  j  - g j) 
  finSum f + finSum  j  - g j)
    ≈⟨ +-congˡ (finSum-neg g) 
  finSum f - finSum g
    

-- δ(i, punchIn i q) = 0 because punchIn i always avoids i
δ-punchIn-avoid : {n : Nat.ℕ} (i : Fin.Fin (Nat.suc n)) (q : Fin.Fin n)
   δ i (Fin.punchIn i q)  0#
δ-punchIn-avoid Fin.zero      q           = refl
δ-punchIn-avoid (Fin.suc i)   Fin.zero    = refl
δ-punchIn-avoid (Fin.suc i)   (Fin.suc q) = δ-punchIn-avoid i q

-- δ(punchIn i p, punchIn i q) = δ(p, q) because punchIn i is injective
δ-punchIn-inj : {n : Nat.ℕ} (i : Fin.Fin (Nat.suc n)) (p q : Fin.Fin n)
   δ (Fin.punchIn i p) (Fin.punchIn i q)  δ p q
δ-punchIn-inj Fin.zero      p           q           = refl
δ-punchIn-inj (Fin.suc i)   Fin.zero    Fin.zero    = refl
δ-punchIn-inj (Fin.suc i)   Fin.zero    (Fin.suc q) = refl
δ-punchIn-inj (Fin.suc i)   (Fin.suc p) Fin.zero    = refl
δ-punchIn-inj (Fin.suc i)   (Fin.suc p) (Fin.suc q) = δ-punchIn-inj i p q

-- reduce-inverse N i j is a right inverse of reduce-matrix M i j s,
-- provided N is a right inverse of M and M i j * s ≈ 1#.
reduce-inverse-correct : {n m : Nat.ℕ}
   (M : Matrix R (Nat.suc n) (Nat.suc m))
   (i : Fin.Fin (Nat.suc n)) (j : Fin.Fin (Nat.suc m))
   (s : R)  M i j * s  1#
   (N : Matrix R (Nat.suc m) (Nat.suc n))
   (∀ p q  matprod M N p q  δ p q)
    p q  matprod (reduce-matrix M i j s) (reduce-inverse N i j) p q  δ p q
reduce-inverse-correct M i j s Mij-inv N MN≡I p' q' = begin
  matprod (reduce-matrix M i j s) (reduce-inverse N i j) p' q'
    ≈⟨ finSum-congr  k'  sub-distribʳ
         (M p (Fin.punchIn j k')) (M p j * s * M i (Fin.punchIn j k')) (N (Fin.punchIn j k') q)) 
  finSum  k'  f₁ k' - f₂ k')
    ≈⟨ finSum-sub f₁ f₂ 
  finSum f₁ - finSum f₂
    ≈⟨ +-cong sum1-eq (-‿cong sum2-eq) 
  (δ p' q' - M p j * N j q) + - (- (M p j * N j q))
    ≈⟨ combine 
  δ p' q'
    
  where
  p = Fin.punchIn i p'
  q = Fin.punchIn i q'
  f₁ = λ k'  M p (Fin.punchIn j k') * N (Fin.punchIn j k') q
  f₂ = λ k'  M p j * s * M i (Fin.punchIn j k') * N (Fin.punchIn j k') q

  -- (a * s) * (M i j * d) ≈ a * d, using Mij-inv and commutativity
  cancel-inv : (a d : R)  (a * s) * (M i j * d)  a * d
  cancel-inv a d = begin
    (a * s) * (M i j * d)
      ≈⟨ *-assoc a s _ 
    a * (s * (M i j * d))
      ≈˘⟨ *-congˡ (*-assoc s (M i j) d) 
    a * ((s * M i j) * d)
      ≈⟨ *-congˡ (*-congʳ (*-comm s (M i j))) 
    a * ((M i j * s) * d)
      ≈⟨ *-congˡ (*-congʳ Mij-inv) 
    a * (1# * d)
      ≈⟨ *-congˡ (*-identityˡ d) 
    a * d
      

  -- First sum ≈ δ p' q' - M p j * N j q  (via finSum-punchIn + MN≡I + δ-punchIn-inj)
  sum1-eq = begin
    finSum  k'  M p (Fin.punchIn j k') * N (Fin.punchIn j k') q)
      ≈⟨ +-cancelˡ-to-sub _ _ _ (finSum-punchIn j  k  M p k * N k q)) 
    matprod M N p q - M p j * N j q
      ≈⟨ +-congʳ (trans (MN≡I p q) (δ-punchIn-inj i p' q')) 
    δ p' q' - M p j * N j q
      

  -- Inner sum in second term ≈ - (M i j * N j q)  (via finSum-punchIn + δ-punchIn-avoid)
  inner-sum-eq = begin
    finSum  k'  M i (Fin.punchIn j k') * N (Fin.punchIn j k') q)
      ≈⟨ +-cancelˡ-to-sub _ _ _ (finSum-punchIn j  k  M i k * N k q)) 
    matprod M N i q - M i j * N j q
      ≈⟨ +-congʳ (trans (MN≡I i q) (δ-punchIn-avoid i q')) 
    0# + - (M i j * N j q)
      ≈⟨ +-identityˡ _ 
    - (M i j * N j q)
      

  -- Second sum ≈ - (M p j * N j q)  (via *-assoc, finSum-scale, inner-sum-eq, cancel-inv)
  sum2-eq = begin
    finSum  k'  M p j * s * M i (Fin.punchIn j k') * N (Fin.punchIn j k') q)
      ≈⟨ finSum-congr  k'  *-assoc α (M i (Fin.punchIn j k')) (N (Fin.punchIn j k') q)) 
    finSum  k'  α * (M i (Fin.punchIn j k') * N (Fin.punchIn j k') q))
      ≈⟨ finSum-scale α β 
    α * finSum β
      ≈⟨ *-congˡ inner-sum-eq 
    α * - (M i j * N j q)
      ≈⟨ neg-distribʳ-* α (M i j * N j q) 
    - (α * (M i j * N j q))
      ≈⟨ -‿cong (cancel-inv (M p j) (N j q)) 
    - (M p j * N j q)
      
    where
    α = M p j * s
    β = λ k'  M i (Fin.punchIn j k') * N (Fin.punchIn j k') q

  -- (δ p' q' - a) + (- (- a)) ≈ δ p' q'  via -‿inverseʳ
  combine = begin
    (δ p' q' - M p j * N j q) + - (- (M p j * N j q))
      ≈⟨ +-assoc (δ p' q') _ _ 
    δ p' q' + (- (M p j * N j q) + - (- (M p j * N j q)))
      ≈⟨ +-congˡ (-‿inverseʳ _) 
    δ p' q' + 0#
      ≈⟨ +-identityʳ _ 
    δ p' q'
      

-- Combining the two: a right inverse of M yields a right inverse of reduce-matrix M i j s.
reduce-surjective : {n m : Nat.ℕ}
   (M : Matrix R (Nat.suc n) (Nat.suc m))
   (i : Fin.Fin (Nat.suc n)) (j : Fin.Fin (Nat.suc m))
   (s : R)  M i j * s  1#
   (N : Matrix R (Nat.suc m) (Nat.suc n))
   (∀ p q  matprod M N p q  δ p q)
   Σ[ N'  Matrix R m n ]  p q  matprod (reduce-matrix M i j s) N' p q  δ p q
reduce-surjective M i j s h N inv =
  reduce-inverse N i j , reduce-inverse-correct M i j s h N inv

-- A surjective matrix with zero columns and at least one row is absurd.
zero-columns : {n : Nat.ℕ}
   (M : Matrix R (Nat.suc n) Nat.zero)
   (N : Matrix R Nat.zero (Nat.suc n))
   (∀ p q  matprod M N p q  δ p q)
   
zero-columns M N MN≡I = sym (MN≡I Fin.zero Fin.zero)

-- A surjective matrix with at least one row consisting only of zeros is absurd.
surj-zero-matrix : {n m : Nat.ℕ}
   (M : Matrix R (Nat.suc n) m)
   (∀ i j  M i j  0#)
   (N : Matrix R m (Nat.suc n))
   (∀ p q  matprod M N p q  δ p q)
   
surj-zero-matrix M M-zero N MN≡I = begin
  1#
    ≈˘⟨ MN≡I Fin.zero Fin.zero 
  matprod M N Fin.zero Fin.zero
    ≈⟨ finSum-congr  j'  *-congʳ (M-zero Fin.zero j')) 
  finSum  j'  0# * N j' Fin.zero)
    ≈⟨ finSum-scale 0#  j'  N j' Fin.zero) 
  0# * finSum  j'  N j' Fin.zero)
    ≈⟨ zeroˡ _ 
  0#
    

-- A surjective matrix with at least one row whose first row is all zeros is absurd.
surj-zero-first-row : {n m : Nat.ℕ}
   (M : Matrix R (Nat.suc n) m)
   (∀ j  M Fin.zero j  0#)
   (N : Matrix R m (Nat.suc n))
   (∀ p q  matprod M N p q  δ p q)
   
surj-zero-first-row M M-zero-row N MN≡I = begin
  1#
    ≈˘⟨ MN≡I Fin.zero Fin.zero 
  matprod M N Fin.zero Fin.zero
    ≈⟨ finSum-congr  j'  *-congʳ (M-zero-row j')) 
  finSum  j'  0# * N j' Fin.zero)
    ≈⟨ finSum-scale 0#  j'  N j' Fin.zero) 
  0# * finSum  j'  N j' Fin.zero)
    ≈⟨ zeroˡ _ 
  0#
    

-- Helper lemmas for the 2×1 example: given a proper ideal I and
-- elements a, b, u, v with ua≈1, ub≈0, va≈0, vb≈1,
-- we can show a is neither invertible mod I nor in I.

-- If ⟨I⟩ is proper and v*a ≈ 0, v*b ≈ 1, then a is not invertible modulo I.
example-case-a-inv-lemma : (I : Pred R 0ℓ)  ¬ 1#   I 
   (a b v : R)  v * a  0#  v * b  1#
   ¬ 1#   I   a  
example-case-a-inv-lemma I ⟨I⟩-proper a b v va0 vb1 p =
  ⟨I⟩-proper (⟨⟩-idempotent (⟨⟩-monotone handle (Eq vb·1≈1 vb1*p)))
  where
  vb1*p : (v * b) * 1#   image ((v * b) *_) (I   a ) 
  vb1*p = ⟨⟩-mult (v * b) p

  vb·1≈1 : (v * b) * 1#  1#
  vb·1≈1 = begin
    (v * b) * 1# ≈⟨ *-identityʳ (v * b) 
    v * b        ≈⟨ vb1 
    1#           

  handle : image ((v * b) *_) (I   a )   I 
  handle (w , eq , inj₁ q) = Eq (≡⇒≈ (PE.sym eq)) (Magnet (Base q))
  handle (w , eq , inj₂ PE.refl) = Eq 0≈y Zero
    where
    0≈vb·w : 0#  (v * b) * w
    0≈vb·w = begin
      0#          ≈˘⟨ zeroˡ b 
      0# * b      ≈˘⟨ *-congʳ va0 
      (v * w) * b ≈⟨ *-assoc v w b 
      v * (w * b) ≈⟨ *-congˡ (*-comm w b) 
      v * (b * w) ≈˘⟨ *-assoc v b w 
      (v * b) * w 

    0≈y : 0#  _
    0≈y = trans 0≈vb·w (≡⇒≈ (PE.sym eq))

-- If ⟨I⟩ is proper and u*a ≈ 1, then a ∉ I.
example-case-a-zero-lemma : (I : Pred R 0ℓ)  ¬ 1#   I 
   (a u : R)  u * a  1#
   a  I  
example-case-a-zero-lemma I ⟨I⟩-proper a u ua1 p =
  ⟨I⟩-proper (Eq ua1 (Magnet (Base p)))

module WithFieldCondition
  (field-condition : (x : R)  (∀ s  ¬ x * s  1#)  x  0#) where

  -- A surjective matrix with more rows than columns is absurd.
  surj-matrix
    : {n m : Nat.ℕ}  m Nat.< n
     (M : Matrix R n m)
     (N : Matrix R m n)
     (∀ p q  matprod M N p q  δ p q)
     
  surj-matrix {Nat.suc _} {Nat.zero}  _   M N MN≡I = zero-columns M N MN≡I
  surj-matrix {Nat.suc _} {Nat.suc _} m<n M N MN≡I =
    surj-zero-first-row M
       j  field-condition (M Fin.zero j)
         s h  let (N' , N'-inv) = reduce-surjective M Fin.zero j s h N MN≡I in
          surj-matrix (Data.Nat.Properties.≤-pred m<n) (reduce-matrix M Fin.zero j s) N' N'-inv))
      N MN≡I

module WithMaximalIdeal
  (I : Pred R 0ℓ)
  (I-maximal : (x : R)  ¬ 1#   I   x    x  I)
  where

  open QR R… I public

  -- A maximal ideal gives a field: non-invertible elements are zero.
  R/M-is-field : (x : R)  ((s : R)  ¬ (x * s) ≈/M 1#)  x ≈/M 0#
  R/M-is-field x not-inv = Sum (Base (I-maximal x derive-⊥)) (⟨M⟩-neg Zero)
    where
    derive-⊥ : 1#   I   x    
    derive-⊥ one∈I∪x with ideal-decompose I x one∈I∪x
    ... | u , s , one≈u+sx , u∈⟨I⟩ = not-inv s (Eq (sym (inverse-unique u (x * s - 1#) sum≈0)) (⟨M⟩-neg u∈⟨I⟩))
      where
      sum≈0 : u + (x * s - 1#)  0#
      sum≈0 = begin
        u + (x * s - 1#)
          ≈⟨ +-congˡ (+-congʳ (*-comm x s)) 
        u + (s * x - 1#)
          ≈˘⟨ +-assoc u (s * x) (- 1#) 
        (u + s * x) - 1#
          ≈˘⟨ +-congʳ one≈u+sx 
        1# - 1#
          ≈⟨ -‿inverseʳ 1# 
        0#