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


open import Algebra
open import Relation.Binary
open import Algebra.Monus
open import Level

module Data.MonoidalHeap {s} {𝑆 : Type s} (monus : TMPOM 𝑆) where

open TMPOM monus

open import Prelude
open import Data.List using (List; _∷_; [])
import Data.Nat as 
import Data.Nat.Properties as 

𝒮 : Type s
𝒮 = 𝑆  𝑆

⟦_⇑⟧ : 𝑆  𝒮
⟦_⇑⟧ = _∙_

⟦_⇓⟧ : 𝒮  𝑆
 x ⇓⟧ = x ε

infixl 10 _⊙_
_⊙_ : (𝑆  A)  𝑆  𝑆  A
f  x = λ y  f (x  y)

infixr 6 _∹_&_
data Heap (V : 𝑆  Type a) : Type (a ℓ⊔ s) where
  [] : Heap V
  _∹_&_ : (key : 𝑆) (val : V key) (children : List (Heap (V  key)))  Heap V

Heap⋆ : (V : 𝑆  Type a)  Type (a ℓ⊔ s)
Heap⋆ V = List (Heap V)

private
  variable
    v : Level
    V : 𝑆  Type v

⊙ε : V  V  ε
⊙ε {V = V} i x = V (sym (ε∙ x) i)

lemma :  x y k  x  y  k   x ⇑⟧   y ⇑⟧   k ⇑⟧
lemma x y k x≡y∙k i z = (cong (_∙ z) x≡y∙k ; assoc y k z) i

merge : Heap V  Heap V  Heap V
merge [] ys = ys
merge (x  xv & xs) [] = x  xv & xs
merge {V = V} (x  xv & xs) (y  yv & ys) with x ≤|≥ y
... | inl (k , x≤y) = x  xv & (k  subst V x≤y yv & subst (List  Heap  _∘_ V) (lemma y x k x≤y) ys  xs)
... | inr (k , x≥y) = y  yv & (k  subst V x≥y xv & subst (List  Heap  _∘_ V) (lemma x y k x≥y) xs  ys)

mergeQs⁺ : Heap V  Heap⋆ V  Heap V
mergeQs⁺ x₁ [] = x₁
mergeQs⁺ x₁ (x₂  []) = merge x₁ x₂
mergeQs⁺ x₁ (x₂  x₃  xs) = merge (merge x₁ x₂) (mergeQs⁺ x₃ xs)

mergeQs : Heap⋆ V  Heap V
mergeQs [] = []
mergeQs (x  xs) = mergeQs⁺ x xs

singleton :  x  V x  Heap V
singleton x xv = x  xv & []

insert :  x  V x  Heap V  Heap V
insert x xv = merge (singleton x xv)

minView : Heap V  Maybe ( p × V p × Heap (V  p))
minView [] = nothing
minView (x  xv & xs) = just (x , xv , mergeQs xs)

variable
  v₁ v₂ : Level
  V₁ : 𝑆  Type v₁
  V₂ : 𝑆  Type v₂

mutual
  maps : (∀ {x}  V₁ x  V₂ x)  Heap⋆ V₁  Heap⋆ V₂
  maps f [] = []
  maps f (x  xs) = map f x  maps f xs

  map : (∀ {x}  V₁ x  V₂ x)  Heap V₁  Heap V₂
  map f [] = []
  map f (k  v & xs) = k  f v & maps f xs

mutual
  size : Heap V  
  size [] = zero
  size (_  _ & xs) = suc (sizes xs)

  sizes : Heap⋆ V  
  sizes [] = 0
  sizes (x  xs) = size x ℕ.+ sizes xs

open import Data.Maybe using (maybe)
open import Path.Reasoning
open import Cubical.Foundations.Prelude using (substRefl)

lemma₂ :  {x y : 𝑆  𝑆} xs (p : x  y)  sizes (subst (List  Heap  _∘_ V) p xs)  sizes xs
lemma₂ {V = V} xs = J  _ p  sizes (subst (List  Heap  _∘_ V) p xs)  sizes xs) (cong sizes (substRefl {B = List  Heap  _∘_ V} xs))

merge-size : (xs ys : Heap V)  size (merge xs ys)  size xs ℕ.+ size ys
merge-size [] ys = refl
merge-size (x  xv & xs) [] = sym (ℕ.+-idʳ _)
merge-size {V = V} (x  xv & xs) (y  yv & ys) with x ≤|≥ y
merge-size {V = V} (x  xv & xs) (y  yv & ys) | inr (k , x≥y) =
  suc (suc (sizes (subst (List  Heap  _∘_ V) (lemma x y k x≥y) xs)) ℕ.+ sizes ys) ≡˘⟨ ℕ.+-suc _ (sizes ys) 
  suc (sizes (subst (List  Heap  _∘_ V) (lemma x y k x≥y) xs)) ℕ.+ suc (sizes ys) ≡⟨ cong (ℕ._+ suc (sizes ys)) (cong suc (lemma₂ {V = V} xs (lemma x y k x≥y))) 
  suc (sizes xs) ℕ.+ suc (sizes ys) 
merge-size {V = V} (x  xv & xs) (y  yv & ys) | inl (k , x≤y) =
  suc (suc (sizes (subst (List  Heap  _∘_ V) (lemma y x k x≤y) ys)) ℕ.+ sizes xs) ≡˘⟨ ℕ.+-suc _ (sizes xs) 
  suc (sizes (subst (List  Heap  _∘_ V) (lemma y x k x≤y) ys)) ℕ.+ suc (sizes xs) ≡⟨ cong (ℕ._+ suc (sizes xs)) (cong suc (lemma₂ {V = V} ys (lemma y x k x≤y))) 
  suc (sizes ys) ℕ.+ suc (sizes xs) ≡⟨ ℕ.+-comm (suc (sizes ys)) (suc (sizes xs)) 
  suc (sizes xs) ℕ.+ suc (sizes ys) 

mutual
  minViewSizes : (xs : Heap⋆ V)  sizes xs  size (mergeQs xs)
  minViewSizes [] = refl
  minViewSizes (x  xs) = minViewSizes⁺ x xs

  minViewSizes⁺ : (x : Heap V)  (xs : Heap⋆ V)  sizes (x  xs)  size (mergeQs⁺ x xs)
  minViewSizes⁺ x₁ [] = ℕ.+-idʳ _
  minViewSizes⁺ x₁ (x₂  []) = cong  z  size x₁ ℕ.+ z) (ℕ.+-idʳ _) ; sym (merge-size x₁ x₂)
  minViewSizes⁺ x₁ (x₂  x₃  xs) =
    size x₁ ℕ.+ (size x₂ ℕ.+ sizes (x₃  xs)) ≡˘⟨ ℕ.+-assoc (size x₁) (size x₂) (sizes (x₃  xs)) 
    (size x₁ ℕ.+ size x₂) ℕ.+ sizes (x₃  xs) ≡⟨ cong ((size x₁ ℕ.+ size x₂) ℕ.+_) (minViewSizes⁺ x₃ xs) 
    (size x₁ ℕ.+ size x₂) ℕ.+ size (mergeQs⁺ x₃ xs) ≡˘⟨ cong (ℕ._+ size (mergeQs⁺ x₃ xs)) (merge-size x₁ x₂) 
    size (merge x₁ x₂) ℕ.+ size (mergeQs⁺ x₃ xs) ≡˘⟨ merge-size (merge x₁ x₂) (mergeQs⁺ x₃ xs) 
    size (merge (merge x₁ x₂) (mergeQs⁺ x₃ xs)) 

minViewSize : (xs : Heap V)  size xs  maybe zero (suc  size  snd  snd) (minView xs)
minViewSize [] = refl
minViewSize (x  xv & xs) = cong suc (minViewSizes xs)

zer : Heap⋆ V
zer = []

one : Heap⋆ V
one = []  []

open import Data.List using (_++_; concatMap)

_<+>_ : Heap⋆ V  Heap⋆ V  Heap⋆ V
_<+>_ = _++_

multIn : (p : 𝑆  𝑆)  (c :  {x y}  V (p x)  V y  V (p (x  y)))  (V  V  p)  Heap⋆ (V  p)  Heap⋆ V  Heap⋆ (V  p)
multIn {V = V} p c f [] ys = []
multIn {V = V} p c f ([]  xs) ys = maps f ys ++ multIn p c f xs ys
multIn {V = V} p c f (x  xv & xc  xs) ys = x  xv & multIn (p   x ⇑⟧)  v₁ v₂  subst V (cong p (assoc x _ _)) (c v₁ v₂)) (c xv) xc ys  multIn p c f xs ys

appl : (∀ {x y}  V x  V y  V (x  y))  Heap⋆ V  Heap⋆ V  Heap⋆ V
appl {V = V} f xs ys = multIn {V = V} id f id xs ys