C (gcc) -lm, 268 197 194 bytes
x[9];e(n,c){int w=sqrt(n/6*8+1)/2-.5,b=n/6+w*~w/2,*z=x+b;for(n%=6;c--;)n-2?n-3?n-4?n-5?x[w-b]=n?*z:b:e(w-b,*z):e(w-b,1,e(b,1)):*z&&--*z:++*z;}main(n){e(x[1]=n,scanf("%d",&n));printf("%d",1+*x);}
Try it online!
Now 3 more bytes off thanks to @ceilingcat again (saving a pointer z
to x[b]
, and then using *z
instead of x[b]
throughout).
Thanks to C golfing expert @ceilingcat for reducing this by an amazing 71 bytes!
I decided to write an answer that works completely differently from the other solutions posted so far. This uses diagonalization of the primitive recursive functions (it's not a function that grows faster than any primitive recursive function, the way the Ackermann and Sudan functions do).
I don't think there's any way to golf this to be as short as the Ackermann or Sudan programs, but it has two advantages: (1) It's easy to understand the proof that it's not primitive recursive, and (2) you can actually run it on reasonably sized inputs without running out of time or getting stack overflows!
The basic idea behind this function \$F\$ is first to enumerate all programs to compute primitive recursive functions of one variable. Let \$P_0, P_1, ...\$ be this enumeration. Then, for any input \$n,\$ here's how to compute \$F(n)\$: First supply \$n\$ as input to the program \$P_n\$ and run that. When \$P_n\$ halts with an integer as output (as it's guaranteed to do, because it's computing a primitive recursive function), add \$1\$ to that output. That's \$F(n).\$
\$F\$ is clearly total.
Now,
\$F\$ is not the function computed by \$P_0\$ because \$F(0)\$ is one higher than the output of \$P_0\$ on input \$0,\$
\$F\$ is not the function computed by \$P_1\$ because \$F(1)\$ is one higher than the output of \$P_1\$ on input \$1,\$
\$F\$ is not the function computed by \$P_2\$ because \$F(2)\$ is one higher than the output of \$P_2\$ on input \$2,\$
etc. In general, \$F\$ isn't the function computed by \$P_n,\$ because \$F(n)\$ is one higher than the output of \$P_n\$ on input \$n.\$
So \$F\$ isn't the same as the function computed by any of the programs \$P_n.\$ But those are all the primitive recursive functions. So \$F\$ isn't primitive recursive.
The way I enumerate all the primitive recursive functions is by implementing a variant of Uwe Schöning's programming language LOOP. It is known that the functions computable by a LOOP program are precisely the primitive recursive functions. (These programs actually cover all primitive recursive functions, not just the primitive recursive functions of one variable, even though that's ultimately all we would need.)
My variant miniLOOP is even simpler than the original language. Just as in LOOP, there are variables \$x_0, x_1, x_2, \dots\$; each of these variables can hold a natural number (a non-negative integer). To use a miniLOOP program to compute a function of \$k\$ variables, you store the values of the \$k\$ arguments in \$x_1, \dots, x_k,\$ and then run the program. The output is the value of \$x_0\$ at the end.
Here are the basic programming statements available in miniLOOP (all variables are restricted to the natural numbers):
Statements can also be constructed from other statements using the following two constructs:
\$P;Q\$ where \$P\$ and \$Q\$ are statements; this means to execute \$P\$ first and then \$Q.\$
\$\text{LOOP } x_n \text{ DO } P \text{ END},\$ which means to execute \$P\$ repeatedly, \$x_n\$ times in a row. (Repeating it 0 times means not doing it at all, of course.) Note that the number of repetitions is the value that \$x_n\$ has when the loop starts. Even if the body of the loop changes the value of \$x_n,\$ the number of repetitions won't change. This is the key thing that makes this an implementation of primitive recursion.
For example, the following program doubles its input:
LOOP x1 DO x1++ END
x0 = x1
(Recall that a function of one variable takes its input in \$x_1\$ and leaves its output in \$x_0.\$)
You can check that even though miniLOOP is a bit simpler than the LOOP language defined in the Wikipedia article, you can simulate all the LOOP constructs with miniLOOP programs. So miniLOOP also computes precisely the primitive recursive functions.
Every miniLOOP program is assigned a number; that is how we enumerate them. This enumeration uses the Cantor pairing function
$$\pi(x,y)=\frac{(x+y)(x+y+1)}{2}+y.$$
Here is the numerical assignment:
\$x_n=c\$ is assigned the number \$6 \pi(n,c).\$
\$x_n=x_m\$ is assigned the number \$6 \pi(n,m)+1.\$
\$x_m\$++ is assigned all the numbers \$6 \pi(n,m)+2\$ for any \$n\$ (it doesn't matter that one program can be included multiple times in the enumeration).
\$x_m\$-- is assigned all the numbers \$6 \pi(n,m)+3\$ for any \$n.\$
\$P;Q\$ is assigned all the numbers \$6 \pi(q,p)+4\$, where \$p\$ is assigned to \$P\$ and \$q\$ is assigned to \$Q.\$ (\$q\$ and \$p\$ are "backwards" in this formula only because this is code golf and I ended up saving a few bytes by doing it that way.)
\$\text{LOOP } x_n \text{ DO } P \text{ END}\$ is assigned the numbers \$6 \pi(p,n)+5,\$ where \$p\$ is a number assigned to \$P.\$
Note that every number is assigned to a unique program. (A program can have more than one number assigned to it, but a number is associated with exactly one program.) And it's easy to take a number and figure out what program it's assigned to.
For example, you can check that the program above that doubles its input is assigned 1667230. This is computed as \$6 \pi(13,731)+4,\$ where \$13 =6\pi(0,1)+1\$ and \$731=6\pi(14,1)+5.\$ In that last formula, \$14=6\pi(0,1)+2.\$
In the C program, \$x\$ is a global array holding all the variables needed for what you're running. I've only declared it to hold 9 variables, since that's plenty for demonstrating it, but in practice you would really want to allow that to grow using malloc
as needed.
The function e
takes an input n
and runs the miniLOOP program assigned to n
. It assumes that x[1], ..., x[k]
have already been set up as desired for the input, and it leaves the output in x[0]
.
The main program simply takes its input n
, stores it in x[1]
, and calls e(n)
to run the miniLOOP program assigned to n
. It then adds 1
to the output and prints that as the output of the main program.
As described in the outline at the beginning, this program halts on every input. But it's not primitive recursive, since it disagrees with miniLOOP program number n
(at input n
), and those miniLOOP programs compute all the primitive recursive functions.
The TIO link shows what this program does with input 1667230
. Recall that 1667230
is assigned to a miniLOOP program that doubles its input, and you can see that the output of the main program here is 3334461
(which is not equal to double 1667230, being one higher, as intended).
f(n)=1
still unproven? 3) The input is mapped to one output natural number (finite in size), so an infinite array/stream would not count as valid output. \$\endgroup\$