------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of permutations using setoid equality
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

open import Relation.Binary

module Data.List.Relation.Binary.Permutation.Setoid.Properties
  {a } (S : Setoid a )
  where

open import Algebra
open import Data.List.Base as List
open import Data.List.Relation.Binary.Pointwise using (Pointwise)
import Data.List.Relation.Binary.Equality.Setoid as Equality
import Data.List.Relation.Binary.Permutation.Setoid as Permutation
open import Data.List.Relation.Unary.Any as Any using (Any; here; there)
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
open import Data.List.Relation.Unary.AllPairs using (AllPairs; []; _∷_)
import Data.List.Relation.Unary.Unique.Setoid as Unique
import Data.List.Membership.Setoid as Membership
open import Data.List.Membership.Setoid.Properties using (∈-∃++; ∈-insert)
open import Data.List.Relation.Binary.BagAndSetEquality
  using (bag; _∼[_]_; empty-unique; drop-cons; commutativeMonoid)
import Data.List.Properties as Lₚ
open import Data.Product using (_,_; _×_; ; ∃₂; proj₁; proj₂)
open import Function using (_∘_; _⟨_⟩_; flip)
open import Function.Equality using (_⟨$⟩_)
open import Function.Inverse as Inv using (inverse)
open import Level
open import Relation.Unary using (Pred)
open import Relation.Binary.PropositionalEquality as 
  using (_≡_ ; refl ; cong; cong₂; subst; _≢_; inspect)

private
  variable
    b p r : Level

open Setoid S using (_≈_) renaming (Carrier to A; refl to ≈-refl; sym to ≈-sym; trans to ≈-trans)
open Permutation S
open Membership S
open Unique S using (Unique)
open Equality S using (_≋_; []; _∷_; ≋-refl; ≋-sym; ≋-trans)
open PermutationReasoning

------------------------------------------------------------------------
-- Relationships to other predicates
------------------------------------------------------------------------

All-resp-↭ :  {P : Pred A p}  P Respects _≈_  (All P) Respects _↭_
All-resp-↭ resp refl           pxs             = pxs
All-resp-↭ resp (prep x≈y p)   (px  pxs)      = resp x≈y px  All-resp-↭ resp p pxs
All-resp-↭ resp (swap ≈₁ ≈₂ p) (px  py  pxs) = resp ≈₂ py  resp ≈₁ px  All-resp-↭ resp p pxs
All-resp-↭ resp (trans p₁ p₂)  pxs             = All-resp-↭ resp p₂ (All-resp-↭ resp p₁ pxs)

Any-resp-↭ :  {P : Pred A p}  P Respects _≈_  (Any P) Respects _↭_
Any-resp-↭ resp refl         pxs                 = pxs
Any-resp-↭ resp (prep x≈y p) (here px)           = here (resp x≈y px)
Any-resp-↭ resp (prep x≈y p) (there pxs)         = there (Any-resp-↭ resp p pxs)
Any-resp-↭ resp (swap x y p) (here px)           = there (here (resp x px))
Any-resp-↭ resp (swap x y p) (there (here px))   = here (resp y px)
Any-resp-↭ resp (swap x y p) (there (there pxs)) = there (there (Any-resp-↭ resp p pxs))
Any-resp-↭ resp (trans p₁ p₂) pxs                = Any-resp-↭ resp p₂ (Any-resp-↭ resp p₁ pxs)

AllPairs-resp-↭ :  {R : Rel A r}  Symmetric R  R Respects₂ _≈_  (AllPairs R) Respects _↭_
AllPairs-resp-↭ sym _    refl             pxs             = pxs
AllPairs-resp-↭ sym resp (prep x≈y p)     (  pxs)       =
  All-resp-↭ (proj₁ resp) p (All.map (proj₂ resp x≈y) ) 
  AllPairs-resp-↭ sym resp p pxs
AllPairs-resp-↭ sym resp@( , ) (swap eq₁ eq₂ p) ((∼₁  ∼₂)  ∼₃  pxs) =
  (sym ( eq₂ ( eq₁ ∼₁))  All-resp-↭  p (All.map ( eq₂) ∼₃)) 
  All-resp-↭  p (All.map ( eq₁) ∼₂) 
  AllPairs-resp-↭ sym resp p pxs
AllPairs-resp-↭ sym resp (trans p₁ p₂)    pxs             =
  AllPairs-resp-↭ sym resp p₂ (AllPairs-resp-↭ sym resp p₁ pxs)

∈-resp-↭ :  {x}  (x ∈_) Respects _↭_
∈-resp-↭ = Any-resp-↭ (flip ≈-trans)

-- Note should be possible to simplify once ≉-respˡ from #783 is
-- merged in
Unique-resp-↭ : Unique Respects _↭_
Unique-resp-↭ = AllPairs-resp-↭ (_∘ ≈-sym)
  ((λ z≈y x≉z x≈y  x≉z (≈-trans x≈y (≈-sym z≈y))) ,
  λ x≈z x≉y z≈y  x≉y (≈-trans x≈z z≈y))

------------------------------------------------------------------------
-- Relationships to other predicates
------------------------------------------------------------------------

≋⇒↭ : _≋_  _↭_
≋⇒↭ []            = refl
≋⇒↭ (x≈y  xs≋ys) = prep x≈y (≋⇒↭ xs≋ys)

↭-respʳ-≋ : _↭_ Respectsʳ _≋_
↭-respʳ-≋ xs≋ys               refl                 = ≋⇒↭ xs≋ys
↭-respʳ-≋ (x≈y  xs≋ys)       (prep eq zs↭xs)      = prep (≈-trans eq x≈y) (↭-respʳ-≋ xs≋ys zs↭xs)
↭-respʳ-≋ (x≈y  w≈z  xs≋ys) (swap eq₁ eq₂ zs↭xs) = swap (≈-trans eq₁ w≈z) (≈-trans eq₂ x≈y) (↭-respʳ-≋ xs≋ys zs↭xs)
↭-respʳ-≋ xs≋ys               (trans ws↭zs zs↭xs)  = trans ws↭zs (↭-respʳ-≋ xs≋ys zs↭xs)

↭-respˡ-≋ : _↭_ Respectsˡ _≋_
↭-respˡ-≋ xs≋ys               refl                 = ≋⇒↭ (≋-sym xs≋ys)
↭-respˡ-≋ (x≈y  xs≋ys)       (prep eq zs↭xs)      = prep (≈-trans (≈-sym x≈y) eq) (↭-respˡ-≋ xs≋ys zs↭xs)
↭-respˡ-≋ (x≈y  w≈z  xs≋ys) (swap eq₁ eq₂ zs↭xs) = swap (≈-trans (≈-sym x≈y) eq₁) (≈-trans (≈-sym w≈z) eq₂) (↭-respˡ-≋ xs≋ys zs↭xs)
↭-respˡ-≋ xs≋ys               (trans ws↭zs zs↭xs)  = trans (↭-respˡ-≋ xs≋ys ws↭zs) zs↭xs

------------------------------------------------------------------------
-- Properties of list functions
------------------------------------------------------------------------

------------------------------------------------------------------------
-- map

module _ (T : Setoid b ) where

  open Setoid T using () renaming (_≈_ to _≈′_)
  open Permutation T using () renaming (_↭_ to _↭′_)

  map⁺ :  {f}  f Preserves _≈_  _≈′_ 
          {xs ys}  xs  ys  map f xs ↭′ map f ys
  map⁺ pres refl          = refl
  map⁺ pres (prep x p)    = prep (pres x) (map⁺ pres p)
  map⁺ pres (swap x y p)  = swap (pres x) (pres y) (map⁺ pres p)
  map⁺ pres (trans p₁ p₂) = trans (map⁺ pres p₁) (map⁺ pres p₂)

------------------------------------------------------------------------
-- _++_

shift :  {v w}  v  w  (xs ys : List A)  xs ++ [ v ] ++ ys  w  xs ++ ys
shift {v} {w} v≈w []       ys = prep v≈w refl
shift {v} {w} v≈w (x  xs) ys = begin
  x  (xs ++ [ v ] ++ ys) <⟨ shift v≈w xs ys 
  x  w  xs ++ ys        <<⟨ refl 
  w  x  xs ++ ys        

++⁺ˡ :  xs {ys zs : List A}  ys  zs  xs ++ ys  xs ++ zs
++⁺ˡ []       ys↭zs = ys↭zs
++⁺ˡ (x  xs) ys↭zs = prep ≈-refl (++⁺ˡ xs ys↭zs)

++⁺ʳ :  {xs ys : List A} zs  xs  ys  xs ++ zs  ys ++ zs
++⁺ʳ zs refl          = refl
++⁺ʳ zs (prep x )    = prep x (++⁺ʳ zs )
++⁺ʳ zs (swap x y )  = swap x y (++⁺ʳ zs )
++⁺ʳ zs (trans ↭₁ ↭₂) = trans (++⁺ʳ zs ↭₁) (++⁺ʳ zs ↭₂)

++⁺ : _++_ Preserves₂ _↭_  _↭_  _↭_
++⁺ ws↭xs ys↭zs = trans (++⁺ʳ _ ws↭xs) (++⁺ˡ _ ys↭zs)

-- Algebraic properties

++-identityˡ : LeftIdentity _↭_ [] _++_
++-identityˡ xs = refl

++-identityʳ : RightIdentity _↭_ [] _++_
++-identityʳ xs = ↭-reflexive (Lₚ.++-identityʳ xs)

++-identity : Identity _↭_ [] _++_
++-identity = ++-identityˡ , ++-identityʳ

++-assoc : Associative _↭_ _++_
++-assoc xs ys zs = ↭-reflexive (Lₚ.++-assoc xs ys zs)

++-comm : Commutative _↭_ _++_
++-comm []       ys = ↭-sym (++-identityʳ ys)
++-comm (x  xs) ys = begin
  x  xs ++ ys         ↭⟨ prep ≈-refl (++-comm xs ys) 
  x  ys ++ xs         ≡⟨ cong  v  x  v ++ xs) (≡.sym (Lₚ.++-identityʳ _)) 
  (x  ys ++ []) ++ xs ↭⟨ ++⁺ʳ xs (↭-sym (shift ≈-refl ys [])) 
  (ys ++ [ x ]) ++ xs  ↭⟨ ++-assoc ys [ x ] xs 
  ys ++ ([ x ] ++ xs)  ≡⟨⟩
  ys ++ (x  xs)       

-- Structures

++-isMagma : IsMagma _↭_ _++_
++-isMagma = record
  { isEquivalence = ↭-isEquivalence
  ; ∙-cong        = ++⁺
  }

++-isSemigroup : IsSemigroup _↭_ _++_
++-isSemigroup = record
  { isMagma = ++-isMagma
  ; assoc   = ++-assoc
  }

++-isMonoid : IsMonoid _↭_ _++_ []
++-isMonoid = record
  { isSemigroup = ++-isSemigroup
  ; identity    = ++-identity
  }

++-isCommutativeMonoid : IsCommutativeMonoid _↭_ _++_ []
++-isCommutativeMonoid = record
  { isSemigroup = ++-isSemigroup
  ; identityˡ   = ++-identityˡ
  ; comm        = ++-comm
  }

-- Bundles

++-magma : Magma a (a  )
++-magma = record
  { isMagma = ++-isMagma
  }

++-semigroup : Semigroup a (a  )
++-semigroup = record
  { isSemigroup = ++-isSemigroup
  }

++-monoid : Monoid a (a  )
++-monoid = record
  { isMonoid = ++-isMonoid
  }

++-commutativeMonoid : CommutativeMonoid a (a  )
++-commutativeMonoid = record
  { isCommutativeMonoid = ++-isCommutativeMonoid
  }

-- Some other useful lemmas

zoom :  h {t xs ys : List A}  xs  ys  h ++ xs ++ t  h ++ ys ++ t
zoom h {t} = ++⁺ˡ h  ++⁺ʳ t

inject :   (v : A) {ws xs ys zs}  ws  ys  xs  zs 
         ws ++ [ v ] ++ xs  ys ++ [ v ] ++ zs
inject v ws↭ys xs↭zs = trans (++⁺ˡ _ (prep ≈-refl xs↭zs)) (++⁺ʳ _ ws↭ys)

shifts :  xs ys {zs : List A}  xs ++ ys ++ zs  ys ++ xs ++ zs
shifts xs ys {zs} = begin
   xs ++ ys  ++ zs ↭˘⟨ ++-assoc xs ys zs 
  (xs ++ ys) ++ zs ↭⟨ ++⁺ʳ zs (++-comm xs ys) 
  (ys ++ xs) ++ zs ↭⟨ ++-assoc ys xs zs 
   ys ++ xs  ++ zs 

------------------------------------------------------------------------
-- _∷ʳ_

∷↭∷ʳ :  (x : A) xs  x  xs  xs ∷ʳ x
∷↭∷ʳ x xs = ↭-sym (begin
  xs ++ [ x ]   ↭⟨ shift ≈-refl xs [] 
  x  xs ++ []  ≡⟨ Lₚ.++-identityʳ _ 
  x  xs        )
  where open PermutationReasoning