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

open import Algebra
open import Relation.Unary
open import Relation.Binary hiding (Decidable)

module Data.FingerTree.Split
  {r m}
  ( : Monoid r m)
  {s}
  { : Pred (Monoid.Carrier ) s}
  (ℙ-resp :  Respects (Monoid._≈_ ))
  (ℙ? : Decidable )
  where

open import Relation.Nullary using (¬_; yes; no; Dec)
open import Level using (_⊔_)
open import Data.Product
open import Function
open import Data.List as List using (List; _∷_; [])

open import Data.FingerTree.Measures 
open import Data.FingerTree.Structures 
open import Data.FingerTree.Reasoning 
open import Data.FingerTree.View  using (deepₗ; deepᵣ)
open import Data.FingerTree.Cons  using (listToTree)

open import Relation.Nullary using (Dec; yes; no)
open import Relation.Nullary.Decidable using (True; toWitness; False; toWitnessFalse)

open σ  ... 

{-# DISPLAY σ.μ _ = μ #-}

open Monoid  renaming (Carrier to 𝓡)

open import Data.FingerTree.Relation.Binary.Reasoning.FasterInference.Setoid setoid

infixr 5 _∣_
record _∣_ (left focus : 𝓡) : Set s where
  constructor ¬[_]ℙ[_]
  field
    ¬ℙ : ¬  left
    !ℙ :  (left  focus)
open _∣_

infixl 2 _≈◄⟅_⟆ _≈▻⟅_⟆ _≈⟅_∣_⟆ _◄_ _▻_
_◄_ :  {l f₁ f₂}  l  f₁  f₂  ¬  (l  f₁)  (l  f₁)  f₂
!ℙ (p  ¬ℙf) = ℙ-resp (sym (assoc _ _ _)) (!ℙ p)
¬ℙ (p  ¬ℙf) = ¬ℙf

_▻_ :  {l f₁ f₂}  l  f₁  f₂   (l  f₁)  l  f₁
!ℙ (p  ℙf) = ℙf
¬ℙ (p  ℙf) = ¬ℙ p

_≈◄⟅_⟆ :  {x y z}  x  y  x  z  z  y
¬ℙ (x⟅y⟆ ≈◄⟅ x≈z ) = ¬ℙ x⟅y⟆  ℙ-resp (sym x≈z)
!ℙ (x⟅y⟆ ≈◄⟅ x≈z ) = ℙ-resp (≪∙ x≈z) (!ℙ x⟅y⟆)

_≈▻⟅_⟆ :  {x y z}  x  y  y  z  x  z
¬ℙ (x⟅y⟆ ≈▻⟅ y≈z ) = ¬ℙ x⟅y⟆
!ℙ (x⟅y⟆ ≈▻⟅ y≈z ) = ℙ-resp (∙≫ y≈z) (!ℙ x⟅y⟆)

_≈⟅_∣_⟆ :  {x₁ y₁ x₂ y₂}  x₁  y₁  x₁  x₂  y₁  y₂  x₂  y₂
¬ℙ (x⟅y⟆ ≈⟅ x≈  y≈ ) = ¬ℙ x⟅y⟆  ℙ-resp (sym x≈)
!ℙ (x⟅y⟆ ≈⟅ x≈  y≈ ) = ℙ-resp (∙-cong x≈ y≈) (!ℙ x⟅y⟆)

¬∄ℙ :  {i}  ¬ (i  ε)
¬∄ℙ i⟅ε⟆ = ¬ℙ i⟅ε⟆ (ℙ-resp (identityʳ _) (!ℙ i⟅ε⟆))

module SplitStructures where
  record Split′ (i : 𝓡) {a b} (Σ : Set a) (A : Set b)  _ : σ Σ   _ : σ A  : Set (a  b  s) where
    constructor _∷⟨_⟩∷_[_]
    field
      left′  : Σ
      focus′ : A
      right′ : Σ
      proof′ : i  μ left′  μ focus′

  instance
    σ-Split′ :   {a b} {Σ : Set a} {A : Set b}  _ : σ Σ   _ : σ A  {i : 𝓡}  σ (Split′ i Σ A)
    μ  σ-Split′ {i = i}  (l ∷⟨ x ⟩∷ r [ _ ]) = i  (μ l  (μ x  μ r))

  infixl 2 _i≈[_]
  _i≈[_] :  {a b} {Σ : Set a} {A : Set b}  _ : σ Σ   _ : σ A    {i xs}  μ⟨ Split′ i Σ A ⟩≈ (i  xs)   {j}  i  j  μ⟨ Split′ j Σ A ⟩≈ (j  xs)
  xs ∷⟨ x ⟩∷ ys [ p₁ ] ⇑[ p₂ ] i≈[ i≈ ] = xs ∷⟨ x ⟩∷ ys [ p₁ ≈◄⟅ ≪∙ i≈  ] ⇑[ ≪∙ sym i≈  p₂  ≪∙ i≈ ]
  {-# INLINE _i≈[_] #-}

  open import Data.Empty using (⊥-elim)

  infixl 2 _≈ℙ_[_]
  record ⟪ℙ⟫ (x : 𝓡) : Set (s  r  m) where
    constructor _≈ℙ_[_]
    field
      result : Dec ( x)
      stored : 𝓡
      equiv  : stored  x
  open ⟪ℙ⟫

  ⟪ℙ?⟫ :  x  ⟪ℙ⟫ x
  result (⟪ℙ?⟫ x) = ℙ? x
  stored (⟪ℙ?⟫ x) = x
  equiv  (⟪ℙ?⟫ x) = refl

  module _ {a} {Σ : Set a}  _ : σ Σ  where
    splitList : (i : 𝓡)  (xs : List Σ)  i  μ xs  μ⟨ Split′ i (List Σ) Σ ⟩≈ (i  μ xs)
    splitList i [] s = ⊥-elim (¬∄ℙ s)
    splitList i (x  xs) s with ⟪ℙ?⟫ (i  μ x)
    ... | yes p ≈ℙ i∙x [ i∙x≈ ] = [] ∷⟨ x ⟩∷ xs [ s  p ≈◄⟅     ] ⇑[   ]
    ... | no ¬p ≈ℙ i∙x [ i∙x≈ ] with splitList i∙x xs (s  ¬p ≈◄⟅ sym i∙x≈ ) i≈[ i∙x≈ ]
    ... | ys ∷⟨ y ⟩∷ zs [ s′ ] ⇑[ ys≈ ] = (x  ys) ∷⟨ y ⟩∷ zs [ s′ ≈◄⟅    ] ⇑[   ] ≈[ ys≈ ]′ ≈[   ]

    splitNode :  i  (xs : Node Σ)  i  μ xs  μ⟨ Split′ i (List Σ) Σ ⟩≈ (i  μ xs)
    splitNode i xs s = do
      ys  nodeToList xs [ _ ∙> sz  sz ]
      splitList i ys (s ≈▻⟅ sym (_ ≈? _) )

    splitDigit :  i  (xs : Digit Σ)  i  μ xs  μ⟨ Split′ i (List Σ) Σ ⟩≈ (i  μ xs)
    splitDigit i xs s = digitToList xs [ _ ∙> sz  sz ] >>= λ ys  splitList i ys (s ≈▻⟅ sym (_ ≈? _) )

    splitTree-l :  i  (ls : Digit Σ)  (m : Tree  Node Σ )  (rs : Digit Σ)  i  μ ls  μ⟨ Split′ i (Tree Σ) Σ ⟩≈ (i  (μ ls  (μ m  μ rs)))
    splitTree-l i ls m rs s with splitDigit i ls s
    splitTree-l i ls m rs s | lsₗ ∷⟨ mₗ ⟩∷ rsₗ [ p ] ⇑[ l≈ ] = [ (   ⍮′ ≪∙ l≈  assoc _ _ _) ]≈ do
      ls′  listToTree lsₗ [ i ∙> (sz <∙ _)  sz ]
      rs′  deepₗ rsₗ m rs [ i ∙> (_ ∙> (_ ∙> sz))  sz ]
      ls′ ∷⟨ mₗ ⟩∷ rs′ [ p ≈◄⟅ ∙≫ sym (_ ≈? _)   ] 

    splitTree-r :  i  (ls : Digit Σ)  (m : Tree  Node Σ )  (rs : Digit Σ)   i∙ls∙m  i∙ls∙m   (i  μ ls  μ m)  (i  μ ls  μ m)  μ rs  μ⟨ Split′ i (Tree Σ) Σ ⟩≈ (i  (μ ls  (μ m  μ rs)))
    splitTree-r i ls m rs i′ i′≈ s with splitDigit i′ rs (s ≈◄⟅ sym i′≈ )
    splitTree-r i ls m rs i′ i′≈ s | lsᵣ ∷⟨ mᵣ ⟩∷ rsᵣ [ p ] ⇑[ r≈ ] = [ lemma ]≈ do
        ls′  deepᵣ ls m lsᵣ [ i ∙> (sz <∙ _)  sz ]
        rs′  listToTree rsᵣ [ i ∙> (_ ∙> (_ ∙> sz))  sz ]
        ls′ ∷⟨ mᵣ ⟩∷ rs′ [ p ≈◄⟅ ≪∙ i′≈    ⍮′ ∙≫ sym (μ ls′ ≈? (μ ls  (μ m  μ lsᵣ)))  ] 
      where
      lemma = begin
        i  (μ ls  (μ m  μ lsᵣ)  (μ mᵣ  μ rsᵣ))
          ≈⟨   
        i  μ ls  μ m  (μ lsᵣ  (μ mᵣ  μ rsᵣ))
          ≈⟨ ≪∙ sym i′≈ 
        i′  (μ lsᵣ  (μ mᵣ  μ rsᵣ))
          ≈⟨ r≈ 
        i′  μ rs
          ≈⟨ ≪∙ i′≈ 
        i  μ ls  μ m  μ rs
          ≈⟨   
        i  (μ ls  (μ m  μ rs)) 

  splitTree :  {a} {Σ : Set a}  _ : σ Σ 
              i
             (xs : Tree Σ)
             i  μ xs
             μ⟨ Split′ i (Tree Σ) Σ ⟩≈ (i  μ xs)
  splitTree i empty s = ⊥-elim (¬∄ℙ s)
  splitTree i (single x) s = empty ∷⟨ x ⟩∷ empty [ s ≈◄⟅    ] ⇑[   ]
  splitTree i (deep (𝓂  ls & m & rs ⇑[ 𝓂≈ ])) s with ⟪ℙ?⟫ (i  μ ls)
  ... | yes p₁ ≈ℙ i∙ls [ i∙ls≈ ] = splitTree-l i ls m rs ¬[ ¬ℙ s ]ℙ[ p₁ ]  ≈[ ∙≫ 𝓂≈ ]
  ... | no ¬p₁ ≈ℙ i∙ls [ i∙ls≈ ] with ⟪ℙ?⟫ (i∙ls  μ m)
  ... | no ¬p₂ ≈ℙ i∙ls∙m [ i∙ls∙m≈ ] = splitTree-r i ls m rs i∙ls∙m (i∙ls∙m≈  ≪∙ i∙ls≈) (s ≈▻⟅ sym 𝓂≈   ¬p₁ ≈◄⟅ sym i∙ls≈   ¬p₂ ≈◄⟅ ≪∙ i∙ls≈ ) ≈[ ∙≫ 𝓂≈ ]
  ... | yes p₂ ≈ℙ i∙ls∙m [ i∙ls∙m≈ ] with splitTree i∙ls m (s ≈▻⟅ sym 𝓂≈   ¬p₁ ≈◄⟅ sym i∙ls≈   p₂)
  ... | lsₘ ∷⟨ μmₘ  mₘ ⇑[ mₘ≈ ] ⟩∷ rsₘ [ sₘ ] ⇑[ m≈ ] with splitNode (i∙ls  μ lsₘ) mₘ (sₘ ≈▻⟅ sym mₘ≈ )
  ... | lsₗ ∷⟨ mₗ ⟩∷ rsₗ [ sₗ ] ⇑[ l≈ ] = [ lemma ]≈ do
        ll  deepᵣ ls lsₘ lsₗ [ i ∙> (sz <∙ _)  sz ]
        rr  deepₗ rsₗ rsₘ rs [ i ∙> (_ ∙> (μ mₗ ∙> sz))  sz ]
        ll ∷⟨ mₗ ⟩∷ rr [ sₗ ≈◄⟅ ≪∙ ≪∙ i∙ls≈    ⍮′ ∙≫ sym (_ ≈? _)  ] 
    where
    lemma = begin
      i  (μ ls  (μ lsₘ  μ lsₗ)  (μ mₗ  (μ rsₗ  (μ rsₘ  μ rs))))
        ≈⟨   
      i  μ ls  μ lsₘ  (μ lsₗ  (μ mₗ  μ rsₗ))  (μ rsₘ  μ rs)
        ≈⟨ ≪∙ ≪∙ ≪∙ sym i∙ls≈  l≈ <∙ (μ rsₘ  μ rs) 
      i∙ls  μ lsₘ  μ mₘ  (μ rsₘ  μ rs)
        ≈⟨ ≪∙ ∙≫ mₘ≈ 
      i∙ls  μ lsₘ  μmₘ  (μ rsₘ  μ rs)
        ≈⟨   
      i∙ls  (μ lsₘ  (μmₘ  μ rsₘ))  μ rs
        ≈⟨ m≈ <∙ μ rs 
      i∙ls  μ m  μ rs
        ≈⟨ ≪∙ ≪∙ i∙ls≈ 
      i  μ ls  μ m  μ rs
        ≈⟨   
      i  (μ ls  (μ m  μ rs))
        ≈⟨ ∙≫ 𝓂≈ 
      i  𝓂 


  init-ℙ :  {𝓂}  (¬ℙ⟨ε⟩ : False (ℙ? ε))  (ℙ⟨xs⟩ : True (ℙ? 𝓂))  ε  𝓂
  ¬ℙ (init-ℙ fl tr) = toWitnessFalse fl
  !ℙ (init-ℙ fl tr) = ℙ-resp (sym (identityˡ _)) (toWitness tr)

record Split {a} (Σ : Set a)  _ : σ Σ  : Set (a  r  m  s) where
  constructor _∷⟨_⟩∷_[_]
  field
    left : Tree Σ
    focus : Σ
    right : Tree Σ
    is-split : μ left  μ focus
open Split public

instance
  σ-Split :   {a} {Σ : Set a}  _ : σ Σ   σ (Split Σ)
  μ  σ-Split  (l ∷⟨ x ⟩∷ r [ _ ]) = μ l  (μ x  μ r)

open SplitStructures

split :  {a} {Σ : Set a}  _ : σ Σ 
       {¬ℙ⟨ε⟩ : False (ℙ? ε)}
       (xs : Tree Σ)
       {ℙ⟨xs⟩ : True (ℙ? (μ xs))}
       μ⟨ Split Σ ⟩≈ μ xs
split {¬ℙ⟨ε⟩ = ¬ℙ⟨ε⟩} xs {ℙ⟨xs⟩} with splitTree ε xs (init-ℙ ¬ℙ⟨ε⟩ ℙ⟨xs⟩) ≈[ identityˡ _ ]
... | xs′ ∷⟨ x ⟩∷ ys [ p ] ⇑[ p₂ ] = xs′ ∷⟨ x ⟩∷ ys [ p ≈◄⟅ identityˡ _  ] ⇑[  sym (identityˡ _)  p₂ ]