{-# OPTIONS --rewriting #-} open import Examples.Sorting.Sequential.Comparable module Examples.Sorting.Sequential.MergeSort (M : Comparable) where open Comparable M open import Examples.Sorting.Sequential.Core M open import Calf costMonoid hiding (A) open import Calf.Data.Product open import Calf.Data.Bool open import Calf.Data.Nat open import Calf.Data.List using (list; []; _∷_; _∷ʳ_; [_]; length; _++_; reverse) open import Calf.Data.Equality using (_≡_; refl) open import Calf.Data.IsBoundedG costMonoid open import Calf.Data.IsBounded costMonoid open import Calf.Data.BigO costMonoid open import Relation.Nullary open import Relation.Nullary.Negation open import Relation.Binary.PropositionalEquality as Eq using (_≡_; refl; module ≡-Reasoning) open import Data.Sum using (inj₁; inj₂) open import Function open import Data.Nat as Nat using (ℕ; zero; suc; z≤n; s≤s; _+_; _*_; ⌊_/2⌋; ⌈_/2⌉) import Data.Nat.Properties as N open import Data.Nat.Square open import Data.Nat.Log2 open import Examples.Sorting.Sequential.MergeSort.Split M public open import Examples.Sorting.Sequential.MergeSort.Merge M public sort/clocked : cmp $ Π nat λ k → Π (list A) λ l → Π (meta⁺ (⌈log₂ length l ⌉ Nat.≤ k)) λ _ → F (sort-result l) sort/clocked zero l h = ret (l , refl , short-sorted (⌈log₂n⌉≡0⇒n≤1 (N.n≤0⇒n≡0 h))) sort/clocked (suc k) l h = bind (F (sort-result l)) (split l) λ ((l₁ , l₂) , length₁ , length₂ , l↭l₁++l₂) → let h₁ , h₂ = let open N.≤-Reasoning in (begin ⌈log₂ length l₁ ⌉ ≡⟨ Eq.cong ⌈log₂_⌉ length₁ ⟩ ⌈log₂ ⌊ length l /2⌋ ⌉ ≤⟨ log₂-mono (N.⌊n/2⌋≤⌈n/2⌉ (length l)) ⟩ ⌈log₂ ⌈ length l /2⌉ ⌉ ≤⟨ log₂-suc (length l) h ⟩ k ∎) , (begin ⌈log₂ length l₂ ⌉ ≡⟨ Eq.cong ⌈log₂_⌉ length₂ ⟩ ⌈log₂ ⌈ length l /2⌉ ⌉ ≤⟨ log₂-suc (length l) h ⟩ k ∎) in bind (F (sort-result l)) (sort/clocked k l₁ h₁) λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ h₂) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) sort/clocked/total : ∀ k l h → IsValuable (sort/clocked k l h) sort/clocked/total zero l h u = ↓ refl sort/clocked/total (suc k) l h u rewrite Valuable.proof (split/total l u) = ↓ let ((l₁ , l₂) , length₁ , length₂ , l↭l₁++l₂) = Valuable.value (split/total l u) h₁ , h₂ = let open N.≤-Reasoning in (begin ⌈log₂ length l₁ ⌉ ≡⟨ Eq.cong ⌈log₂_⌉ length₁ ⟩ ⌈log₂ ⌊ length l /2⌋ ⌉ ≤⟨ log₂-mono (N.⌊n/2⌋≤⌈n/2⌉ (length l)) ⟩ ⌈log₂ ⌈ length l /2⌉ ⌉ ≤⟨ log₂-suc (length l) h ⟩ k ∎) , (begin ⌈log₂ length l₂ ⌉ ≡⟨ Eq.cong ⌈log₂_⌉ length₂ ⟩ ⌈log₂ ⌈ length l /2⌉ ⌉ ≤⟨ log₂-suc (length l) h ⟩ k ∎) (l₁' , l₁↭l₁' , sorted-l₁') = Valuable.value (sort/clocked/total k l₁ h₁ u) (l₂' , l₂↭l₂' , sorted-l₂') = Valuable.value (sort/clocked/total k l₂ h₂ u) in let open ≡-Reasoning in begin ( bind (F (sort-result l)) (sort/clocked k l₁ h₁) λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ h₂) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) ≡⟨ Eq.cong (λ e → bind (F (sort-result l)) e λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ h₂) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) (Valuable.proof (sort/clocked/total k l₁ h₁ u)) ⟩ ( bind (F (sort-result l)) (sort/clocked k l₂ h₂) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) ≡⟨ Eq.cong (λ e → bind (F (sort-result l)) e λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) (Valuable.proof (sort/clocked/total k l₂ h₂ u)) ⟩ ( bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) ≡⟨ Eq.cong (λ e → bind (F (sort-result l)) e λ (l' , l₁'++l₂'↭l , l'-sorted) → ret (l' , trans l↭l₁++l₂ (trans (++⁺-↭ l₁↭l₁' l₂↭l₂') l₁'++l₂'↭l) , l'-sorted) ) (Valuable.proof (merge/total (l₁' , l₂') (sorted-l₁' , sorted-l₂') u)) ⟩ ret _ ∎ sort/clocked/cost : cmp $ Π nat λ k → Π (list A) λ l → Π (meta⁺ (⌈log₂ length l ⌉ Nat.≤ k)) λ _ → F unit sort/clocked/cost k l _ = step⋆ (k * length l) sort/clocked/is-bounded : ∀ k l h → IsBoundedG (sort-result l) (sort/clocked k l h) (sort/clocked/cost k l h) sort/clocked/is-bounded zero l h = ≤⁻-refl sort/clocked/is-bounded (suc k) l h = bound/bind/const {e = split l} {f = λ ((l₁ , l₂) , length₁ , length₂ , l↭l₁++l₂) → bind (F (sort-result l)) (sort/clocked k l₁ _) λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ _) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted) } 0 (suc k * length l) (split/is-bounded l) λ ((l₁ , l₂) , length₁ , length₂ , l↭l₁++l₂) → Eq.subst (IsBounded (sort-result l) $ bind (F (sort-result l)) (sort/clocked k l₁ _) λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ _) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted) ) (let open ≡-Reasoning in begin k * length l₁ + (k * length l₂ + (length l₁ + length l₂)) ≡˘⟨ N.+-assoc (k * length l₁) (k * length l₂) (length l₁ + length l₂) ⟩ (k * length l₁ + k * length l₂) + (length l₁ + length l₂) ≡˘⟨ Eq.cong (_+ (length l₁ + length l₂)) (N.*-distribˡ-+ k (length l₁) (length l₂)) ⟩ k * (length l₁ + length l₂) + (length l₁ + length l₂) ≡˘⟨ N.+-comm (length l₁ + length l₂) (k * (length l₁ + length l₂)) ⟩ suc k * (length l₁ + length l₂) ≡˘⟨ Eq.cong (suc k *_) (length-++ l₁) ⟩ suc k * (length (l₁ ++ l₂)) ≡˘⟨ Eq.cong (suc k *_) (↭-length l↭l₁++l₂) ⟩ suc k * length l ∎) $ bound/bind/const {e = sort/clocked k l₁ _} {f = λ (l₁' , l₁↭l₁' , sorted-l₁') → bind (F (sort-result l)) (sort/clocked k l₂ _) λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted) } (k * length l₁) (k * length l₂ + (length l₁ + length l₂)) (sort/clocked/is-bounded k l₁ _) λ (l₁' , l₁↭l₁' , sorted-l₁') → bound/bind/const {e = sort/clocked k l₂ _} {f = λ (l₂' , l₂↭l₂' , sorted-l₂') → bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted) } (k * length l₂) (length l₁ + length l₂) (sort/clocked/is-bounded k l₂ _) λ (l₂' , l₂↭l₂' , sorted-l₂') → Eq.subst (IsBounded (sort-result l) $ bind (F (sort-result l)) (merge (l₁' , l₂') (sorted-l₁' , sorted-l₂')) λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted) ) (let open ≡-Reasoning in begin length l₁' + length l₂' + 0 ≡⟨ N.+-identityʳ (length l₁' + length l₂') ⟩ length l₁' + length l₂' ≡˘⟨ Eq.cong₂ _+_ (↭-length l₁↭l₁') (↭-length l₂↭l₂') ⟩ length l₁ + length l₂ ∎) $ bound/bind/const {B = sort-result l} {e = merge (l₁' , l₂') _} {f = λ (l' , l₁'++l₂'↭l , l'-sorted) → ret {sort-result l} (l' , ↭-trans l↭l₁++l₂ (↭-trans (_↭_.trans (++⁺ʳ-↭ l₂ l₁↭l₁') (++⁺ˡ-↭ l₁' l₂↭l₂')) (↭-trans l₁'++l₂'↭l _↭_.refl)) , l'-sorted)} (length l₁' + length l₂') 0 (merge/is-bounded (l₁' , l₂') _) λ _ → ≤⁻-refl sort : cmp (Π (list A) λ l → F (sort-result l)) sort l = sort/clocked ⌈log₂ length l ⌉ l N.≤-refl sort/total : IsTotal sort sort/total l = sort/clocked/total ⌈log₂ length l ⌉ l N.≤-refl sort/cost : cmp (Π (list A) λ _ → cost) sort/cost l = sort/clocked/cost ⌈log₂ length l ⌉ l N.≤-refl sort/is-bounded : ∀ l → IsBoundedG (sort-result l) (sort l) (sort/cost l) sort/is-bounded l = sort/clocked/is-bounded ⌈log₂ length l ⌉ l N.≤-refl sort/asymptotic : given (list A) measured-via length , sort ∈𝓞(λ n → n * ⌈log₂ n ⌉) sort/asymptotic = f[n]≤g[n]via λ l → Eq.subst (IsBounded (sort-result l) (sort l)) (N.*-comm ⌈log₂ length l ⌉ (length l)) (sort/is-bounded l)