module Library.List.Permutation where
open import Library.Nat
open import Library.List
open import Library.List.Properties
open import Library.Logic
open import Library.Equality
infix 1 #begin_
infixr 2 _#⟨⟩_ _#⟨_⟩_
infix 3 _#end
infix 4 _#_
data _#_ {A : Set} : List A -> List A -> Set where
#refl : {xs : List A} -> xs # xs
#swap : {x y : A} {xs : List A} -> x :: y :: xs # y :: x :: xs
#cong : {x : A} {xs ys : List A} -> xs # ys -> x :: xs # x :: ys
#trans : {xs ys zs : List A} -> xs # ys -> ys # zs -> xs # zs
#symm : {A : Set} {xs ys : List A} -> xs # ys -> ys # xs
#symm #refl = #refl
#symm #swap = #swap
#symm (#cong ps) = #cong (#symm ps)
#symm (#trans ps qs) = #trans (#symm qs) (#symm ps)
#begin_ : {A : Set} {xs ys : List A} -> xs # ys -> xs # ys
#begin_ ps = ps
_#end : {A : Set} (xs : List A) -> xs # xs
_#end xs = #refl
_#⟨_⟩_ : {A : Set} (xs : List A) {ys zs : List A} -> xs # ys -> ys # zs -> xs # zs
_#⟨_⟩_ _ = #trans
_#⟨⟩_ : {A : Set} (xs : List A) {ys : List A} -> xs # ys -> xs # ys
_ #⟨⟩ ps = ps
#length : {A : Set} {xs ys : List A} -> xs # ys -> length xs == length ys
#length #refl = refl
#length #swap = refl
#length (#cong π) = cong succ (#length π)
#length (#trans π π') = tran (#length π) (#length π')
#push : {A : Set} {x : A} {xs ys : List A} -> x :: xs ++ ys # xs ++ x :: ys
#push {xs = []} = #refl
#push {xs = _ :: _} = #trans #swap (#cong #push)
#all : {A : Set} {xs ys : List A} (P : A -> Set) -> xs # ys -> All P xs -> All P ys
#all P #refl ps = ps
#all P #swap (px , py , ps) = py , px , ps
#all P (#cong π) (px , ps) = px , #all P π ps
#all P (#trans π π') ps = #all P π' (#all P π ps)
++-permutation : {A : Set} (xs ys : List A) -> xs ++ ys # ys ++ xs
++-permutation [] ys rewrite ++-unit-r ys = #refl
++-permutation (x :: xs) ys =
#begin
(x :: xs) ++ ys #⟨⟩
x :: xs ++ ys #⟨ #push ⟩
xs ++ x :: ys #⟨ ++-permutation xs (x :: ys) ⟩
(x :: ys) ++ xs #⟨⟩
x :: ys ++ xs #⟨ #push ⟩
ys ++ x :: xs
#end
#cong++r : {A : Set} {xs ys zs : List A} -> xs # ys -> zs ++ xs # zs ++ ys
#cong++r {zs = []} ps = ps
#cong++r {zs = x :: zs} ps = #cong (#cong++r ps)
#cong++l : {A : Set} {xs ys zs : List A} -> xs # ys -> xs ++ zs # ys ++ zs
#cong++l {_} {xs} {ys} {zs} π =
#begin
xs ++ zs #⟨ ++-permutation xs zs ⟩
zs ++ xs #⟨ #cong++r π ⟩
zs ++ ys #⟨ ++-permutation zs ys ⟩
ys ++ zs
#end
reverse-permutation : {A : Set} (xs : List A) -> reverse xs # xs
reverse-permutation [] = #refl
reverse-permutation (x :: xs) =
#begin
reverse (x :: xs) #⟨⟩
reverse xs ++ [ x ] #⟨ ++-permutation (reverse xs) [ x ] ⟩
[ x ] ++ reverse xs #⟨⟩
x :: reverse xs #⟨ #cong (reverse-permutation xs) ⟩
x :: xs
#end