------------------------------------------------------------------------------
-- Dependently typed functional languages - 2011-01
-- General recursion
------------------------------------------------------------------------------
-- Common options
{-# OPTIONS --double-check #-}
{-# OPTIONS --exact-split #-}
{-# OPTIONS --guardedness #-}
{-# OPTIONS --no-sized-types #-}
{-# OPTIONS --warning=all #-}
{-# OPTIONS --warning=error #-}
-- Other options
-- {-# OPTIONS --no-universe-polymorphism #-}
-- We cannot use the `safe` option because we are using the
-- `TERMINATING` flag.
-- {-# OPTIONS --safe #-}
{-# OPTIONS --without-K #-}
module Lecture.GeneralRecursion where
open import Data.List hiding (merge)
open import Data.Nat renaming (suc to succ) hiding (_≤′?_ )
open import Data.Nat.Properties hiding (_≤′?_ ; ≤′-trans )
open import Data.Product
open import Extra.Data.Nat.Properties
open import Extra.Data.Nat.Induction.Lexicographic
open import Extra.Data.Nat.Induction.WellFounded
open import Relation.Nullary
------------------------------------------------------------------------------
-- A structural recursive function.
fac : ℕ → ℕ
fac zero = 1
fac (succ n) = succ n * fac n
------------------------------------------------------------------------------
-- A generalization of structural recursion: The termination checker
swap' : ℕ → ℕ → ℕ
swap' 0 _ = 0
{-# CATCHALL #-}
swap' _ 0 = 0
swap' (succ m) (succ n) = swap' n m
ack : ℕ → ℕ → ℕ
ack zero n = succ n
ack (succ m) zero = ack m (succ zero)
ack (succ m) (succ n) = ack m (ack (succ m) n)
------------------------------------------------------------------------------
-- The with issue
-- (See http://code.google.com/p/agda/issues/detail?id=59)
-- The non-terminating original function.
-- The function is structurally recursive, but the function is not
-- accepted by the termination checker due to the use of the with.
{-# TERMINATING #-}
merge : List ℕ → List ℕ → List ℕ
merge [] l₂ = l₂
{-# CATCHALL #-}
merge l₁ [] = l₁
merge (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes _ = x ∷ merge xs (y ∷ ys)
... | no _ = y ∷ merge (x ∷ xs) ys
-- The merge-trick: Nested withs with the recursive calls.
merge' : List ℕ → List ℕ → List ℕ
merge' [] l₂ = l₂
{-# CATCHALL #-}
merge' l₁ [] = l₁
merge' (x ∷ xs) (y ∷ ys) with x ≤? y | merge' xs (y ∷ ys) | merge' (x ∷ xs) ys
... | yes p | aux | _ = x ∷ aux
... | no ¬p | _ | aux = y ∷ aux
------------------------------------------------------------------------------
-- A non-structural recursive function: simple general recursion
{-# TERMINATING #-}
gcd : ℕ → ℕ → ℕ
gcd 0 0 = 0
gcd (succ m) 0 = succ m
gcd 0 (succ n) = succ n
gcd (succ m) (succ n) with (succ m ≤? succ n)
-- The argument `sucn n ∸ succ m` is not structurally smaller than the
-- argument `succ n`.
... | yes p = gcd (succ m) (succ n ∸ succ m)
-- The argument `succ m ∸ succ n` is not structurally smaller than the
-- argument `succ n`.
... | no ¬p = gcd (succ m ∸ succ n) (succ n)
-- Can we apply the merge-trick?
-- The Bove-Capretta method: The domain predicate
-- (See: Bove, Ana and Capretta, Venanzio (2005a). Modelling General
-- Recursion in Type Theory)
-- Domain predicate for the gcd: GCDDom
-- m : ℕ n : ℕ
------------------- ------------------------ ------------------------
-- GCDDom 0 0 GCDDom (suc m) 0 GCDDom 0 (suc n)
-- m n : ℕ suc m ≤ suc n GCDDom (suc m) (suc n ∸ suc m)
------------------------------------------------------------------------
-- GCDDom (suc m) (suc n)
-- m n : ℕ suc m > suc n GCDDom (suc m ∸ suc n) (suc n)
------------------------------------------------------------------------
-- GCDDom (suc m) (suc n)
data GCDDom : ℕ → ℕ → Set where
gcdDom₁ : GCDDom 0 0
gcdDom₂ : ∀ {m} → GCDDom (succ m) 0
gcdDom₃ : ∀ {n} → GCDDom 0 (succ n)
gcdDom₄ : ∀ {m n} →
succ m ≤′ succ n →
GCDDom (succ m) (succ n ∸ succ m) →
GCDDom (succ m) (succ n)
gcdDom₅ : ∀ {m n} →
succ m >′ succ n →
GCDDom (succ m ∸ succ n) (succ n) →
GCDDom (succ m) (succ n)
-- The gcd function by structural recursion on the domain predicate.
gcdD : ∀ m n → GCDDom m n → ℕ
gcdD .0 .0 gcdDom₁ = 0
gcdD .(succ m) .0 (gcdDom₂ {m}) = succ m
gcdD .0 .(succ n) (gcdDom₃ {n}) = succ n
gcdD .(succ m) .(succ n) (gcdDom₄ {m} {n} _ h) = gcdD (succ m) (succ n ∸ succ m) h
gcdD .(succ m) .(succ n) (gcdDom₅ {m} {n} _ h) = gcdD (succ m ∸ succ n) (succ n) h
-- The gcd function is total.
allGCDDom : ∀ m n → GCDDom m n
allGCDDom m n = <′₂-ind P ih m n
where
P : ℕ → ℕ → Set
P m n = GCDDom m n
helper : ∀ a b → succ (a ∸ b) ≤′ succ a
helper a b = ≤⇒≤′ (s≤s (m∸n≤m a b))
ih : ∀ x y → (∀ x' y' → (x' , y') <′₂ (x , y) → P x' y') → P x y
ih zero zero h = gcdDom₁
ih zero (succ y) h = gcdDom₃
ih (succ x) zero h = gcdDom₂
ih (succ x) (succ y) h with succ x ≤′? succ y
... | yes p = gcdDom₄ p (h (succ x) (succ y ∸ succ x) (<′₂-y (helper y x)))
... | no ¬p = gcdDom₅ (x≰′y→x>′y ¬p) (h (succ x ∸ succ y) (succ y)
(<′₂-x (helper x y)))
-- The final version of the gcd.
gcd' : ℕ → ℕ → ℕ
gcd' m n = gcdD m n (allGCDDom m n)
------------------------------------------------------------------------------
-- A non-structural recursive function: nested recursion
-- The function is not accepted by the termination checker due to the
-- nested recursive call.
{-# TERMINATING #-}
nest : ℕ → ℕ
nest 0 = 0
nest (succ n) = nest (nest n)
-- The Bove-Capretta method: The domain predicate
-- (See: Bove, Ana and Capretta, Venanzio (2001). Nested General
-- Recursion and Partiality in Type Theory)
-- Domain predicate for the nest: NestDom
-----------------
-- NestDom 0
-- n : ℕ NestDom n NestDom (nest n)
-------------------------------------------------------
-- NestDom (succ n)
-- Problem: We want to use the domain predicate to define the function
-- nest, but it requires the function.
-- Solution: Mutual block for the definition of the domain predicate
-- and the function.
mutual
-- The domain predicate of the nest function.
data NestDom : ℕ → Set where
nestDom0 : NestDom 0
nestDomS : ∀ {n} →
(h₁ : NestDom n) →
(h₂ : NestDom (nestD n h₁)) →
NestDom (succ n)
-- The nest function by structural recursion on the domain predicate.
nestD : ∀ n → NestDom n → ℕ
nestD .0 nestDom0 = 0
nestD .(succ n) (nestDomS {n} h₁ h₂) = nestD (nestD n h₁) h₂
nestD-≤′ : ∀ n → (h : NestDom n) → nestD n h ≤′ n
nestD-≤′ .0 nestDom0 = ≤′-refl
nestD-≤′ .(succ n) (nestDomS {n} h₁ h₂) =
≤′-trans (≤′-trans (nestD-≤′ (nestD n h₁) h₂) (nestD-≤′ n h₁))
(≤′-step ≤′-refl)
-- The nest function is total.
allNestDom : ∀ n → NestDom n
allNestDom = <′-wfind P ih
where
P : ℕ → Set
P = NestDom
ih : ∀ y → (∀ x → x <′ y → P x) → P y
ih zero rec = nestDom0
ih (succ y) rec = nestDomS nd-y (rec (nestD y nd-y) (s≤′s (nestD-≤′ y nd-y)))
where
helper : ∀ x → x <′ y → P x
helper x Sx≤′y = rec x (≤′-step Sx≤′y)
nd-y : NestDom y
nd-y = ih y helper
-- The final version of the nest function.
nest' : ℕ → ℕ
nest' n = nestD n (allNestDom n)