------------------------------------------------------------------------
-- The Agda standard library
--
-- Bag and set equality
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --safe #-}

module Data.List.Relation.Binary.BagAndSetEquality where

open import Algebra.Bundles using (CommutativeMonoid)
open import Algebra.Definitions using (Idempotent)
open import Algebra.Structures.Biased using (isCommutativeMonoidˡ)
open import Effect.Monad using (RawMonad)
open import Data.Empty using ()
open import Data.Fin.Base using (Fin; zero; suc)
open import Data.List.Base
  using (List; []; _∷_; map; _++_; concat; [_]; lookup; length)
open import Data.List.Effectful using (monad; module Applicative; module MonadProperties)
import Data.List.Properties as List
open import Data.List.Relation.Unary.Any using (Any; here; there)
open import Data.List.Relation.Unary.Any.Properties
  using (∷↔; map↔; Any-cong; ++↔; concat↔; >>=↔; ++↔++; ⊎↔; ⊥↔Any[])
open import Data.List.Membership.Propositional using (_∈_)
open import Data.List.Membership.Propositional.Properties
  using (∈-∃++)
open import Data.List.Relation.Binary.Subset.Propositional.Properties
  using (⊆-preorder)
open import Data.List.Relation.Binary.Permutation.Propositional
  using (_↭_; ↭-refl; ↭-sym; ↭-prep; module PermutationReasoning)
open import Data.List.Relation.Binary.Permutation.Propositional.Properties
  using (∈-resp-↭; ∈-resp-[σ⁻¹∘σ]; ∈-resp-[σ∘σ⁻¹]; shift; ++-comm)
open import Data.Product.Base as Product using (; _,_; proj₁; proj₂; _×_)
import Data.Product.Function.Dependent.Propositional as Σ
open import Data.Sum.Base as Sum using (_⊎_; [_,_]′; inj₁; inj₂)
open import Data.Sum.Properties using (inj₂-injective; inj₁-injective)
open import Data.Sum.Function.Propositional using (_⊎-cong_)
open import Data.Unit.Polymorphic.Base using ()
open import Function.Base using (_∘_; _$_; id; _⟨_⟩_; case_of_)
open import Function.Bundles using (_↔_; Inverse; Equivalence; mk↔ₛ′; mk⇔)
open import Function.Related.Propositional as Related
  using (↔⇒; ⌊_⌋; ⌊_⌋→; ⇒→; K-refl; SK-sym)
open import Function.Related.TypeIsomorphisms using (×-identityʳ; ∃∃↔∃∃)
open import Function.Properties.Inverse using (↔-sym; ↔-trans; to-from)
open import Level using (Level)
open import Relation.Binary.Core using (_⇒_)
open import Relation.Binary.Definitions using (Trans)
open import Relation.Binary.Bundles using (Preorder; Setoid)
import Relation.Binary.Reasoning.Setoid as ≈-Reasoning
import Relation.Binary.Reasoning.Preorder as ≲-Reasoning
open import Relation.Binary.PropositionalEquality.Core as 
  using (_≡_; _≢_; _≗_; refl)
open import Relation.Binary.PropositionalEquality.Properties as 
  using (module ≡-Reasoning)
open import Relation.Binary.Reasoning.Syntax
open import Relation.Nullary.Negation.Core using (¬_; contradiction)

private
  variable
    a b : Level
    A B : Set a
    x y : A
    ws xs ys zs : List A

------------------------------------------------------------------------
-- Definitions

open Related public using (Kind; SymmetricKind) renaming
  ( implication         to subset
  ; reverseImplication  to superset
  ; equivalence         to set
  ; injection           to subbag
  ; reverseInjection    to superbag
  ; bijection           to bag
  )

[_]-Order : Kind  Set a  Preorder _ _ _
[ k ]-Order A = Related.InducedPreorder₂ {A = A} k _∈_

[_]-Equality : SymmetricKind  Set a  Setoid _ _
[ k ]-Equality A = Related.InducedEquivalence₂ {A = A} k _∈_

infix 4 _∼[_]_

_∼[_]_ :  {a} {A : Set a}  List A  Kind  List A  Set _
_∼[_]_ {A = A} xs k ys = Preorder._≲_ ([ k ]-Order A) xs ys

private
  module Eq  {k a} {A : Set a} = Setoid ([ k ]-Equality A)
  module Ord {k a} {A : Set a} = Preorder ([ k ]-Order A)
  open module ListMonad {} = RawMonad (monad { = })
  module MP = MonadProperties

------------------------------------------------------------------------
-- Bag equality implies the other relations.

bag-=⇒ :  {k}  xs ∼[ bag ] ys  xs ∼[ k ] ys
bag-=⇒ xs≈ys = ↔⇒ xs≈ys

------------------------------------------------------------------------
-- "Equational" reasoning for _⊆_ along with an additional relatedness

module ⊆-Reasoning {A : Set a} where
  private module Base = ≲-Reasoning (⊆-preorder A)

  open Base public
    hiding (step-≈; step-≈˘; step-≈-⟩; step-≈-⟨; step-∼; step-≲)
    renaming (≲-go to ⊆-go)

  open begin-membership-syntax _IsRelatedTo_ _∈_  x  begin x) public
  open ⊆-syntax _IsRelatedTo_ _IsRelatedTo_ ⊆-go public

  module _ {k : Related.ForwardKind} where
    ∼-go : Trans _∼[  k ⌋→ ]_ _IsRelatedTo_ _IsRelatedTo_
    ∼-go eq = ⊆-go (⇒→ eq)

    open ∼-syntax _IsRelatedTo_ _IsRelatedTo_ ∼-go public


------------------------------------------------------------------------
-- Congruence lemmas
------------------------------------------------------------------------
-- _∷_

module _ {k} {x y : A} {xs ys} where

  ∷-cong : x  y  xs ∼[ k ] ys  x  xs ∼[ k ] y  ys
  ∷-cong refl xs≈ys {y} = begin
    y  x  xs        ↔⟨ SK-sym $ ∷↔ (y ≡_) 
    (y  x  y  xs)  ∼⟨ K-refl ⊎-cong xs≈ys 
    (y  x  y  ys)  ↔⟨ ∷↔ (y ≡_) 
    y  x  ys        
    where open Related.EquationalReasoning

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

module _ {k} {f g : A  B} {xs ys} where

  map-cong : f  g  xs ∼[ k ] ys  map f xs ∼[ k ] map g ys
  map-cong f≗g xs≈ys {x} = begin
    x  map f xs            ↔⟨ SK-sym $ map↔ 
    Any  y  x  f y) xs  ∼⟨ Any-cong (↔⇒  helper) xs≈ys 
    Any  y  x  g y) ys  ↔⟨ map↔ 
    x  map g ys            
    where
    open Related.EquationalReasoning

    helper :  y  x  f y  x  g y
    helper y = mk↔ₛ′
       x≡fy  ≡.trans x≡fy (        f≗g y))
       x≡gy  ≡.trans x≡gy (≡.sym $ f≗g y))
       { ≡.refl  ≡.trans-symˡ (f≗g y) })
      λ { ≡.refl  ≡.trans-symʳ (f≗g y) }

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

module _ {k} {xs₁ xs₂ ys₁ ys₂ : List A} where

  ++-cong : xs₁ ∼[ k ] xs₂  ys₁ ∼[ k ] ys₂ 
            xs₁ ++ ys₁ ∼[ k ] xs₂ ++ ys₂
  ++-cong xs₁≈xs₂ ys₁≈ys₂ {x} = begin
    x  xs₁ ++ ys₁       ↔⟨ SK-sym $ ++↔ 
    (x  xs₁  x  ys₁)  ∼⟨ xs₁≈xs₂ ⊎-cong ys₁≈ys₂ 
    (x  xs₂  x  ys₂)  ↔⟨ ++↔ 
    x  xs₂ ++ ys₂       
    where open Related.EquationalReasoning

------------------------------------------------------------------------
-- concat

module _ {k} {xss yss : List (List A)} where

  concat-cong : xss ∼[ k ] yss  concat xss ∼[ k ] concat yss
  concat-cong xss≈yss {x} = begin
    x  concat xss        ↔⟨ SK-sym concat↔ 
    Any (Any (x ≡_)) xss  ∼⟨ Any-cong  _  _ ) xss≈yss 
    Any (Any (x ≡_)) yss  ↔⟨ concat↔ 
    x  concat yss         
    where open Related.EquationalReasoning

------------------------------------------------------------------------
-- _>>=_

module _ {k} {A B : Set a} {xs ys} {f g : A  List B} where

  >>=-cong : xs ∼[ k ] ys  (∀ x  f x ∼[ k ] g x) 
             (xs >>= f) ∼[ k ] (ys >>= g)
  >>=-cong xs≈ys f≈g {x} = begin
    x  (xs >>= f)          ↔⟨ SK-sym >>=↔ 
    Any  y  x  f y) xs  ∼⟨ Any-cong  x  f≈g x) xs≈ys 
    Any  y  x  g y) ys  ↔⟨ >>=↔ 
    x  (ys >>= g)          
    where open Related.EquationalReasoning


------------------------------------------------------------------------
-- _⊛_

module _ {k} {A B : Set a} {fs gs : List (A  B)} {xs ys} where

  ⊛-cong : fs ∼[ k ] gs  xs ∼[ k ] ys  (fs  xs) ∼[ k ] (gs  ys)
  ⊛-cong fs≈gs xs≈ys {x} = begin
    x  (fs  xs)
      ≡⟨ ≡.cong (x ∈_) (Applicative.unfold-⊛ fs xs) 
    x  (fs >>= λ f  xs >>= λ x  pure (f x))
      ∼⟨ >>=-cong fs≈gs  f  >>=-cong xs≈ys λ x  K-refl) 
    x  (gs >>= λ g  ys >>= λ y  pure (g y))
      ≡⟨ ≡.cong (x ∈_) (Applicative.unfold-⊛ gs ys) 
    x  (gs  ys) 
    where open Related.EquationalReasoning

------------------------------------------------------------------------
-- _⊗_

module _ { k} {A B : Set } {xs₁ xs₂ : List A} {ys₁ ys₂ : List B} where

  ⊗-cong : xs₁ ∼[ k ] xs₂  ys₁ ∼[ k ] ys₂ 
           (xs₁  ys₁) ∼[ k ] (xs₂  ys₂)
  ⊗-cong xs₁≈xs₂ ys₁≈ys₂ =
    ⊛-cong (map-cong  _  refl) xs₁≈xs₂) ys₁≈ys₂

------------------------------------------------------------------------
-- Other properties

-- _++_ and [] form a commutative monoid, with either bag or set
-- equality as the underlying equality.

commutativeMonoid : SymmetricKind  Set a  CommutativeMonoid _ _
commutativeMonoid {a} k A = record
  { Carrier             = List A
  ; _≈_                 = _∼[  k  ]_
  ; _∙_                 = _++_
  ; ε                   = []
  ; isCommutativeMonoid = isCommutativeMonoidˡ record
    { isSemigroup = record
      { isMagma = record
        { isEquivalence = Eq.isEquivalence
        ; ∙-cong        = ++-cong
        }
      ; assoc         = λ xs ys zs 
                          Eq.reflexive (List.++-assoc xs ys zs)
      }
    ; identityˡ = λ xs  K-refl
    ; comm      = λ xs ys  ↔⇒ (++↔++ xs ys)
    }
  }
  where open Related.EquationalReasoning

-- The only list which is bag or set equal to the empty list (or a
-- subset or subbag of the list) is the empty list itself.

empty-unique :  {k}  xs ∼[  k ⌋→ ] []  xs  []
empty-unique {xs = []}    _    = refl
empty-unique {xs = _  _} ∷∼[] with ()⇒→ ∷∼[] (here refl)

-- _++_ is idempotent (under set equality).

++-idempotent : Idempotent {A = List A} _∼[ set ]_ _++_
++-idempotent xs {x} =
  x  xs ++ xs
    ∼⟨ mk⇔ ([ id , id ]′  (Inverse.from $ ++↔)) (Inverse.to ++↔  inj₁) 
  x  xs 
  where open Related.EquationalReasoning

-- The list monad's bind distributes from the left over _++_.

>>=-left-distributive :
   (xs : List A) {f g : A  List B} 
  (xs >>= λ x  f x ++ g x) ∼[ bag ] (xs >>= f) ++ (xs >>= g)
>>=-left-distributive {} xs {f} {g} {y} =
  y  (xs >>= λ x  f x ++ g x)                      ↔⟨ SK-sym $ >>=↔ 
  Any  x  y  f x ++ g x) xs                      ↔⟨ SK-sym (Any-cong  _  ++↔) (_ )) 
  Any  x  y  f x  y  g x) xs                   ↔⟨ SK-sym $ ⊎↔ 
  (Any  x  y  f x) xs  Any  x  y  g x) xs)  ↔⟨ >>=↔  _⊎-cong_  >>=↔ 
  (y  (xs >>= f)  y  (xs >>= g))                  ↔⟨ ++↔ 
  y  (xs >>= f) ++ (xs >>= g)                       
  where open Related.EquationalReasoning

-- The same applies to _⊛_.

⊛-left-distributive :
   (fs : List (A  B)) xs₁ xs₂ 
  (fs  (xs₁ ++ xs₂)) ∼[ bag ] (fs  xs₁) ++ (fs  xs₂)
⊛-left-distributive {B = B} fs xs₁ xs₂ = begin
  fs  (xs₁ ++ xs₂)                       ≡⟨ Applicative.unfold-⊛ fs (xs₁ ++ xs₂) 
  (fs >>= λ f  xs₁ ++ xs₂ >>= pure  f)  ≡⟨ (MP.cong (refl {x = fs}) λ f 
                                                MP.right-distributive xs₁ xs₂ (pure  f)) 
  (fs >>= λ f  (xs₁ >>= pure  f) ++
                (xs₂ >>= pure  f))       ≈⟨ >>=-left-distributive fs 

  (fs >>= λ f  xs₁ >>= pure  f) ++
  (fs >>= λ f  xs₂ >>= pure  f)         ≡⟨ ≡.cong₂ _++_ (Applicative.unfold-⊛ fs xs₁) (Applicative.unfold-⊛ fs xs₂) 

  (fs  xs₁) ++ (fs  xs₂)                
  where open ≈-Reasoning ([ bag ]-Equality B)

private

  -- If x ∷ xs is set equal to x ∷ ys, then xs and ys are not
  -- necessarily set equal.

  ¬-drop-cons :  {x : A} 
    ¬ (∀ {xs ys}  x  xs ∼[ set ] x  ys  xs ∼[ set ] ys)
  ¬-drop-cons {x = x} drop-cons with Equivalence.to x∼[] (here refl)
    where
    x,x≈x :  (x  x  []) ∼[ set ] [ x ]
    x,x≈x = ++-idempotent [ x ]

    x∼[] : [ x ] ∼[ set ] []
    x∼[] = drop-cons x,x≈x
  ... | ()

-- However, the corresponding property does hold for bag equality.

drop-cons :  {x : A} {xs ys}  x  xs ∼[ bag ] x  ys  xs ∼[ bag ] ys
drop-cons {x = x} {xs} {ys} x∷xs≈x∷ys =
  ⊎-left-cancellative
    (∼→⊎↔⊎ x∷xs≈x∷ys)
    (lemma x∷xs≈x∷ys)
    (lemma (SK-sym x∷xs≈x∷ys))

  where

  -- TODO: Some of the code below could perhaps be exposed to users.

  -- List membership can be expressed as "there is an index which
  -- points to the element".

  ∈-index :  {z} (xs : List A)  z  xs   λ i  z  lookup xs i
  ∈-index {z = z} [] =
    z  []                              ↔⟨ SK-sym ⊥↔Any[] 
                                       ↔⟨ mk↔ₛ′  ())  { (() , _) })  { (() , _) })  ()) 
    ( λ (i : Fin 0)  z  lookup [] i)  
    where
    open Related.EquationalReasoning
  ∈-index {z = z} (x  xs) =
    z  x  xs                         ↔⟨ SK-sym (∷↔ _) 
    (z  x  z  xs)                   ↔⟨ K-refl ⊎-cong ∈-index xs 
    (z  x   λ i  z  lookup xs i)  ↔⟨ mk↔ₛ′  { (inj₁ p)  zero , p; (inj₂ (i , p))  suc i , p })
                                                   { (zero , p)  inj₁ p; (suc i , p)  inj₂ (i , p) })
                                                   { (zero , _)  refl; (suc _ , _)  refl })
                                                   { (inj₁ _)  refl; (inj₂ _)  refl }) 
    ( λ i  z  lookup (x  xs) i)    
    where
    open Related.EquationalReasoning

  -- The index which points to the element.

  index-of :  {a} {A : Set a} {z} {xs : List A} 
             z  xs  Fin (length xs)
  index-of = proj₁  (Inverse.to (∈-index _))

  -- The type ∃ λ z → z ∈ xs is isomorphic to Fin n, where n is the
  -- length of xs.
  --
  -- Thierry Coquand pointed out that (a variant of) this statement is
  -- a generalisation of the fact that singletons are contractible.

  Fin-length :  {a} {A : Set a}
               (xs : List A)  ( λ z  z  xs)  Fin (length xs)
  Fin-length xs =
    ( λ z  z  xs)                   ↔⟨ Σ.cong K-refl (∈-index xs) 
    ( λ z   λ i  z  lookup xs i)  ↔⟨ ∃∃↔∃∃ _ 
    ( λ i   λ z  z  lookup xs i)  ↔⟨ Σ.cong K-refl (mk↔ₛ′ _  _  _ , refl)  _  refl)  { (_ , refl)  refl })) 
    (Fin (length xs) × )              ↔⟨ ×-identityʳ _ _ 
    Fin (length xs)                    
    where
    open Related.EquationalReasoning

  -- From this lemma we get that lists which are bag equivalent have
  -- related lengths.

  Fin-length-cong :  {a} {A : Set a} {xs ys : List A} 
                    xs ∼[ bag ] ys  Fin (length xs)  Fin (length ys)
  Fin-length-cong {xs = xs} {ys} xs≈ys =
    Fin (length xs)   ↔⟨ SK-sym $ Fin-length xs 
      z  z  xs)  ↔⟨ Σ.cong K-refl xs≈ys 
      z  z  ys)  ↔⟨ Fin-length ys 
    Fin (length ys)   
    where
    open Related.EquationalReasoning

  -- The index-of function commutes with applications of certain
  -- inverses.

  index-of-commutes :
     {a} {A : Set a} {z : A} {xs ys} 
    (xs≈ys : xs ∼[ bag ] ys) (p : z  xs) 
    index-of (Inverse.to xs≈ys p) 
    Inverse.to (Fin-length-cong xs≈ys) (index-of p)
  index-of-commutes {z = z} {xs} {ys} xs≈ys p =
    index-of (to xs≈ys p)                                        ≡⟨ lemma z p 

    index-of (to xs≈ys (proj₂
      (from (Fin-length xs) (to (Fin-length xs) (z , p)))))   ≡⟨⟩

    index-of (proj₂ (Product.map₂ (to xs≈ys)
      (from (Fin-length xs) (to (Fin-length xs) (z , p)))))   ≡⟨⟩

    to (Fin-length ys) (Product.map₂ (to xs≈ys)
      (from (Fin-length xs) (index-of p)))                    ≡⟨⟩

    to (Fin-length-cong xs≈ys) (index-of p)                   
    where
    open ≡-Reasoning
    open Inverse

    lemma :
       z p 
      index-of (to xs≈ys p) 
      index-of (to xs≈ys (proj₂
        (from (Fin-length xs) (to (Fin-length xs) (z , p)))))
    lemma z p with to (Fin-length xs) (z , p)
                 | strictlyInverseʳ (Fin-length xs) (z , p)
    lemma .(lookup xs i) .(from (∈-index xs) (i , refl)) | i | refl =
      refl

  -- Bag equivalence isomorphisms preserve index equality. Note that
  -- this means that, even if the underlying equality is proof
  -- relevant, a bag equivalence isomorphism cannot map two distinct
  -- proofs, that point to the same position, to different positions.

  index-equality-preserved :
     {a} {A : Set a} {z : A} {xs ys} {p q : z  xs}
    (xs≈ys : xs ∼[ bag ] ys) 
    index-of p  index-of q 
    index-of (Inverse.to xs≈ys p) 
    index-of (Inverse.to xs≈ys q)
  index-equality-preserved {p = p} {q} xs≈ys eq =
    index-of (Inverse.to xs≈ys p)                   ≡⟨ index-of-commutes xs≈ys p 
    Inverse.to (Fin-length-cong xs≈ys) (index-of p) ≡⟨ ≡.cong (Inverse.to (Fin-length-cong xs≈ys)) eq 
    Inverse.to (Fin-length-cong xs≈ys) (index-of q) ≡⟨ ≡.sym $ index-of-commutes xs≈ys q 
    index-of (Inverse.to xs≈ys q)                   
    where
    open ≡-Reasoning

  -- The old inspect idiom.

  inspect :  {a} {A : Set a} (x : A)   (x ≡_)
  inspect x = x , refl

  -- A function is "well-behaved" if any "left" element which is the
  -- image of a "right" element is in turn not mapped to another
  -- "left" element.

  Well-behaved :  {a b c} {A : Set a} {B : Set b} {C : Set c} 
                 (A  B  A  C)  Set _
  Well-behaved f =
     {b a a′}  f (inj₂ b)  inj₁ a  f (inj₁ a)  inj₁ a′

  -- The type constructor _⊎_ is left cancellative for certain
  -- well-behaved inverses.

  ⊎-left-cancellative :
     {a b c} {A : Set a} {B : Set b} {C : Set c}
    (f : (A  B)  (A  C)) 
    Well-behaved (Inverse.to   f) 
    Well-behaved (Inverse.from f) 
    B  C
  ⊎-left-cancellative {A = A} = λ inv to-hyp from-hyp  mk↔ₛ′
    (g (to   inv) to-hyp)
    (g (from inv) from-hyp)
    (g∘g (SK-sym inv) from-hyp to-hyp)
    (g∘g         inv  to-hyp from-hyp)
    where
    open Inverse

    module _
      {a b c} {A : Set a} {B : Set b} {C : Set c}
      (f : A  B  A  C)
      (hyp : Well-behaved f)
      where

      mutual

        g : B  C
        g b = g′ (inspect (f (inj₂ b)))

        g′ :  {b}   (f (inj₂ b) ≡_)  C
        g′ (inj₂ c , _)  = c
        g′ (inj₁ a , eq) = g″ eq (inspect (f (inj₁ a)))

        g″ :  {a b} 
             f (inj₂ b)  inj₁ a   (f (inj₁ a) ≡_)  C
        g″ _   (inj₂ c , _)   = c
        g″ eq₁ (inj₁ _ , eq₂) = contradiction eq₂ (hyp eq₁)

    g∘g :  {b c} {B : Set b} {C : Set c}
          (f : (A  B)  (A  C)) 
          (to-hyp   : Well-behaved (to   f)) 
          (from-hyp : Well-behaved (from f)) 
           b  g (from f) from-hyp (g (to f) to-hyp b)  b
    g∘g f to-hyp from-hyp b = g∘g′
      where
      open ≡-Reasoning

      g∘g′ : g (from f) from-hyp (g (to f) to-hyp b)  b
      g∘g′ with inspect (to f (inj₂ b))
      g∘g′ | inj₂ c , eq₁ with inspect (from f (inj₂ c))
      ...   | inj₂ b′ , eq₂ = inj₂-injective (
        inj₂ b′            ≡⟨ ≡.sym eq₂ 
        from f (inj₂ c)   ≡⟨ to-from f eq₁ 
        inj₂ b            )
      ...   | inj₁ a  , eq₂ with
        inj₁ a             ≡⟨ ≡.sym eq₂ 
        from f (inj₂ c)    ≡⟨ to-from f eq₁ 
        inj₂ b             
      ... | ()
      g∘g′ | inj₁ a , eq₁ with inspect (to f (inj₁ a))
      g∘g′ | inj₁ a , eq₁ | inj₁ a′ , eq₂ = contradiction eq₂ (to-hyp eq₁)
      g∘g′ | inj₁ a , eq₁ | inj₂ c  , eq₂ with inspect (from f (inj₂ c))
      g∘g′ | inj₁ a , eq₁ | inj₂ c  , eq₂ | inj₂ b′ , eq₃ with
        inj₁ a             ≡⟨ ≡.sym (to-from f eq₂) 
        from f (inj₂ c)    ≡⟨ eq₃ 
        inj₂ b′            
      ... | ()
      g∘g′ | inj₁ a , eq₁ | inj₂ c  , eq₂ | inj₁ a′ , eq₃ with inspect (from f $ inj₁ a′)
      g∘g′ | inj₁ a , eq₁ | inj₂ c  , eq₂ | inj₁ a′ , eq₃ | inj₁ a″ , eq₄ = contradiction eq₄ (from-hyp eq₃)
      g∘g′ | inj₁ a , eq₁ | inj₂ c  , eq₂ | inj₁ a′ , eq₃ | inj₂ b′ , eq₄ = inj₂-injective (
        let lemma =
              inj₁ a′            ≡⟨ ≡.sym eq₃ 
              from f (inj₂ c)    ≡⟨ to-from f eq₂ 
              inj₁ a             
        in
        inj₂ b′             ≡⟨ ≡.sym eq₄ 
        from f (inj₁ a′)    ≡⟨ ≡.cong (from f  inj₁) $ inj₁-injective lemma 
        from f (inj₁ a)     ≡⟨ to-from f eq₁ 
        inj₂ b              )

  -- Some final lemmas.

  ∼→⊎↔⊎ :
     {x : A} {xs ys} 
    x  xs ∼[ bag ] x  ys 
     {z}  (z  x  z  xs)  (z  x  z  ys)
  ∼→⊎↔⊎ {x = x} {xs} {ys} x∷xs≈x∷ys {z} =
    (z  x  z  xs)  ↔⟨ ∷↔ _ 
    z  x  xs        ↔⟨ x∷xs≈x∷ys 
    z  x  ys        ↔⟨ SK-sym (∷↔ _) 
    (z  x  z  ys)  
    where
    open Related.EquationalReasoning

  lemma :  {xs ys} (inv : x  xs ∼[ bag ] x  ys) {z} 
          Well-behaved (Inverse.to (∼→⊎↔⊎ inv {z}))
  lemma {xs} inv {b = z∈xs} {a = p} {a′ = q} hyp₁ hyp₂ = case contra of λ ()
    where
    open Inverse
    open ≡-Reasoning
    contra : zero  suc (index-of {xs = xs} z∈xs)
    contra = begin
      zero
        ≡⟨⟩
      index-of {xs = x  xs} (here p)
        ≡⟨⟩
      index-of {xs = x  xs} (to (∷↔ _) $ inj₁ p)
        ≡⟨ ≡.cong (index-of  (to (∷↔ (_ ≡_)))) $ to-from (∼→⊎↔⊎ inv) {x = inj₁ p} hyp₂ 
      index-of {xs = x  xs} (to (∷↔ _) $ (from (∼→⊎↔⊎ inv) $ inj₁ q))
        ≡⟨ ≡.cong index-of $ strictlyInverseˡ (∷↔ _) (from inv (here q)) 
      index-of {xs = x  xs} (to (SK-sym inv) $ here q)
        ≡⟨ index-equality-preserved (SK-sym inv) refl 
      index-of {xs = x  xs} (to (SK-sym inv) $ here p)
        ≡⟨ ≡.cong index-of $ strictlyInverseˡ (∷↔ _) (from inv (here p)) 
      index-of {xs = x  xs} (to (∷↔ _) (from (∼→⊎↔⊎ inv) $ inj₁ p))
        ≡⟨ ≡.cong (index-of  (to (∷↔ (_ ≡_)))) $ to-from (∼→⊎↔⊎ inv) {x = inj₂ z∈xs} hyp₁ 
      index-of {xs = x  xs} (to (∷↔ _) $ inj₂ z∈xs)
        ≡⟨⟩
      index-of {xs = x  xs} (there z∈xs)
        ≡⟨⟩
      suc (index-of {xs = xs} z∈xs)
        


------------------------------------------------------------------------
-- Relationships to propositional permutation

↭⇒∼bag : _↭_ {A = A}  _∼[ bag ]_
↭⇒∼bag xs↭ys {v} =
  mk↔ₛ′ (∈-resp-↭ xs↭ys) (∈-resp-↭ (↭-sym xs↭ys)) (∈-resp-[σ∘σ⁻¹] xs↭ys) (∈-resp-[σ⁻¹∘σ] xs↭ys)

∼bag⇒↭ : _∼[ bag ]_  _↭_ {A = A}
∼bag⇒↭ {A = A} {[]}     eq with reflempty-unique (↔-sym eq) = ↭-refl
∼bag⇒↭ {A = A} {x  xs} eq
  with zs₁ , zs₂ , refl∈-∃++ (Inverse.to (eq {x}) (here refl)) = begin
    x  xs           ↭⟨ ↭-prep x (∼bag⇒↭ (drop-cons (↔-trans eq (comm zs₁ (x  zs₂))))) 
    x  (zs₂ ++ zs₁) ↭⟨ ↭-prep x (++-comm zs₂ zs₁) 
    x  (zs₁ ++ zs₂) ↭⟨ shift x zs₁ zs₂ 
    zs₁ ++ x  zs₂   
    where
    open CommutativeMonoid (commutativeMonoid bag A)
    open PermutationReasoning