{-# 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