\begin{code}
{-# OPTIONS --cubical --safe --postfix-projections #-}

open import Relation.Binary
open import Prelude hiding (tt)

module Data.List.Sort {e} {E : Type e} {r} (totalOrder : TotalOrder E r) where

open import Relation.Binary.Construct.LowerBound totalOrder

open TotalOrder totalOrder renaming (refl to refl-≤)
open TotalOrder lb-ord renaming (trans to ⌊trans⌋) using ()

open import Data.List
open import Data.Unit.UniversePolymorphic as Poly using (tt)
open import Data.List.Relation.Binary.Permutation
open import Function.Isomorphism
open import Data.Fin
open import Data.List.Membership
\end{code}
%<*insert>
\begin{code}
insert : E  List E  List E
insert x [] = x  []
insert x (y  xs) with x ≤? y
... | inl  x≤y  = x   y  xs
... | inr  y≤x  = y   insert x xs
\end{code}
%</insert>
%<*insertion-sort>
\begin{code}
sort : List E  List E
sort []        = []
sort (x  xs)  = insert x (sort xs)
\end{code}
%</insertion-sort>
\begin{code}

private variable lb : ⌊∙⌋

Sorted-cons : E  (⌊∙⌋  Type r)  ⌊∙⌋  Type r
Sorted-cons x xs lb = (lb ≤⌊ x ) × xs  x 

SortedFrom : ⌊∙⌋  List E  Type r
SortedFrom = flip (foldr Sorted-cons (const Poly.⊤))

Sorted : List E  Type r
Sorted = SortedFrom ⌊⊥⌋

insert-sorts :  x xs  lb ≤⌊ x   SortedFrom lb xs  SortedFrom lb (insert x xs)
insert-sorts x [] lb≤x Pxs = lb≤x , tt
insert-sorts x (y  xs) lb≤x (lb≤y , Sxs) with x ≤? y
... | inl x≤y = lb≤x , x≤y , Sxs
... | inr y≤x = lb≤y , insert-sorts x xs y≤x Sxs
\end{code}
%<*sort-sorts>
\begin{code}
sort-sorts :  xs  Sorted (sort xs)
\end{code}
%</sort-sorts>
\begin{code}
sort-sorts [] = tt
sort-sorts (x  xs) = insert-sorts x (sort xs) tt (sort-sorts xs)

insert-perm :  x xs  insert x xs  x  xs
insert-perm x [] = reflₚ
insert-perm x (y  xs) with x ≤? y
... | inl x≤y = consₚ x reflₚ
... | inr y≤x = consₚ y (insert-perm x xs)  transₚ  swapₚ y x xs

\end{code}
%<*sort-perm>
\begin{code}
sort-perm :  xs  sort xs  xs
\end{code}
%</sort-perm>
\begin{code}
sort-perm [] = reflₚ {xs = []}
sort-perm (x  xs) = insert-perm x (sort xs)  transₚ {xs = insert x (sort xs)}  consₚ x (sort-perm xs)

ord-in :  x xs  SortedFrom lb xs  x  xs  lb ≤⌊ x 
ord-in {lb = lb} x (x₁  xs) p (f0 , x∈xs) = subst (lb ≤⌊_⌋) x∈xs (p .fst)
ord-in {lb} x (y  xs) p (fs n , x∈xs) = ⌊trans⌋ {lb} (p .fst) (ord-in x xs (p .snd) (n , x∈xs))

perm-head :  {lbˣ lbʸ} x xs y ys  SortedFrom lbˣ (x  xs)  SortedFrom lbʸ (y  ys)  (x  xs  y  ys)  x  y
perm-head x xs y ys Sxs Sys xs⇔ys with xs⇔ys _ .inv (f0 , refl)
... | f0  , y∈xs = y∈xs
... | fs n , y∈xs with xs⇔ys _ .fun (f0 , refl)
... | f0  , x∈ys = sym x∈ys
... | fs m , x∈ys = antisym (ord-in y xs (Sxs .snd) (n , y∈xs)) (ord-in x ys (Sys .snd) (m , x∈ys))

perm-same :  {lbˣ lbʸ} xs ys  SortedFrom lbˣ xs  SortedFrom lbʸ ys  xs  ys  xs  ys
perm-same [] [] Sxs Sys xs⇔ys = refl
perm-same [] (y  ys) Sxs Sys xs⇔ys = ⊥-elim (xs⇔ys _ .inv (f0 , refl) .fst)
perm-same (x  xs) [] Sxs Sys xs⇔ys = ⊥-elim (xs⇔ys _ .fun (f0 , refl) .fst)
perm-same {lbˣ} {lbʸ} (x  xs) (y  ys) Sxs Sys xs⇔ys =
  let h = perm-head {lbˣ} {lbʸ} x xs y ys Sxs Sys xs⇔ys
  in cong₂ _∷_ h
      (perm-same xs ys (Sxs .snd) (Sys .snd) (tailₚ x xs ys (subst  y′  x  xs  y′  ys) (sym h) xs⇔ys)))
\end{code}
%<*sorted-perm-eq>
\begin{code}
sorted-perm-eq :  xs ys  Sorted xs  Sorted ys  xs  ys  xs  ys
\end{code}
%</sorted-perm-eq>
\begin{code}
sorted-perm-eq = perm-same
\end{code}
%<*perm-invar>
\begin{code}
perm-invar :  xs ys  xs  ys  sort xs  sort ys
\end{code}
%</perm-invar>
\begin{code}
perm-invar xs ys xs⇔ys =
  perm-same
    (sort xs)
    (sort ys)
    (sort-sorts xs)
    (sort-sorts ys)
     k  sort-perm xs k  trans-⇔  xs⇔ys k  trans-⇔  sym-⇔ (sort-perm ys k))
\end{code}