{-# OPTIONS --safe #-}

module Multiset.ListQuotient.ToInjectivity where

open import Multiset.Prelude
open import Multiset.Util using (!_ ; isInjective ; isSurjective)
open import Multiset.ListQuotient.Base

open import Multiset.Limit.Chain using (Limit)
import      Multiset.Axioms.Completeness as Completeness
open import Multiset.Limit.TerminalChain as TerminalChain hiding (cut ; pres)

open import Multiset.Omniscience using (LLPO)

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

open import Cubical.Data.Unit as Unit using (Unit ; tt)
open import Cubical.Data.List as List hiding ([_])
open import Cubical.Data.Sigma as Sigma
open import Cubical.Data.Nat.Base hiding (_^_)

open import Cubical.Relation.Nullary

open import Cubical.HITs.PropositionalTruncation as PT
  using
    ( ∥_∥₁
    ; ∣_∣₁
    )
open import Cubical.HITs.PropositionalTruncation.Monad using (_>>=_ ; _>>_ ; return)

open import Cubical.HITs.SetQuotients as SQ

instance
  FunctorM : Functor M
  FunctorM .Functor.map = mapM
  FunctorM .Functor.map-id = mapM-id
  FunctorM .Functor.map-comp = mapM-comp

open Limit

decEqM^ : (n : )  Discrete (M ^ n)
decEqM^ zero xs ys = yes refl
decEqM^ (suc n) = decEqM (decEqM^ n) 

dec∈M^ : (n : ) (x : M ^ n) (ys : List (M ^ n))  Dec (x  ys)
dec∈M^ n x ys with dec∈ (decEqM^ n) x ys
... | yes (y , m , p) = yes (subst  z  z  ys) (sym p) m)
... | no ¬p = no  m  ¬p (x , m , refl))

isSetM^ :  n  isSet (M ^ n)
isSetM^ zero = Unit.isSetUnit*
isSetM^ (suc n) = isSetM

!^ :  n  M ^ (suc n)  M ^ n
!^ n = M map-!^ n

module _ where
  open Limit

  cut : (n : )  Lim M  M ^ n
  cut = TerminalChain.cut M

pres : M (Lim M)  ShLim M
pres = TerminalChain.pres M

module M-Completeness = Completeness (TerminalChain.ch M) decEqM^ isSetM^

Complete* : Type
Complete* = M-Completeness.Complete*

complete*⇒pres-inj : Complete*  isInjective pres
complete*⇒pres-inj complete* = pres-inj where
  pres-inj-drel : (xs ys : List (Lim M))
     (∀ n  DRelator _≡_ (List.map (cut n) xs) (List.map (cut n) ys))
     DRelator _≡_ xs ys
  pres-inj-drel [] ys drel = nil
  pres-inj-drel (x  xs) ys drel = goal where
    drel∃ :  n  ∃[ m  (cut n x  List.map (cut n) ys) ]
      DRelator _≡_ (List.map (cut n) xs)  (remove (List.map (cut n) ys) m)
    drel∃ n = do
      (y , xₙ≡y , r)  consInvDRelator (drel n)
      return $ subst
         y  Σ[ y∈ysₙ  (y  map (cut n) ys) ] DRelator _≡_ (map (cut n) xs) (remove (map (cut n) ys) y∈ysₙ))
        (sym xₙ≡y)
        r

    x∈ys-approx :  n   cut n x  map (cut n) ys ∥₁
    x∈ys-approx n = do
      (xₙ∈ysₙ , _)  drel∃ n
      return xₙ∈ysₙ

    ∥x∈ys∥ :  x  ys ∥₁
    ∥x∈ys∥ = complete* x ys x∈ys-approx

    goal* : x  ys  DRelator _≡_ (x  xs) ys
    goal* x∈ys = cons  x , (refl {x = x}) , x∈ys , ind ∣₁ where
      ys∖x = remove ys x∈ys

      ind-approx :  n  DRelator _≡_ (map (cut n) xs) (map (cut n) ys∖x)
      ind-approx n = equivFun (PT.propTruncIdempotent≃ (isPropDRelator _ _ _)) $ do
        (xₙ∈ysₙ , drel)  drel∃ n
        let ysₙ = map (cut n) ys
            xsₙ = map (cut n) xs

            xₙ∈ysₙ′ : cut n x  ysₙ
            xₙ∈ysₙ′ = ∈mapList x∈ys

            ysₙ∖xₙ  = remove ysₙ xₙ∈ysₙ
            ysₙ∖xₙ′ = remove ysₙ xₙ∈ysₙ′

            d₁ : DRelator _≡_ ysₙ∖xₙ ysₙ∖xₙ′
            d₁ = removeDRelator  _  refl) xₙ∈ysₙ xₙ∈ysₙ′

            d₂ : DRelator _≡_ xsₙ ysₙ∖xₙ
            d₂ = drel

            d : DRelator _≡_ xsₙ ysₙ∖xₙ′
            d = transDRelator _∙_ d₂ d₁

        let remove-path : remove ysₙ (∈mapList x∈ys)  map (cut n) ys∖x
            remove-path = sym (remove-mapList x∈ys)
        return $ subst (DRelator _≡_ (map (cut n) xs)) remove-path d

      ind : DRelator _≡_ xs ys∖x
      ind = pres-inj-drel _ _ ind-approx

    goal : DRelator _≡_ (x  xs) ys
    goal = PT.rec (isPropDRelator _ _ _) goal* ∥x∈ys∥

  cut-rel : {xs ys : List (Lim M)}
     (pres-≡ : pres [ xs ]  pres [ ys ])
      n  Relator _≡_ (map (cut n) xs) (map (cut n) ys)
  cut-rel {xs} {ys} pres-≡ n = effective (isPropRelator _≡_) (isEquivRelRelator isEquivRel≡) _ _ goal
    where
      goal : [ map (cut n) xs ]  [ map (cut n) ys ]
      goal = cong elements pres-≡ ≡$ n

  module _ (xs ys : List (Lim M)) (pres-≡ : pres [ xs ]  pres [ ys ]) where
    pres-inj-rel : Relator _≡_ xs ys
    pres-inj-rel .fst = pres-inj-drel xs ys (fst  cut-rel pres-≡)
    pres-inj-rel .snd = pres-inj-drel ys xs (snd  cut-rel pres-≡)

    pres-inj* : [ xs ]  [ ys ]
    pres-inj* = SQ.eq/ xs ys pres-inj-rel

  is-prop-pres-inj* : (x y : List (Lim M) / Relator _≡_)  isProp (pres x  pres y  x  y)
  is-prop-pres-inj* x y = isPropΠ λ _  isSetM x y

  pres-inj : isInjective pres
  pres-inj = SQ.elimProp2 is-prop-pres-inj* pres-inj*

llpo⇒complete* : LLPO  Complete*
llpo⇒complete* = M-Completeness.llpo⇒complete*

llpo⇒pres-inj : LLPO  isInjective pres
llpo⇒pres-inj = complete*⇒pres-inj  llpo⇒complete*