{-# OPTIONS --no-positivity-check --no-termination-check --allow-unsolved-metas #-}

open import Algebra
open import Prelude
open import Relation.Binary
open import WellFounded
open import Algebra.Monus
open import Data.Maybe

module Control.Comonad.IntervalHeap {s} {𝑆 : Type s}
  (mon : TMAPOM 𝑆)
  {𝑊 : 𝑆  Type s  Type s}
  (comon : GradedComonad (TMAPOM.monoid mon) 𝑊)
  where

open TMAPOM mon
open GradedComonad comon renaming (map to cmap)

Weighted : (𝑆  Type a  Type b)  Type a  Type (s ℓ⊔ b)
Weighted C A =  w × C w A

private variable u v w : 𝑆

module OnFunctor {𝐹 : Type s  Type s} (functor : Functor 𝐹) where
  open Functor functor renaming (map to fmap)

  record Cofree⁺ (w : 𝑆) (A : Type s) : Type s where
    constructor ⟪_⟫
    field step : 𝑊 w (A × 𝐹 (Weighted Cofree⁺ A))
  open Cofree⁺ public

  Cofree : Type s  Type s
  Cofree = Cofree⁺ ε

  extend′ : (Cofree A  B)  Cofree⁺ w A  Cofree⁺ w B
  extend′ f x .step = x .step =>>[ ∙ε _ ] λ ys  f  ys  , fmap (map₂ (extend′ f)) (snd (extract ys)) 

  extract′ : Cofree A  A
  extract′ = fst  extract  step

  traceT : (𝑊 ε A  B)  (𝑊 ε A  𝐹 (Weighted 𝑊 A))  𝑊 w A  Cofree⁺ w B
  traceT f c r .step = r =>>[ ∙ε _ ] λ rs  f rs , fmap (map₂ (traceT f c)) (c rs)

  iterT : (𝑊 ε A  𝐹 (Weighted 𝑊 A))  𝑊 ε A  Cofree A
  iterT = traceT extract

  ana : (𝐺 : 𝑆  Type s)  (∀ {w}  𝐺 w  𝑊 w (A × 𝐹 ( u × 𝐺 u)))  𝐺 w  Cofree⁺ w A
  ana 𝐺 alg x .step = cmap (map₂ (fmap (map₂ (ana 𝐺 alg)))) (alg x)

module AsHeap (_<*>_ :  {w A B}  𝑊 w (A  B)  𝑊 w A  𝑊 w B) where
  open import Data.List.Properties using (listFunctor)
  open import Data.List using (List; _∷_; [])
  open OnFunctor listFunctor
  Heap : Type s  Type s
  Heap = Weighted Cofree⁺ 

  _∪_ : Heap A  Heap A  Heap A
  ( , xs)  ( , ys) with  ≤|≥ 
  ... | inl (k , y≡x∙k) =  ,  (step ys =>>[ sym y≡x∙k ] λ { y (x , xs)  x , ((k ,  y )  xs)}) <*> step xs 
  ... | inr (k , x≡y∙k) =  ,  (step xs =>>[ sym x≡y∙k ] λ { x (y , ys)  y , ((k ,  x )  ys)}) <*> step ys 

  merge⁺ : Heap A  List (Heap A)  Heap A
  merge⁺ x [] = x
  merge⁺ x₁ (x₂  []) = x₁  x₂
  merge⁺ x₁ (x₂  x₃  xs) = (x₁  x₂)  merge⁺ x₃ xs

  merge : List (Heap A)  Maybe (Heap A)
  merge [] = nothing
  merge (x  xs) = just (merge⁺ x xs)

  
  open import Data.Maybe.Properties using (maybeFunctor)
  open import Data.Maybe using (mapMaybe)

  module L = OnFunctor maybeFunctor

  search : Cofree⁺ w A  L.Cofree⁺ w A
  search = L.ana (flip Cofree⁺ _) (cmap (map₂ merge)  step)

  data Tree {a} (A : Type a) : Type a where
    [_] : A  Tree A
    _*_ : Tree A  Tree A  Tree A

  List⁺ : Type  Type
  List⁺ A = A × List A

  -- huffman′ : Heap A → Tree A
  -- huffman′ = {!!}
  
-- data Heap (A : Type s) : Type s where
--   _◃_ : (w : 𝑆) → (xs : 𝐹 w (A × List (Heap A))) → Heap A

-- extend : (Heap A → B) → Heap A → Heap B
-- extend f (w ◃ xs) = w ◃ (xs =>>[ ∙ε w ] (λ ys → f (ε ◃ ys) , Lmap (extend f) (snd (extract ys))))

-- module _ (2-monoid : ∀ {A B w} → 𝐹 w A → 𝐹 w B → 𝐹 w (A × B)) where
--   _∪_ : Heap A → Heap A → Heap A
--   (xw ◃ xs) ∪ (yw ◃ ys) with xw ≤|≥ yw
--   ... | inl (k , p) = xw ◃ map (λ { (y , (x , xs)) → x , (k ◃ y) ∷ xs }) (2-monoid (ys =>>[ sym p ] id) xs)
--   ... | inr (k , p) = yw ◃ map (λ { (x , (y , ys)) → y , (k ◃ x) ∷ ys }) (2-monoid (xs =>>[ sym p ] id) ys)

-- -- mutual
-- --   record Heap (A : Type a) : Type (s ℓ⊔ a) where
-- --     inductive; constructor _◃_
-- --     field
-- --       hd : A
-- --       tl : Next A

-- --   record Next {a} (A : Type a) : Type (s ℓ⊔ a) where
-- --     coinductive; constructor ⟪_⟫
-- --     field next : Span A

-- --   data Span {a} (A : Type a) : Type (s ℓ⊔ a) where
-- --     [] : Span A
-- --     until : (s : 𝑆) → (s≢ε : s ≢ ε) → (xs : Heap A) → Span A
-- -- open Heap public
-- -- open Next public

-- -- State : Type a → Type _
-- -- State A = 𝑆 → A × 𝑆

-- -- pop′ : (s : 𝑆) → Acc _<_ s → Heap A → A × 𝑆
-- -- pop′ s₂ a xs with xs .tl .next
-- -- pop′ s₂ a xs | [] = xs .hd , ε
-- -- pop′ s₂ a xs | until s₁ s₁≢ε ys with s₁ ≤? s₂
-- -- pop′ s₂ a xs | until s₁ s₁≢ε ys | no s₁≰s₂ = xs .hd , fst (<⇒≤ s₁≰s₂)
-- -- pop′ s₂ (acc wf) xs | until s₁ s₁≢ε ys | yes (k₁ , s₂≡s₁∙k₁) = pop′ k₁ (wf k₁ lemma) ys
-- --   where
-- --   lemma : k₁ < s₂
-- --   lemma (k₂ , k₁≡s₂∙k₂) = s₁≢ε (zeroSumFree s₁ k₂ (sym (cancel k₁ _ _ p)))
-- --     where
-- --     p : k₁ ∙ ε ≡ k₁ ∙ (s₁ ∙ k₂)
-- --     p = ∙ε k₁ ; k₁≡s₂∙k₂ ; cong (_∙ k₂) s₂≡s₁∙k₁ ; cong (_∙ k₂) (comm s₁ k₁) ; assoc k₁ s₁ k₂

-- -- pop : Heap A → State A
-- -- pop xs s = pop′ s (wf s) xs

-- -- mutual
-- --   stepFrom : State A → (s : 𝑆) → Dec (s ≡ ε) → Span A
-- --   stepFrom f s (yes p) = []
-- --   stepFrom f s (no ¬p) = until s ¬p (tabulate (f ∘ (s ∙_)))

-- --   tabulate : State A → Heap A
-- --   tabulate f =
-- --     let x , s = f ε
-- --     in x ◃ λ where .next → stepFrom f s (s ≟ ε)

-- -- pop-ε : (xs : Heap A) (a : Acc _<_ ε) → pop′ ε a xs .fst ≡ xs .hd
-- -- pop-ε xs _ with xs .tl .next
-- -- pop-ε xs _ | [] = refl
-- -- pop-ε xs _ | until s s≢ε ys with s ≤? ε
-- -- pop-ε xs _ | until s s≢ε ys | no  s≰ε = refl
-- -- pop-ε xs _ | until s s≢ε ys | yes s≤ε = ⊥-elim (s≢ε (antisym s≤ε (positive s)))

-- -- -- slide : Heap A → Heap A
-- -- -- slide xs with xs .tl .next
-- -- -- slide xs | [] = xs
-- -- -- slide xs | [] = []

-- -- -- pop-tl : ∀ (x : A) s₁ (s₁≢ε : s₁ ≢ ε) xs s₂ → pop (x ◃ ⟪ until s₁ s₁≢ε xs ⟫) (s₁ ∙ s₂) ≡ pop xs s₂
-- -- -- pop-tl x s₁ s₁≢ε xs s₂ with s₁ ≤? (s₁ ∙ s₂)
-- -- -- pop-tl x s₁ s₁≢ε xs s₂ | no  s₁≰s₁∙s₂ = ⊥-elim (s₁≰s₁∙s₂ (s₂ , refl))
-- -- -- pop-tl x s₁ s₁≢ε xs s₂ | yes (k , s₁≤s₁∙s₂) =
-- -- --   let p = cancel s₁ s₂ k s₁≤s₁∙s₂
-- -- --   in {!!} ; cong (λ w → pop′ s₂ w xs) (isPropAcc {!!} (wf s₂))

-- -- -- seg-leftInv′ : (x : Heap A) → tabulate (pop x) ≡ x
-- -- -- seg-leftInv′ x = {!!}

-- -- -- mutual
-- -- --   seg-leftInv′ : (xs : Heap A) → stepFrom (pop xs) (pop xs ε .snd) (pop xs ε .snd ≟ ε) ≡ xs .tl .next
-- -- --   seg-leftInv′ (x ◃ xs) with pop (x ◃ xs) ε .snd ≟ ε
-- -- --   seg-leftInv′ (x ◃ xs) | yes s≡ε = {!!}
-- -- --   seg-leftInv′ (x ◃ xs) | no  s≢ε = {!!}

-- -- --   seg-leftInv : (x : Heap A) → tabulate (pop x) ≡ x
-- -- --   seg-leftInv (x ◃ xs) i .hd = pop-ε (x ◃ xs) (wf ε) i
-- -- --   seg-leftInv (x ◃ xs) i .tl .next = seg-leftInv′ (x ◃ xs) i

-- -- -- state-iso : Heap A ⇔ State A
-- -- -- state-iso .fun = pop
-- -- -- state-iso .inv = tabulate
-- -- -- state-iso .rightInv = {!!}
-- -- -- state-iso .leftInv  = {!!}