-- Taken from https://github.com/vikraman/generalised-species/
module Cubical.Structures.Set.CMon.SList.Length where

open import Cubical.Structures.Prelude

open import Cubical.Data.Empty as E
open import Cubical.Data.Nat
open import Cubical.Data.Prod
open import Cubical.Data.Sigma
open import Cubical.Data.Sum
open import Cubical.Data.Unit

open import Cubical.Foundations.Univalence

open import Cubical.Functions.Embedding

open import Cubical.Structures.Set.CMon.SList.Base as SList

private
  variable
     : Level
    A B : Type 
    ϕ : isSet A
    a x : A
    xs ys : SList A

disjNilCons : x  xs  [] -> 
disjNilCons p = transport  i  fst (code (p i))) tt
  where code : SList A -> hProp ℓ-zero
        code = Elim.f ( , isProp⊥)
           _ _ -> Unit , isPropUnit)
           _ _ _ _ -> Unit , isPropUnit)
           _ -> isSetHProp)

disjConsNil : []  x  xs -> 
disjConsNil p = disjNilCons (sym p)

length : SList A  
length = Elim.f 0  _ n -> suc n)  _ _ _ -> refl)  _ -> isSetℕ)

length++ : (x y : SList A)  length (x ++ y)  length x + length y
length++ x y = ElimProp.f {B = λ xs  length (xs ++ y)  length xs + length y}
  (isSetℕ _ _) refl  a p -> cong suc p) x

lenZeroOut : (xs : SList A)  length xs  0  []  xs
lenZeroOut = ElimProp.f (isPropΠ λ _  trunc _ _)
   _ -> refl)
   x p q -> E.rec (snotz q))

lenZeroEqv : (xs : SList A)  (length xs  0)  ([]  xs)
lenZeroEqv xs = propBiimpl→Equiv (isSetℕ _ _) (trunc _ _) (lenZeroOut xs)  p i  length (p (~ i)))

isSingPred : SList A  A  Type _
isSingPred xs a = [ a ]  xs

isSingPredProp : (xs : SList A) (z : A)  isProp (isSingPred xs z)
isSingPredProp xs a = trunc [ a ] xs

isSing : SList A  Type _
isSing xs = Σ _ (isSingPred xs)

Sing : Type   Type 
Sing A = Σ (SList A) isSing

[_]IsSing : (a : A)  isSing [ a ]
[ a ]IsSing = a , refl

sing=isProp :  {x y : A}  isProp ([ x ]  [ y ])
sing=isProp {x = x} {y = y} = trunc [ x ] [ y ]

singEqIn :  {x y : A}  x  y  [ x ]  [ y ]
singEqIn p i = [ p i ]

singEqIsContr :  {x y : A}  x  y  isContr ([ x ]  [ y ])
singEqIsContr p = singEqIn p , sing=isProp (singEqIn p)

lenOneDown : (x : A) (xs : SList A)  length (x  xs)  1  []  xs
lenOneDown x xs p = lenZeroOut xs (injSuc p)

lenOneCons : (x : A) (xs : SList A)  length (x  xs)  1  isSing (x  xs)
lenOneCons x xs p =
  let q = lenOneDown x xs p
  in transport  i  isSing (x  q i)) [ x ]IsSing

lenTwoBot : (x y : A) (xs : SList A)  (length (y  x  xs)  1)  
lenTwoBot x y xs =  p  E.rec (snotz (injSuc p))) , record { equiv-proof = E.elim }

isContrlenTwoBotToX : (X : Type ) (x y : A) (xs : SList A)  isContr (length (y  x  xs)  1  X)
isContrlenTwoBotToX X x y xs = subst  Z  isContr (Z  X)) (sym (ua (lenTwoBot x y xs))) (isContr⊥→A {A = X})

lenOneSwap : {A : Type } (x y : A) (xs : SList A)
             PathP  i  length (SList.swap x y xs i)  1  isSing (SList.swap x y xs i))
                     p  lenOneCons x (y  xs) p)
                     p  lenOneCons y (x  xs) p)
lenOneSwap {A = A} x y xs =
  transport (sym (PathP≡Path  i  length (SList.swap x y xs i)  1  isSing (SList.swap x y xs i)) _ _))
            (isContr→isProp (isContrlenTwoBotToX _ x y xs) _ _)

lenOne : Type   Type _
lenOne A = Σ (SList A)  xs  length xs  1)

module _ {ϕ : isSet A} where

  isSingSet : (xs : SList A)  isSet (isSing xs)
  isSingSet xs = isSetΣ ϕ λ x  isProp→isSet (isSingPredProp xs x)

  lenOneOut : (xs : SList A)  length xs  1  isSing xs
  lenOneOut = Elim.f  p  E.rec (znots p))
     x {xs} f p  lenOneCons x xs p)
     x y {xs} f  lenOneSwap x y xs)
    λ xs  isSetΠ  p  isSingSet xs)

  head : lenOne A  A
  head (xs , p) = lenOneOut xs p .fst

  -- not definitional due to lack of regularity
  headΒ : (a : A)  head ([ a ] , refl)  a
  headΒ a i = transp  _  A) i a

  lenOnePath : (a b : A)  Type _
  lenOnePath a b = Path (lenOne A) ([ a ] , refl) ([ b ] , refl)

  lenOnePathIn : {a b : A}  [ a ]  [ b ]  lenOnePath a b
  lenOnePathIn = Σ≡Prop  xs  isSetℕ _ _)

  [_]Inj : {a b : A}  [ a ]  [ b ]  a  b
  [_]Inj {a = a} {b = b} p = aux (lenOnePathIn p)
    where aux : lenOnePath a b  a  b
          aux p = sym (headΒ a)  cong head p  headΒ b

  isSingProp : (xs : SList A)  isProp (isSing xs)
  isSingProp xs (a , ψ) (b , ξ) = Σ≡Prop (isSingPredProp xs) ([_]Inj (ψ  sym ξ))

  lenOneEqv : (xs : SList A)  (length xs  1)  (isSing xs)
  lenOneEqv xs = propBiimpl→Equiv (isSetℕ _ _) (isSingProp xs) (lenOneOut xs)  χ  cong length (sym (χ .snd)))

  lenOneSetEqv : lenOne A  A
  lenOneSetEqv = isoToEquiv (iso head g headΒ g-f)
    where g : A  lenOne A
          g a = [ a ] , refl
          g-f : (as : lenOne A)  g (head as)  as
          g-f (as , ϕ) =
            let (a , ψ) = lenOneOut as ϕ
            in Σ≡Prop  xs  isSetℕ _ _) {u = ([ a ] , refl)} {v = (as , ϕ)} ψ

  singSetEqv : Sing A  A
  singSetEqv = isoToEquiv (iso f  a  [ a ] , [ a ]IsSing)  _  refl) ret)
    where f : Sing A  A
          f s = s .snd .fst
          ret : (s : Sing A)  ([ f s ] , f s , refl)  s
          ret (s , ψ) = Σ≡Prop isSingProp (ψ .snd)