open import Algebra.Structure.OICM

module Free.LeftRegularMonoid.Properties
  {X : Set} {_≈_ : X  X  Set} {_≠_ : X  X  Set}
  (≠-AR : IsPropDecTightApartnessRelation _≈_ _≠_)
  where

open import Data.Empty
open import Data.FreshList.InductiveInductive
open import Relation.Binary.PropositionalEquality renaming (isEquivalence to ≡-isEquivalence)
open import Relation.Nullary
open import Data.Sum
open import Data.Product
open import Algebra.Structures

open import Free.LeftRegularMonoid.Base ≠-AR

private
  ≠-irrefl  = IsPropDecTightApartnessRelation.irrefl ≠-AR
  ≠-sym  = IsPropDecTightApartnessRelation.sym ≠-AR
  ≠-cotrans = IsPropDecTightApartnessRelation.cotrans ≠-AR
  ≠-prop = IsPropDecTightApartnessRelation.prop ≠-AR
  ≠-dec = IsPropDecTightApartnessRelation.eqOrApart ≠-AR
  ≈-refl = IsPropDecTightApartnessRelation.refl ≠-AR
  ≈-sym = IsPropDecTightApartnessRelation.≈-sym ≠-AR
  ≈-trans = IsPropDecTightApartnessRelation.trans ≠-AR

  ≠-cons-cong = WithIrr.cons-cong _≠_ ≠-prop

-[]-resp-≈ : (xs : UniqueList)  {y z : X}  y  z  xs -[ y ]  xs -[ z ]
-[]-resp-≈ [] y≈z = refl
-[]-resp-≈ (cons x xs x#xs) {y} {z} y≈z with ≠-dec x y |  ≠-dec x z
... | inj₁ x≈y | inj₁ x≈z = refl
... | inj₁ x≈y | inj₂ x≠z = ⊥-elim (≠-irrefl (≈-trans x≈y y≈z) x≠z)
... | inj₂ x≠y | inj₁ x≈z = ⊥-elim (≠-irrefl (≈-trans x≈z (≈-sym y≈z)) x≠y)
... | inj₂ x≠y | inj₂ x≠z = ≠-cons-cong refl (-[]-resp-≈ xs y≈z)

-[]-order-irrelevant : (xs : UniqueList)  (y z : X)  xs -[ y ] -[ z ]  xs -[ z ] -[ y ]
-[]-order-irrelevant [] y z = refl
-[]-order-irrelevant (cons x xs x#xs) y z with ≠-dec x y in eqxy |  ≠-dec x z in eqxz
... | inj₁ x≈y | inj₁ x≈z = -[]-resp-≈ xs ((≈-trans (≈-sym x≈z) x≈y))
... | inj₁ x≈y | inj₂ x≠z rewrite eqxy = refl
... | inj₂ x≠y | inj₁ x≈z rewrite eqxz = refl
... | inj₂ x≠y | inj₂ x≠z rewrite eqxz rewrite eqxy = ≠-cons-cong refl (-[]-order-irrelevant xs y z)

remove-union : (xs ys : UniqueList)  (z : X)  union xs ys -[ z ]  union (xs -[ z ]) (ys -[ z ])
remove-union [] ys z = refl
remove-union (cons x xs x#xs) ys z with ≠-dec x z
... | inj₁ x≈z = trans (remove-union xs ys x) (cong₂ union (remove-fresh-idempotent xs x x#xs) (-[]-resp-≈ ys x≈z))
... | inj₂ x≠z = ≠-cons-cong refl (trans (-[]-order-irrelevant (union xs ys) x z) (cong (_-[ x ]) (remove-union xs ys z)))

remove-union-fresh : (xs ys : UniqueList)  (x : X)  x # xs  union xs ys -[ x ]  union xs (ys -[ x ])
remove-union-fresh xs ys x x#xs = trans (remove-union xs ys x) (cong  w  union w (ys -[ x ])) (remove-fresh-idempotent xs x x#xs))

remove-cons : (xs : UniqueList)  (x : X)  (x#xs : x # xs)  (y : X)  x  y  cons x xs x#xs -[ y ]  xs
remove-cons xs x x#xs y x≈y with ≠-dec x y
... | inj₁ x≈y = refl
... | inj₂ x≠y = ⊥-elim (≠-irrefl x≈y x≠y)

unitʳ : (xs : UniqueList)  union xs []  xs
unitʳ [] = refl
unitʳ (cons x xs x#xs) = ≠-cons-cong refl (trans (cong (_-[ x ]) (unitʳ xs)) (remove-fresh-idempotent xs x x#xs))

unitˡ : (xs : UniqueList)  union [] xs  xs
unitˡ xs = refl

assoc : (xs ys zs : UniqueList)  union (union xs ys) zs  union xs (union ys zs)
assoc [] ys zs = refl
assoc (cons x xs x#xs) ys zs = ≠-cons-cong refl (trans (lemma ys) (cong (_-[ x ]) (assoc xs ys zs))) where
  lemma :  ys  (union (union xs ys -[ x ]) zs -[ x ])  (union (union xs ys) zs -[ x ])
  lemma [] = cong  w  union w zs -[ x ]) (trans (trans (cong (_-[ x ]) (unitʳ xs)) (remove-fresh-idempotent xs x x#xs)) (sym (unitʳ xs)))
  lemma (cons y ys y#ys) = begin
    union (union xs (cons y ys y#ys) -[ x ]) zs -[ x ]
      ≡⟨ remove-union (union xs (cons y ys y#ys) -[ x ]) zs x 
    union (union xs (cons y ys y#ys) -[ x ] -[ x ]) (zs -[ x ])
      ≡⟨ cong  w  union w (zs -[ x ])) (remove-fresh-idempotent _ x (remove-removes (union xs (cons y ys y#ys)) x)) 
    union (union xs (cons y ys y#ys) -[ x ]) (zs -[ x ])
      ≡⟨ sym (remove-union (union xs (cons y ys y#ys)) zs x) 
    union (union xs (cons y ys y#ys)) zs -[ x ]
     where open ≡-Reasoning

idem : (xs : UniqueList)  union xs xs  xs
idem [] = refl
idem (cons x xs x#xs) = ≠-cons-cong refl (trans lemma (idem xs))
  where
    lemma : (union xs (cons x xs x#xs) -[ x ])  union xs xs
    lemma = begin
      union xs (cons x xs x#xs) -[ x ]
        ≡⟨ remove-union xs (cons x xs x#xs) x 
      union (xs -[ x ]) (cons x xs x#xs -[ x ])
        ≡⟨ cong₂ union (remove-fresh-idempotent xs x x#xs) (remove-cons xs x x#xs x ≈-refl) 
      union xs xs  where open ≡-Reasoning

leftregular : (xs ys : UniqueList)  union (union xs ys) xs  union xs ys
leftregular [] ys = unitʳ ys
leftregular (cons x xs x#xs) ys = ≠-cons-cong refl (begin
  union (union xs ys -[ x ]) (cons x xs x#xs) -[ x ]
    ≡⟨ cong  w  union w (cons x xs x#xs) -[ x ]) (remove-union xs ys x) 
  union (union (xs -[ x ]) (ys -[ x ])) (cons x xs x#xs) -[ x ]
    ≡⟨ remove-union (union (xs -[ x ]) (ys -[ x ])) (cons x xs x#xs) x 
  union (union (xs -[ x ]) (ys -[ x ]) -[ x ]) ((cons x xs x#xs) -[ x ])
    ≡⟨ cong₂ union (trans (remove-union (xs -[ x ]) (ys -[ x ]) x)
                          (cong₂ union (trans (remove-fresh-idempotent (xs -[ x ]) x (remove-removes xs x)) (remove-fresh-idempotent xs x x#xs))
                                       (remove-fresh-idempotent (ys -[ x ]) x (remove-removes ys x))))
                   (remove-cons xs x x#xs x ≈-refl) 
  union (union xs (ys -[ x ])) xs
    ≡⟨ leftregular xs (ys -[ x ]) 
  union xs (ys -[ x ])
    ≡⟨ cong  w  union w (ys -[ x ])) (sym (remove-fresh-idempotent xs x x#xs)) 
  union (xs -[ x ]) (ys -[ x ])
    ≡⟨ sym (remove-union xs ys x) 
  union xs ys -[ x ]
    ) where open ≡-Reasoning