{-# OPTIONS --without-K  #-}
module sets.fin.universe where

open import sum
open import decidable
open import equality.core
open import equality.calculus
open import equality.inspect
open import function.isomorphism
open import function.core
open import function.extensionality
open import function.fibration
open import function.overloading
open import sets.core
open import sets.properties
open import sets.nat.core
  hiding (_≟_)
open import sets.nat.ordering
  hiding (compare)
open import sets.fin.core
open import sets.fin.ordering
open import sets.fin.properties
open import sets.unit
open import sets.empty
open import sets.bool
open import sets.vec.dependent
open import hott.level.core
open import hott.level.closure
open import hott.level.sets

fin-struct-iso :  {n}  Fin (suc n)  (  Fin n)
fin-struct-iso = record
  { to = λ { zero  inj₁ tt; (suc i)  inj₂ i }
  ; from = [  _  zero) ,⊎ (suc) ]
  ; iso₁ = λ { zero  refl ; (suc i)  refl }
  ; iso₂ = λ { (inj₁ _)  refl; (inj₂ i)  refl } }

fin0-empty :   Fin 0
fin0-empty = sym≅ (empty-⊥-iso  ()))

fin1-unit :   Fin 1
fin1-unit = record
  { to = λ _  zero
  ; from = λ { zero  tt ; (suc ()) }
  ; iso₁ = λ _  refl
  ; iso₂ = λ { zero  refl ; (suc ()) } }

fin2-bool : Bool  Fin 2
fin2-bool = record
  { to = λ b  if b then # 0 else # 1
  ; from = true ∷∷ false ∷∷ ⟦⟧
  ; iso₁ = λ { true  refl ; false  refl }
  ; iso₂ = refl ∷∷ refl ∷∷ ⟦⟧ }

fin-sum :  {n m}  (Fin n  Fin m)  Fin (n + m)
fin-sum {0} = record
  { to = λ { (inj₁ ()) ; (inj₂ j)  j }
  ; from = λ { j  inj₂ j }
  ; iso₁ = λ { (inj₁ ()); (inj₂ j)  refl }
  ; iso₂ = λ _  refl }
fin-sum {suc n}{m} = begin
    (Fin (suc n)  Fin m)
  ≅⟨ ⊎-ap-iso fin-struct-iso refl≅ 
    ((  Fin n)  Fin m)
  ≅⟨ ⊎-assoc-iso 
    (  (Fin n  Fin m))
  ≅⟨ ⊎-ap-iso refl≅ fin-sum 
    (  Fin (n + m))
  ≅⟨ sym≅ fin-struct-iso 
    Fin (suc (n + m))
  
  where open ≅-Reasoning

fin-prod :  {n m}  (Fin n × Fin m)  Fin (n * m)
fin-prod {0} = iso  { (() , _) })  ())  { (() , _) })  ())
fin-prod {suc n}{m} = begin
    (Fin (suc n) × Fin m)
  ≅⟨ ×-ap-iso fin-struct-iso refl≅ 
    ((  Fin n) × Fin m)
  ≅⟨ ⊎×-distr-iso 
    (( × Fin m)  (Fin n × Fin m))
  ≅⟨ ⊎-ap-iso ×-left-unit fin-prod 
    (Fin m  (Fin (n * m)))
  ≅⟨ fin-sum 
    (Fin (m + (n * m)))
  ≡⟨ refl 
    Fin (suc n * m)
  
  where open ≅-Reasoning

fin-exp :  {n m}  (Fin n  Fin m)  Fin (m ^ n)
fin-exp {0} = record
  { to = λ f  zero
  ; from =  ()) ∷∷ ⟦⟧
  ; iso₁ = λ f  funext λ ()
  ; iso₂ = refl ∷∷ ⟦⟧ }
fin-exp {suc n}{m} = begin
    (Fin (suc n)  Fin m)
  ≅⟨ (Π-ap-iso fin-struct-iso λ _  refl≅) 
    ((  Fin n)  Fin m)
  ≅⟨ iso  f  f (inj₁ tt) , f ∘' inj₂)
          { (i , _) (inj₁ _)  i
            ; (_ , g) (inj₂ i)  g i })
          f  funext λ { (inj₁ tt)  refl
                          ; (inj₂ i)  refl })
          { (i , g)  refl }) 
    (Fin m × (Fin n  Fin m))
  ≅⟨ ×-ap-iso refl≅ fin-exp 
    (Fin m × Fin (m ^ n))
  ≅⟨ fin-prod 
    Fin (m ^ (suc n))
  
  where open ≅-Reasoning

factorial :   
factorial 0 = 1
factorial (suc n) = suc n * factorial n

fin-inj-iso :  {n}  (Fin n  Fin n)  (Fin n  Fin n)
fin-inj-iso {n} = record
  { to = λ f  (apply f , iso⇒inj f)
  ; from = λ { (f , inj)  inj⇒iso f inj }
  ; iso₁ = λ f  iso-eq-h2 (fin-set n) (fin-set n) refl
  ; iso₂ = λ { (f , inj)  inj-eq-h2 (fin-set n) refl } }

fin-iso-iso :  {n}  (Fin n  Fin n)  Fin (factorial n)
fin-iso-iso {0} = record
  { to = λ { (f , _)  zero }
  ; from = λ _   ()) ,  { {()} p })
  ; iso₁ = λ { (f , _)  inj-eq-h2 (fin-set _) (funext λ ()) }
  ; iso₂ = λ { zero  refl ; (suc ()) } }
fin-iso-iso {suc n} = begin
    (X  X)
  ≅⟨ sym≅ (total-iso classify) 
    (Σ X λ i  classify ⁻¹ i)
  ≅⟨ (Σ-ap-iso₂ λ i  trans≅ (sym≅ (const-fibre i)) fibre-zero-iso) 
    (Fin (suc n) × (Fin n  Fin n))
  ≅⟨ ×-ap-iso refl≅ fin-iso-iso 
    (Fin (suc n) × Fin (factorial n))
  ≅⟨ fin-prod 
    Fin (factorial (suc n))
  
  where
    X = Fin (suc n)
    open ≅-Reasoning

    classify : (X  X)  X
    classify (f , _) = f zero

    const-fibre : (i : X)  classify ⁻¹ zero  classify ⁻¹ i
    const-fibre i = begin
        ( Σ (X  X) λ { (f , _)  f zero  zero } )
      ≅⟨ ( Σ-ap-iso (transpose-inj-iso' zero i)  { (f , inj)
          mk-prop-iso (fin-set _ _ _) (fin-set _ _ _)
                       (ap (transpose zero i)) (u f) }) ) 
        ( Σ (X  X) λ { (f , _)  f zero  i } )
      
      where
        u : (f : X  X)
           transpose zero i (f zero)  i
           f zero  zero
        u f p = sym (_≅_.iso₁ (transpose-iso zero i) (f zero))
              · (ap (transpose zero i) p
              · transpose-β₂ zero i)

    fibre-zero-iso : classify ⁻¹ zero  (Fin n  Fin n)
    fibre-zero-iso = record
      { to = λ { (f , z)  fin-inj-remove₀ f z }
      ; from = λ { g  fin-inj-add g , refl }
      ; iso₁ = λ { (f , p)
                  unapΣ (inj-eq-h2 (fin-set (suc n))
                         (funext (α f p ))
                         , h1⇒prop (fin-set (suc n) _ _) _ _) }
      ; iso₂ = λ g  inj-eq-h2 (fin-set n) refl }
        where
            α : (f : Fin (suc n)  Fin (suc n))
               (p : apply f zero  zero)
               (i : Fin (suc n))
               apply (fin-inj-add (fin-inj-remove₀ f p)) i
               apply f i
            α _ p zero = sym p
            α (f , f-inj) p (suc i) = pred-β (f (suc i))
               q  fin-disj _ (f-inj (p · sym q)))

fin-subsets :  {n}  (Fin n  Bool)  Fin (2 ^ n)
fin-subsets = trans≅ (Π-ap-iso refl≅ λ _  fin2-bool) fin-exp