\begin{code}
{-# OPTIONS --cubical --safe --postfix-projections #-}

module Cardinality.Finite.SplitEnumerable where

open import Prelude
open import Container
open import Data.Fin
open import Data.Sigma.Properties
open import Function.Surjective.Properties
open import Data.Fin.Properties

import Cardinality.Finite.SplitEnumerable.Container as 
import Cardinality.Finite.SplitEnumerable.Inductive as 𝕃
open import Cardinality.Finite.SplitEnumerable.Isomorphism

open import Data.Nat.Literals
open import Data.Fin.Literals
open import Literals.Number

private
  variable
    u : Level
    U : A  Type u

module _ {a} {A : Type a} where
 open 
 open import Container.List
 open import Container.Membership ( , Fin)
 open import Relation.Binary.Equivalence.Reasoning (⇔-equiv {a})

\end{code}
%<*split-enum-is-split-surj-short>
\begin{code}
 split-enum-is-split-surj : ℰ! A  Σ[ n   ] (Fin n ↠! A)
 split-enum-is-split-surj = reassoc
\end{code}
%</split-enum-is-split-surj-short>
\begin{code}

 ℰ!⇔Fin↠! :
\end{code}
%<*is-split-inj-type>
\begin{code}
   ℰ! A  Σ[ n   ] (Fin n ↠! A)
\end{code}
%</is-split-inj-type>
\begin{code}
 ℰ!⇔Fin↠! =
\end{code}
%<*is-split-inj>
\begin{code}
   ℰ! A                                                       ≋⟨⟩
   Σ[ xs  List A ] ((x : A)  x  xs)                        ≋⟨⟩
   Σ[ xs  List A ] ((x : A)  fiber (snd xs) x)              ≋⟨⟩
   Σ[ xs  List A ] SplitSurjective (snd xs)                  ≋⟨⟩
   Σ[ xs    , Fin  A ] SplitSurjective (snd xs)           ≋⟨⟩
   Σ[ xs  Σ[ n   ] (Fin n  A) ] SplitSurjective (snd xs)  ≋⟨ reassoc 
   Σ[ n   ] Σ[ f  (Fin n  A) ] SplitSurjective f          ≋⟨⟩
   Σ[ n   ] (Fin n ↠! A) 
\end{code}
%</is-split-inj>
%<*split-is-discrete>
\begin{code}
 ℰ!⇒Discrete : ℰ! A  Discrete A
 ℰ!⇒Discrete  = flip Discrete-distrib-surj discreteFin
               snd
               ℰ!⇔Fin↠! .fun
\end{code}
%</split-is-discrete>
\begin{code}
module _ where
 open 𝕃
 open import Data.List.Sugar hiding ([_])
 open import Data.List.Syntax
 open import Data.List.Membership

 module BoolSlop where
\end{code}
%<*bool-slop>
\begin{code}
   ℰ!⟨2⟩ : ℰ! Bool
   ℰ!⟨2⟩ .fst = [ false , true , false ]
   ℰ!⟨2⟩ .snd false  = 0  , refl
   ℰ!⟨2⟩ .snd true   = 1  , refl
\end{code}
%</bool-slop>
\begin{code}
\end{code}
%<*bool-rev>
\begin{code}
 ℰ!⟨2⟩′ : ℰ! Bool
 ℰ!⟨2⟩′ .fst = [ true , false ]
 ℰ!⟨2⟩′ .snd false  = 1  , refl
 ℰ!⟨2⟩′ .snd true   = 0  , refl
\end{code}
%</bool-rev>
%<*bool-inst>
\begin{code}
 ℰ!⟨2⟩ : ℰ! Bool
 ℰ!⟨2⟩ .fst = [ false , true ]
 ℰ!⟨2⟩ .snd false  = 0  , refl
 ℰ!⟨2⟩ .snd true   = 1  , refl
\end{code}
%</bool-inst>
%<*top-bot-inst>
\begin{code}
 ℰ!⟨⊤⟩ : ℰ! 
 ℰ!⟨⊤⟩ .fst = [ tt ]
 ℰ!⟨⊤⟩ .snd _ = 0 , refl

 ℰ!⟨⊥⟩ : ℰ! 
 ℰ!⟨⊥⟩ = [] , λ ()
\end{code}
%</top-bot-inst>
%<*sup-sigma>
\begin{code}
 sup-Σ :  List A 
          ((x : A)  List (U x)) 
          List (Σ A U)
 sup-Σ xs ys = do  x  xs
                   y  ys x
                   [ x , y ]
\end{code}
%</sup-sigma>
\begin{code}
 cov-Σ : (x : A)
        (y : U x)
        (xs : List A)
        (ys :  x  List (U x))
        x  xs
        y  ys x
        (x , y)  sup-Σ xs ys
 cov-Σ xᵢ yᵢ (x  xs) ys (fs n , x∈xs) y∈ys =
   map (x ,_) (ys x) ++◇ cov-Σ xᵢ yᵢ xs ys (n , x∈xs) y∈ys
 cov-Σ xᵢ yᵢ (x  xs) ys (f0  , x∈xs) y∈ys =
   subst  x′  (xᵢ , yᵢ)  sup-Σ (x′  xs) ys) (sym x∈xs)
   (map (xᵢ ,_) (ys xᵢ) ◇++ cong-∈ (xᵢ ,_) (ys xᵢ) y∈ys)
\end{code}
%<*split-enum-sigma>
\begin{code}
 _|Σ|_ : ℰ! A  (∀ x  ℰ! (U x))  ℰ! (Σ A U)
\end{code}
%</split-enum-sigma>
\begin{code}
 (xs |Σ| ys) .fst = sup-Σ (xs .fst) (fst  ys)
 (xs |Σ| ys) .snd (x , y) = cov-Σ x y (xs .fst) (fst  ys) (xs .snd x) (ys x .snd y)

 ℰ!⟨Fin⟩ :  {n}  ℰ! (Fin n)
 ℰ!⟨Fin⟩ .fst = tabulate _ id
 ℰ!⟨Fin⟩ .snd = fin∈tabulate id

 _|×|_ : ℰ! A  ℰ! B  ℰ! (A × B)
 xs |×| ys = xs |Σ| const ys

 _|⊎|_ : ℰ! A  ℰ! B  ℰ! (A  B)
 (xs |⊎| ys) .fst = map inl (xs .fst) ++ map inr (ys .fst)
 (xs |⊎| ys) .snd (inl x) = map inl (xs .fst) ◇++ cong-∈ inl (xs .fst) (xs .snd x)
 (xs |⊎| ys) .snd (inr y) = map inl (xs .fst) ++◇ cong-∈ inr (ys .fst) (ys .snd y)

 _|+|_ : ℰ! A  ℰ! B  ℰ! (Σ[ t  Bool ] (if t then A else B))
 xs |+| ys = ℰ!⟨2⟩ |Σ| bool ys xs

 module TupleUniverseMonomorphic where
   open import Data.Tuple.UniverseMonomorphic

   ℰ!⟨Tuple⟩ :  {n} {U : Fin n  Type₀}  (∀ i  ℰ! (U i))  ℰ! (Tuple n U)
   ℰ!⟨Tuple⟩ {n = zero}  f = (_  []) , λ _  f0 , refl
   ℰ!⟨Tuple⟩ {n = suc n} f = f f0 |×| ℰ!⟨Tuple⟩ (f  fs)

 open import Data.Tuple

 ℰ!⟨Tuple⟩ :  {n u} {U : Lift a (Fin n)  Type u}  (∀ i  ℰ! (U i))  ℰ! (Tuple n U)
 ℰ!⟨Tuple⟩ {n = zero}  f = (_  []) , λ _  f0 , refl
 ℰ!⟨Tuple⟩ {n = suc n} f = f (lift f0) |×| ℰ!⟨Tuple⟩ (f  lift  fs  lower)

 ℰ!⟨Lift⟩ : ℰ! A  ℰ! (Lift b A)
 ℰ!⟨Lift⟩ xs .fst = map lift (xs .fst)
 ℰ!⟨Lift⟩ xs .snd x = cong-∈ lift (xs .fst) (xs .snd (x .lower))

 ℰ!⟨≡⟩ : (x y : A)  ℰ! A  ℰ! (x  y)
 ℰ!⟨≡⟩ x y e with ℰ!⇒Discrete (𝕃⇔ℒ⟨ℰ!⟩ .fun e) x y
 ℰ!⟨≡⟩ x y e | yes p = (p  []) , λ q  (f0 , Discrete→isSet (ℰ!⇒Discrete (𝕃⇔ℒ⟨ℰ!⟩ .fun e)) x y p q)
 ℰ!⟨≡⟩ x y e | no ¬p = [] , (⊥-elim  ¬p)

 open import Data.List.Filter
 open import Cardinality.Finite.SplitEnumerable.Inductive

 module _ {p} {P : A  Type p} where

\end{code}
%<*subobject>
\begin{code}
  filter-subobject :
    (∀ x  isProp (P x)) 
    (∀ x  Dec (P x)) 
    ℰ! A 
    ℰ! (Σ[ x  A ] P x)
\end{code}
%</subobject>
\begin{code}
  filter-subobject isPropP P? xs .fst = filter P? (xs .fst)
  filter-subobject isPropP P? xs .snd (x , v) = filter-preserves isPropP P? (xs .fst) x v (xs .snd x)
\end{code}