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

module Cardinality.Infinite.Split where

open import Prelude
open import Data.List.Kleene
open import Data.Fin
import Data.Nat as 
open import Data.Nat using (_+_)
open import Cubical.Data.Sigma.Properties
open import Cubical.Foundations.Prelude using (J)
import Data.List.Kleene.Membership as Kleene
open import Codata.Stream
open import Data.Sigma.Properties

private
  variable
    u : Level
    U : A  Type u
    s : Level
    S :   Type s
ℵ! : Type a  Type a
\end{code}
%<*split-count>
\begin{code}
ℵ! A = Σ[ xs  Stream A ] ((x : A)  x  xs)
\end{code}
%</split-count>
%<*split-surj>
\begin{code}
ℵ!⇔ℕ↠! : ℵ! A  ( ↠! A)
ℵ!⇔ℕ↠! = refl
\end{code}
%</split-surj>
\begin{code}

-- module _ where
--   open import Cardinality.Finite.SplitEnumerable.Inductive



--   ℰ!⇒ℵ! : ℰ! A → ℵ! A
--   ℰ!⇒ℵ! ℰ!⟨A⟩ .fst x = {!!}
--   ℰ!⇒ℵ! ℰ!⟨A⟩ .snd = {!!}

infixl 6 _*_ _*⋆_[_]
mutual
\end{code}
%<*sigma-sup>
\begin{code}
  _*⋆_[_] : Stream A  (∀ x  Stream (U x))  Stream (Σ A U )
  xs *⋆ ys [ zero   ] = []
  xs *⋆ ys [ suc n  ] =  (xs * ys) n

  _*_ : Stream A  (∀ x  Stream (U x))  Stream (Σ A U )
  (xs * ys) n .head  = let x = xs 0 in x , ys x n
  (xs * ys) n .tail  = (xs  suc) *⋆ ys [ n ]
\end{code}
%</sigma-sup>
\begin{code}
cantor : Stream A  (∀ x  Stream (U x))  Stream (Σ A U)
cantor xs ys = concat (xs * ys)

*-cover :  (x : A) xs (y : U x) (ys :  x  Stream (U x))  x  xs  y  ys x  (x , y) ∈² xs * ys
*-cover {U = U} x xs y ys (n , x∈xs) (m , y∈ys) = (n + m) , lemma xs n x∈xs
  where
  lemma :  xs n  xs n  x  (x , y) Kleene.∈⁺ (xs * ys) (n + m)
  lemma xs zero    x∈xs .fst = f0
  lemma xs zero    x∈xs .snd i .fst = x∈xs i
  lemma xs zero    x∈xs .snd i .snd = J  z z≡  PathP  j  U (sym z≡ j)) (ys z m) y) y∈ys (sym x∈xs) i
  lemma xs (suc n) x∈xs = let i , p = lemma (xs  suc) n x∈xs in fs i , p

_|Σ|_ : ℵ! A  (∀ x  ℵ! (U x))  ℵ! (Σ A U)
(xs |Σ| ys) .fst = cantor (xs .fst) (fst  ys)
(xs |Σ| ys) .snd (x , y) =
  concat-∈
    (x , y)
    (xs .fst * (fst  ys))
    (*-cover x (xs .fst) y (fst  ys) (xs .snd x) (ys x .snd y))

open import Data.Nat using (_+_)

infixl 6 _∔_
_∔_ :     
zero    m = m
suc n   m = n  suc m

∔-suc :  n m  suc n  m  suc (n  m)
∔-suc zero    m = refl
∔-suc (suc n) m = ∔-suc n (suc m)

∔0 :  n  n  zero  n
∔0 zero    = refl
∔0 (suc n) = ∔-suc n 0 ; cong suc (∔0 n)

module _ (xs : Stream A) where
  mutual
    star⁺ : (A   B)  B   Stream (B )
    star⁺ k ks zero    = k [] & ks
    star⁺ k ks (suc i) = plus⁺ zero (k  ∹_) ks i

    plus⋆ :   (A   B)  B   Stream (B )
    plus⋆ n k ks zero    = ks
    plus⋆ n k ks (suc i) =  plus⁺ n k ks i

    plus⁺ :   (A   B)  B   Stream (B )
    plus⁺ n k ks i = star⁺ (k  (xs n &_)) (plus⋆ (suc n) k ks i) i

  star : Stream (A )
  star = concat (star⁺ id [])

  plus : Stream (A )
  plus = concat (plus⁺ zero id [])

  module _ (cover :  x  x  xs) where
    dist : A   
    dist = foldr⋆  y ys  suc (cover y .fst + ys)) zero

    mutual
      star⁺-cover : (k : A   B)  (ks : B )   x  k x Kleene.∈⁺ star⁺ k ks (dist x)
      star⁺-cover k ks [] = f0 , refl
      star⁺-cover k ks ( x ) = plus⁺-cover (k  ∹_) ks x

      plus⁺-cover :  (k : A   B) ks   x  k x Kleene.∈⁺ plus⁺ zero k ks (cover (head x) .fst + dist (tail x))
      plus⁺-cover k ks (x & xxs) =
        let n , p = cover x
            m , q = plus⁺-dist n (k ) ks xxs
            z = m , q ; cong (k  (_& xxs)) p
        in plus⁺-shift zero (dist xxs) n k ks (x & xxs) (subst  s  k (x & xxs) Kleene.∈⁺ plus⁺ s k ks (dist xxs)) (sym (∔0 (cover x .fst))) z)

      plus⁺-dist :  n (k : A   B) ks   xxs  k (xs n & xxs) Kleene.∈⁺ plus⁺ n k ks (dist xxs)
      plus⁺-dist n k ks xxs = star⁺-cover (k  _&_ (xs n)) (plus⋆ (suc n) k ks (dist xxs)) xxs

      plus⁺-run :  n i (k : A   B) ks   xxs  xxs Kleene.∈⋆ ks  xxs Kleene.∈⁺ plus⁺ n k ks i
      plus⁺-run n zero    k ks xxs (m , p) = fs m , p
      plus⁺-run n (suc i) k ks xxs =
        plus⁺-run zero i (k  (xs n &_)  ∹_) (plus⋆ (suc n) k ks (suc i)) xxs 
        plus⁺-run (suc n) i k ks xxs

      plus⁺-shift :  i d n (k : A   B) (ks : B )   xxs  k xxs Kleene.∈⁺ plus⁺ (n  i) k ks d  k xxs Kleene.∈⁺ plus⁺ i k ks (n + d)
      plus⁺-shift i d zero    k ks xxs p = p
      plus⁺-shift i d (suc n) k ks xxs p = plus⁺-run zero (n + d)  z  k (xs i &  z)) ( plus⁺ (suc i) k ks (n + d)) (k xxs) (plus⁺-shift (suc i) d n k ks xxs p)

    star-cover :  x  x  star
    star-cover x = concat-∈ x (star⁺ id []) (dist x , star⁺-cover id [] x)

    plus-cover :  x  x  plus
    plus-cover x = concat-∈ x (plus⁺ zero id []) (cover (head x) .fst + dist (tail x) , plus⁺-cover id [] x)

|star| : ℵ! A  ℵ! (A )
|star| xs .fst = star (xs .fst)
|star| xs .snd = star-cover (xs .fst) (xs .snd)

|plus| : ℵ! A  ℵ! (A )
|plus| xs .fst = plus (xs .fst)
|plus| xs .snd = plus-cover (xs .fst) (xs .snd)

open import Data.Bool using (not; bool)

x≢¬x :  x  x  not x
x≢¬x false p = subst (bool  ) p tt
x≢¬x true  p = subst (bool  ) p tt
\end{code}
%<*cantor-diag>
\begin{code}
cantor-diag : ¬ ℵ! (  Bool)
cantor-diag (sup , cov) =
  let n , p = cov  n  not (sup n n))
  in x≢¬x _ (cong (_$ n) p)
\end{code}
%</cantor-diag>
\begin{code}

 : Type a  Type a
 A =  ℵ! A 

open import Function.Surjective.Properties
open import Data.Nat.Properties using (discreteℕ)
open import HITs.PropositionalTruncation
open import Relation.Nullary.Discrete.Properties

ℵ!⇒Discrete : ℵ! A  Discrete A
ℵ!⇒Discrete xs = Discrete-distrib-surj xs discreteℕ

ℰ⇒Discrete :  A  Discrete A
ℰ⇒Discrete = rec isPropDiscrete ℵ!⇒Discrete
\end{code}