{-# OPTIONS --safe #-}

module Multiset.Axioms.Choice where

open import Multiset.Prelude

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

open import Cubical.Data.Nat.Base
open import Cubical.Data.Unit.Base
open import Cubical.Data.Sigma.Base

open import Cubical.HITs.SetTruncation as ST using (∥_∥₂)
open import Cubical.HITs.PropositionalTruncation as PT using (∥_∥₁)
open import Cubical.HITs.Truncation.FromNegTwo.Base using (∥_∥_)

--- The Axiom of Choice
AC : (ℓA ℓB ℓR : Level)  Type (ℓ-suc (ℓ-max ℓA (ℓ-max ℓB ℓR)))
AC ℓA ℓB ℓR =
   {A : Type ℓA} {B : A  Type ℓB}
   (R : (a : A)  (B a)  Type ℓR)
   isSet A
   (∀ a  isSet (B a))
   (∀ a b  isProp (R a b))
   (∀ a   Σ[ b  B a ] R a b ∥₁)
    Σ[ f  ((a : A)  B a) ] (∀ a  R a (f a)) ∥₁

isPropAC :  {ℓA ℓB ℓR}  isProp (AC ℓA ℓB ℓR)
isPropAC = isPropImplicitΠ2 λ A B  isPropΠ5 λ _ _ _ _ _  PT.isPropPropTrunc

module _ {ℓA ℓB ℓR : Level} {A : Type ℓA} {B : A  Type ℓB}
  (R : (a : A)  (B a)  Type ℓR)
  (setA : isSet A)
  (setB :  a  isSet (B a))
  (propR :  a b  isProp (R a b))
  where

  hasAC : Type _
  hasAC = (∀ a   Σ[ b  B a ] R a b ∥₁)   Σ[ f  ((a : A)  B a) ] (∀ a  R a (f a)) ∥₁

-- The (n, m)-truncated Axiom of Choice, as stated in Exercise I.7.8, HoTT book:
AC[_,_] : (n m : )  (ℓA ℓB : Level)  Type _
AC[_,_] n m ℓA ℓB =
   (A : Type ℓA) (B : A  Type ℓB)
   isSet A
   isOfHLevelDep n B
   ((a : A)   B a  m)
    ((a : A)  B a)  m

-- An instanciated version of AC[ 2 , 1 ]:
AC[Set,Prop] : (ℓA ℓB : Level)  Type _
AC[Set,Prop] ℓA ℓB =
   (A : Type ℓA) (B : A  Type ℓB)
   isSet A
   (∀ a  isSet (B a))
   ((a : A)   B a ∥₁)
    ((a : A)  B a) ∥₁

isPropAC[Set,Prop] :  {ℓA ℓB}  isProp (AC[Set,Prop] ℓA ℓB)
isPropAC[Set,Prop] = isPropΠ5 λ _ _ _ _ _  PT.isPropPropTrunc

module _ {ℓA ℓB} where
  AC→AC[Set,Prop] : AC ℓA ℓB ℓ-zero  AC[Set,Prop] ℓA ℓB
  AC→AC[Set,Prop] ac A B setA setB f = PT.map  { (f , _) a  f a })
    (ac {A = A} {B = B}  a b  Unit) setA setB  { _ _ tt tt  refl }) λ a  PT.map  b  b , tt) (f a))

  AC[Set,Prop]→AC : AC[Set,Prop] ℓA ℓB  AC ℓA ℓB ℓ-zero
  AC[Set,Prop]→AC ac {A = A} {B = B} R setA setB propR f = PT.map  { g  (fst  g) , snd  g })
    (ac A  a  Σ[ b  (B a) ] R a b) setA  a  isSetΣ (setB a)  b  isProp→isSet (propR a b))) f)

  -- AC[ 2 , 1 ] is equivalent to AC as defined AC:
  AC≃AC[Set,Prop] : AC ℓA ℓB ℓ-zero  AC[Set,Prop] ℓA ℓB
  AC≃AC[Set,Prop] = propBiimpl→Equiv isPropAC isPropAC[Set,Prop] AC→AC[Set,Prop] AC[Set,Prop]→AC

-- An instanciated version of AC[ 3 , 2 ]:
AC[Gpd,Set] : (ℓA ℓB : Level)  Type _
AC[Gpd,Set] ℓA ℓB =
   (A : Type ℓA) (B : A  Type ℓB)
   isSet A
   (∀ a  isGroupoid (B a))
   ((a : A)   B a ∥₂)
    ((a : A)  B a) ∥₂

AC[Gpd,Set]-Rel : (ℓA ℓB ℓR : Level)  Type _
AC[Gpd,Set]-Rel ℓA ℓB ℓR =
   {A : Type ℓA} {B : A  Type ℓB}
   (R : (a : A)  B a  Type ℓR)
   isSet A
   (∀ a  isGroupoid (B a))
   (∀ a b  isProp (R a b))
   ((a : A)   (Σ[ b  B a ] R a b ) ∥₂)
    Σ[ f  ((a : A)  B a) ] ((a : A)  R a (f a)) ∥₂

AC[Gpd,Set]→AC[Gpd,Set]-Rel :  {ℓA ℓB}  AC[Gpd,Set] ℓA ℓB  AC[Gpd,Set]-Rel ℓA ℓB ℓB
AC[Gpd,Set]→AC[Gpd,Set]-Rel ac {A = A} {B = B} R setA grpB propR f =
  ST.map  g  (fst  g) , (snd  g))
    (ac A  a  Σ[ b  (B a) ] R a b) setA  a  isGroupoidΣ (grpB a) λ b  isProp→isOfHLevelSuc 2 (propR a b)) f)