module Tools.Size where
open import Data.Nat.Induction
open import Data.Nat.Properties
open import Induction.WellFounded
import Relation.Binary.Construct.On as On
open import Tools.Nat
open import Tools.Product
infixr 6 _⊕_
data Size : Set where
  leaf : Size
  _⊕_  : Size → Size → Size
pattern node s = s ⊕ leaf
private variable
  l l₁ l₂ r r₁ r₂ s s₁ s₂ s₃ : Size
infix 10 ↙_ ↘_
infix  4 _≤ˢ_
data _≤ˢ_ : Size → Size → Set where
  leaf : leaf ≤ˢ leaf
  _⊕_  : l₁ ≤ˢ l₂ → r₁ ≤ˢ r₂ → l₁ ⊕ r₁ ≤ˢ l₂ ⊕ r₂
  ↙_   : s ≤ˢ l → s ≤ˢ l ⊕ r
  ↘_   : s ≤ˢ r → s ≤ˢ l ⊕ r
infixr 6 _↙⊕_ _⊕↘_
infix  4 _<ˢ_
data _<ˢ_ : Size → Size → Set where
  _↙⊕_ : l₁ <ˢ l₂ → r₁ ≤ˢ r₂ → l₁ ⊕ r₁ <ˢ l₂ ⊕ r₂
  _⊕↘_ : l₁ ≤ˢ l₂ → r₁ <ˢ r₂ → l₁ ⊕ r₁ <ˢ l₂ ⊕ r₂
  ↙_   : s ≤ˢ l → s <ˢ l ⊕ r
  ↘_   : s ≤ˢ r → s <ˢ l ⊕ r
opaque
  
  <ˢ→≤ˢ : s₁ <ˢ s₂ → s₁ ≤ˢ s₂
  <ˢ→≤ˢ (↙ p)    = ↙ p
  <ˢ→≤ˢ (↘ p)    = ↘ p
  <ˢ→≤ˢ (p ↙⊕ q) = <ˢ→≤ˢ p ⊕ q
  <ˢ→≤ˢ (p ⊕↘ q) = p ⊕ <ˢ→≤ˢ q
opaque
  
  ◻ : s ≤ˢ s
  ◻ {s = leaf}  = leaf
  ◻ {s = _ ⊕ _} = ◻ ⊕ ◻
opaque
  
  ≤ˢ-trans : s₁ ≤ˢ s₂ → s₂ ≤ˢ s₃ → s₁ ≤ˢ s₃
  ≤ˢ-trans p         leaf      = p
  ≤ˢ-trans p         (↙ q)     = ↙ ≤ˢ-trans p q
  ≤ˢ-trans p         (↘ q)     = ↘ ≤ˢ-trans p q
  ≤ˢ-trans (↙ p)     (q₁ ⊕ q₂) = ≤ˢ-trans p (↙ q₁)
  ≤ˢ-trans (↘ p)     (q₁ ⊕ q₂) = ≤ˢ-trans p (↘ q₂)
  ≤ˢ-trans (p₁ ⊕ p₂) (q₁ ⊕ q₂) = ≤ˢ-trans p₁ q₁ ⊕ ≤ˢ-trans p₂ q₂
opaque
  
  <ˢ-trans-≤ˢʳ : s₁ <ˢ s₂ → s₂ ≤ˢ s₃ → s₁ <ˢ s₃
  <ˢ-trans-≤ˢʳ p          leaf      = p
  <ˢ-trans-≤ˢʳ p          (↙ q)     = ↙ ≤ˢ-trans (<ˢ→≤ˢ p) q
  <ˢ-trans-≤ˢʳ p          (↘ q)     = ↘ ≤ˢ-trans (<ˢ→≤ˢ p) q
  <ˢ-trans-≤ˢʳ (↙ p)      (q ⊕ _)   = ↙ ≤ˢ-trans p q
  <ˢ-trans-≤ˢʳ (↘ p)      (_ ⊕ q)   = ↘ ≤ˢ-trans p q
  <ˢ-trans-≤ˢʳ (p₁ ↙⊕ p₂) (q₁ ⊕ q₂) = <ˢ-trans-≤ˢʳ p₁ q₁ ↙⊕
                                      ≤ˢ-trans p₂ q₂
  <ˢ-trans-≤ˢʳ (p₁ ⊕↘ p₂) (q₁ ⊕ q₂) = ≤ˢ-trans p₁ q₁ ⊕↘
                                      <ˢ-trans-≤ˢʳ p₂ q₂
opaque
  
  <ˢ-trans : s₁ <ˢ s₂ → s₂ <ˢ s₃ → s₁ <ˢ s₃
  <ˢ-trans p q = <ˢ-trans-≤ˢʳ p (<ˢ→≤ˢ q)
opaque
  
  <ˢ-trans-≤ˢˡ : s₁ ≤ˢ s₂ → s₂ <ˢ s₃ → s₁ <ˢ s₃
  <ˢ-trans-≤ˢˡ leaf      q          = q
  <ˢ-trans-≤ˢˡ (↙ p)     q          = <ˢ-trans (↙ p) q
  <ˢ-trans-≤ˢˡ (↘ p)     q          = <ˢ-trans (↘ p) q
  <ˢ-trans-≤ˢˡ p@(_ ⊕ _) (↙ q)      = ↙ ≤ˢ-trans p q
  <ˢ-trans-≤ˢˡ p@(_ ⊕ _) (↘ q)      = ↘ ≤ˢ-trans p q
  <ˢ-trans-≤ˢˡ (p₁ ⊕ p₂) (q₁ ↙⊕ q₂) = <ˢ-trans-≤ˢˡ p₁ q₁ ↙⊕
                                      ≤ˢ-trans p₂ q₂
  <ˢ-trans-≤ˢˡ (p₁ ⊕ p₂) (q₁ ⊕↘ q₂) = ≤ˢ-trans p₁ q₁ ⊕↘
                                      <ˢ-trans-≤ˢˡ p₂ q₂
opaque
  
  ∃-<ˢ : ∃ λ s₂ → s₁ <ˢ s₂
  ∃-<ˢ {s₁} = node s₁ , ↙ ◻
opaque
  
  ⊕≤ˢ→<ˢˡ : s₁ ⊕ s₂ ≤ˢ s₃ → s₁ <ˢ s₃
  ⊕≤ˢ→<ˢˡ (↙ p)   = ↙ <ˢ→≤ˢ (⊕≤ˢ→<ˢˡ p)
  ⊕≤ˢ→<ˢˡ (↘ p)   = ↘ <ˢ→≤ˢ (⊕≤ˢ→<ˢˡ p)
  ⊕≤ˢ→<ˢˡ (p ⊕ _) = ↙ p
opaque
  
  ⊕≤ˢ→<ˢʳ : s₁ ⊕ s₂ ≤ˢ s₃ → s₂ <ˢ s₃
  ⊕≤ˢ→<ˢʳ (↙ p)   = ↙ <ˢ→≤ˢ (⊕≤ˢ→<ˢʳ p)
  ⊕≤ˢ→<ˢʳ (↘ p)   = ↘ <ˢ→≤ˢ (⊕≤ˢ→<ˢʳ p)
  ⊕≤ˢ→<ˢʳ (_ ⊕ p) = ↘ p
opaque
  
  size-of-size : Size → Nat
  size-of-size leaf    = 0
  size-of-size (l ⊕ r) = 1 + size-of-size l + size-of-size r
opaque
  unfolding size-of-size
  
  
  size-of-size-mono-≤ :
    s₁ ≤ˢ s₂ → size-of-size s₁ ≤ size-of-size s₂
  size-of-size-mono-≤ leaf =
    ≤-refl
  size-of-size-mono-≤ (↙ p) =
    ≤-trans (size-of-size-mono-≤ p) (≤-trans (m≤m+n _ _) (n≤1+n _))
  size-of-size-mono-≤ (↘ p) =
    ≤-trans (size-of-size-mono-≤ p) (≤-trans (m≤n+m _ _) (n≤1+n _))
  size-of-size-mono-≤ (p ⊕ q) =
    s≤s (+-mono-≤ (size-of-size-mono-≤ p) (size-of-size-mono-≤ q))
opaque
  unfolding size-of-size
  
  
  size-of-size-mono-< :
    s₁ <ˢ s₂ → size-of-size s₁ < size-of-size s₂
  size-of-size-mono-< (↙ p) =
    s≤s (≤-trans (size-of-size-mono-≤ p) (m≤m+n _ _))
  size-of-size-mono-< (↘ p) =
    s≤s (≤-trans (size-of-size-mono-≤ p) (m≤n+m _ _))
  size-of-size-mono-< (p ↙⊕ q) =
    s≤s (+-mono-<-≤ (size-of-size-mono-< p) (size-of-size-mono-≤ q))
  size-of-size-mono-< (p ⊕↘ q) =
    s≤s (+-mono-≤-< (size-of-size-mono-≤ p) (size-of-size-mono-< q))
opaque
  
  <ˢ-well-founded : WellFounded _<ˢ_
  <ˢ-well-founded =
    Subrelation.wellFounded
      size-of-size-mono-<
      (On.wellFounded size-of-size <-wellFounded)
opaque
  
  well-founded-induction :
    ∀ {p} (P : Size → Set p) →
    (∀ s₁ → (∀ {s₂} → s₂ <ˢ s₁ → P s₂) → P s₁) →
    ∀ s → P s
  well-founded-induction =
    All.wfRec <ˢ-well-founded _