{-# OPTIONS --without-K --rewriting #-}

open import lib.Basics
open import lib.NType2
open import lib.types.Nat
open import lib.types.Subtype

module lib.types.Fin where

Fin : ℕ → Type₀
Fin n = Σ ℕ (_< n)

instance
  Fin-reader : ∀ {n} → FromNat (Fin n)
  FromNat.in-range (Fin-reader {n}) m = m < n
  FromNat.read (Fin-reader {n}) m ⦃ m<n ⦄ = m , m<n

Fin-S : ∀ {n} → Fin n → Fin (S n)
Fin-S (n , lt) = n , ltSR lt

Fin-S^ : ∀ {n} → (m : ℕ) → Fin n → Fin (m + n)
Fin-S^ O <n = <n
Fin-S^ (S m) <n = Fin-S (Fin-S^ m <n)

Fin-S^' : ∀ {n} → (m : ℕ) → Fin n → Fin (ℕ-S^' m n)
Fin-S^' O <n = <n
Fin-S^' (S m) <n = Fin-S^' m (Fin-S <n)

Fin-to-≠ : ∀ {n} (<n : Fin n) → fst <n ≠ n
Fin-to-≠ <n = <-to-≠ (snd <n)

Fin-prop : ℕ → SubtypeProp ℕ lzero
Fin-prop n = ((_< n) , λ _ → <-is-prop)

abstract
  Fin-is-set : {n : ℕ} → is-set (Fin n)
  Fin-is-set {n} = Subtype-level (Fin-prop n)

  Fin-has-dec-eq : {n : ℕ} → has-dec-eq (Fin n)
  Fin-has-dec-eq {n} = Subtype-has-dec-eq (Fin-prop n) ℕ-has-dec-eq

private
  Fin-pred=-type : {n : ℕ} → Fin n → Fin n → Type₀
  Fin-pred=-type (_ , ltSR _) (_ , ltS) = ⊤
  Fin-pred=-type (_ , ltS) (_ , ltSR _) = ⊤
  Fin-pred=-type (m , ltS) (n , ltS) = m == n :> ℕ
  Fin-pred=-type {S n} (m , ltSR m<n) (o , ltSR o<n) = (m , m<n) == (o , o<n) :> Fin n

  Fin-pred= : {n : ℕ} {x y : Fin n} → x == y → Fin-pred=-type x y
  Fin-pred= {x = (_ , ltS)} idp = idp
  Fin-pred= {x = (_ , ltSR _)} idp = idp

abstract
  ltSR≠ltS : ∀ {m} (<m : Fin m) → Fin-S <m ≠ (m , ltS)
  ltSR≠ltS <m = Fin-to-≠ <m ∘ ap fst

  ltS≠ltSR : ∀ {m} (<m : Fin m) → (m , ltS) ≠ Fin-S <m
  ltS≠ltSR <m = Fin-to-≠ <m ∘ ! ∘ ap fst

abstract
  Fin-S-is-inj : ∀ {n} → is-inj (Fin-S {n = n})
  Fin-S-is-inj _ _ = Fin-pred=

  Fin-S-≠ : ∀ {n} {<n₀ <n₁ : Fin n} (p : <n₀ ≠ <n₁) → Fin-S <n₀ ≠ Fin-S <n₁
  Fin-S-≠ {<n₀ = <n₀} {<n₁} p = p ∘ Fin-S-is-inj <n₀ <n₁

  Fin-S^-≠ : ∀ {n} m {<n₀ <n₁ : Fin n} (p : <n₀ ≠ <n₁) → Fin-S^ m <n₀ ≠ Fin-S^ m <n₁
  Fin-S^-≠ O p = p
  Fin-S^-≠ (S n) p = Fin-S-≠ (Fin-S^-≠ n p)

  Fin-S^'-≠ : ∀ {n} m {<n₀ <n₁ : Fin n} (p : <n₀ ≠ <n₁) → Fin-S^' m <n₀ ≠ Fin-S^' m <n₁
  Fin-S^'-≠ O p = p
  Fin-S^'-≠ (S n) p = Fin-S^'-≠ n (Fin-S-≠ p)

Fin-equiv-Empty : Fin 0 ≃ Empty
Fin-equiv-Empty = equiv to from to-from from-to where
  to : Fin 0 → Empty
  to (_ , ())

  from : Empty → Fin 0
  from ()

  abstract
    to-from : ∀ x → to (from x) == x
    to-from ()

    from-to : ∀ x → from (to x) == x
    from-to (_ , ())

Fin-equiv-Coprod : {n : ℕ} → Fin (S n) ≃ Fin n ⊔ Unit
Fin-equiv-Coprod {n} = equiv to from to-from from-to where
  to : Fin (S n) → Fin n ⊔ Unit
  to (m , ltS) = inr unit
  to (m , ltSR lt) = inl (m , lt)

  from : Fin n ⊔ Unit → Fin (S n)
  from (inr _) = n , ltS
  from (inl fin) = Fin-S fin

  abstract
    to-from : ∀ x → to (from x) == x
    to-from (inr _) = idp
    to-from (inl _) = idp

    from-to : ∀ x → from (to x) == x
    from-to (_ , ltS) = idp
    from-to (_ , ltSR _) = idp