||| Properties of Ackermann functions
module Data.Nat.Ack

%default total

-- Primitive recursive functions are functions that can be calculated
-- by programs that don't use unbounded loops. Almost all common
-- mathematical functions are primitive recursive.

-- Uncomputable functions are functions that can't be calculated by
-- any programs at all. One example is the Busy Beaver function:
--   BB(k) = the maximum number of steps that can be executed by a
--           halting Turing machine with k states.
-- The values of the Busy Beaver function are unimaginably large for
-- any but the smallest inputs.

-- The Ackermann function is the most well-known example of a total
-- computable function that is not primitive recursive, i.e. a general
-- recursive function. It grows strictly faster than any primitive
-- recursive function, but also strictly slower than a function like
-- the Busy Beaver.

-- There are many variations of the Ackermann function. Here is one
-- common definition
-- (see https://sites.google.com/site/pointlesslargenumberstuff/home/2/ackermann)
-- that uses nested recursion:

ackRec : Nat -> Nat -> Nat
-- Base rule
ackRec Z m = S m
-- Prime rule
ackRec (S k) Z = ackRec k 1
-- Catastrophic rule
ackRec (S k) (S j) = ackRec k $ ackRec (S k) j

-- The so-called "base rule" and "prime rule" work together to ensure
-- termination. Happily, the Idris totality checker has no issues.

-- An unusual "repeating" defintion of the function is given in the
-- book The Little Typer:

ackRep : Nat -> Nat -> Nat
ackRep Z = (+) 1
ackRep (S k) = repeat (ackRep k)
  where
    repeat : (Nat -> Nat) -> Nat -> Nat
    repeat f Z = f 1
    repeat f (S k) = f (repeat f k)

-- These two definitions don't look like they define the same
-- function, but they do:

ackRepRec : (n, m : Nat) -> ackRep n m = ackRec n m
ackRepRec Z _ = Refl
ackRepRec (S k) Z = ackRepRec k 1
ackRepRec (S k) (S j) =
  rewrite sym $ ackRepRec (S k) j in
    ackRepRec k $ ackRep (S k) j
