module Algebra.Field.Properties where
open import Level
open import Function
open import Data.Nat hiding (_+_; _*_; _^_; NonZero; nonZero; _⊔_)
open import Data.Integer hiding (_+_; _*_; _^_; NonZero; nonZero; _⊔_)
open import Data.Product
open import Data.Vec
open import Algebra
open import Algebra.Field.Base
open import Algebra.Ring.Properties as R
open import Algebra.CommRing.Properties as CR
open import Relation.Binary as RB hiding (Decidable)
open import Relation.Unary as RU hiding (Decidable)
open import Relation.Nullary
open import Relation.Nullary.Decidable.Core
module FieldUnits {ℓ ℓ'} (field' : Field ℓ ℓ') where
  open Field field' renaming (Carrier to A)
  semiRing : RawSemiring _ _
  semiRing = record { Carrier = A ; _≈_ = _≈_ ; _+_ = _+_ ; _*_ = _*_ ; 0# = 0# ; 1# = 1# }
  open import Algebra.Definitions.RawSemiring semiRing
  open import Relation.Binary.Reasoning.Setoid setoid
  open CR.Units commutativeRing
  private
    A≢0 = Σ[ a ∈ A ] (a ≉ 0#)
    variable
      n : ℕ
  _[_]^_ : (a : A) → (a≉0 : a ≉ 0#) → ℤ → A
  a [ a≉0 ]^ (+ n) = a ^ n
  a [ a≉0 ]^ -[1+ n ] = inv a a≉0 ^ ℕ.suc n
  _^n_ : A≢0 → ℕ → A≢0
  (a , a≉0) ^n n = (a ^ n) , a^n≉0 where
    a^n·a^-n≡1' = ^-presUnit a n (_ , hasInverse a a≉0)
    a^n = a ^ n
    a^-n = a^n·a^-n≡1' .proj₁
    a^n·a^-n≡1 : (a^n * a^-n) ≈ 1#
    a^n·a^-n≡1 = a^n·a^-n≡1' .proj₂
    a^n≉0 : a^n ≉ 0#
    a^n≉0 a^n≡0 = 0≢1 (trans (sym (trans (*-congʳ a^n≡0) (0LeftAnnihilates _))) a^n·a^-n≡1)
  _^z_ : A≢0 → ℤ → A≢0
  a ^z (+ n) = a ^n n
  a@(a' , a≉0) ^z -[1+ n ] = (a⁻¹ , a⁻¹≉0) ^n (ℕ.suc n)  where
    a⁻¹ = inv a' a≉0
    a⁻¹≉0 : a⁻¹ ≉ 0#
    a⁻¹≉0 a⁻¹≡0 = 0≢1 (trans (sym (trans (*-congˡ a⁻¹≡0) (0RightAnnihilates _))) (hasInverse a' a≉0))
  _*n_ : A≢0 → A≢0 → A≢0
  p@(a , a≉0) *n q@(b , b≉0) = (a * b) , a*b≉0 where
    b⁻¹ = inv b b≉0
    b*b⁻¹≡1 = hasInverse b b≉0
    a*b≉0 : (a * b) ≉ 0#
    a*b≉0 a*b≈0 = a≉0 (begin
      a             ≈˘⟨ *-identityʳ _ ⟩
      a * 1#        ≈˘⟨ *-congˡ b*b⁻¹≡1 ⟩
      a * (b * b⁻¹) ≈˘⟨ *-assoc _ _ _ ⟩
      a * b * b⁻¹   ≈⟨ *-congʳ a*b≈0 ⟩
      0# * b⁻¹      ≈⟨ 0LeftAnnihilates _ ⟩
      0# ∎)
  foldr≉0 : (a : A≢0) (xs : Vec A≢0 n) → foldr _ (λ x y → x .proj₁ * y) (a .proj₁) xs ≉ 0#
  foldr≉0 (_ , a≉0) [] = a≉0
  foldr≉0 a (b ∷ xs) = (b *n (_ , foldr≉0 a xs)) .proj₂
  record NonZero (a : A) : Set ℓ' where
    constructor nonZeroC
    field
      nonZero : a ≉ 0#
  mulVec : Vec A≢0 n → A≢0
  mulVec xs = _ , (foldr≉0 (1# , (0≢1 ∘ sym)) xs)
  module DiscA (disc : RB.Decidable _≈_) where
    decP≉0 : (p : A) → Dec (NonZero p)
    decP≉0 p with disc p 0#
    ... | yes p≈0 = no λ where (nonZeroC p≉0) → p≉0 p≈0
    ... | no  p≉0 = yes (nonZeroC p≉0)
    private
      data Decidable {ℓ} {ℓ'} {A : Set ℓ} (P : A → Set ℓ') : Set (ℓ ⊔ ℓ') where
        isDec : (∀ a → Dec (P a)) → Decidable P
      data AllVec {ℓ} {ℓ'} {A : Set ℓ} (P : A → Set ℓ') : Vec A n → Set (ℓ ⊔ ℓ') where
        emp  : AllVec P []
        consVec : {x : A} (p : P x) {xs : Vec A n} (allXS : AllVec P xs) → AllVec P (x ∷ xs)
      variable
        ℓ'' : Level
        A' B C D : Set ℓ''
        B' B'' : (a : A) → Set ℓ''
      DecAll : Decidable B' → (xs : Vec A n) → Dec (AllVec B' xs)
      DecAll P [] = yes emp
      DecAll P'@(isDec P) (x ∷ xs) with P x | DecAll P' xs
      ... | no ¬p | _ = no (λ where (consVec p allXs) → ¬p p)
      ... | yes p | no ¬q = no (λ where (consVec p allXs) → ¬q allXs)
      ... | yes p | yes q = yes (consVec p q)
    instance
      toDecAll : ⦃ decB : Decidable B' ⦄ {xs : Vec A n} {decVec : True (DecAll decB xs)} → AllVec B' xs
      toDecAll ⦃ decB = decB ⦄ {[]} {decVec} = emp
      toDecAll ⦃ decB = P'@(isDec P) ⦄ {x ∷ xs} {decVec} with P x | DecAll P' xs
      ... | yes x | yes xs = consVec x xs
      DecNonZero : Decidable NonZero
      DecNonZero = isDec decP≉0
    vec→vec≉0 : (xs : Vec A n) ⦃ xs≉0 : AllVec NonZero xs ⦄ → Vec A≢0 n
    vec→vec≉0 [] = []
    vec→vec≉0 (x ∷ xs) ⦃ consVec (nonZeroC nonZero) xs≉0 ⦄ = (x , nonZero) ∷ vec→vec≉0 xs ⦃ xs≉0 ⦄