[Agda] termination checking
Peter Selinger
selinger at mathstat.dal.ca
Thu Dec 18 19:06:09 CET 2014
I have a question about termination checking. The following example
has been vexing me. I do not understand why the function
RecordExample.div-aux fails termination checking, while both
RecordExample.fibonacci and DataTypeExample.div-aux pass it.
Agda version 2.4.2.2. Might this be a subtle bug in the termination
checker with respect to inductive records? Or is there something
obvious that I am missing.
Any help appreciated! Thanks, -- Peter
P.S.: although the example works if I use a datatype, I prefer using a
record, because then I can use the constructor in let-bindings, and
also when matching against irrelevant arguments. In the case of
datatypes, these two kinds of pattern matches are not allowed.
----------------------------------------------------------------------
open import Data.Nat
open import Relation.Nullary
module TerminationExample where
-- Some true lemmas about natural numbers. Proofs omitted for brevity.
postulate
lemma-refl : ∀ n -> n ≤ n
lemma-suc : ∀ n -> n ≤ suc n
lemma-order : ∀ {n m} -> ¬ n ≤ m -> n > 0
lemma-minus : ∀ n m -> n > 0 -> n ∸ suc m < n
module RecordExample where
-- A natural number is inductive if all smaller numbers are inductive.
record Inductive (n : ℕ) : Set where
inductive
constructor cind
field cind-field : ∀ {m} -> m < n -> Inductive m
-- All natural numbers are inductive. Proof omitted for brevity.
postulate
lemma-inductive : ∀ n -> Inductive n
-- Auxiliary function for integer division. The termination checker
-- fails but I don't know why. The third argument is clearly
-- decreasing:
--
-- h proof < cind h
{-# NON_TERMINATING #-}
div-aux : (n : ℕ) -> ℕ -> Inductive n -> ℕ
div-aux n zero (cind h) = zero
div-aux n (suc m) (cind h) with n ≤? suc m
div-aux n (suc m) (cind h) | yes p = zero
div-aux n (suc m) (cind h) | no p = suc (div-aux (n ∸ suc m) (suc m) (h proof))
where
proof : n ∸ suc m < n
proof = lemma-minus n m (lemma-order p)
-- Integer division.
div : ℕ -> ℕ -> ℕ
div n m = div-aux n m (lemma-inductive n)
-- However, it does not *always* fail. Here is a situation that is a
-- priori very similar, but termination checking succeeds without
-- any problems.
fibonacci-aux : (n : ℕ) -> Inductive n -> ℕ
fibonacci-aux 0 (cind h) = 1
fibonacci-aux 1 (cind h) = 1
fibonacci-aux (suc (suc n)) (cind h) = fibonacci-aux n (h proof1) + fibonacci-aux (suc n) (h proof2)
where
proof1 : n < suc (suc n)
proof1 = s≤s (lemma-suc n)
proof2 : suc n < suc (suc n)
proof2 = lemma-refl (suc (suc n))
module DataTypeExample where
-- Redoing the same example using an inductive datatype instead of
-- an inductive record.
data Inductive : (n : ℕ) -> Set where
cind : ∀ {n} -> (∀ {m} -> m < n -> Inductive m) -> Inductive n
postulate
lemma-inductive : ∀ n -> Inductive n
-- This time it works like a charm.
div-aux : (n : ℕ) -> ℕ -> Inductive n -> ℕ
div-aux n zero (cind h) = zero
div-aux n (suc m) (cind h) with n ≤? suc m
div-aux n (suc m) (cind h) | yes p = zero
div-aux n (suc m) (cind h) | no p = suc (div-aux (n ∸ suc m) (suc m) (h proof))
where
proof : n ∸ suc m < n
proof = lemma-minus n m (lemma-order p)
-- Integer division.
div : ℕ -> ℕ -> ℕ
div n m = div-aux n m (lemma-inductive n)
----------------------------------------------------------------------
More information about the Agda
mailing list