{-# OPTIONS --safe #-}

module Multiset.Bag.Base where

open import Multiset.Bij as Bij

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Structure
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Function using (_∘_)

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism

open import Cubical.Functions.FunExtEquiv using (funExtDep)

open import Cubical.Data.FinSet using (FinSet)
open import Cubical.Data.Sigma as Σ
open import Cubical.Data.Nat as 
  using ()

-- ⚠ WARNING: Load-bearing abstract block ⚠
--
-- Agda should under no circumstances be given the chance to
-- reduce the definition below.  For variables (x : Bij), the
-- term below *will* make Agda v2.6.2.2 loop forever.  Or at
-- least the last time it went on for more than 2h on a laptop.
--
-- The results at the end of this module, especially naturality
-- (isNaturalBagToteIso), can see through this abstract block.
-- They are defined carefully to not exhibit the loopy behaviour.
--
-- In a perfect world, unfolding would be controlled at use-site
-- (*ahem*, [1]), and these results could live in a module that
-- is not `Bag.Base`.
--
-- [1]: https://github.com/agda/agda/pull/6354
abstract
  Idx : Bij  Type ℓ-zero
  Idx = λ x   Bij→FinSet x 

  IdxEquiv : (x : Bij)  (Idx x)   Bij→FinSet x 
  IdxEquiv x = idEquiv  Bij→FinSet x 

Vect :  {}  Type   Bij  Type 
Vect X k = Idx k  X

module _ {} {X : Type } where
  VectIso : (x : Bij)  Iso (Vect X x) ( equivFun Bij≃FinSet x   X)
  VectIso x = equiv→Iso (IdxEquiv x) (idEquiv X)

  isOfHLevelVect :  {k} (n : )  isOfHLevel n X  isOfHLevel n (Vect X k)
  isOfHLevelVect n hLevel = isOfHLevelΠ n  idx  hLevel)

Bag :  {}  Type   Type 
Bag X = Σ[ k  Bij ] Vect X k

module _ {} {X : Type } where
  BagPathP :  {m n : Bij}
     (p : m  n)
     {v : Vect X m}
     {w : Vect X n}
     (q : PathP  i  Vect X (p i)) v w)
     Path (Bag X) (m , v) (n , w)
  BagPathP p q i = p i , q i

  BagPathPExt :  {m n : Bij}
     (p : m  n)
     {v : Vect X m}
     {w : Vect X n}
     ({idx₀ : Idx m} {idx₁ : Idx n} (q : PathP  i  Idx (p i)) idx₀ idx₁)  v idx₀  w idx₁)
     Path (Bag X) (m , v) (n , w)
  BagPathPExt p q = BagPathP p (funExtDep q)

  isGroupoidBag : isGroupoid X  isGroupoid (Bag X)
  isGroupoidBag gpdX = isGroupoidΣ isGroupoidBij  k  isOfHLevelVect {k = k} 3 gpdX)

private
  variable
    ℓX ℓY ℓZ : Level
    X : Type ℓX
    Y : Type ℓY
    Z : Type ℓZ

map : (f : X  Y)  (Bag X  Bag Y)
map f (k , members) = (k , f  members)

mapId : (xs : Bag X)  map  x  x) xs  xs
mapId xs = refl

map∘map : (f : X  Y) (g : Y  Z)
   (xs : Bag X)
   map g (map f xs)  map (g  f) xs
map∘map f g xs = refl

open Iso
open import Multiset.Tote.Base as Tote using (Tote)

abstract
  Bag-Tote-Iso : Iso (Bag X) (Tote X)
  Bag-Tote-Iso = Σ-cong-iso (equivToIso Bij≃FinSet) VectIso

Bag≃Tote : Bag X  Tote X
Bag≃Tote = isoToEquiv Bag-Tote-Iso

isNaturalBagToteEquiv : (f : X  Y)  equivFun Bag≃Tote  map f  Tote.map f  equivFun Bag≃Tote
abstract
  isNaturalBagToteEquiv f = refl