------------------------------------------------------------------------ -- Agda Helium -- -- Ways to modify pseudocode statements and expressions. ------------------------------------------------------------------------ {-# OPTIONS --safe --without-K #-} module Helium.Data.Pseudocode.Manipulate where open import Helium.Data.Pseudocode.Core open import Algebra.Bundles using (IdempotentCommutativeMonoid) import Algebra.Solver.IdempotentCommutativeMonoid as ICMSolver import Algebra.Solver.Ring.AlmostCommutativeRing as ACR import Algebra.Solver.Ring.Simple as RingSolver open import Data.Fin as Fin using (suc; punchOut; punchIn) open import Data.Fin.Patterns open import Data.Nat as ℕ using (ℕ; suc; _<_; _≤_; z≤n; s≤s; _⊔_) import Data.Nat.Induction as ℕᵢ import Data.Nat.Properties as ℕₚ open import Data.Product using (∃; _×_; _,_; proj₁; proj₂; -,_; <_,_>) open import Data.Product.Nary.NonDependent using (Product; uncurryₙ) open import Data.Product.Relation.Binary.Lex.Strict open import Data.Sum using (inj₁; inj₂) open import Data.Unit.Polymorphic using (⊤) open import Data.Vec as Vec using (Vec; []; _∷_; lookup; insert; remove) import Data.Vec.Properties as Vecₚ open import Data.Vec.Recursive as Vecᵣ using (2+_) open import Data.Vec.Relation.Unary.All as All using (All; []; _∷_) open import Function open import Function.Nary.NonDependent using (_⇉_; Sets; congₙ; 0ℓs) open import Helium.Data.Pseudocode.Core open import Induction.WellFounded as Wf using (WellFounded) import Relation.Binary.Construct.On as On open import Relation.Binary.PropositionalEquality open import Relation.Nullary using (yes; no) private variable k m n o : ℕ ret t t′ t₁ t₂ : Type Σ Γ Δ ts : Vec Type n private punchOut-insert : ∀ {a} {A : Set a} (xs : Vec A n) {i j} (i≢j : i ≢ j) x → lookup xs (punchOut i≢j) ≡ lookup (insert xs i x) j punchOut-insert xs {i} {j} i≢j x = begin lookup xs (punchOut i≢j) ≡˘⟨ cong (flip lookup (punchOut i≢j)) (Vecₚ.remove-insert xs i x) ⟩ lookup (remove (insert xs i x) i) (punchOut i≢j) ≡⟨ Vecₚ.remove-punchOut (insert xs i x) i≢j ⟩ lookup (insert xs i x) j ∎ where open ≡-Reasoning lookupᵣ-map : ∀ {a b} {A : Set a} {B : Set b} {f : A → B} i (xs : A Vecᵣ.^ n) → Vecᵣ.lookup i (Vecᵣ.map f n xs) ≡ f (Vecᵣ.lookup i xs) lookupᵣ-map {n = 1} 0F xs = refl lookupᵣ-map {n = 2+ n} 0F xs = refl lookupᵣ-map {n = 2+ n} (suc i) xs = lookupᵣ-map i (proj₂ xs) ⊔-0-boundedLattice : IdempotentCommutativeMonoid _ _ ⊔-0-boundedLattice = record { isIdempotentCommutativeMonoid = record { isCommutativeMonoid = ℕₚ.⊔-0-isCommutativeMonoid ; idem = ℕₚ.⊔-idem } } open ICMSolver ⊔-0-boundedLattice using (_⊜_; _⊕_) renaming (solve to solve-⊔; id to ε) open RingSolver (ACR.fromCommutativeSemiring ℕₚ.+-*-commutativeSemiring) ℕₚ._≟_ using (_:=_; _:+_; _:*_; con) renaming (solve to solve-+) open ℕₚ.≤-Reasoning private [_]_$_⊗_ : ∀ {a b c} {A : Set a} {B : Set b} m → (C : A → B → Set c) → A Vecᵣ.^ m → B Vecᵣ.^ m → Set c [ m ] f $ xs ⊗ ys = Vecᵣ.foldr ⊤ id (const _×_) m (Vecᵣ.zipWith f m xs ys) ⨆[_]_ : ∀ n → ℕ Vecᵣ.^ n → ℕ ⨆[_]_ = Vecᵣ.foldl (const ℕ) 0 id (const (flip _⊔_)) ⨆-step : ∀ m x xs → ⨆[ 2+ m ] (x , xs) ≡ x ⊔ ⨆[ suc m ] xs ⨆-step 0 x xs = refl ⨆-step (suc m) x (y , xs) = begin-equality ⨆[ 2+ suc m ] (x , y , xs) ≡⟨ ⨆-step m (x ⊔ y) xs ⟩ x ⊔ y ⊔ ⨆[ suc m ] xs ≡⟨ ℕₚ.⊔-assoc x y _ ⟩ x ⊔ (y ⊔ ⨆[ suc m ] xs) ≡˘⟨ cong (_ ⊔_) (⨆-step m y xs) ⟩ x ⊔ ⨆[ 2+ m ] (y , xs) ∎ join-lubs : ∀ n m {lhs rhs} → [ m ] (λ x y → x ≤ y ⊔ n) $ lhs ⊗ rhs → ⨆[ m ] lhs ≤ (⨆[ m ] rhs) ⊔ n join-lubs n 0 {lhs} {rhs} ≤s = z≤n join-lubs n 1 {lhs} {rhs} ≤s = ≤s join-lubs n (2+ m) {x , lhs} {y , rhs} (x≤y⊔n , ≤s) = begin ⨆[ 2+ m ] (x , lhs) ≡⟨ ⨆-step m x lhs ⟩ x ⊔ ⨆[ suc m ] lhs ≤⟨ ℕₚ.⊔-mono-≤ x≤y⊔n (join-lubs n (suc m) ≤s) ⟩ y ⊔ n ⊔ (⨆[ suc m ] rhs ⊔ n) ≡⟨ solve-⊔ 3 (λ a b c → (a ⊕ c) ⊕ b ⊕ c ⊜ (a ⊕ b) ⊕ c) refl y _ n ⟩ y ⊔ ⨆[ suc m ] rhs ⊔ n ≡˘⟨ cong (_⊔ _) (⨆-step m y rhs) ⟩ ⨆[ 2+ m ] (y , rhs) ⊔ n ∎ lookup-⨆-≤ : ∀ i (xs : ℕ Vecᵣ.^ n) → Vecᵣ.lookup i xs ≤ ⨆[ n ] xs lookup-⨆-≤ {1} 0F x = ℕₚ.≤-refl lookup-⨆-≤ {2+ n} 0F (x , xs) = begin x ≤⟨ ℕₚ.m≤m⊔n x _ ⟩ x ⊔ ⨆[ suc n ] xs ≡˘⟨ ⨆-step n x xs ⟩ ⨆[ 2+ n ] (x , xs) ∎ lookup-⨆-≤ {2+ n} (suc i) (x , xs) = begin Vecᵣ.lookup i xs ≤⟨ lookup-⨆-≤ i xs ⟩ ⨆[ suc n ] xs ≤⟨ ℕₚ.m≤n⊔m x _ ⟩ x ⊔ ⨆[ suc n ] xs ≡˘⟨ ⨆-step n x xs ⟩ ⨆[ 2+ n ] (x , xs) ∎ Σ[_]_ : ∀ n → ℕ Vecᵣ.^ n → ℕ Σ[_]_ = Vecᵣ.foldl (const ℕ) 0 id (const (flip ℕ._+_)) Σ-step : ∀ m x xs → Σ[ 2+ m ] (x , xs) ≡ x ℕ.+ Σ[ suc m ] xs Σ-step 0 x xs = refl Σ-step (suc m) x (y , xs) = begin-equality Σ[ 2+ suc m ] (x , y , xs) ≡⟨ Σ-step m (x ℕ.+ y) xs ⟩ x ℕ.+ y ℕ.+ Σ[ suc m ] xs ≡⟨ ℕₚ.+-assoc x y _ ⟩ x ℕ.+ (y ℕ.+ Σ[ suc m ] xs) ≡˘⟨ cong (x ℕ.+_) (Σ-step m y xs) ⟩ x ℕ.+ Σ[ 2+ m ] (y , xs) ∎ lookup-Σ-≤ : ∀ i (xs : ℕ Vecᵣ.^ n) → Vecᵣ.lookup i xs ≤ Σ[ n ] xs lookup-Σ-≤ {1} 0F x = ℕₚ.≤-refl lookup-Σ-≤ {2+ n} 0F (x , xs) = begin x ≤⟨ ℕₚ.m≤m+n x _ ⟩ x ℕ.+ Σ[ suc n ] xs ≡˘⟨ Σ-step n x xs ⟩ Σ[ 2+ n ] (x , xs) ∎ lookup-Σ-≤ {2+ n} (suc i) (x , xs) = begin Vecᵣ.lookup i xs ≤⟨ lookup-Σ-≤ i xs ⟩ Σ[ suc n ] xs ≤⟨ ℕₚ.m≤n+m _ x ⟩ x ℕ.+ Σ[ suc n ] xs ≡˘⟨ Σ-step n x xs ⟩ Σ[ 2+ n ] (x , xs) ∎ foldr-lubs : ∀ {a b c} {A : Set a} {B : ℕ → Set b} (f : ∀ {n} → A → B n → B (suc n)) y (P : ∀ {n} → B n → Set c) → P y → (∀ {n} a {b : B n} → P b → P (f a b)) → ∀ (xs : Vec A n) → P (Vec.foldr B f y xs) foldr-lubs f y P y∈P f-pres [] = y∈P foldr-lubs f y P y∈P f-pres (x ∷ xs) = f-pres x (foldr-lubs f y P y∈P f-pres xs) module CallDepth where expr : Expression Σ Γ t → ℕ exprs : All (Expression Σ Γ) ts → ℕ locRef : LocalReference Σ Γ t → ℕ locStmt : LocalStatement Σ Γ → ℕ fun : Function Σ Γ ret → ℕ expr (lit x) = 0 expr (state i) = 0 expr (var i) = 0 expr (e ≟ e₁) = expr e ⊔ expr e₁ expr (e > n) = expr e expr (rnd e) = expr e expr (fin f e) = expr e expr (asInt e) = expr e expr nil = 0 expr (cons e e₁) = expr e ⊔ expr e₁ expr (head e) = expr e expr (tail e) = expr e expr (call f es) = exprs es ⊔ suc (fun f) expr (if e then e₁ else e₂) = expr e ⊔ expr e₁ ⊔ expr e₂ exprs [] = 0 exprs (e ∷ es) = exprs es ⊔ expr e locRef (var i) = 0 locRef [ ref ] = locRef ref locRef (unbox ref) = locRef ref locRef (merge ref ref₁ x) = locRef ref ⊔ locRef ref₁ ⊔ expr x locRef (slice ref x) = locRef ref ⊔ expr x locRef (cut ref x) = locRef ref ⊔ expr x locRef (cast eq ref) = locRef ref locRef nil = 0 locRef (cons ref ref₁) = locRef ref ⊔ locRef ref₁ locRef (head ref) = locRef ref locRef (tail ref) = locRef ref locStmt (s ∙ s₁) = locStmt s ⊔ locStmt s₁ locStmt skip = 0 locStmt (ref ≔ e) = locRef ref ⊔ expr e locStmt (declare x s) = locStmt s ⊔ expr x locStmt (if x then s) = locStmt s ⊔ expr x locStmt (if x then s else s₁) = locStmt s ⊔ locStmt s₁ ⊔ expr x locStmt (for n s) = locStmt s fun (declare x f) = fun f ⊔ expr x fun (s ∙return e) = locStmt s ⊔ expr e homo-!! : ∀ (ref : LocalReference Σ Γ t) → expr (!! ref) ≡ locRef ref homo-!! (var i) = refl homo-!! [ ref ] = homo-!! ref homo-!! (unbox ref) = homo-!! ref homo-!! (merge ref ref₁ x) = cong₂ (λ m n → m ⊔ n ⊔ _) (homo-!! ref) (homo-!! ref₁) homo-!! (slice ref x) = cong (_⊔ _) (homo-!! ref) homo-!! (cut ref x) = cong (_⊔ _) (homo-!! ref) homo-!! (cast eq ref) = homo-!! ref homo-!! nil = refl homo-!! (cons ref ref₁) = cong₂ _⊔_ (homo-!! ref) (homo-!! ref₁) homo-!! (head ref) = homo-!! ref homo-!! (tail ref) = homo-!! ref ∷ˡ-≤ : ∀ (e : Expression Σ Γ t) (es : All (Expression Σ Γ) ts) → expr e ≤ exprs (e ∷ es) ∷ˡ-≤ e es = ℕₚ.m≤n⊔m (exprs es) (expr e) ∷ʳ-≤ : ∀ (e : Expression Σ Γ t) (es : All (Expression Σ Γ) ts) → exprs es ≤ exprs (e ∷ es) ∷ʳ-≤ e es = ℕₚ.m≤m⊔n (exprs es) (expr e) lookup-≤ : ∀ i (es : All (Expression Σ Γ) ts) → expr (All.lookup i es) ≤ exprs es lookup-≤ 0F (e ∷ es) = ∷ˡ-≤ e es lookup-≤ (suc i) (e ∷ es) = ℕₚ.≤-trans (lookup-≤ i es) (∷ʳ-≤ e es) call>0 : ∀ (f : Function Σ Δ t) (es : All (Expression Σ Γ) Δ) → 0 < expr (call f es) call>0 f es = ℕₚ.<-transˡ ℕₚ.0<1+n (ℕₚ.m≤n⊔m (exprs es) (suc (fun f))) module Cast where expr : t ≡ t′ → Expression Σ Γ t → Expression Σ Γ t′ expr refl e = e locRef : t ≡ t′ → LocalReference Σ Γ t → LocalReference Σ Γ t′ locRef refl ref = ref homo-!! : ∀ (eq : t ≡ t′) (ref : LocalReference Σ Γ t) → expr eq (!! ref) ≡ !! (locRef eq ref) homo-!! refl _ = refl expr-depth : ∀ (eq : t ≡ t′) (e : Expression Σ Γ t) → CallDepth.expr (expr eq e) ≡ CallDepth.expr e expr-depth refl _ = refl module Elim where expr : ∀ i → Expression Σ (insert Γ i t′) t → Expression Σ Γ t′ → Expression Σ Γ t exprs : ∀ i → All (Expression Σ (insert Γ i t′)) ts → Expression Σ Γ t′ → All (Expression Σ Γ) ts expr i (lit x) e′ = lit x expr i (state j) e′ = state j expr i (var j) e′ with i Fin.≟ j ... | yes refl = Cast.expr (sym (Vecₚ.insert-lookup _ i _)) e′ ... | no i≢j = Cast.expr (punchOut-insert _ i≢j _) (var (punchOut i≢j)) expr i (e ≟ e₁) e′ = expr i e e′ ≟ expr i e₁ e′ expr i (e > n) e′ = expr i e e′ >> n expr i (rnd e) e′ = rnd (expr i e e′) expr i (fin f e) e′ = fin f (expr i e e′) expr i (asInt e) e′ = asInt (expr i e e′) expr i nil e′ = nil expr i (cons e e₁) e′ = cons (expr i e e′) (expr i e₁ e′) expr i (head e) e′ = head (expr i e e′) expr i (tail e) e′ = tail (expr i e e′) expr i (call f es) e′ = call f (exprs i es e′) expr i (if e then e₁ else e₂) e′ = if expr i e e′ then expr i e₁ e′ else expr i e₂ e′ exprs i [] e′ = [] exprs i (e ∷ es) e′ = expr i e e′ ∷ exprs i es e′ expr-depth : ∀ i (e : Expression Σ _ t) (e′ : Expression Σ Γ t′) → CallDepth.expr (expr i e e′) ≤ CallDepth.expr e ⊔ CallDepth.expr e′ exprs-depth : ∀ i (es : All (Expression Σ _) ts) (e′ : Expression Σ Γ t′) → CallDepth.exprs (exprs i es e′) ≤ CallDepth.exprs es ⊔ CallDepth.expr e′ expr-depth i (lit x) e′ = z≤n expr-depth i (state j) e′ = z≤n expr-depth i (var j) e′ with i Fin.≟ j ... | yes refl = ℕₚ.≤-reflexive (Cast.expr-depth (sym (Vecₚ.insert-lookup _ i _)) e′) ... | no i≢j = ℕₚ.≤-trans (ℕₚ.≤-reflexive (Cast.expr-depth (punchOut-insert _ i≢j _) (var (punchOut i≢j)))) z≤n expr-depth i (e ≟ e₁) e′ = join-lubs (CallDepth.expr e′) 2 (expr-depth i e e′ , expr-depth i e₁ e′) expr-depth i (e > n) e′ = expr-depth i e e′ expr-depth i (rnd e) e′ = expr-depth i e e′ expr-depth i (fin f e) e′ = expr-depth i e e′ expr-depth i (asInt e) e′ = expr-depth i e e′ expr-depth i nil e′ = z≤n expr-depth i (cons e e₁) e′ = join-lubs (CallDepth.expr e′) 2 (expr-depth i e e′ , expr-depth i e₁ e′) expr-depth i (head e) e′ = expr-depth i e e′ expr-depth i (tail e) e′ = expr-depth i e e′ expr-depth i (call f es) e′ = join-lubs (CallDepth.expr e′) 2 (exprs-depth i es e′ , ℕₚ.m≤m⊔n _ (CallDepth.expr e′)) expr-depth i (if e then e₁ else e₂) e′ = join-lubs (CallDepth.expr e′) 3 (expr-depth i e e′ , expr-depth i e₁ e′ , expr-depth i e₂ e′) exprs-depth i [] e′ = z≤n exprs-depth i (e ∷ es) e′ = join-lubs (CallDepth.expr e′) 2 (exprs-depth i es e′ , expr-depth i e e′) module Weaken where expr : ∀ i t′ → Expression Σ Γ t → Expression Σ (insert Γ i t′) t exprs : ∀ i t′ → All (Expression Σ Γ) ts → All (Expression Σ (insert Γ i t′)) ts expr i t′ (lit x) = lit x expr i t′ (state j) = state j expr i t′ (var j) = Cast.expr (Vecₚ.insert-punchIn _ i t′ j) (var (punchIn i j)) expr i t′ (e ≟ e₁) = expr i t′ e ≟ expr i t′ e₁ expr i t′ (e > n) = expr i t′ e >> n expr i t′ (rnd e) = rnd (expr i t′ e) expr i t′ (fin f e) = fin f (expr i t′ e) expr i t′ (asInt e) = asInt (expr i t′ e) expr i t′ nil = nil expr i t′ (cons e e₁) = cons (expr i t′ e) (expr i t′ e₁) expr i t′ (head e) = head (expr i t′ e) expr i t′ (tail e) = tail (expr i t′ e) expr i t′ (call f es) = call f (exprs i t′ es) expr i t′ (if e then e₁ else e₂) = if expr i t′ e then expr i t′ e₁ else expr i t′ e₂ exprs i t′ [] = [] exprs i t′ (e ∷ es) = expr i t′ e ∷ exprs i t′ es locRef : ∀ i t′ → LocalReference Σ Γ t → LocalReference Σ (insert Γ i t′) t locRef i t′ (var j) = Cast.locRef (Vecₚ.insert-punchIn _ i t′ j) (var (punchIn i j)) locRef i t′ [ ref ] = [ locRef i t′ ref ] locRef i t′ (unbox ref) = unbox (locRef i t′ ref) locRef i t′ (merge ref ref₁ e) = merge (locRef i t′ ref) (locRef i t′ ref₁) (expr i t′ e) locRef i t′ (slice ref e) = slice (locRef i t′ ref) (expr i t′ e) locRef i t′ (cut ref e) = cut (locRef i t′ ref) (expr i t′ e) locRef i t′ (cast eq ref) = cast eq (locRef i t′ ref) locRef i t′ nil = nil locRef i t′ (cons ref ref₁) = cons (locRef i t′ ref) (locRef i t′ ref₁) locRef i t′ (head ref) = head (locRef i t′ ref) locRef i t′ (tail ref) = tail (locRef i t′ ref) locStmt : ∀ i t′ → LocalStatement Σ Γ → LocalStatement Σ (insert Γ i t′) locStmt i t′ (s ∙ s₁) = locStmt i t′ s ∙ locStmt i t′ s₁ locStmt i t′ skip = skip locStmt i t′ (ref ≔ val) = locRef i t′ ref ≔ expr i t′ val locStmt i t′ (declare e s) = declare (expr i t′ e) (locStmt (suc i) t′ s) locStmt i t′ (if x then s) = if expr i t′ x then locStmt i t′ s locStmt i t′ (if x then s else s₁) = if expr i t′ x then locStmt i t′ s else locStmt i t′ s₁ locStmt i t′ (for n s) = for n (locStmt (suc i) t′ s) homo-!! : ∀ i t′ (ref : LocalReference Σ Γ t) → expr i t′ (!! ref) ≡ !! (locRef i t′ ref) homo-!! i t′ (var j) = Cast.homo-!! (Vecₚ.insert-punchIn _ i t′ j) (var (punchIn i j)) homo-!! i t′ [ ref ] = cong [_] (homo-!! i t′ ref) homo-!! i t′ (unbox ref) = cong unbox (homo-!! i t′ ref) homo-!! i t′ (merge ref ref₁ e) = cong₂ (λ x y → merge x y _) (homo-!! i t′ ref) (homo-!! i t′ ref₁) homo-!! i t′ (slice ref e) = cong (λ x → slice x _) (homo-!! i t′ ref) homo-!! i t′ (cut ref e) = cong (λ x → cut x _) (homo-!! i t′ ref) homo-!! i t′ (cast eq ref) = cong (cast eq) (homo-!! i t′ ref) homo-!! i t′ nil = refl homo-!! i t′ (cons ref ref₁) = cong₂ cons (homo-!! i t′ ref) (homo-!! i t′ ref₁) homo-!! i t′ (head ref) = cong head (homo-!! i t′ ref) homo-!! i t′ (tail ref) = cong tail (homo-!! i t′ ref) expr-depth : ∀ i t′ (e : Expression Σ Γ t) → CallDepth.expr (expr i t′ e) ≡ CallDepth.expr e exprs-depth : ∀ i t′ (es : All (Expression Σ Γ) ts) → CallDepth.exprs (exprs i t′ es) ≡ CallDepth.exprs es expr-depth i t′ (lit x) = refl expr-depth i t′ (state j) = refl expr-depth i t′ (var j) = Cast.expr-depth (Vecₚ.insert-punchIn _ i t′ j) (var (punchIn i j)) expr-depth i t′ (e ≟ e₁) = cong₂ _⊔_ (expr-depth i t′ e) (expr-depth i t′ e₁) expr-depth i t′ (e > n) = expr-depth i t′ e expr-depth i t′ (rnd e) = expr-depth i t′ e expr-depth i t′ (fin f e) = expr-depth i t′ e expr-depth i t′ (asInt e) = expr-depth i t′ e expr-depth i t′ nil = refl expr-depth i t′ (cons e e₁) = cong₂ _⊔_ (expr-depth i t′ e) (expr-depth i t′ e₁) expr-depth i t′ (head e) = expr-depth i t′ e expr-depth i t′ (tail e) = expr-depth i t′ e expr-depth i t′ (call f es) = cong (_⊔ _) (exprs-depth i t′ es) expr-depth i t′ (if e then e₁ else e₂) = congₙ 3 (λ a b c → a ⊔ b ⊔ c) (expr-depth i t′ e) (expr-depth i t′ e₁) (expr-depth i t′ e₂) exprs-depth i t′ [] = refl exprs-depth i t′ (e ∷ es) = cong₂ _⊔_ (exprs-depth i t′ es) (expr-depth i t′ e) locRef-depth : ∀ i t′ (ref : LocalReference Σ Γ t) → CallDepth.locRef (locRef i t′ ref) ≡ CallDepth.locRef ref locRef-depth i t′ ref = begin-equality CallDepth.locRef (locRef i t′ ref) ≡˘⟨ CallDepth.homo-!! (locRef i t′ ref) ⟩ CallDepth.expr (!! (locRef i t′ ref)) ≡˘⟨ cong CallDepth.expr (homo-!! i t′ ref) ⟩ CallDepth.expr (expr i t′ (!! ref)) ≡⟨ expr-depth i t′ (!! ref) ⟩ CallDepth.expr (!! ref) ≡⟨ CallDepth.homo-!! ref ⟩ CallDepth.locRef ref ∎ locStmt-depth : ∀ i t′ (s : LocalStatement Σ Γ) → CallDepth.locStmt (locStmt i t′ s) ≡ CallDepth.locStmt s locStmt-depth i t′ (s ∙ s₁) = cong₂ _⊔_ (locStmt-depth i t′ s) (locStmt-depth i t′ s₁) locStmt-depth i t′ skip = refl locStmt-depth i t′ (ref ≔ val) = cong₂ _⊔_ (locRef-depth i t′ ref) (expr-depth i t′ val) locStmt-depth i t′ (declare e s) = cong₂ _⊔_ (locStmt-depth (suc i) t′ s) (expr-depth i t′ e) locStmt-depth i t′ (if x then s) = cong₂ _⊔_ (locStmt-depth i t′ s) (expr-depth i t′ x) locStmt-depth i t′ (if x then s else s₁) = congₙ 3 (λ a b c → a ⊔ b ⊔ c) (locStmt-depth i t′ s) (locStmt-depth i t′ s₁) (expr-depth i t′ x) locStmt-depth i t′ (for n s) = locStmt-depth (suc i) t′ s module Subst where expr : ∀ i → Expression Σ Γ t → Expression Σ Γ (lookup Γ i) → Expression Σ Γ t exprs : ∀ i → All (Expression Σ Γ) ts → Expression Σ Γ (lookup Γ i) → All (Expression Σ Γ) ts expr i (lit x) e′ = lit x expr i (state j) e′ = state j expr i (var j) e′ with i Fin.≟ j ... | yes refl = e′ ... | no i≢j = var j expr i (e ≟ e₁) e′ = expr i e e′ ≟ expr i e₁ e′ expr i (e > n) e′ = expr i e e′ >> n expr i (rnd e) e′ = rnd (expr i e e′) expr i (fin f e) e′ = fin f (expr i e e′) expr i (asInt e) e′ = asInt (expr i e e′) expr i nil e′ = nil expr i (cons e e₁) e′ = cons (expr i e e′) (expr i e₁ e′) expr i (head e) e′ = head (expr i e e′) expr i (tail e) e′ = tail (expr i e e′) expr i (call f es) e′ = call f (exprs i es e′) expr i (if e then e₁ else e₂) e′ = if expr i e e′ then expr i e₁ e′ else expr i e₂ e′ exprs i [] e′ = [] exprs i (e ∷ es) e′ = expr i e e′ ∷ exprs i es e′ expr-depth : ∀ i (e : Expression Σ Γ t) e′ → CallDepth.expr (expr i e e′) ≤ CallDepth.expr e ⊔ CallDepth.expr e′ exprs-depth : ∀ i (es : All (Expression Σ Γ) ts) e′ → CallDepth.exprs (exprs i es e′) ≤ CallDepth.exprs es ⊔ CallDepth.expr e′ expr-depth i (lit x) e′ = z≤n expr-depth i (state j) e′ = z≤n expr-depth i (var j) e′ with i Fin.≟ j ... | yes refl = ℕₚ.≤-refl ... | no i≢j = z≤n expr-depth i (e ≟ e₁) e′ = join-lubs (CallDepth.expr e′) 2 (expr-depth i e e′ , expr-depth i e₁ e′) expr-depth i (e > n) e′ = expr-depth i e e′ expr-depth i (rnd e) e′ = expr-depth i e e′ expr-depth i (fin f e) e′ = expr-depth i e e′ expr-depth i (asInt e) e′ = expr-depth i e e′ expr-depth i nil e′ = z≤n expr-depth i (cons e e₁) e′ = join-lubs (CallDepth.expr e′) 2 (expr-depth i e e′ , expr-depth i e₁ e′) expr-depth i (head e) e′ = expr-depth i e e′ expr-depth i (tail e) e′ = expr-depth i e e′ expr-depth i (call f es) e′ = join-lubs (CallDepth.expr e′) 2 (exprs-depth i es e′ , ℕₚ.m≤m⊔n _ (CallDepth.expr e′)) expr-depth i (if e then e₁ else e₂) e′ = join-lubs (CallDepth.expr e′) 3 (expr-depth i e e′ , expr-depth i e₁ e′ , expr-depth i e₂ e′) exprs-depth i [] e′ = z≤n exprs-depth i (e ∷ es) e′ = join-lubs (CallDepth.expr e′) 2 (exprs-depth i es e′ , expr-depth i e e′) module SubstAll where expr : Expression Σ Γ t → All (Expression Σ Δ) Γ → Expression Σ Δ t exprs : All (Expression Σ Γ) ts → All (Expression Σ Δ) Γ → All (Expression Σ Δ) ts expr (lit x) es′ = lit x expr (state j) es′ = state j expr (var j) es′ = All.lookup j es′ expr (e ≟ e₁) es′ = expr e es′ ≟ expr e₁ es′ expr (e > n) es′ = expr e es′ >> n expr (rnd e) es′ = rnd (expr e es′) expr (fin f e) es′ = fin f (expr e es′) expr (asInt e) es′ = asInt (expr e es′) expr nil es′ = nil expr (cons e e₁) es′ = cons (expr e es′) (expr e₁ es′) expr (head e) es′ = head (expr e es′) expr (tail e) es′ = tail (expr e es′) expr (call f es) es′ = call f (exprs es es′) expr (if e then e₁ else e₂) es′ = if expr e es′ then expr e₁ es′ else expr e₂ es′ exprs [] es′ = [] exprs (e ∷ es) es′ = expr e es′ ∷ exprs es es′ expr-depth : ∀ (e : Expression Σ Γ t) (es′ : All (Expression Σ Δ) Γ) → CallDepth.expr (expr e es′) ≤ CallDepth.expr e ⊔ CallDepth.exprs es′ exprs-depth : ∀ (es : All (Expression Σ Γ) ts) (es′ : All (Expression Σ Δ) Γ) → CallDepth.exprs (exprs es es′) ≤ CallDepth.exprs es ⊔ CallDepth.exprs es′ expr-depth (lit x) es′ = z≤n expr-depth (state j) es′ = z≤n expr-depth (var j) es′ = CallDepth.lookup-≤ j es′ expr-depth (e ≟ e₁) es′ = join-lubs (CallDepth.exprs es′) 2 (expr-depth e es′ , expr-depth e₁ es′) expr-depth (e > n) es′ = expr-depth e es′ expr-depth (rnd e) es′ = expr-depth e es′ expr-depth (fin f e) es′ = expr-depth e es′ expr-depth (asInt e) es′ = expr-depth e es′ expr-depth nil es′ = z≤n expr-depth (cons e e₁) es′ = join-lubs (CallDepth.exprs es′) 2 (expr-depth e es′ , expr-depth e₁ es′) expr-depth (head e) es′ = expr-depth e es′ expr-depth (tail e) es′ = expr-depth e es′ expr-depth (call f es) es′ = join-lubs (CallDepth.exprs es′) 2 (exprs-depth es es′ , ℕₚ.m≤m⊔n _ (CallDepth.exprs es′)) expr-depth (if e then e₁ else e₂) es′ = join-lubs (CallDepth.exprs es′) 3 (expr-depth e es′ , expr-depth e₁ es′ , expr-depth e₂ es′) exprs-depth [] es′ = z≤n exprs-depth (e ∷ es) es′ = join-lubs (CallDepth.exprs es′) 2 (exprs-depth es es′ , expr-depth e es′) module Update where expr : LocalReference Σ Γ t → Expression Σ Γ t → Expression Σ Γ t′ → Expression Σ Γ t′ expr (var i) val e′ = Subst.expr i e′ val expr [ ref ] val e′ = expr ref (unbox val) e′ expr (unbox ref) val e′ = expr ref [ val ] e′ expr (merge ref ref₁ e) val e′ = expr ref₁ (cut val e) (expr ref (slice val e) e′) expr (slice ref e) val e′ = expr ref (merge val (cut (!! ref) e) e) e′ expr (cut ref e) val e′ = expr ref (merge (slice (!! ref) e) val e) e′ expr (cast eq ref) val e′ = expr ref (cast (sym eq) val) e′ expr nil val e′ = e′ expr (cons ref ref₁) val e′ = expr ref₁ (tail val) (expr ref (head val) e′) expr (head ref) val e′ = expr ref (cons val (tail (!! ref))) e′ expr (tail ref) val e′ = expr ref (cons (head (!! ref)) val) e′ expr-depth : ∀ (ref : LocalReference Σ Γ t) val (e′ : Expression Σ Γ t′) → CallDepth.expr (expr ref val e′) ≤ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr val) expr-depth (var i) val e′ = Subst.expr-depth i e′ val expr-depth [ ref ] val e′ = expr-depth ref (unbox val) e′ expr-depth (unbox ref) val e′ = expr-depth ref [ val ] e′ expr-depth (merge ref ref₁ e) val e′ = begin CallDepth.expr (expr ref₁ (cut val e) (expr ref (slice val e) e′)) ≤⟨ expr-depth ref₁ _ _ ⟩ CallDepth.expr (expr ref (slice val e) e′) ⊔ (CallDepth.locRef ref₁ ⊔ (CallDepth.expr val ⊔ CallDepth.expr e)) ≤⟨ ℕₚ.⊔-monoˡ-≤ _ (expr-depth ref (slice val e) e′) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ CallDepth.expr e)) ⊔ (CallDepth.locRef ref₁ ⊔ (CallDepth.expr val ⊔ CallDepth.expr e)) ≡⟨ solve-⊔ 5 (λ a b c d e → (a ⊕ (b ⊕ (e ⊕ d))) ⊕ (c ⊕ (e ⊕ d)) ⊜ a ⊕ (((b ⊕ c) ⊕ d) ⊕ e)) refl (CallDepth.expr e′) (CallDepth.locRef ref) _ _ _ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.locRef ref₁ ⊔ CallDepth.expr e ⊔ CallDepth.expr val) ∎ expr-depth (slice ref e) val e′ = begin CallDepth.expr (expr ref (merge val (cut (!! ref) e) e) e′) ≤⟨ expr-depth ref (merge val (cut (!! ref) e) e) e′ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ (CallDepth.expr (!! ref) ⊔ CallDepth.expr e) ⊔ CallDepth.expr e)) ≡⟨ cong (λ x → CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ (x ⊔ _) ⊔ _))) (CallDepth.homo-!! ref) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr e) ⊔ CallDepth.expr e)) ≡⟨ cong (CallDepth.expr e′ ⊔_) $ solve-⊔ 3 (λ a b c → a ⊕ ((c ⊕ (a ⊕ b)) ⊕ b) ⊜ (a ⊕ b) ⊕ c) refl (CallDepth.locRef ref) _ _ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr e ⊔ CallDepth.expr val) ∎ expr-depth (cut ref e) val e′ = begin CallDepth.expr (expr ref (merge (slice (!! ref) e) val e) e′) ≤⟨ expr-depth ref (merge (slice (!! ref) e) val e) e′ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr (!! ref) ⊔ CallDepth.expr e ⊔ CallDepth.expr val ⊔ CallDepth.expr e)) ≡⟨ cong (λ x → CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (x ⊔ _ ⊔ _ ⊔ _))) (CallDepth.homo-!! ref) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr e ⊔ CallDepth.expr val ⊔ CallDepth.expr e)) ≡⟨ cong (CallDepth.expr e′ ⊔_) $ solve-⊔ 3 (λ a b c → a ⊕ (((a ⊕ b) ⊕ c) ⊕ b) ⊜ (a ⊕ b) ⊕ c) refl (CallDepth.locRef ref) _ _ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr e ⊔ CallDepth.expr val) ∎ expr-depth (cast eq ref) val e′ = expr-depth ref (cast (sym eq) val) e′ expr-depth nil val e′ = ℕₚ.m≤m⊔n (CallDepth.expr e′) _ expr-depth (cons ref ref₁) val e′ = begin CallDepth.expr (expr ref₁ (tail val) (expr ref (head val) e′)) ≤⟨ expr-depth ref₁ (tail val) (expr ref (head val) e′) ⟩ CallDepth.expr (expr ref (head val) e′) ⊔ (CallDepth.locRef ref₁ ⊔ CallDepth.expr val) ≤⟨ ℕₚ.⊔-monoˡ-≤ _ (expr-depth ref (head val) e′) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr val) ⊔ (CallDepth.locRef ref₁ ⊔ CallDepth.expr val) ≡⟨ solve-⊔ 4 (λ a b c d → (a ⊕ (b ⊕ d)) ⊕ (c ⊕ d) ⊜ a ⊕ ((b ⊕ c) ⊕ d)) refl (CallDepth.expr e′) (CallDepth.locRef ref) _ _ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.locRef ref₁ ⊔ CallDepth.expr val) ∎ expr-depth (head ref) val e′ = begin CallDepth.expr (expr ref (cons val (tail (!! ref))) e′) ≤⟨ expr-depth ref (cons val (tail (!! ref))) e′ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ CallDepth.expr (!! ref))) ≡⟨ cong (λ x → CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ x))) (CallDepth.homo-!! ref) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr val ⊔ CallDepth.locRef ref)) ≡⟨ cong (CallDepth.expr e′ ⊔_) (solve-⊔ 2 (λ a b → a ⊕ (b ⊕ a) ⊜ a ⊕ b) refl (CallDepth.locRef ref) _) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr val) ∎ expr-depth (tail ref) val e′ = begin CallDepth.expr (expr ref (cons (head (!! ref)) val) e′) ≤⟨ expr-depth ref (cons (head (!! ref)) val) e′ ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.expr (!! ref) ⊔ CallDepth.expr val)) ≡⟨ cong (λ x → CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (x ⊔ _))) (CallDepth.homo-!! ref) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr val)) ≡⟨ cong (CallDepth.expr e′ ⊔_) (solve-⊔ 2 (λ a b → a ⊕ (a ⊕ b) ⊜ a ⊕ b) refl (CallDepth.locRef ref) _) ⟩ CallDepth.expr e′ ⊔ (CallDepth.locRef ref ⊔ CallDepth.expr val) ∎ module Inline where private elses : LocalStatement Σ Γ → ℕ elses (s ∙ s₁) = elses s ℕ.+ elses s₁ elses skip = 0 elses (ref ≔ val) = 0 elses (declare e s) = elses s elses (if x then s) = elses s elses (if x then s else s₁) = 1 ℕ.+ elses s ℕ.+ elses s₁ elses (for n s) = elses s structure : LocalStatement Σ Γ → ℕ structure (s ∙ s₁) = 1 ℕ.+ structure s ℕ.+ structure s₁ structure skip = 0 structure (ref ≔ val) = 0 structure (declare e s) = structure s structure (if x then s) = 1 ℕ.+ 3 ℕ.* structure s structure (if x then s else s₁) = 1 ℕ.+ 3 ℕ.* structure s ℕ.+ structure s₁ structure (for n s) = 1 ℕ.+ structure s scope : LocalStatement Σ Γ → ℕ scope (s ∙ s₁) = 0 scope skip = 0 scope (ref ≔ val) = 0 scope (declare e s) = 1 ℕ.+ scope s scope (if x then s) = 2 ℕ.* scope s scope (if x then s else s₁) = 0 scope (for n s) = 0 weaken-elses : ∀ i t′ (s : LocalStatement Σ Γ) → elses (Weaken.locStmt i t′ s) ≡ elses s weaken-elses i t′ (s ∙ s₁) = cong₂ ℕ._+_ (weaken-elses i t′ s) (weaken-elses i t′ s₁) weaken-elses i t′ skip = refl weaken-elses i t′ (ref ≔ val) = refl weaken-elses i t′ (declare e s) = weaken-elses (suc i) t′ s weaken-elses i t′ (if x then s) = weaken-elses i t′ s weaken-elses i t′ (if x then s else s₁) = cong₂ (λ m n → 1 ℕ.+ m ℕ.+ n) (weaken-elses i t′ s) (weaken-elses i t′ s₁) weaken-elses i t′ (for n s) = weaken-elses (suc i) t′ s weaken-structure : ∀ i t′ (s : LocalStatement Σ Γ) → structure (Weaken.locStmt i t′ s) ≡ structure s weaken-structure i t′ (s ∙ s₁) = cong₂ (λ m n → 1 ℕ.+ m ℕ.+ n) (weaken-structure i t′ s) (weaken-structure i t′ s₁) weaken-structure i t′ skip = refl weaken-structure i t′ (ref ≔ val) = refl weaken-structure i t′ (declare e s) = weaken-structure (suc i) t′ s weaken-structure i t′ (if x then s) = cong (λ m → 1 ℕ.+ 3 ℕ.* m) (weaken-structure i t′ s) weaken-structure i t′ (if x then s else s₁) = cong₂ (λ m n → 1 ℕ.+ 3 ℕ.* m ℕ.+ n) (weaken-structure i t′ s) (weaken-structure i t′ s₁) weaken-structure i t′ (for n s) = cong suc (weaken-structure (suc i) t′ s) weaken-scope : ∀ i t′ (s : LocalStatement Σ Γ) → scope (Weaken.locStmt i t′ s) ≡ scope s weaken-scope i t′ (s ∙ s₁) = refl weaken-scope i t′ skip = refl weaken-scope i t′ (ref ≔ val) = refl weaken-scope i t′ (declare e s) = cong suc (weaken-scope (suc i) t′ s) weaken-scope i t′ (if x then s) = cong (2 ℕ.*_) (weaken-scope i t′ s) weaken-scope i t′ (if x then s else s₁) = refl weaken-scope i t′ (for n s) = refl RecItem : Vec Type n → Set RecItem Σ = ∃ λ n → ∃ λ (Γ : Vec Type n) → LocalStatement Σ Γ P : ∀ (Σ : Vec Type n) → RecItem Σ → Set P Σ (_ , Γ , s) = ∀ {t} (e : Expression Σ Γ t) → ∃ λ (e′ : Expression Σ Γ t) → CallDepth.expr e′ ≤ CallDepth.locStmt s ⊔ CallDepth.expr e index : RecItem Σ → ℕ × ℕ × ℕ index = < elses , < structure , scope > > ∘ proj₂ ∘ proj₂ infix 4 _≺_ _≺_ : RecItem Σ → RecItem Σ → Set _≺_ = ×-Lex _≡_ _<_ (×-Lex _≡_ _<_ _<_) on index ≺-wellFounded : WellFounded (_≺_ {Σ = Σ}) ≺-wellFounded = On.wellFounded index (×-wellFounded ℕᵢ.<-wellFounded (×-wellFounded ℕᵢ.<-wellFounded ℕᵢ.<-wellFounded)) ≤∧<⇒≺ : ∀ (item item₁ : RecItem Σ) → (_≤_ on proj₁ ∘ index) item item₁ → (×-Lex _≡_ _<_ _<_ on proj₂ ∘ index) item item₁ → item ≺ item₁ ≤∧<⇒≺ item item₁ ≤₁ <₂ with proj₁ (index item) ℕₚ.> n) = suc (Σ[ 1 ] structure e) structure (rnd e) = suc (Σ[ 1 ] structure e) structure (fin f e) = suc (Σ[ 1 ] structure e) structure (asInt e) = suc (Σ[ 1 ] structure e) structure (nil) = suc (Σ[ 0 ] _) structure (cons e e₁) = suc (Σ[ 2 ] (structure e , structure e₁)) structure (head e) = suc (Σ[ 1 ] structure e) structure (tail e) = suc (Σ[ 1 ] structure e) structure (call f es) = structures es structure (if e then e₁ else e₂) = suc (Σ[ 3 ] (structure e , structure e₁ , structure e₂)) structures [] = 1 structures (e ∷ es) = structures es ℕ.+ structure e structures>0 : ∀ (es : All (Expression Σ Γ) ts) → 0 < structures es structures>0 [] = ℕₚ.0<1+n structures>0 (e ∷ es) = ℕₚ.<-transˡ (structures>0 es) (ℕₚ.m≤m+n _ _) structure-∷ˡ-< : ∀ (e : Expression Σ Γ t) (es : All (Expression Σ Γ) ts) → structure e < structures (e ∷ es) structure-∷ˡ-< e es = ℕₚ.m0 es) structure-∷ʳ-≤ : ∀ (e : Expression Σ Γ t) (es : All (Expression Σ Γ) ts) → structures es ≤ structures (e ∷ es) structure-∷ʳ-≤ e es = ℕₚ.m≤m+n _ _ RecItem : Vec Type m → Vec Type n → Set RecItem Σ Γ = ∃ (Expression Σ Γ) RecItems : Vec Type m → Vec Type n → Set RecItems Σ Γ = ∃ λ n → ∃ (All (Expression Σ Γ) {n}) P : ∀ (Σ : Vec Type m) (Γ : Vec Type n) → RecItem Σ Γ → Set P Σ Γ (t , e) = ∃ λ (e′ : Expression Σ Γ t) → CallDepth.expr e′ ≡ 0 Ps : ∀ (Σ : Vec Type k) (Γ : Vec Type m) → RecItems Σ Γ → Set Ps Σ Γ (n , ts , es) = ∃ λ (es′ : All (Expression Σ Γ) ts) → CallDepth.exprs es′ ≡ 0 index : RecItem Σ Γ → ℕ × ℕ index = < CallDepth.expr , structure > ∘ proj₂ index′ : RecItems Σ Γ → ℕ × ℕ index′ = < CallDepth.exprs , structures > ∘ proj₂ ∘ proj₂ infix 4 _≺_ _≺′_ _≺′′_ _≺_ : RecItem Σ Γ → RecItem Σ Γ → Set _≺_ = ×-Lex _≡_ _<_ _<_ on index _≺′_ : RecItem Σ Γ → RecItems Σ Γ → Set item ≺′ items = ×-Lex _≡_ _<_ _<_ (index item) (index′ items) _≺′′_ : RecItems Σ Γ → RecItems Σ Γ → Set _≺′′_ = ×-Lex _≡_ _<_ _≤_ on index′ ≺-wellFounded : WellFounded (_≺_ {Σ = Σ} {Γ = Γ}) ≺-wellFounded = On.wellFounded index (×-wellFounded ℕᵢ.<-wellFounded ℕᵢ.<-wellFounded) ≤∧<⇒≺ : ∀ (item item₁ : RecItem Σ Γ) → (_≤_ on proj₁ ∘ index) item item₁ → (_<_ on proj₂ ∘ index) item item₁ → item ≺ item₁ ≤∧<⇒≺ item item₁ ≤₁ <₂ with proj₁ (index item) ℕₚ.> n) rec = proj₁ e′ >> n , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) (_>> n) refl) helper (_ , rnd e) rec = rnd (proj₁ e′) , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) rnd refl) helper (_ , fin f e) rec = fin f (proj₁ e′) , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) (fin f) refl) helper (_ , asInt e) rec = asInt (proj₁ e′) , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) asInt refl) helper (_ , nil) rec = nil , refl helper (_ , cons e e₁) rec = cons (proj₁ e′) (proj₁ e₁′) , cong₂ _⊔_ (proj₂ e′) (proj₂ e₁′) where e′ = rec (-, e) (rec-helper 0F (e ∷ e₁ ∷ []) cons refl) e₁′ = rec (-, e₁) (rec-helper 1F (e ∷ e₁ ∷ []) cons refl) helper (_ , head e) rec = head (proj₁ e′) , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) head refl) helper (_ , tail e) rec = tail (proj₁ e′) , proj₂ e′ where e′ = rec (-, e) (rec-helper 0F (e ∷ []) tail refl) helper (_ , call f es) rec = rec (-, Inline.fun f (proj₁ es′)) (inj₁ (begin-strict CallDepth.expr (Inline.fun f (proj₁ es′)) ≤⟨ Inline.fun-depth f (proj₁ es′) ⟩ CallDepth.fun f ⊔ CallDepth.exprs (proj₁ es′) ≡⟨ cong (CallDepth.fun f ⊔_) (proj₂ es′) ⟩ CallDepth.fun f ⊔ 0 ≡⟨ ℕₚ.⊔-identityʳ _ ⟩ CallDepth.fun f <⟨ ℕₚ.n<1+n _ ⟩ suc (CallDepth.fun f) ≤⟨ ℕₚ.m≤n⊔m (CallDepth.exprs es) _ ⟩ CallDepth.exprs es ⊔ suc (CallDepth.fun f) ∎)) where rec′ : ∀ item → item ≺′ (-, (-, es)) → P _ _ item rec′ item i≺es = rec item (lemma item i≺es) where lemma : ∀ item → item ≺′ (-, -, es) → item ≺ (-, call f es) lemma item (inj₁ <-depth) = inj₁ (begin-strict CallDepth.expr (proj₂ item) <⟨ <-depth ⟩ CallDepth.exprs es ≤⟨ ℕₚ.m≤m⊔n (CallDepth.exprs es) _ ⟩ CallDepth.expr (call f es) ∎) lemma item (inj₂ (≡-depth , <-structure)) = ≤∧<⇒≺ item (-, call f es) (begin CallDepth.expr (proj₂ item) ≡⟨ ≡-depth ⟩ CallDepth.exprs es ≤⟨ ℕₚ.m≤m⊔n (CallDepth.exprs es) _ ⟩ CallDepth.expr (call f es) ∎) <-structure rec′′ : ∀ items → items ≺′′ (-, (-, es)) → Ps _ _ items rec′′ (_ , _ , es′) = go es′ where go : ∀ (es′ : All (Expression _ _) ts) → (-, -, es′) ≺′′ (-, -, es) → Ps _ _ (-, -, es′) go [] ≺′′ = [] , refl go (e ∷ es′) ≺′′ = proj₁ a ∷ proj₁ b , cong₂ _⊔_ (proj₂ b) (proj₂ a) where a = rec′ (-, e) (≺′-≺′′-trans (-, e) (-, -, e ∷ es′) (-, -, es) (∷ˡ-≺′ e es′) ≺′′) b = go es′ (≺′′-trans (-, -, es′) (-, -, e ∷ es′) (-, -, es) (∷ʳ-≺′′ e es′) ≺′′) es′ = helper′ (-, -, es) rec′ rec′′ helper (_ , (if e then e₁ else e₂)) rec = (if proj₁ e′ then proj₁ e₁′ else proj₁ e₂′) , congₙ 3 (λ a b c → a ⊔ b ⊔ c) (proj₂ e′) (proj₂ e₁′) (proj₂ e₂′) where e′ = rec (-, e) (rec-helper 0F (e ∷ e₁ ∷ e₂ ∷ []) if_then_else_ refl) e₁′ = rec (-, e₁) (rec-helper 1F (e ∷ e₁ ∷ e₂ ∷ []) if_then_else_ refl) e₂′ = rec (-, e₂) (rec-helper 2F (e ∷ e₁ ∷ e₂ ∷ []) if_then_else_ refl) helper′ (_ , _ , []) rec′ rec′′ = [] , refl helper′ (_ , _ , e ∷ es) rec′ rec′′ = proj₁ a ∷ proj₁ b , cong₂ _⊔_ (proj₂ b) (proj₂ a) where a = rec′ (-, e) (∷ˡ-≺′ e es) b = rec′′ (-, -, es) (∷ʳ-≺′′ e es) expr : Expression Σ Γ t → Expression Σ Γ t expr e = proj₁ (Wf.All.wfRec ≺-wellFounded _ (P _ _) helper (-, e)) expr-depth : ∀ (e : Expression Σ Γ t) → CallDepth.expr (expr e) ≡ 0 expr-depth e = proj₂ (Wf.All.wfRec ≺-wellFounded _ (P _ _) helper (-, e))