open import Level using (Level; _⊔_)
open import Function
open import Data.Empty
open import Data.Unit.Polymorphic
open import Data.Nat as  hiding (_⊔_)
open import Data.Nat.Properties using (<⇒≤; ≤⇒≯)
open import Data.Product
open import Data.Irrelevant
open import Data.Sum
open import Data.Fin as F
open import Data.Fin.Properties as F
open import Data.Fin.Induction
open import Induction.WellFounded
open import Relation.Binary.Definitions using (Tri; tri<; tri≈; tri>)
open import Relation.Binary.PropositionalEquality
import Relation.Binary.Reasoning.Setoid as ReasonSetoid
open import Relation.Nullary hiding (Irrelevant; Recomputable)
open import Relation.Unary using (Pred; Recomputable)
open import Relation.Unary.Consequences
open import Algebra hiding (Invertible)
open import Algebra.Apartness
import Algebra.Properties.Ring as RingProps
import Algebra.Definitions as ADefs

private variable
  c  ℓ₁ ℓ₂ : Level
  A B : Set 
  m n : 
  i j k : Fin n

i≢j→si≢sj : i  j  F.suc i  F.suc j
i≢j→si≢sj i≢j refl = i≢j refl

IsInj₁ :  {a b} {A : Set a} {B : Set b}  A  B  Set _
IsInj₁ (inj₁ _) = 
IsInj₁ (inj₂ _) = 

fromIsInj₁ :  {a b} {A : Set a} {B : Set b} {a⊎b : A  B}  IsInj₁ a⊎b  A
fromIsInj₁ {a⊎b = inj₁ x} _ = x

record Σ′ (A : Set ) (B : A  Set ℓ₁) : Set (  ℓ₁) where
  constructor _∙∙_
  field
    fst′′ : A
    .snd′′ : B fst′′
open Σ′ public

IsInj₂ :  {a b} {A : Set a} {B : Set b}  A  B  Set _
IsInj₂ (inj₁ _) = 
IsInj₂ (inj₂ _) = 

fromIsInj₂ :  {a b} {A : Set a} {B : Set b} {a⊎b : A  B}  IsInj₂ a⊎b  B
fromIsInj₂ {a⊎b = inj₂ x} _ = x

split : (a⊎b : A  B)  Σ′ A  a  inj₁ a  a⊎b)  Σ′ B λ b  inj₂ b  a⊎b
split (inj₁ x) = inj₁ (x ∙∙ refl)
split (inj₂ y) = inj₂ (y ∙∙ refl)

inj< : {i j : Fin n}  i F.< j  inject₁ i F.< inject₁ j
inj< {_} {zero} {suc j} (s≤s i<j) = s≤s z≤n
inj< {_} {suc i} {suc j} (s≤s i<j) = s≤s (inj< i<j)

inj<from :  (i : Fin n)  inject₁ i F.< fromℕ n
inj<from zero = s≤s z≤n
inj<from (suc i) = s≤s (inj<from i)

cong≤ˡ :  {i j k : }  i ℕ.≤ j  j  k  i ℕ.≤ k
cong≤ˡ p refl = p

cong≤ʳ :  {i j k : }  j  k  i ℕ.≤ j  i ℕ.≤ k
cong≤ʳ refl p = p

cong≤ʳ₂ :  {i j k : }  i  j  i ℕ.≤ k  j ℕ.≤ k
cong≤ʳ₂ refl p = p

lowerMaximum : (i : Fin (suc n)) (i≢n : i  fromℕ n)  Fin n
lowerMaximum {zero} zero i≢n = ⊥-elim (i≢n refl)
lowerMaximum {suc n} zero i≢n = zero
lowerMaximum {suc n} (suc i) i≢n = suc (lowerMaximum i  i≡n  i≢n (cong suc i≡n)))

lowerMaximumInjective : {i j : Fin (suc n)} {i≢n : i  fromℕ n} {j≢n : j  fromℕ n}
   lowerMaximum i i≢n  lowerMaximum j j≢n  i  j
lowerMaximumInjective {zero} {zero} {zero} {i≢n} {j≢n} leqn = refl
lowerMaximumInjective {suc n} {zero} {zero} {i≢n} {j≢n} leqn = refl
lowerMaximumInjective {suc n} {suc i} {suc j} {i≢n} {j≢n} leqn =
  cong suc (lowerMaximumInjective (suc-injective leqn))

finSuc : Fin n  Fin (suc n)
finSuc zero = zero
finSuc (suc p) = suc (finSuc p)

finSuc≢fromℕ : {i : Fin n}  finSuc i  fromℕ n
finSuc≢fromℕ {_} {zero} ()
finSuc≢fromℕ {_} {suc i} eqn = finSuc≢fromℕ (suc-injective eqn)

lowerMaximum≡p : {p : Fin (suc n)} (fp≢n : finSuc p  F.suc (fromℕ n))  lowerMaximum (finSuc p) fp≢n  p
lowerMaximum≡p {p = zero} fp≢n = refl
lowerMaximum≡p {suc n} {p = suc p} fp≢n = cong suc (lowerMaximum≡p λ fp≡n  fp≢n (cong suc fp≡n))

isMaximum : Fin n  Set
isMaximum {suc n} = _≡ fromℕ n

isMaximum? : (p : Fin n)  Dec (isMaximum p)
isMaximum? {suc n} = F._≟ fromℕ n

maximum≥all : {i j : Fin n}  isMaximum i  i F.≥ j
maximum≥all {suc n} {zero} {zero} imax = z≤n
maximum≥all {suc zero} {zero} {suc ()} refl
maximum≥all {suc (suc n)} {zero} {suc j} ()
maximum≥all {suc n} {suc i} {zero} imax = z≤n
maximum≥all {suc (suc n)} {suc i} {suc j} imax = s≤s (maximum≥all (suc-injective imax))

_max⊎>_ : (i j : Fin n)  Set
i max⊎> j = isMaximum i × isMaximum j  i F.> j

Fin⊤ :   Set
Fin⊤ n = Fin n  

≤∧s≢⇒≤ :  {m n}  m ℕ.≤ suc n  m  suc n  m ℕ.≤ n
≤∧s≢⇒≤ {_} {n} z≤n q = z≤n
≤∧s≢⇒≤ {suc _} {zero} (s≤s z≤n) q = ⊥-elim (q refl)
≤∧s≢⇒≤ {suc _} {suc n} (s≤s p) q = s≤s (≤∧s≢⇒≤ p λ where refl  q refl)

i>j→i≢j : i F.> j  i  inject₁ j
i>j→i≢j i>j refl = <⇒≢ (≤̄⇒inject₁< i>j) refl

i>j→i≥j :  {i} {j : Fin n}  i F.≥ suc j  i F.≥ inject₁ j
i>j→i≥j = ≤-trans (<⇒≤ (≤̄⇒inject₁< ≤-refl))

i>j→i>injJ : i F.> j  i F.> inject₁ j
i>j→i>injJ = cong≤ʳ₂ (cong suc (sym (toℕ-inject₁ _)))

<→Σinject :  {i j : Fin (suc n)}  i F.< j  Σ[ k  Fin n ] inject₁ k  i
<→Σinject {suc n} {zero} {suc j} i<j = zero , refl
<→Σinject {suc n} {i = suc i} {suc j} (s≤s i<j) =
  let (p , q) = <→Σinject {i = i} {j} i<j in
  suc p , cong suc q

punchOut-fromℕ :  {i : Fin (suc n)} (n≢i : fromℕ n  i)  toℕ (punchOut n≢i)  toℕ i
punchOut-fromℕ {zero}  {zero } n≢i = ⊥-elim (n≢i refl)
punchOut-fromℕ {suc n} {zero } n≢i = refl
punchOut-fromℕ {suc n} {suc i} n≢i = cong suc (punchOut-fromℕ λ where refl  n≢i refl)

punchIn-fromℕ : (i : Fin n)  toℕ (punchIn (fromℕ _) i)  toℕ i
punchIn-fromℕ zero = refl
punchIn-fromℕ (suc i) = cong suc (punchIn-fromℕ i)

>-weakInduction-endingFrom :  (P : Pred (Fin (suc n)) ) 
                                {i}  P i 
                               (∀ j  P (suc j)  P (inject₁ j)) 
                                {j}  j F.≤ i  P j
>-weakInduction-endingFrom {n} P {i} Pi Pᵢ₊₁⇒Pᵢ {j} j≤i = induct (>-wellFounded _) (F.<-cmp i j) j≤i
  where
  induct :  {j}  Acc F._>_ j  Tri (i F.< j) (i  j) (i F.> j)  j F.≤ i  P j
  induct (acc rs) (tri≈ _ refl _) j≤i = Pi
  induct (acc rs) (tri< i>j _ _) j≤i with ()≤⇒≯ j≤i i>j
  induct {j} (acc rs) (tri> _ _ i>j) _ = subst P isj≡j (Pᵢ₊₁⇒Pᵢ sj PSj)
    where

    sj×≡ : Σ[ k  Fin n ] inject₁ k  j
    sj×≡ = <→Σinject i>j

    sj = sj×≡ .proj₁

    isj≡j : inject₁ sj  j
    isj≡j = sj×≡ .proj₂

    sj>j : suc sj F.> j
    sj>j = subst (suc sj F.>_) isj≡j (≤̄⇒inject₁< {i = sj} {j = sj} (≤-refl {x = sj}))

    sj≤i : suc sj F.≤ i
    sj≤i = subst  x  suc x ℕ.≤ toℕ i) (trans (sym (cong toℕ isj≡j)) (toℕ-inject₁ sj)) i>j

    PSj : P (suc sj)
    PSj = induct (rs sj>j) (F.<-cmp i (suc sj)) sj≤i

≤Rec : Recomputable (F._≤_ {_} {n} i)
≤Rec = dec⇒recomputable (F._≤?_ _)

suc-pred : n ℕ.> 0  ℕ.suc (ℕ.pred n)  n
suc-pred (s≤s {n = zero} z≤n) = refl
suc-pred (s≤s {n = suc n} z≤n) rewrite suc-pred ((s≤s {n = n} z≤n)) = refl

module hFieldProps (hField : HeytingField c ℓ₁ ℓ₂) where

  open module HF = HeytingField hField
  open HeytingCommutativeRing heytingCommutativeRing using (commutativeRing)
  open CommutativeRing commutativeRing using (ring)
  open ADefs _≈_ using (Invertible)
  open ReasonSetoid HF.setoid
  open RingProps ring

  private variable
    x y : Carrier

  tightʳ : x # y  ¬ x  y
  tightʳ p q = tight _ _ .proj₂ q p

  tightBoth : x # y  x  y  A
  tightBoth x#y x≈y = contradiction x≈y (tightʳ x#y)

  x#0→x⁻¹*x≈1 : (x#0 : x # 0#)  #⇒invertible x#0 .proj₁ HF.* x  1#
  x#0→x⁻¹*x≈1 {x} x#0 = helper (#⇒invertible x#0)
    where
    helper : (hf@(x⁻¹ , _) : Invertible 1# HF._*_ (x HF.- 0#))  x⁻¹ HF.* x  1#
    helper (x⁻¹ , x⁻¹*x≈1 , _) = begin
      x⁻¹ HF.* x           ≈˘⟨ *-congˡ (+-identityʳ _) 
      x⁻¹ HF.* (x HF.+ 0#) ≈˘⟨ *-congˡ (+-congˡ -0#≈0#) 
      x⁻¹ HF.* (x HF.- 0#)  ≈⟨ x⁻¹*x≈1 
      1#