{-# OPTIONS --safe #-}

--------------------------------------------------------------------------------
-- Quotients over vectors.
--
-- One of the main tricks in order to implement free monad quotients is lifting
-- set quotients over vectors.
--
-- If we have a function
--
--   A → Syntax B / _~ₜ_
--
-- In order to implement bind, we might want a way to change that to:
--
--   (A → Syntax B) / _~_
--
-- This isn't usually possible, but we can restrict the parameters a little to
-- allow it.
--
--   Finite A ⟹ ∃ n × A ≃ Fin n
--
--   Fin n → (Syntax B / _∼ₜ_)
--            ≃
--    Vec (Syntax B / _~ₜ_) n
--
-- We can traverse over this vector to extract the quotient.
--------------------------------------------------------------------------------

open import Prelude
open import Data.Vec
open import FinitenessConditions

module Data.Vec.SetQuotients where

private
  variable
    n m : 
    _~_ : A  A  Type b

open SplitQuotientedChoiceAt

--------------------------------------------------------------------------------
-- A relation on the contents of a vector can be lifted to a relation on the
-- vector itself. Here we add reflexivity to the underlying relation, which is
-- necessary to implement traverse below.
--------------------------------------------------------------------------------

module FinInst where
  infixr 5 _◂~_
  _◂~_ : A / _~_  Vec A n / Pointwise _~_  Vec A (suc n) / Pointwise _~_
  _◂~_ =
    rec2/
      squash/
       x xs  [ x  xs ])
       x y xs p  eq/ _ _  λ { zero  eq/ x y p ; (suc i)  refl })
      λ x ys zs p  eq/ _ _ λ { zero  refl ; (suc i)  p i}

  finTrav : Vec (A / _~_) n  Vec A n / Pointwise _~_
  finTrav {A = A} {_~_ = _~_} {n = n} = vec-foldr  n  Vec _ n / Pointwise _~_) _◂~_  [ ⟅⟆ ]

  fin-SplitQuotientedChoice : SplitQuotientedChoice (Fin n) b c
  fin-SplitQuotientedChoice {B = A} {_~_} .undistrib xs = finTrav xs , funExt (lemma xs)
    where
    lemma : (x : Vec (A / _~_) n) (i : Fin n)  dist (finTrav x) i  x i
    lemma {n = .(suc _)} x zero    = 
      elimProp2/
        {P = λ xz xs  xz  x zero  dist  (xz ◂~ xs) zero  x zero }
         _ _  isProp→ (squash/ _ _))
         xz xs xzp  xzp)
        (x zero)
        (finTrav (x  suc))
        refl 
    lemma {n = .(suc _)} x (suc i) =
      elimProp2/
        {P = λ xz xs  xs  finTrav (x  suc)  dist  (xz ◂~ xs) (suc i)  x (suc i) }
         _ _  isProp→ (squash/ _ _))
         xz xs xsp  cong  k  dist  k i) xsp ; lemma (x  suc) i)
        (x zero)
        (finTrav (x  suc))
        refl 
open FinInst using (fin-SplitQuotientedChoice) public