------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of the generalised view of appending two lists
------------------------------------------------------------------------

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

module Data.List.Relation.Ternary.Appending.Properties where

open import Data.List.Base using (List; [])
open import Data.List.Relation.Ternary.Appending
open import Data.List.Relation.Binary.Pointwise as Pw using (Pointwise; []; _∷_)
open import Data.Product.Base as Product using (∃-syntax; _×_; _,_)
open import Function.Base using (id)
open import Data.List.Relation.Binary.Pointwise.Base as Pw using (Pointwise; []; _∷_)
open import Data.List.Relation.Binary.Pointwise.Properties as Pw using (transitive)
open import Level using (Level)
open import Relation.Binary.Core using (REL; Rel; _⇒_)
open import Relation.Binary.Definitions using (Trans)
open import Relation.Binary.PropositionalEquality.Core using (_≡_; refl)
open import Relation.Binary.Construct.Composition using (_;_)

private
  variable
    a  l r : Level
    A A′ B B′ C C′ D D′ : Set a
    R S T U V W X Y : REL A B 
    as bs cs ds : List A

module _  (RST : Trans R S T) (USV : Trans U S V) where

  respʳ-≋ : Appending R U as bs cs  Pointwise S cs ds  Appending T V as bs ds
  respʳ-≋ ([]++ rs) es       = []++ Pw.transitive USV rs es
  respʳ-≋ (l  lrs) (e  es) = RST l e  respʳ-≋ lrs es

module _  {T : REL A B l} (RST : Trans R S T)
          {W : REL A B r} (ERW : Trans U V W)
          where

  respˡ-≋ :  {as′ bs′}  Pointwise R as′ as  Pointwise U bs′ bs 
            Appending S V as bs cs  Appending T W as′ bs′ cs
  respˡ-≋ []         esʳ ([]++ rs) = []++ Pw.transitive ERW esʳ rs
  respˡ-≋ (  esˡ) esʳ (l  lrs) = RST  l  respˡ-≋ esˡ esʳ lrs

conicalˡ : Appending R S as bs []  as  []
conicalˡ ([]++ rs) = refl

conicalʳ : Appending R S as bs []  bs  []
conicalʳ ([]++ []) = refl

through→ :
  (R  (S ; T)) 
  ((U ; V)  (W ; T)) 
  ∃[ xs ] Pointwise U as xs × Appending V R xs bs cs 
  ∃[ ys ] Appending W S as bs ys × Pointwise T ys cs
through→ f g (_ , [] , []++ rs) =
  let _ , rs′ , ps′ = Pw.unzip (Pw.map f rs) in
  _ , []++ rs′ , ps′
through→ f g (_ , p  ps , l  lrs) =
  let _ , l′ , p′ = g (_ , p , l) in
  Product.map _ (Product.map (l′ ∷_) (p′ ∷_)) (through→ f g (_ , ps , lrs))

through← :
  ((R ; S)  T) 
  ((U ; S)  (V ; W)) 
  ∃[ ys ] Appending U R as bs ys × Pointwise S ys cs 
  ∃[ xs ] Pointwise V as xs × Appending W T xs bs cs
through← f g (_ , []++ rs′ , ps′) =
  _ , [] , []++ (Pw.transitive  r′ p′  f (_ , r′ , p′)) rs′ ps′)
through← f g (_ , l′  lrs′ , p′  ps′) =
  let _ , p , l = g (_ , l′ , p′) in
  Product.map _ (Product.map (p ∷_) (l ∷_)) (through← f g (_ , lrs′ , ps′))

assoc→ :
  (R  (S ; T)) 
  ((U ; V)  (W ; T)) 
  ((Y ; V)  X) 
  ∃[ xs ] Appending Y U as bs xs × Appending V R xs cs ds 
  ∃[ ys ] Appending W S bs cs ys × Appending X T as ys ds
assoc→ f g h (_ , []++ rs , lrs′) =
  let _ , mss , ss′ = through→ f g (_ , rs , lrs′) in
  _ , mss , []++ ss′
assoc→ f g h (_ , l  lrs , l′  lrs′) =
  Product.map₂ (Product.map₂ (h (_ , l , l′) ∷_)) (assoc→ f g h (_ , lrs , lrs′))

assoc← :
  ((S ; T)  R) 
  ((W ; T)  (U ; V)) 
  (X  (Y ; V)) 
  ∃[ ys ] Appending W S bs cs ys × Appending X T as ys ds 
  ∃[ xs ] Appending Y U as bs xs × Appending V R xs cs ds
assoc← f g h (_ , mss , []++ ss′) =
  let _ , rs , lrs′ = through← f g (_ , mss , ss′) in
  _ , []++ rs , lrs′
assoc← f g h (_ , mss , m′  mss′) =
  let _ , l , l′ = h m′ in
  Product.map _ (Product.map (l ∷_) (l′ ∷_)) (assoc← f g h (_ , mss , mss′))