{-# OPTIONS --safe #-}
module Multiset.FMSet.FiniteFinality where
open import Cubical.Foundations.Everything
open import Multiset.Prelude
open import Multiset.Util.SetTruncation using (setTruncEquiv)
open import Multiset.Tote as Tote
open import Multiset.FMSet as FMSet
open import Multiset.Bij
open import Multiset.Bag.Base as Bag
  using
    ( Bag
    )
open import Multiset.Bag.Properties as Bag
  using (BagLim ; bagLimitEquiv; isGroupoidBagLim)
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.Properties using (preCompEquiv)
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Structure
open import Cubical.Data.Nat
open import Cubical.Data.Unit
open import Cubical.Data.Sigma
open import Cubical.HITs.PropositionalTruncation as PT
open import Cubical.HITs.SetTruncation as ST 
open import Cubical.HITs.SetQuotients as SQ
open import Cubical.Data.Sigma.Properties using (Σ-cong-equiv)
open import Multiset.FMSet.Finality
open import Multiset.FiniteChoice
open import Cubical.Data.SumFin
module FinalityFinite
  (ana : {X : Type} → (c : X → Bag X) → X → BagLim)
  (anaEq : {X : Type} (c : X → Bag X)
    → ana c ≡ Bag.fix⁺ ∘ Bag.map (ana c) ∘ c)
  (anaUniq : {X : Type} (c : X → Bag X)
    → (h : X → BagLim)
    → h ≡ Bag.fix⁺ ∘ (Bag.map h) ∘ c
    → ana c ≡ h)
  (e : {Y : Type} → ∥ Bag Y ∥₂ ≃ FMSet Y)
  (eNat : ∀{Y Z} (f : Y → Z)→ equivFun e ∘ ST.map (Bag.map f) ≡ FMSet.map f ∘ equivFun e)
  where
  unfold : FMSet ∥ BagLim ∥₂ ≃ ∥ BagLim ∥₂
  unfold = compEquiv STInvariance.STInvarianceEquiv (compEquiv (invEquiv e) (setTruncEquiv bagLimitEquiv))
  unfoldEq : compEquiv (invEquiv (STInvariance.STInvarianceEquiv)) unfold ≡ compEquiv (invEquiv e) (setTruncEquiv bagLimitEquiv)
  unfoldEq =
    compEquiv-assoc _ _ _
    ∙ cong (λ x → compEquiv x (compEquiv (invEquiv e) (setTruncEquiv bagLimitEquiv))) (invEquiv-is-linv _)
    ∙ compEquivIdEquiv _
  
  open import Multiset.Categories.Coalgebra
  coalg : Coalgebra FMSetFunctor
  coalg .Coalgebra.carrier = ∥ BagLim ∥₂ , isSetSetTrunc
  coalg .Coalgebra.str = invEq unfold
  module _ {n : ℕ} where
    ana₂' : (c : Fin n → Bag (Fin n)) → Fin n → ∥ BagLim ∥₂
    ana₂' c = ∣_∣₂ ∘ ana c
    ana₂ : (c : Fin n → FMSet (Fin n)) → Fin n → ∥ BagLim ∥₂
    ana₂ c = recₙ (isSetΠ (λ _ → isSetSetTrunc)) ana₂' (invEq e ∘ c)
    ana₂Eq : (c : Fin n → FMSet (Fin n)) 
      → ana₂ c ≡ equivFun unfold ∘ FMSet.map (ana₂ c) ∘ c
    ana₂Eq c =
      elimₙ {B = λ c → recₙ (isSetΠ (λ _ → isSetSetTrunc)) ana₂' c ≡ equivFun unfold ∘ FMSet.map (recₙ (isSetΠ (λ _ → isSetSetTrunc)) ana₂' c) ∘ equivFun e ∘ c}
            (λ c → isProp→isSet (λ x y i j k → isSetSetTrunc _ _ (λ i → x i k) (λ i → y i k) i j))
            (λ c → elimₙ-comp {B = λ _ → Fin n → ∥ BagLim ∥₂} (λ _ → isSetΠ (λ _ → isSetSetTrunc)) ana₂' c
                    ∙ cong (∣_∣₂ ∘_) (anaEq c)
                    ∙ cong (λ x → ST.map Bag.fix⁺ ∘ x ∘ ST.map (Bag.map (ana c)) ∘ ∣_∣₂ ∘ c) (sym (funExt (retEq e)))
                    ∙ cong (λ x → ST.map Bag.fix⁺ ∘ invEq e ∘ x ∘ ∣_∣₂ ∘ c) (eNat (ana c))
                    ∙ cong (λ x → x ∘ FMSet.map (ana c) ∘ equivFun e ∘ ∣_∣₂ ∘ c) (sym (cong equivFun unfoldEq))
                    ∙ cong (λ x → equivFun unfold ∘ x ∘ equivFun e ∘ ∣_∣₂ ∘ c) (funExt (mapComp ∣_∣₂ (ana c))) 
                    ∙ cong (λ x → equivFun unfold ∘ FMSet.map x ∘ equivFun e ∘ ∣_∣₂ ∘ c) (sym (elimₙ-comp {B = λ _ → Fin n → ∥ BagLim ∥₂} (λ _ → isSetΠ (λ _ → isSetSetTrunc)) ana₂' c)))
            (invEq e ∘ c)
      ∙ cong (λ x → equivFun unfold ∘ FMSet.map (ana₂ c) ∘ x ∘ c) (funExt (secEq e))
    ana₂Uniq : (c : Fin n → FMSet (Fin n))
      → (h : Fin n → ∥ BagLim ∥₂)
      → h ≡ equivFun unfold ∘ FMSet.map h ∘ c
      → ana₂ c ≡ h
    ana₂Uniq c h eq =
      elim2ₙ {B = λ c h → h ≡ equivFun unfold ∘ FMSet.map h ∘ equivFun e ∘ c → recₙ (isSetΠ (λ _ → isSetSetTrunc)) ana₂' c ≡ h}
            (λ _ _ → isSetΠ (λ _ → isProp→isSet (λ x y i j k → isSetSetTrunc _ _ (λ i → x i k) (λ i → y i k) i j)))
            (λ c h eq → let eq' = eq
                                  ∙ cong (λ x → equivFun unfold ∘ x ∘ equivFun e ∘ ∣_∣₂ ∘ c) (sym (funExt (FMSet.mapComp ∣_∣₂ h)))
                                  ∙ cong (λ x → x ∘ FMSet.map h ∘ equivFun e ∘ ∣_∣₂ ∘ c) (cong equivFun unfoldEq)
                                  ∙ cong (λ x → ST.map Bag.fix⁺ ∘ invEq e ∘ x ∘ ∣_∣₂ ∘ c) (sym (eNat h))
                                  ∙ cong (λ x → ST.map Bag.fix⁺ ∘ x ∘ ST.map (Bag.map h) ∘ ∣_∣₂ ∘ c) (funExt (retEq e)) in elimₙ-comp {B = λ _ → Fin n → ∥ BagLim ∥₂} (λ _ → isSetΠ (λ _ → isSetSetTrunc)) ana₂' c ∙ funExt (Iso.inv PathIdTrunc₀Iso ∘ unbox₁ (PT.map (λ r → funExt⁻ (anaUniq c h (funExt r))) (box₁ (Iso.fun PathIdTrunc₀Iso ∘ funExt⁻ eq')))))
            (invEq e ∘ c)
            h
            (eq ∙ cong (λ x → equivFun unfold ∘ FMSet.map h ∘ x ∘ c) (sym (funExt (secEq e))))
    isContrAna : (c : Fin n → FMSet (Fin n))
      → isContr (Σ[ h ∈ (Fin n → ∥ BagLim ∥₂) ] h ≡ equivFun unfold ∘ FMSet.map h ∘ c)
    isContrAna c =
      ( (ana₂ c , ana₂Eq c) 
      , λ { (h , is-ana)
          → Σ≡Prop (λ c → isOfHLevelPath' 1 (isSetΠ λ _ → isSetSetTrunc) c _) (ana₂Uniq c h is-ana) 
        }
      )
  fin : ∀ {n} → (c : Fin n → FMSet (Fin n)) → Coalgebra FMSetFunctor
  fin {n = n} c = coalgebra {carrier = Fin n , isSetFin} c
  uniqueFiniteCoalg : (n : ℕ) → (c : Fin n → FMSet (Fin n))
    → isContr (CoalgebraHom FMSetFunctor (fin c) coalg)
  uniqueFiniteCoalg n c = ana₃ , CoalgebraHom≡ _ ∘ ana₃Eq where
    ana₃ : CoalgebraHom FMSetFunctor (fin c) coalg
    ana₃ .CoalgebraHom.carrierHom = ana₂ c
    ana₃ .CoalgebraHom.strHom = funExt λ x →
      invEq unfold (ana₂ c x)                                   ≡⟨ cong (invEq unfold) (funExt⁻ (ana₂Eq c) x) ⟩
      invEq unfold (equivFun unfold (FMSet.map (ana₂ c) (c x))) ≡⟨ retEq unfold _ ⟩∎
      FMSet.map (ana₂ c) (c x) ∎
    module _ ((coalgebraHom f f-hom) : CoalgebraHom _ (fin c) coalg) where
      isCoalg→isCorec : f ≡ equivFun unfold ∘ FMSet.map f ∘ c
      isCoalg→isCorec = funExt λ x →
        f x                                   ≡⟨ sym (secEq unfold (f x)) ⟩
        equivFun unfold (invEq unfold (f x))  ≡⟨ cong (equivFun unfold) (funExt⁻ f-hom x) ⟩∎
        equivFun unfold (FMSet.map f (c x))   ∎
      ana₃Eq : ana₂ c ≡ f
      ana₃Eq = ana₂Uniq c f isCoalg→isCorec