1

I'm trying to define binary numbers in agda but agda cant see that ⟦_⇑⟧ is terminating. I really dont want to have to break out accessibility relations. How can I show agda that n gets smaller?

module Binary where

open import Data.Nat using (ℕ; _+_; +-suc)
open ℕ

2* : ℕ → ℕ
2* n = n + n

data Parity : ℕ → Set where
   : ∀ k → Parity (2* k)
   : ∀ k → Parity (1 + 2* k)

parity : ∀ n → Parity n
parity zero =  0
parity (suc n) with parity n
parity (suc .(k + k)) |  k =  k
parity (suc .(suc (k + k))) |  k rewrite sym (+-suc k k) =  (suc k)

data  : Set where
   : 
   :  → 
   :  → 

⟦_⇓⟧ :  → ℕ
⟦  ⇓⟧ = 0
⟦  b ⇓⟧ = 2* ⟦ b ⇓⟧
⟦  b ⇓⟧ = 1 + 2* ⟦ b ⇓⟧

⟦_⇑⟧ : ℕ → 
⟦ zero ⇑⟧ = 
⟦ suc n ⇑⟧ with parity n
⟦ suc .(k + k) ⇑⟧ |  k =  ⟦ k ⇑⟧
⟦ suc .(suc (k + k)) ⇑⟧ |  k =  ⟦ k ⇑⟧

Error:

Termination checking failed for the following functions:
  ⟦_⇑⟧
Problematic calls:
  ⟦ k ⇑⟧
MrO
  • 1,291
  • 7
  • 14
Kyle McKean
  • 445
  • 2
  • 11
  • 1
    What do you mean you don't want to break out well-founded relations ? You mean you don't want to use them or you don't want to learn how to ? Because in your specific case, since your recursive call is not made on a sctructurally smaller argument, you have no choice but to use them. Thankfully they exist in the standard library. I can show you how to use them provided this is what you want to do. Now that I think of it, there is another solution, which is to disable termination checking for this definition using the associated pragma, but I highly suggest you not to go for that option. – MrO Nov 12 '19 at 23:11
  • i know how to use well founded relations. id just like to not be forced into using them all the time. it seems to me like it is structurally decreasing but agda doesn't see that. it unwraps one suc per function call at least. – Kyle McKean Nov 13 '19 at 10:53
  • It's definitely not structurally recursive. Lemme make an answer rather than a comment to explain to you why. – MrO Nov 13 '19 at 11:06

1 Answers1

3

Termination checking fails for good reasons, since the following function is not structurally recursive over its input:

⟦_⇑⟧ : ℕ → 
⟦ zero ⇑⟧ = 
⟦ suc n ⇑⟧ with parity n
⟦ suc .(k + k) ⇑⟧ |  k =  ⟦ k ⇑⟧
⟦ suc .(suc (k + k)) ⇑⟧ |  k =  ⟦ k ⇑⟧

Agda even tells you what is the problematic call : ⟦ k ⇑⟧. (in this case, there are two of these ill-formed calls).

While I agree it might look that the function is called on a structurally smaller argument it's not the case. To understand why, you have to see that k is used as input for a function call, _+_, and that only the result of this function is structurally smaller than n, not k itself. And agda has no way of knowing the following property about _+_ :

∀ {n} → n < suc (n + n)

Should you provide a proof of such lemma, you could use the fact that _<_ is well founded to make your function structurally recursive over Acc, but it seems you are reluctant to do so.

An easy way to understand why Agda cannot know that this terminates is the following: imagine you replace suc .(k + k) by suc .(a ∸ b) and recursively call your function over a. From agda's point of view, both are the same cases, and in this case, it does not usually terminate unless b happens to be 0.


Here is the module corrected in terms of termination :

module Binary where

open import Data.Nat
open import Data.Nat.Properties
open import Relation.Binary.PropositionalEquality
open import Data.Product
open import Induction.Nat
open import Induction.WellFounded

2* : ℕ → ℕ
2* n = n + n

data Parity : ℕ → Set where
   : ∀ k → Parity (2* k)
   : ∀ k → Parity (1 + 2* k)

parity : ∀ n → Parity n
parity zero =  0
parity (suc n) with parity n
parity (suc .(k + k)) |  k =  k
parity (suc .(suc (k + k))) |  k rewrite sym (+-suc k k) =  (suc k)

data  : Set where
   : 
   :  → 
   :  → 

⟦_⇓⟧ :  → ℕ
⟦  ⇓⟧ = 0
⟦  b ⇓⟧ = 2* ⟦ b ⇓⟧
⟦  b ⇓⟧ = 1 + 2* ⟦ b ⇓⟧

decr : ∀ {n} → n < suc (n + n)
decr {n} = s≤s (m≤m+n n n) 

helper : (n : ℕ) → Acc _<_ n → 
helper zero a = 
helper (suc n) a with parity n
helper (suc .(k + k)) (acc rs) |  k =  (helper k (rs _ decr))
helper (suc .(suc (k + k))) (acc rs) |  k =  (helper k (rs _ (s≤s (<⇒≤ decr))))

⟦_⇑⟧ : ℕ → 
⟦ n ⇑⟧ = helper n (<-wellFounded n)

I was also skeptical about the correctness of your definitions and it turns out I was right, for instance, considering the following definition:

test : ℕ
test = ⟦ ⟦ 124 ⇑⟧ ⇓⟧

Agda returns 2 when evaluating test.

Considering the definition:

test₁ : ℕ
test₁ = ⟦ ⟦ 16 ⇑⟧ ⇓⟧

Agda returns 14 when evaluating test₁

In order to correct your definitions, you can take inspiration from what is done in the standard library, either in the module Data.Bin (deprecated) or in the module Data.Nat.Binary depending on which version of the stdlib you have.

MrO
  • 1,291
  • 7
  • 14