{-# OPTIONS --rewriting #-}

module Examples.Amortized.SplayTree.Balance where

open import Algebra.Cost

costMonoid = ℕ-CostMonoid
open CostMonoid costMonoid renaming (_+_ to _⊕_)

open import Calf costMonoid 
open import Calf.Data.Nat 
open import Calf.Data.List hiding (find)
open import Calf.Data.IsBounded costMonoid

open import Data.Nat as Nat using (ℕ; _<_; _≤?_; _<?_; zero)
open import Data.Nat.Properties as Nat using (module ≤-Reasoning; _≟_)

open import Relation.Binary.PropositionalEquality as Eq using (_≡_; _≢_; refl; module ≡-Reasoning)

open import Data.Nat.Logarithm

open import Examples.Amortized.SplayTree.Base
open import Examples.Amortized.SplayTree.SplayTree
open import Examples.Amortized.SplayTree.LaxHomomorphism 

open BST
open BSTHom 

data BSTOperation : Set where
  findKey : val nat → BSTOperation

apply : BSTOperation → (t : BST) → val (t .T) → cmp (F (t .T))
apply (findKey k) bst t = bind (F _) (bst .find t k) (λ (_ , t') → ret t')

fold-apply : (ops : List BSTOperation) → (bst : BST) → (t : val (bst .T)) → cmp (F (bst .T))
fold-apply [] bst t = ret t
fold-apply (op ∷ ops) bst t = 
  bind (F _) (fold-apply ops bst t) (λ t' → 
    bind (F _) (apply op bst t') ret)

commutes : (ops : List BSTOperation) → (t : val (SplayTree .T)) → 
    bind (F _) (fold-apply ops SplayTree t) (ST⇒LT .ϕ)
  ≤⁻[ F (ListTree .T) ]
    bind (F _) (ST⇒LT .ϕ t) (λ t' → fold-apply ops ListTree t')
commutes [] t = ≤⁻-refl
commutes (findKey k ∷ ops) t = 
  let open ≤⁻-Reasoning (F _) in 
  begin 
    bind (F _) (fold-apply ops SplayTree t) (λ t' → 
      bind (F _) (SplayTree .find t' k) (λ (_ , t'') → ST⇒LT .ϕ t''))
  ≲⟨ bind-monoʳ-≤⁻ (fold-apply ops SplayTree t) (λ t' → ST⇒LT .ϕ/find t' k) ⟩
    bind (F _) (fold-apply ops SplayTree t) (λ t' → 
      bind (F _) (ST⇒LT .ϕ t') (λ t'' → 
        bind (F _) (ListTree .find t'' k) (λ (_ , t''') → ret t''')))
  ≡⟨⟩
    bind (F _) (bind (F _) (fold-apply ops SplayTree t) (ST⇒LT .ϕ)) (λ t' → 
      bind (F _) (ListTree .find t' k) (λ (_ , t'') → ret t''))
  ≲⟨ bind-monoˡ-≤⁻ (λ t' → bind (F _) (ListTree .find t' k) (λ (_ , t'') → ret t'')) (commutes ops t) ⟩
    bind (F _) (bind (F _) (ST⇒LT .ϕ t) (λ t' → fold-apply ops ListTree t')) (λ t' → 
      bind (F _) (ListTree .find t' k) (λ (_ , t'') → ret t''))
  ∎

balance : (keys : val (list nat)) (ops : List BSTOperation) → 
  let 
    n = length (sort/val (deduplicate {R = _≡_} Nat._≟_ keys)) 
    m = length ops
  in 
  IsBounded (SplayTree .T) 
    (bind (F _) (SplayTree .fromList keys) (λ t → fold-apply ops SplayTree t))
      ((m * ((3 * ⌊log₂ n ⌋) + 1)) + (n * ⌊log₂ n ⌋))
balance keys ops = ST/bound keys ops
  where 
    LT/bound : (keys : val (list nat)) → (ops : List BSTOperation) →
        fold-apply ops ListTree keys
      ≤⁻[ F _ ] 
        step (F _) ((length (ops)) * ((3 * ⌊log₂ (length keys) ⌋) + 1)) (ret keys)
    LT/bound keys [] = ≤⁻-refl
    LT/bound keys (findKey k ∷ ops) with LT/bound keys ops
    ... | leq = 
      let open ≤⁻-Reasoning (F _) in
      begin
        bind (F _) (fold-apply ops ListTree keys) (λ l' → 
          step (F _) ((3 * ⌊log₂ (length l') ⌋) + 1) 
            (bind (F _) (listFind l' k) (λ _ → ret l')))
      ≡⟨ Eq.cong (λ e → bind (F _) (fold-apply ops ListTree keys) e) (funext λ l' → 
          Eq.cong (λ e → step (F _) ((3 * ⌊log₂ (length l') ⌋) + 1) e) (list/find/bind/lemma l' k)) ⟩
        bind (F _) (fold-apply ops ListTree keys) (λ l' → 
          step (F _) ((3 * ⌊log₂ (length l') ⌋) + 1) (ret l'))
      ≲⟨ bind-monoˡ-≤⁻ (((λ l' → step (F _) ((3 * ⌊log₂ (length l') ⌋) + 1) (ret l')))) leq ⟩
        step (F _) (((length (ops)) * ((3 * ⌊log₂ (length keys) ⌋) + 1)) + ((3 * ⌊log₂ (length keys) ⌋) + 1)) (ret keys)
      ≡⟨ Eq.cong (λ e → step (F _) e (ret keys)) 
          (Nat.+-comm ((length (ops)) * ((3 * ⌊log₂ (length keys) ⌋) + 1)) 
            ((3 * ⌊log₂ (length keys) ⌋) + 1)) ⟩
        step (F _) (((1 + length ops) * ((3 * ⌊log₂ (length keys) ⌋) + 1))) (ret keys)
      ∎
    
    ST/bound : (keys : val (list nat)) → (ops : List BSTOperation) →
      IsBounded (SplayTree .T) (bind (F _) (SplayTree .fromList keys) (λ t → 
        fold-apply ops SplayTree t))
          (((length ops) * ((3 * ⌊log₂ length (sort/val (deduplicate {R = _≡_} Nat._≟_ keys)) ⌋) + 1)) + 
              (length (sort/val (deduplicate {R = _≡_} Nat._≟_ keys)) * 
              ⌊log₂ length (sort/val (deduplicate {R = _≡_} Nat._≟_ keys)) ⌋))
    ST/bound keys ops with LT/bound (sort/val (deduplicate {R = _≡_} Nat._≟_ keys)) ops
    ... | leq = 
      let
        key-set = (sort/val (deduplicate {R = _≡_} Nat._≟_ keys))
        N = length (key-set)
        M = length (ops)
      in
      let open ≤⁻-Reasoning (F _) in
      begin
        bind (F _) (SplayTree .fromList keys) (λ t → 
          bind (F _) (fold-apply ops SplayTree t) (λ _ → ret triv))
      ≲⟨ bind-monoʳ-≤⁻ (SplayTree .fromList keys) (λ t → 
          bind-monoʳ-≤⁻ (fold-apply ops SplayTree t) (λ t' → 
            ST⇒LT .ϕ/total t')) ⟩
        bind (F _) (SplayTree .fromList keys) (λ t → 
          bind (F _) (fold-apply ops SplayTree t) (λ t' → 
            bind (F _) (ST⇒LT .ϕ t') (λ _ → ret triv)))
      ≡⟨⟩
        bind (F _) (SplayTree .fromList keys) (λ t → 
          bind (F _) (bind (F _) (fold-apply ops SplayTree t) (ST⇒LT .ϕ)) (λ _ →
            ret triv))
      ≲⟨ bind-monoʳ-≤⁻ (SplayTree .fromList keys) (λ t → 
          bind-monoˡ-≤⁻ (λ _ → ret triv) (commutes ops t)) ⟩
        bind (F _) (SplayTree .fromList keys) (λ t → 
          bind (F _) (bind (F _) (ST⇒LT .ϕ t) (λ t' → fold-apply ops ListTree t')) (λ _ →
            ret triv))
      ≡⟨⟩
        bind (F _) (SplayTree .fromList keys) (λ t → 
          bind (F _) (ST⇒LT .ϕ t) (λ t' → 
            bind (F _) (fold-apply ops ListTree t') (λ _ →
              ret triv)))
      ≡⟨⟩
        bind (F _) (bind (F _) (SplayTree .fromList keys) (ST⇒LT .ϕ)) (λ t →
          bind (F _) (fold-apply ops ListTree t) (λ _ → ret triv))
      ≲⟨ bind-monoˡ-≤⁻ (λ t → bind (F _) (fold-apply ops ListTree t) (λ _ → ret triv)) 
          (ST⇒LT .ϕ/fromList keys) ⟩
        bind (F _) (ListTree .fromList keys) (λ l → 
          bind (F _) (fold-apply ops ListTree l) (λ _ → 
            ret triv))
      ≡⟨⟩
        step (F _) (N * ⌊log₂ N ⌋) (
          bind (F _) (fold-apply ops ListTree key-set) 
            (λ _ → ret triv))
      ≲⟨ step-monoʳ-≤⁻ (N * ⌊log₂ N ⌋) 
          (bind-monoˡ-≤⁻ (λ _ → ret triv) leq) ⟩
        step (F _) (N * ⌊log₂ N ⌋) (
          bind {A = list nat} (F _) (step (F _) (M * ((3 * ⌊log₂ N ⌋) + 1)) (ret key-set)) (λ _ →
            ret triv))
      ≡⟨⟩
        step (F _) ((N * ⌊log₂ N ⌋) + (M * ((3 * ⌊log₂ N ⌋) + 1))) (ret triv)
      ≡⟨ Eq.cong (λ e → step (F _) e (ret triv)) 
          (Nat.+-comm (N * ⌊log₂ N ⌋) ((M * ((3 * ⌊log₂ N ⌋) + 1)))) ⟩
        step (F _) ((M * ((3 * ⌊log₂ N ⌋) + 1)) + (N * ⌊log₂ N ⌋)) (ret triv)
      ∎