-- Properties of (pre)additive categories
{-# OPTIONS --safe #-}

module Cubical.Categories.Additive.Properties where

open import Cubical.Algebra.AbGroup.Base
open import Cubical.Algebra.Group.Properties
open import Cubical.Categories.Additive.Base
open import Cubical.Foundations.Prelude

private
  variable
     ℓ' : Level


-- Some useful lemmas about preadditive categories
module PreaddCategoryTheory (C : PreaddCategory  ℓ') where
  open PreaddCategory C

  -- Pure group theory lemmas
  +idl = λ {x y}  AbGroupStr.+IdL (homAbStr x y)
  +idr = λ {x y}  AbGroupStr.+IdR (homAbStr x y)
  +invl = λ {x y}  AbGroupStr.+InvL (homAbStr x y)
  +invr = λ {x y}  AbGroupStr.+InvR (homAbStr x y)
  +assoc = λ {x y}  AbGroupStr.+Assoc (homAbStr x y)
  +comm = λ {x y}  AbGroupStr.+Comm (homAbStr x y)

  -dist+ : {x y : ob} (f g : Hom[ x , y ]) 
      - (f + g)  (- f) + (- g)
  -dist+ {x} {y} f g =
    let open GroupTheory (AbGroup→Group (Hom[ x , y ] , homAbStr x y)) in
      (invDistr _ _)  (+comm _ _)

  +4assoc : {x y : ob} (a b c d : Hom[ x , y ]) 
      (a + b) + (c + d)    a + (b + c) + d
  +4assoc a b c d = sym (+assoc _ _ _)  cong (a +_) (+assoc _ _ _)

  +4comm : {x y : ob} (a b c d : Hom[ x , y ]) 
      (a + b) + (c + d)    (a + c) + (b + d)
  +4comm a b c d = +4assoc _ _ _ _ 
      cong  u  a + (u + d)) (+comm _ _)  sym (+4assoc _ _ _ _)


  -- Interaction of categorical & group structure
  ⋆0hl :  {w x y} (f : Hom[ x , y ])  0h  f  0h {w}
  ⋆0hl f =
      0h  f                              ≡⟨ sym (+idr _) 
      0h  f  +  0h                       ≡⟨ cong (0h  f +_) (sym (+invr _)) 
      0h  f  +  (0h  f  -  0h  f)      ≡⟨ +assoc _ _ _ 
      (0h  f  +  0h  f)  -  0h  f      ≡⟨ cong (_- 0h  f) (sym (⋆distr+ _ _ _)) 
      (0h + 0h)  f  -  0h  f            ≡⟨ cong  a  a  f - 0h  f) (+idr _) 
      0h  f  -  0h  f                   ≡⟨ +invr _ 
      0h                                  

  ⋆0hr :  {x y z} (f : Hom[ x , y ])  f  0h  0h {x} {z}
  ⋆0hr f =
      f  0h                              ≡⟨ sym (+idr _) 
      f  0h  +  0h                       ≡⟨ cong (f  0h +_) (sym (+invr _)) 
      f  0h  +  (f  0h  -  f  0h)      ≡⟨ +assoc _ _ _ 
      (f  0h  +  f  0h)  -  f  0h      ≡⟨ cong (_- f  0h) (sym (⋆distl+ _ _ _)) 
      f  (0h + 0h)  -  f  0h            ≡⟨ cong  a  f  a - f  0h) (+idr _) 
      f  0h  -  f  0h                   ≡⟨ +invr _ 
      0h                                  

  -distl⋆ :  {x y z} (f : Hom[ x , y ]) (g : Hom[ y , z ])
     (- f)  g  - (f  g)
  -distl⋆ {x} {y} {z} f g =
    let open GroupTheory (AbGroup→Group (Hom[ x , z ] , homAbStr x z)) in
    invUniqueR (
      f  g + (- f)  g     ≡⟨ sym (⋆distr+ _ _ _) 
      (f - f)  g           ≡⟨ cong (_⋆ g) (+invr _) 
      0h  g                ≡⟨ ⋆0hl _ 
      0h                    
    )

  -distr⋆ :  {x y z} (f : Hom[ x , y ]) (g : Hom[ y , z ])
     f  (- g)  - (f  g)
  -distr⋆ {x} {y} {z} f g =
    let open GroupTheory (AbGroup→Group (Hom[ x , z ] , homAbStr x z)) in
    invUniqueR (
      f  g + f  (- g)     ≡⟨ sym (⋆distl+ _ _ _) 
      f  (g - g)           ≡⟨ cong (f ⋆_) (+invr _) 
      f  0h                ≡⟨ ⋆0hr _ 
      0h                    
    )


-- Matrix notation for morphisms  (x₁ ⊕ ⋯ ⊕ x­­ₙ) → (y₁ ⊕ ⋯ ⊕ yₘ)
-- in an additive category
module MatrixNotation (A : AdditiveCategory  ℓ') where
    open AdditiveCategory A
    open PreaddCategoryTheory preaddcat

    -- Polymorphic biproduct morphisms
    i₁ = λ {x y}  Biproduct.i₁ (biprod x y)
    i₂ = λ {x y}  Biproduct.i₂ (biprod x y)
    π₁ = λ {x y}  Biproduct.π₁ (biprod x y)
    π₂ = λ {x y}  Biproduct.π₂ (biprod x y)
    -- and axioms
    i₁⋆π₁ = λ {x y}  Biproduct.i₁⋆π₁ (biprod x y)
    i₁⋆π₂ = λ {x y}  Biproduct.i₁⋆π₂ (biprod x y)
    i₂⋆π₁ = λ {x y}  Biproduct.i₂⋆π₁ (biprod x y)
    i₂⋆π₂ = λ {x y}  Biproduct.i₂⋆π₂ (biprod x y)
    ∑π⋆i  = λ {x y}  Biproduct.∑π⋆i  (biprod x y)


    _∣_ :  {x y y'}  Hom[ x , y ]  Hom[ x , y' ]  Hom[ x , y  y' ]
    f  f' = (f  i₁) + (f'  i₂)
    infixr 5 _∣_

    _`_ :  {y y' z}  Hom[ y , z ]  Hom[ y' , z ]  Hom[ y  y' , z ]
    g ` g' = (π₁  g) + (π₂  g')
    infixr 6 _`_


    -- Use the matrix notation like this:
    private module _ (x y : ob) (f : Hom[ x , y ]) where

      h : Hom[ x  x  x , y  y  y  y ]

      h =  f  ` - f `  0h   
           0h `  0h `  f    
           f  ` - f `  0h   
           0h `  f  ` f + f


    -- Exchange law
    exchange :  {x x' y y'} (a : Hom[ x , y ]) (b : Hom[ x' , y ])
            (c : Hom[ x , y' ]) (d : Hom[ x' , y' ]) 
        (a ` b  c ` d)    (a  c) ` (b  d)

    exchange a b c d =
        (π₁  a  +  π₂  b)  i₁  +  (π₁  c  +  π₂  d)  i₂
                ≡⟨ cong₂ _+_ (⋆distr+ _ _ _) (⋆distr+ _ _ _) 
        ((π₁  a)  i₁  +  (π₂  b)  i₁)  +  ((π₁  c)  i₂  +  (π₂  d)  i₂)
                ≡⟨ +4comm _ _ _ _ 
        ((π₁  a)  i₁  +  (π₁  c)  i₂)  +  ((π₂  b)  i₁  +  (π₂  d)  i₂)
                ≡⟨ cong₂ _+_ (cong₂ _+_ (⋆Assoc _ _ _) (⋆Assoc _ _ _))
                            (cong₂ _+_ (⋆Assoc _ _ _) (⋆Assoc _ _ _)) 
        (π₁  (a  i₁)  +  π₁  (c  i₂))  +  (π₂  (b  i₁)  +  π₂  (d  i₂))
                ≡⟨ sym (cong₂ _+_ (⋆distl+ _ _ _) (⋆distl+ _ _ _)) 
        π₁  (a  i₁  +  c  i₂)  +  π₂  (b  i₁  +  d  i₂)
                


    -- iₖ times a row
    module _ {x x' y : ob} (a : Hom[ x , y ]) (b : Hom[ x' , y ]) where

      i₁⋆row :  i₁  (a ` b)    a
      i₁⋆row =
          i₁  (π₁  a  +  π₂  b)
                  ≡⟨ ⋆distl+ _ _ _ 
          i₁  (π₁  a)  +  i₁  (π₂  b)
                  ≡⟨ cong₂ _+_ (sym (⋆Assoc _ _ _)) (sym (⋆Assoc _ _ _)) 
          (i₁  π₁)  a  +  (i₁  π₂)  b
                  ≡⟨ cong₂  u v  u  a + v  b) i₁⋆π₁ i₁⋆π₂ 
          id  a  +  0h  b
                  ≡⟨ cong₂ _+_ (⋆IdL _) (⋆0hl _) 
          a  +  0h
                  ≡⟨ +idr _ 
          a       

      i₂⋆row :  i₂  (a ` b)    b
      i₂⋆row =
          i₂  (π₁  a  +  π₂  b)
                  ≡⟨ ⋆distl+ _ _ _ 
          i₂  (π₁  a)  +  i₂  (π₂  b)
                  ≡⟨ cong₂ _+_ (sym (⋆Assoc _ _ _)) (sym (⋆Assoc _ _ _)) 
          (i₂  π₁)  a  +  (i₂  π₂)  b
                  ≡⟨ cong₂  u v  u  a + v  b) i₂⋆π₁ i₂⋆π₂ 
          0h  a  +  id  b
                  ≡⟨ cong₂ _+_ (⋆0hl _) (⋆IdL _) 
          0h  +  b
                  ≡⟨ +idl _ 
          b       


    -- A column times πₖ
    module _ {x y y' : ob} (a : Hom[ x , y ]) (b : Hom[ x , y' ]) where

      col⋆π₁ :  (a  b)  π₁    a
      col⋆π₁ =
          (a  i₁  +  b  i₂)  π₁
                  ≡⟨ ⋆distr+ _ _ _ 
          (a  i₁)  π₁  +  (b  i₂)  π₁
                  ≡⟨ cong₂ _+_ (⋆Assoc _ _ _) (⋆Assoc _ _ _) 
          a  (i₁  π₁)  +  b  (i₂  π₁)
                  ≡⟨ cong₂  u v  a  u + b  v) i₁⋆π₁ i₂⋆π₁ 
          a  id  +  b  0h
                  ≡⟨ cong₂ _+_ (⋆IdR _) (⋆0hr _) 
          a  +  0h
                  ≡⟨ +idr _ 
          a       

      col⋆π₂ :  (a  b)  π₂    b
      col⋆π₂ =
          (a  i₁  +  b  i₂)  π₂
                  ≡⟨ ⋆distr+ _ _ _ 
          (a  i₁)  π₂  +  (b  i₂)  π₂
                  ≡⟨ cong₂ _+_ (⋆Assoc _ _ _) (⋆Assoc _ _ _) 
          a  (i₁  π₂)  +  b  (i₂  π₂)
                  ≡⟨ cong₂  u v  a  u + b  v) i₁⋆π₂ i₂⋆π₂ 
          a  0h  +  b  id
                  ≡⟨ cong₂ _+_ (⋆0hr _) (⋆IdR _) 
          0h  +  b
                  ≡⟨ +idl _ 
          b       

    -- iₖ / πₖ times a diagonal matrix
    module _ {x x' y y'} (a : Hom[ x , y ]) (b : Hom[ x' , y' ]) where

      i₁⋆diag :  i₁  (a ` 0h  0h ` b)    a  i₁
      i₁⋆diag =
          i₁  (a ` 0h  0h ` b)          ≡⟨ cong (i₁ ⋆_) (exchange _ _ _ _) 
          i₁  ((a  0h) ` (0h  b))      ≡⟨ i₁⋆row _ _ 
          (a  0h)                        ≡⟨ cong (a  i₁ +_) (⋆0hl _) 
          a  i₁  +  0h                   ≡⟨ +idr _ 
          a  i₁                          

      i₂⋆diag :  i₂  (a ` 0h  0h ` b)    b  i₂
      i₂⋆diag =
          i₂  (a ` 0h  0h ` b)          ≡⟨ cong (i₂ ⋆_) (exchange _ _ _ _) 
          i₂  ((a  0h) ` (0h  b))      ≡⟨ i₂⋆row _ _ 
          (0h  b)                        ≡⟨ cong (_+ b  i₂) (⋆0hl _) 
          0h  +  b  i₂                   ≡⟨ +idl _ 
          b  i₂                          

      diag⋆π₁ :  (a ` 0h  0h ` b)  π₁    π₁  a
      diag⋆π₁ =
          (a ` 0h  0h ` b)  π₁      ≡⟨ col⋆π₁ _ _ 
          a ` 0h                      ≡⟨ cong (π₁  a +_) (⋆0hr _) 
          π₁  a  +  0h               ≡⟨ +idr _ 
          π₁  a                      

      diag⋆π₂ :  (a ` 0h  0h ` b)  π₂    π₂  b
      diag⋆π₂ =
          (a ` 0h  0h ` b)  π₂      ≡⟨ col⋆π₂ _ _ 
          0h ` b                      ≡⟨ cong (_+ π₂  b) (⋆0hr _) 
          0h  +  π₂  b               ≡⟨ +idl _ 
          π₂  b                      


    -- Matrix addition
    addRow :  {x y z}
        (f f' : Hom[ x , z ]) (g g' : Hom[ y , z ]) 
        (f ` g) + (f' ` g')  (f + f') ` (g + g')

    addRow f f' g g' =
        (π₁  f + π₂  g) + (π₁  f' + π₂  g')
                ≡⟨ +4comm _ _ _ _ 
        (π₁  f + π₁  f') + (π₂  g + π₂  g')
                ≡⟨ sym (cong₂ _+_ (⋆distl+ _ _ _) (⋆distl+ _ _ _)) 
        π₁  (f + f') + π₂  (g + g')
                

    addCol :  {x y z}
        (f f' : Hom[ x , y ]) (g g' : Hom[ x , z ]) 
        (f  g) + (f'  g')  (f + f')  (g + g')

    addCol f f' g g' =
        (f  i₁ + g  i₂) + (f'  i₁ + g'  i₂)
                ≡⟨ +4comm _ _ _ _ 
        (f  i₁ + f'  i₁) + (g  i₂ + g'  i₂)
                ≡⟨ sym (cong₂ _+_ (⋆distr+ _ _ _) (⋆distr+ _ _ _)) 
        (f + f')  i₁ + (g + g')  i₂
                


    private
      ⋆4assoc :  {x y z w v} (a : Hom[ x , y ]) (b : Hom[ y , z ])
              (c : Hom[ z , w ]) (d : Hom[ w , v ]) 
          (a  b)  (c  d)  a  (b  c)  d
      ⋆4assoc a b c d = (⋆Assoc _ _ _)  cong (a ⋆_) (sym (⋆Assoc _ _ _))

      ⋆4assoc' :  {x y z w v} (a : Hom[ x , y ]) (b : Hom[ y , z ])
              (c : Hom[ z , w ]) (d : Hom[ w , v ]) 
          (a  b)  (c  d)  (a  (b  c))  d
      ⋆4assoc' a b c d = sym (⋆Assoc _ _ _)  cong (_⋆ d) (⋆Assoc _ _ _)


    -- Matrix multiplication
    innerProd :  {x y₁ y₂ z} (f₁ : Hom[ x , y₁ ]) (g₁ : Hom[ y₁ , z ])
                              (f₂ : Hom[ x , y₂ ]) (g₂ : Hom[ y₂ , z ]) 
        (f₁  f₂)  (g₁ ` g₂)  (f₁  g₁) + (f₂  g₂)

    innerProd f₁ g₁ f₂ g₂ =
        (f₁  i₁ + f₂  i₂)  (π₁  g₁ + π₂  g₂)
                ≡⟨ ⋆distr+ _ _ _ 
        (f₁  i₁)  (π₁  g₁ + π₂  g₂)  +  (f₂  i₂)  (π₁  g₁ + π₂  g₂)
                ≡⟨ cong₂ _+_ (⋆distl+ _ _ _) (⋆distl+ _ _ _) 
        ((f₁  i₁)  (π₁  g₁)  +  (f₁  i₁)  (π₂  g₂))
            +  (f₂  i₂)  (π₁  g₁)  +  (f₂  i₂)  (π₂  g₂)
                ≡⟨ cong₂ _+_ (cong₂ _+_ eq₁₁ eq₁₂) (cong₂ _+_ eq₂₁ eq₂₂) 
        (f₁  g₁  +  0h)  +  0h  +  f₂  g₂
                ≡⟨ cong₂ _+_ (+idr _) (+idl _) 
        (f₁  g₁) + (f₂  g₂)
                
     where
      eq₁₁ = (f₁  i₁)  (π₁  g₁)      ≡⟨ ⋆4assoc _ _ _ _ 
             f₁  (i₁  π₁)  g₁        ≡⟨ cong  u  _  u  _) i₁⋆π₁ 
             f₁  id  g₁               ≡⟨ cong (f₁ ⋆_) (⋆IdL _) 
             f₁  g₁                    

      eq₁₂ = (f₁  i₁)  (π₂  g₂)      ≡⟨ ⋆4assoc _ _ _ _ 
             f₁  (i₁  π₂)  g₂        ≡⟨ cong  u  _  u  _) i₁⋆π₂ 
             f₁  0h  g₂               ≡⟨ cong (f₁ ⋆_) (⋆0hl _) 
             f₁  0h                    ≡⟨ ⋆0hr _ 
             0h                         

      eq₂₁ = (f₂  i₂)  (π₁  g₁)      ≡⟨ ⋆4assoc _ _ _ _ 
             f₂  (i₂  π₁)  g₁        ≡⟨ cong  u  _  u  _) i₂⋆π₁ 
             f₂  0h  g₁               ≡⟨ cong (f₂ ⋆_) (⋆0hl _) 
             f₂  0h                    ≡⟨ ⋆0hr _ 
             0h                         

      eq₂₂ = (f₂  i₂)  (π₂  g₂)      ≡⟨ ⋆4assoc _ _ _ _ 
             f₂  (i₂  π₂)  g₂        ≡⟨ cong  u  _  u  _) i₂⋆π₂ 
             f₂  id  g₂               ≡⟨ cong (f₂ ⋆_) (⋆IdL _) 
             f₂  g₂                    


    outerProd :  {x₁ x₂ y z₁ z₂} (f₁ : Hom[ x₁ , y ]) (g₁ : Hom[ y , z₁ ])
                                  (f₂ : Hom[ x₂ , y ]) (g₂ : Hom[ y , z₂ ]) 

        (f₁ ` f₂)  (g₁  g₂)    f₁  g₁ ` f₂  g₁ 
                                  f₁  g₂ ` f₂  g₂

    outerProd {y = y} f₁ g₁ f₂ g₂ =
        (π₁  f₁  +  π₂  f₂)  (g₁  i₁  +  g₂  i₂)
                ≡⟨ ⋆distl+ _ _ _ 
        (π₁  f₁  +  π₂  f₂)  (g₁  i₁)  +  (π₁  f₁  +  π₂  f₂)  (g₂  i₂)
                ≡⟨ cong₂ _+_ (eq g₁ i₁) (eq g₂ i₂) 
           (π₁  (f₁  g₁)  +  π₂  (f₂  g₁))  i₁
        +  (π₁  (f₁  g₂)  +  π₂  (f₂  g₂))  i₂
                
     where
      eq = λ {z w} (g : Hom[ y , z ]) (i : Hom[ z , w ]) 
        (π₁  f₁  +  π₂  f₂)  (g  i)
                ≡⟨ ⋆distr+ _ _ _ 
        (π₁  f₁)  (g  i)  +  (π₂  f₂)  (g  i)
                ≡⟨ cong₂ _+_ (⋆4assoc' _ _ _ _) (⋆4assoc' _ _ _ _) 
        (π₁  (f₁  g))  i  +  (π₂  (f₂  g))  i
                ≡⟨ sym (⋆distr+ _ _ _) 
        (π₁  (f₁  g)  +  π₂  (f₂  g))  i