{-# OPTIONS --safe #-}

{-
This file contains a definition of the n-level axiom of coice,
i.e. the statment that the canonical map

∥ Πₐ Bₐ ∥ₙ → (Πₐ ∥ Bₐ ∥ₙ)

is an equivalence.
-}

module Cubical.Axiom.Choice where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism

open import Cubical.Data.Nat
open import Cubical.Data.Sigma
open import Cubical.Data.Empty as 
open import Cubical.Data.Fin as FN
open import Cubical.Data.Fin.Inductive as IndF
open import Cubical.Data.Nat.Order

open import Cubical.HITs.Truncation as TR
open import Cubical.HITs.PropositionalTruncation as PT

private
  variable
     ℓ' ℓ'' : Level

choiceMap : {A : Type } {B : A  Type ℓ'} (n : )
   hLevelTrunc n ((a : A)  B a)
   (a : A)  hLevelTrunc n (B a)
choiceMap n =
  TR.rec (isOfHLevelΠ n  _  isOfHLevelTrunc n))
         λ f a   f a ∣ₕ

-- n-level axiom of choice
satAC : (ℓ' : Level) (n : ) (A : Type )   Type (ℓ-max  (ℓ-suc ℓ'))
satAC ℓ' n A = (B : A  Type ℓ')  isEquiv (choiceMap {A = A} {B} n)

-- Version of (propositional) AC with ∃
AC∃-map : {A : Type } {B : A  Type ℓ'}
     {C : (a : A)  B a  Type ℓ''}
   ∃[ f  ((a : A)  B a) ] ((a : _)  C a (f a))
   ((a : A)   (B a) (C a))
AC∃-map =
  PT.rec (isPropΠ  _  squash₁))
    λ f  λ a   f .fst a , f .snd a ∣₁

satAC∃ :  {} (ℓ' ℓ'' : Level) (A : Type )  Type _
satAC∃ ℓ' ℓ'' A = (B : A  Type ℓ') (C : (a : A)  B a  Type ℓ'')
   isEquiv (AC∃-map {B = B} {C = C})

satAC→satAC∃ : {A : Type }
   satAC (ℓ-max ℓ' ℓ'') (suc zero) A  satAC∃ ℓ' ℓ'' A
satAC→satAC∃ F B C = propBiimpl→Equiv squash₁ (isPropΠ  _  squash₁))
  _
   f  invEq propTrunc≃Trunc1 (TR.map  f  fst  f , λ a  f a .snd )
          (invEq (_ , F  x  Σ (B x) (C x)))
            λ a  fst propTrunc≃Trunc1 (f a)))) .snd

-- All types satisfy (-2) level axiom of choice
satAC₀ : {A : Type }  satAC ℓ' 0 A
satAC₀ B =
  isoToIsEquiv (iso  _ _  tt*)  _  tt*)  _  refl) λ _  refl)

-- Fin m satisfies AC for any level n.
FinSatAC : (n m : )   {}  satAC  n (FN.Fin m)
FinSatAC n zero B =
  isoToIsEquiv (iso _
     f    x  ⊥.rec (FN.¬Fin0 x)) ∣ₕ)
     f  funExt λ x  ⊥.rec (FN.¬Fin0 x))
    (TR.elim  _  isOfHLevelPath n (isOfHLevelTrunc n) _ _)
      λ a  cong  ∣_∣ₕ (funExt λ x  ⊥.rec (FN.¬Fin0 x))))
FinSatAC n (suc m) B =
  subst isEquiv (ac-eq n m {B} (FinSatAC n m))
    (isoToIsEquiv (ac-map' n m B (FinSatAC n m)))
  where
  ac-map' :  {} (n m : ) (B : FN.Fin (suc m)  Type )
     (satAC  n (FN.Fin m))
     Iso (hLevelTrunc n ((x : _)  B x)) ((x : _)  hLevelTrunc n (B x))
  ac-map' n m B ise =
    compIso (mapCompIso (CharacΠFinIso m))
            (compIso (truncOfProdIso n)
              (compIso (Σ-cong-iso-snd λ _  equivToIso
                (_ , ise  x  B (FN.fsuc x))))
                (invIso (CharacΠFinIso m))))

  ac-eq : (n m : ) {B : _}  (eq : satAC  n (FN.Fin m))
        Iso.fun (ac-map' n m B eq)  choiceMap {B = B} n
  ac-eq zero m {B = B} x = refl
  ac-eq (suc n) m {B = B} x =
    funExt (TR.elim  _  isOfHLevelPath (suc n)
                             (isOfHLevelΠ (suc n)
                               _  isOfHLevelTrunc (suc n))) _ _)
      λ F  funExt
      λ { (zero , p) j
            transp  i  B (lem₁ p (j  i))) j (F (lem₁ p j)) ∣ₕ
        ; (suc x , p) j
            transp  i  B (lem₂ x p (j  i))) j (F (lem₂ x p j)) ∣ₕ})
    where
    lem₁ : (p : _ )  FN.fzero  (zero , p)
    lem₁ p = Fin-fst-≡ refl

    lem₂ : (x : _) (p : suc x < suc m)
       Path (FN.Fin _) (FN.fsuc (x , pred-≤-pred p)) (suc x , p)
    lem₂ x p = Fin-fst-≡ refl

-- Key result for construction of cw-approx at lvl 0
satAC∃Fin : (n : )  satAC∃  ℓ' (FN.Fin n)
satAC∃Fin n = satAC→satAC∃ (FinSatAC 1 n)

InductiveFinSatAC : (n m : )   {}  satAC  n (IndF.Fin m)
InductiveFinSatAC n m {} =
  subst (satAC  n) (isoToPath (Iso-Fin-InductiveFin m)) (FinSatAC n m)

InductiveFinSatAC∃ : (n : )  satAC∃  ℓ' (IndF.Fin n)
InductiveFinSatAC∃ { = } {ℓ'} n =
  subst (satAC∃  ℓ') (isoToPath (Iso-Fin-InductiveFin n)) (satAC∃Fin n)