open import Data.Sum using (_⊎_; inj₁; inj₂)

module OrnamentsAndBinomialHeaps
  (Val : Set) (_≤_ : Val  Val  Set) (_≤?_ : (x y : Val)  x  y  y  x) where

open import Function using (const; _∘_; case_of_)
open import Data.Empty using ()
open import Data.Unit using (; tt)
open import Data.Product as Product using (Σ; proj₁; proj₂; _,_; Σ-syntax; <_,_>; uncurry; _×_)
open import Data.Maybe as Maybe using (Maybe; just; nothing)
open import Data.Nat using (; zero; suc)
open import Data.List using (List; []; _∷_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; subst; cong)


--------
-- Datatype descriptions

data RDesc (I : Set) : Set₁ where
  ṿ   : (is : List I)  RDesc I
  σ   : (S : Set) (D : S  RDesc I)  RDesc I

syntax σ S  s  D) = σ[ s ∈ S ] D

Desc : Set  Set₁
Desc I = I  RDesc I

 : {I : Set}  List I  (I  Set)  Set
 []       X = 
 (i  is) X = X i ×  is X

⟦_⟧ : {I : Set}  RDesc I  (I  Set)  Set
 ṿ is   X =  is X
 σ S D  X = Σ[ s  S ]  D s  X

 : {I : Set}  Desc I  (I  Set)  (I  Set)
 D X i =  D i  X

_⇉_ : {I : Set}  (I  Set)  (I  Set)  Set
X  Y =  {i}  X i  Y i

data μ {I : Set} (D : Desc I) : I  Set where
  con :  D (μ D)  μ D

data ListTag : Set where
  ‘nil  : ListTag
  ‘cons : ListTag

NatD : Desc 
NatD tt = σ ListTag λ { ‘nil   ṿ []
                      ; ‘cons  ṿ (tt  []) }

ListD : Set  Desc 
ListD A tt = σ ListTag λ { ‘nil   ṿ []
                         ; ‘cons  σ[ _  A ] ṿ (tt  []) }

VecD : Set  Desc 
VecD A zero    = ṿ []
VecD A (suc n) = σ[ _  A ] ṿ (n  [])

data ConMenu : Set where
  ‘always  : ConMenu
  ‘indexed : ConMenu

Fin'D : Desc 
Fin'D n = σ ConMenu λ { ‘always   ṿ []
                      ; ‘indexed  case n of λ { zero     σ  λ ()
                                               ; (suc m)  ṿ (m  []) } }

List' : Set  Set
List' A = μ (ListD A) tt

pattern []'       = con (‘nil  , tt)
pattern _∷'_ a as = con (‘cons , a , as , tt)

_++'_ : {A : Set}  List' A  List' A  List' A
[]'       ++' ys = ys
(x ∷' xs) ++' ys = x ∷' (xs ++' ys)

mutual

  fold : {I : Set} {D : Desc I} {X : I  Set}   D X  X  μ D  X
  fold {D = D} f {i} (con ds) = f (mapFold (D i) f ds)

  mapFold : {I : Set} {D : Desc I} (D' : RDesc I)  {X : I  Set}  ( D X  X)   D'  (μ D)   D'  X
  mapFold (ṿ is)   f ds         = mapFold-Ṗ f is ds
  mapFold (σ S D') f (s , ds)   = s , mapFold (D' s) f ds

  mapFold-Ṗ : {I : Set} {D : Desc I} {X : I  Set}  ( D X  X)  (is : List I)   is (μ D)   is X
  mapFold-Ṗ f []       _        = tt
  mapFold-Ṗ f (i  is) (d , ds) = fold f d , mapFold-Ṗ f is ds


--------
-- Ornaments

data Inv {A B : Set} (f : A  B) : B  Set where
  ok : (x : A)  Inv f (f x)

und : {A B : Set} {f : A  B} {y : B}  Inv f y  A
und (ok x) = x

data Ė {I J : Set} (e : J  I) : List J  List I  Set where
  []  : Ė e [] []
  _∷_ : {j : J} {i : I} (eq : e j  i) {js : List J} {is : List I} (eqs : Ė e js is)  Ė e (j  js) (i  is)

data ROrn {I J : Set} (e : J  I) : RDesc I  RDesc J  Set₁ where
  ṿ   : {js : List J} {is : List I} (eqs : Ė e js is)  ROrn e (ṿ is) (ṿ js)
  σ   : (S : Set) {D : S  RDesc I} {E : S  RDesc J} (O : (s : S)  ROrn e (D s) (E s))  ROrn e (σ S D) (σ S E)
  Δ   : (T : Set) {D : RDesc I} {E : T  RDesc J} (O : (t : T)  ROrn e D (E t))  ROrn e D (σ T E)
     : {S : Set} (s : S) {D : S  RDesc I} {E : RDesc J} (O : ROrn e (D s) E)  ROrn e (σ S D) E

syntax Δ T  t  O) = Δ[ t ∈ T ] O

Orn : {I J : Set} (e : J  I) (D : Desc I) (E : Desc J)  Set₁
Orn {I} e D E = {i : I} (j : Inv e i)  ROrn e (D i) (E (und j))

erase : {I J : Set} {e : J  I} {D : RDesc I} {E : RDesc J}  ROrn e D E  {X : I  Set}   E  (X  e)   D  X
erase (ṿ []          ) tt       = tt
erase (ṿ (refl  eqs)) (x , xs) = x , erase (ṿ eqs) xs
erase (σ S O)          (s , xs) = s , erase (O s) xs
erase (Δ T O)          (t , xs) = erase (O t) xs
erase ( s O)          xs       = s , erase O xs

ornAlg : {I J : Set} {e : J  I} {D : Desc I} {E : Desc J} (O : Orn e D E)   E (μ D  e)  (μ D  e)
ornAlg O {j} = con  erase (O (ok j))

ornForget : {I J : Set} {e : J  I} {D : Desc I} {E : Desc J} (O : Orn e D E)  μ E  (μ D  e)
ornForget {E = E} O = fold (ornAlg {E = E} O)

! : {A : Set}  A  
! _ = tt

NatD-ListD : (A : Set)  Orn ! NatD (ListD A)
NatD-ListD A (ok tt) = σ ListTag λ { ‘nil   ṿ []
                                   ; ‘cons  Δ[ _  A ] ṿ (refl  []) }

ListD-VecD : (A : Set)  Orn ! (ListD A) (VecD A)
ListD-VecD A (ok zero   ) =  ‘nil  (ṿ [])
ListD-VecD A (ok (suc n)) =  ‘cons (σ[ _  A ] ṿ (refl  []))


--------
-- Ornamental descriptions

data ROrnDesc {I : Set} (J : Set) (e : J  I) : RDesc I  Set₁ where
  ṿ   : {is : List I} (js :  is (Inv e))  ROrnDesc J e (ṿ is)
  σ   : (S : Set) {D : S  RDesc I} (O : (s : S)  ROrnDesc J e (D s))  ROrnDesc J e (σ S D)
  Δ   : (T : Set) {D : RDesc I} (O : T  ROrnDesc J e D)  ROrnDesc J e D
     : {S : Set} (s : S) {D : S  RDesc I} (O : ROrnDesc J e (D s))  ROrnDesc J e (σ S D)

OrnDesc : {I : Set} (J : Set) (e : J  I) (D : Desc I)  Set₁
OrnDesc {I} J e D = {i : I} (j : Inv e i)  ROrnDesc J e (D i)

OrdListOD : OrnDesc Val ! (ListD Val)
OrdListOD (ok b) = σ ListTag λ { ‘nil   ṿ tt
                               ; ‘cons  σ[ x  Val ] Δ[ _  (b  x) ] ṿ (ok x , tt) }

Ṗ-toList : {I J : Set} {X : I  Set}  (X  const J)  (is : List I)   is X  List J
Ṗ-toList f []        tt        = []
Ṗ-toList f (i  is)  (x , xs)  = f x  Ṗ-toList f is xs

und-Ṗ : {I J : Set} {e : J  I} (is : List I)   is (Inv e)  List J
und-Ṗ = Ṗ-toList und

toRDesc : {I J : Set} {e : J  I} {D : RDesc I}  ROrnDesc J e D  RDesc J
toRDesc (ṿ {is} js) = ṿ (und-Ṗ is js)
toRDesc (σ S O)     = σ[ s  S ] toRDesc (O s)
toRDesc (Δ T O)     = σ[ t  T ] toRDesc (O t)
toRDesc ( s O)     = toRDesc O

⌊_⌋ : {I J : Set} {e : J  I} {D : Desc I}  OrnDesc J e D  Desc J
 O  j = toRDesc (O (ok j))

to≡ : {A B : Set} {f : A  B} {y : B}  (x : Inv f y)  f (und x)  y
to≡ (ok x) = refl

to≡-Ṗ : {I J : Set} {e : J  I} (is : List I) (js :  is (Inv e))  Ė e (und-Ṗ is js) is
to≡-Ṗ []       _        = []
to≡-Ṗ (i  is) (j , js) = to≡ j  to≡-Ṗ is js

toROrn : {I J : Set} {e : J  I} {D : RDesc I}  (O : ROrnDesc J e D)  ROrn e D (toRDesc O)
toROrn (ṿ js)  = ṿ (to≡-Ṗ _ js)
toROrn (σ S O) = σ[ s  S ] toROrn (O s)
toROrn (Δ T O) = Δ[ t  T ] toROrn (O t)
toROrn ( s O) =  s (toROrn O)

⌈_⌉ : {I J : Set} {e : J  I} {D : Desc I}  (O : OrnDesc J e D)  Orn e D  O 
 O  (ok j) = toROrn (O (ok j))


--------
-- Parallel composition

from≡ :  {A B} (f : A  B) {x y}  f x  y  Inv f y
from≡ f {x} refl = ok x

record _⋈_ {A B C : Set} (f : A  C) (g : B  C) : Set where
  constructor _,_
  field
    {c} : C
    a   : Inv f c
    b   : Inv g c

pull : {A B C : Set} {f : A  C} {g : B  C}  f  g  C
pull = _⋈_.c

pc-Ė : {I J K : Set} {e : J  I} {f : K  I} {is : List I} {js : List J} {ks : List K} 
       Ė e js is  Ė f ks is   is (Inv (pull {J} {K} {I} {e} {f}))
pc-Ė             []           []           = tt
pc-Ė {e = e} {f} (eeq  eeqs) (feq  feqs) = ok (from≡ e eeq , from≡ f feq) , pc-Ė eeqs feqs

mutual

  pcROrn :  {I J K} {e : J  I} {f : K  I} {D : RDesc I} {E : RDesc J} {F : RDesc K}  ROrn e D E  ROrn f D F  ROrnDesc (e  f) pull D
  pcROrn (ṿ eeqs) (ṿ feqs) = ṿ (pc-Ė eeqs feqs)
  pcROrn (ṿ eeqs) (Δ T P)  = Δ[ t  T ] pcROrn (ṿ eeqs) (P t)
  pcROrn (σ S O)  (σ .S P) = σ[ s  S ] pcROrn (O s) (P s)
  pcROrn (σ f O)  (Δ T P)  = Δ[ t  T ] pcROrn (σ f O) (P t)
  pcROrn (σ S O)  ( s P)  =  s (pcROrn (O s) P)
  pcROrn (Δ T O)  P        = Δ[ t  T ] pcROrn (O t) P
  pcROrn ( s O)  (σ S P)  =  s (pcROrn O (P s))
  pcROrn ( s O)  (Δ T P)  = Δ[ t  T ] pcROrn ( s O) (P t)
  pcROrn ( s O)  ( s' P) = Δ (s  s') (pcROrn-double∇ O P)

  pcROrn-double∇ : {I J K S : Set} {e : J  I} {f : K  I} {D : S  RDesc I} {E : RDesc J} {F : RDesc K} {s s' : S} 
                   ROrn e (D s) E  ROrn f (D s') F  s  s'  ROrnDesc (e  f) pull (σ S D)
  pcROrn-double∇ {s = s} O P refl =  s (pcROrn O P)

pcOrn :  {I J K} {e : J  I} {f : K  I} {D E F}  Orn e D E  Orn f D F  OrnDesc (e  f) pull D
pcOrn {e = e} {f} {D} {E} {F} O P (ok (j , k)) = pcROrn (O j) (P k)

OrdVec : Val    Set
OrdVec b n = μ  pcOrn {E =  OrdListOD } {F = VecD Val}  OrdListOD  (ListD-VecD Val)  (ok b , ok n)


--------
-- Optimised predicates

und-from≡ :  {A B} (f : A  B) {x y}  (eq : f x  y)  und (from≡ f eq)  x
und-from≡ f refl = refl

π₁ : {A B C : Set} {f : A  C} {g : B  C}  f  g  A
π₁ = und  _⋈_.a

diff-Ė-l :  {I J K} {e : J  I} {f : K  I} {is js ks} (eeqs : Ė e js is) (feqs : Ė f ks is)  Ė π₁ (und-Ṗ is (pc-Ė eeqs feqs)) js
diff-Ė-l         []           _            = []
diff-Ė-l {e = e} (eeq  eeqs) (feq  feqs) = und-from≡ e eeq  diff-Ė-l eeqs feqs

mutual

  diffROrn-l :  {I J K} {e : J  I} {f : K  I} {D E F}
               (O : ROrn e D E) (P : ROrn f D F)  ROrn π₁ E (toRDesc (pcROrn O P))
  diffROrn-l (ṿ eeqs) (ṿ feqs) = ṿ (diff-Ė-l eeqs feqs)
  diffROrn-l (ṿ eeqs) (Δ T P)  = Δ[ t  T ] diffROrn-l (ṿ eeqs) (P t)
  diffROrn-l (σ S O)  (σ .S P) = σ[ s  S ] diffROrn-l (O s) (P s)
  diffROrn-l (σ S O)  (Δ T P)  = Δ[ t  T ] diffROrn-l (σ S O) (P t)
  diffROrn-l (σ S O)  ( s P)  =  s (diffROrn-l (O s) P)
  diffROrn-l (Δ T O)  P        = σ[ t  T ] diffROrn-l (O t) P
  diffROrn-l ( s O)  (σ S P)  = diffROrn-l O (P s)
  diffROrn-l ( s O)  (Δ T P)  = Δ[ t  T ] diffROrn-l ( s O) (P t)
  diffROrn-l ( s O)  ( s' P) = Δ (s  s') (diffROrn-l-double∇ O P)

  diffROrn-l-double∇ :  {I J K} {e : J  I} {f : K  I} {S} {D E F} {s s' : S} 
                       (O : ROrn e (D s) E) (P : ROrn f (D s') F) (eq : s  s') 
                       ROrn π₁ E (toRDesc (pcROrn-double∇ {D = D} O P eq))
  diffROrn-l-double∇ O P refl = diffROrn-l O P

diffOrn-l :  {I J K} {e : J  I} {f : K  I} {D E F}
            (O : Orn e D E) (P : Orn f D F)  Orn π₁ E  pcOrn {E = E} {F} O P 
diffOrn-l {e = e} {f} {D} {E} {F} O P (ok (j , k)) = diffROrn-l (O j) (P k)

Ṗ-map : {I : Set} {X Y : I  Set}  (X  Y)  (is : List I)   is X   is Y
Ṗ-map f []       _        = tt
Ṗ-map f (i  is) (x , xs) = f x , Ṗ-map f is xs

erode : {I : Set} (D : RDesc I)  {J : I  Set}   D  J  ROrnDesc (Σ I J) proj₁ D
erode (ṿ is)  js         = ṿ (Ṗ-map  {i} j  ok (i , j)) is js)
erode (σ S D) (s , js)   =  s (erode (D s) js)

singOrn : {I : Set} (D : Desc I)  OrnDesc (Σ I (μ D)) proj₁ D
singOrn D (ok (i , con ds)) = erode (D i) ds

OptP : {I J : Set} {e : J  I} {D : Desc I} {E : Desc J} (O : Orn e D E) {i : I}  Inv e i  μ D i  Set
OptP {D = D} {E} O j x = μ  pcOrn {E = E} {F =  singOrn D } O  singOrn D   (j , (ok (_ , x)))

Ordered : Val  List' Val  Set
Ordered b = OptP {E =  OrdListOD }  OrdListOD  (ok b)


-------
-- Promotions and upgrades

record Promotion (X Y : Set) : Set₁ where
  field
    Predicate  : X  Set
    forget     : Y  X
    complement : (y : Y)  Predicate (forget y)
    promote    : (x : X)  Predicate x  Y
    coherence  : (x : X) (p : Predicate x)  forget (promote x p)  x

ornProm : {I J : Set} {e : J  I} {D : Desc I} {E : Desc J}  Orn e D E  (j : J)  Promotion (μ D (e j)) (μ E j)
ornProm {e = e} {D} {E} O j = record
  { Predicate  = OptP {E = E} O (ok j)
  ; forget     = ornForget O
  ; complement = complement
  ; promote    = promote
  ; coherence  = coherence
  }
  where
    postulate  -- derived from the pullback property of parallel composition
      complement : (y : μ E j)  OptP {E = E} O (ok j) (ornForget O y)
      promote    : (x : μ D (e j))  OptP {E = E} O (ok j) x  μ E j
      coherence  : (x : μ D (e j)) (com : OptP {E = E} O (ok j) x)  ornForget O (promote x com)  x

record Upgrade (X Y : Set) : Set₁ where
  field
    Predicate  : X  Set
    Coherence  : X  Y  Set
    complement : (x : X) (y : Y)  Coherence x y  Predicate x
    promote    : (x : X)  Predicate x  Y
    coherence  : (x : X) (p : Predicate x)  Coherence x (promote x p)

toUpgrade : {X Y : Set}  Promotion X Y  Upgrade X Y
toUpgrade p = record
  { Predicate  = Promotion.Predicate p
  ; Coherence  = λ x y   Promotion.forget p y  x
  ; complement = λ {._ y refl   Promotion.complement p y }
  ; promote    = Promotion.promote p
  ; coherence  = Promotion.coherence p }

_⇀_ : {X Y Z W : Set}  Promotion X Y  Upgrade Z W  Upgrade (X  Z) (Y  W)
_⇀_ {X} {Y} p u = record
  { Predicate  = λ f  (x : X)  Promotion.Predicate p x  Upgrade.Predicate u (f x)
  ; Coherence  = λ f g  (y : Y)  Upgrade.Coherence u (f (Promotion.forget p y)) (g y)
  ; complement = λ f g c x com  Upgrade.complement u (f x) (g (Promotion.promote p x com))
                                   (subst  x'  Upgrade.Coherence u (f x') (g (Promotion.promote p x com)))
                                          (Promotion.coherence p x com) (c (Promotion.promote p x com)))
  ; promote    = λ f com  uncurry (Upgrade.promote u)  Product.map f (com _)  < Promotion.forget p , Promotion.complement p >
  ; coherence  = λ f com y  Upgrade.coherence u (f (Promotion.forget p y)) (com (Promotion.forget p y) (Promotion.complement p y)) }

new : (I : Set) {X : Set} {Y : I  Set} (u : (i : I)  Upgrade X (Y i))  Upgrade X ((i : I)  Y i)
new I u = record
  { Predicate  = λ x   (i : I)  Upgrade.Predicate (u i) x
  ; Coherence  = λ x y  (i : I)  Upgrade.Coherence (u i) x (y i)
  ; complement = λ x y coh i  Upgrade.complement (u i) x (y i) (coh i)
  ; promote    = λ x com i  Upgrade.promote (u i) x (com i)
  ; coherence  = λ x com i  Upgrade.coherence (u i) x (com i) }

syntax new I  i  u) = ∀⁺[ i ∈ I ] u

new' : (I : Set) {X : Set} {Y : I  Set} (u : (i : I)  Upgrade X (Y i))  Upgrade X ({i : I}  Y i)
new' I u = record
  { Predicate  = λ x   {i : I}  Upgrade.Predicate (u i) x
  ; Coherence  = λ x y  {i : I}  Upgrade.Coherence (u i) x (y {i})
  ; complement = λ x y coh {i}  Upgrade.complement (u i) x (y {i}) (coh {i})
  ; promote    = λ x com {i}  Upgrade.promote (u i) x (com {i})
  ; coherence  = λ x com {i}  Upgrade.coherence (u i) x (com {i}) }

syntax new' I  i  u) = ∀⁺[[ i ∈ I ]] u

fromUpgrade : {X Y : Set} (u : Upgrade X Y)  Promotion X (Σ[ y  Y ] Σ[ x  X ] Upgrade.Coherence u x y)
fromUpgrade u = record
 { Predicate  = Upgrade.Predicate u
 ; forget     = proj₁  proj₂
 ; complement = λ { (y , x , coh)  Upgrade.complement u x y coh }
 ; promote    = λ x com  Upgrade.promote u x com , x , Upgrade.coherence u x com
 ; coherence  = λ _ _  refl }


--------
-- McBride's instance searching technique (ICFP 2014)

if_then_else_ : {A B C : Set}  A  B  ({{_ : A}}  C)  ({{_ : B}}  C)  C
if inj₁ a then t else u = t {{a}}
if inj₂ b then t else u = u {{b}}

record Σᴵ (A : Set) (B : A  Set) : Set where
  constructor ⟨_⟩ 
  field
    fst     : A
    {{snd}} : B fst

infix 2 Σᴵ-syntax

Σᴵ-syntax : (A : Set)  (A  Set)  Set
Σᴵ-syntax = Σᴵ

syntax Σᴵ-syntax A  x  B) = Σᴵ[ x ∈ A ] B


--------
-- Binomial trees

descend :   List 
descend zero    = []
descend (suc n) = n  descend n

data BForest :   Val  Set where
  forest : {r : } {x : Val}   (descend r)  i  Σᴵ[ t  Σ Val (BForest i) ] (x  proj₁ t))  BForest r x

pattern _◃_ t ts = forest ( t  , ts)

BTree :   Set
BTree r = Σ Val (BForest r)

root : {r : }  BTree r  Val
root = proj₁

children : {r : } (t : BTree r)  BForest r (root t)
children = proj₂

attach : {r : } (t u : BTree r) {{_ : root u  root t}}  BTree (suc r)
attach t (y , forest us) = y , forest ( t  , us)

link : {r : }  BTree r  BTree r  BTree (suc r)
link t u = if root t ≤? root u then attach u t else attach t u


--------
-- Binary numbers and binomial heaps

data BinTag : Set where
  `nil  : BinTag
  `zero : BinTag
  `one  : BinTag

BinD : Desc 
BinD tt = σ BinTag λ { `nil   ṿ []
                     ; `zero  ṿ (tt  [])
                     ; `one   ṿ (tt  []) }

Bin : Set
Bin = μ BinD tt

pattern nilᴮ    = con (`nil  ,     tt)
pattern zeroᴮ b = con (`zero , b , tt)
pattern oneᴮ  b = con (`one  , b , tt)

BHeapOD : OrnDesc  ! BinD
BHeapOD (ok r) = σ BinTag λ { `nil   ṿ tt
                            ; `zero  ṿ (ok (suc r) , tt)
                            ; `one   Δ[ _  BTree r ] ṿ (ok (suc r) , tt) }

BHeap :   Set
BHeap = μ  BHeapOD 

toBin : {r : }  BHeap r  Bin
toBin = ornForget  BHeapOD 

BHeapᵁ :   Bin  Set
BHeapᵁ r b = OptP {E =  BHeapOD }  BHeapOD  (ok r) b

pattern nilᵁ     = con (    tt)
pattern zeroᵁ  h = con (h , tt)
pattern oneᵁ t h = con (t , h , tt)

Bin-BHeap : (r : )  Promotion Bin (BHeap r)
Bin-BHeap = ornProm  BHeapOD 


--------
-- Increment and insertion

incr : Bin  Bin
incr nilᴮ      = oneᴮ nilᴮ
incr (zeroᴮ b) = oneᴮ b
incr (oneᴮ  b) = zeroᴮ (incr b)

incr-upg : Upgrade (Bin  Bin) ({r : }  BTree r  BHeap r  BHeap r)
incr-upg = ∀⁺[[ r   ]] ∀⁺[ _  BTree r ] (Bin-BHeap r  toUpgrade (Bin-BHeap r))

insTᵁ : Upgrade.Predicate incr-upg incr
     -- {r : ℕ} → BTree r → (b : Bin) → BHeapᵁ r b → BHeapᵁ r (incr b)
insTᵁ t nilᴮ      nilᵁ       = oneᵁ t nilᵁ
insTᵁ t (zeroᴮ b) (zeroᵁ  h) = oneᵁ t h
insTᵁ t (oneᴮ  b) (oneᵁ u h) = zeroᵁ (insTᵁ (link t u) b h)

insT : {r : }  BTree r  BHeap r  BHeap r
insT = Upgrade.promote incr-upg incr insTᵁ

incr-insT-coherence : Upgrade.Coherence incr-upg incr insT
                   -- {r : ℕ} (t : BTree r) (h : BHeap r) → toBin (insT t h) ≡ incr (toBin h)
incr-insT-coherence = Upgrade.coherence incr-upg incr insTᵁ


--------
-- Decrement and extraction

decr : Bin  Maybe Bin
decr nilᴮ      = nothing
decr (zeroᴮ b) = Maybe.map oneᴮ (decr b)
decr (oneᴮ  b) = just (zeroᴮ b)

_⁺×_ : (X : Set) {Y Z : Set}  Promotion Y Z  Promotion Y (X × Z)
X ⁺× p = record
  { Predicate  = λ y  X × Promotion.Predicate p y
  ; forget     = Promotion.forget p  proj₂
  ; complement = λ { (x , z)  x , Promotion.complement p z }
  ; promote    = λ {y (x , com)  x , Promotion.promote p y com }
  ; coherence  = λ { y (x , com)  Promotion.coherence p y com } }

data Maybe' {A : Set} (X : A  Set) : Maybe A  Set where
  just    : {a : A}  X a  Maybe' X (just a)
  nothing : Maybe' X nothing

MaybeP : {A B : Set}  Promotion A B  Promotion (Maybe A) (Maybe B)
MaybeP p = record
  { Predicate  = Maybe' (Promotion.Predicate p)
  ; forget     = Maybe.map (Promotion.forget p)
  ; complement = λ { nothing   nothing
                   ; (just b)  just (Promotion.complement p b) }
  ; promote    = λ { nothing  _         nothing
                   ; (just a) (just x)  just (Promotion.promote p a x) }
  ; coherence  = λ { nothing  _         refl
                   ; (just a) (just x)  cong just (Promotion.coherence p a x) } }

mapMaybe' : {A B : Set} {X : A  Set} {Y : B  Set} 
            {f : A  B}  ({a : A}  X a  Y (f a))  {ma : Maybe A}  Maybe' X ma  Maybe' Y (Maybe.map f ma)
mapMaybe' f nothing  = nothing
mapMaybe' f (just b) = just (f b)

decr-upg : Upgrade (Bin  Maybe Bin) ({r : }  BHeap r  Maybe (BTree r × BHeap r))
decr-upg = ∀⁺[[ r   ]] (Bin-BHeap r  toUpgrade (MaybeP (BTree r ⁺× Bin-BHeap r)))

extractᵁ : Upgrade.Predicate decr-upg decr
        -- {r : ℕ} (b : Bin) → BHeapᵁ r b → Maybe' (λ b' → BTree r × BHeapᵁ r b') (decr b)
extractᵁ nilᴮ      nilᵁ       = nothing
extractᵁ (zeroᴮ b) (zeroᵁ  h) = mapMaybe'  { ((x , t  ts) , h')  (x , forest ts) , oneᵁ t h' }) (extractᵁ b h)
extractᵁ (oneᴮ  b) (oneᵁ x h) = just (x , zeroᵁ h)

extract : {r : }  BHeap r  Maybe (BTree r × BHeap r)
extract = Upgrade.promote decr-upg decr extractᵁ

decr-extract-coherence : Upgrade.Coherence decr-upg decr extract
                      -- {r : ℕ} (h : BHeap r) → Maybe.map (toBin ∘ proj₂) (extract h) ≡ decr (toBin h)
decr-extract-coherence = Upgrade.coherence decr-upg decr extractᵁ