Let's see if we can do something half-elegant, without depending on 1 <= n <= 10.
- Instead of looping we'll of course use recursion.
- Instead of an if for terminating the recursion, we'll use an array of function pointers!
(We still need comparison operators, such as <
and ==
.)
EDIT: damaru used the function pointers trick first.
This gives: [All code is untested, no C compiler under hand!]
typedef int (*unary_fptr)(int);
int ret_1(int n) {
return 1;
}
int fact(int n) {
unary_fptr ret_1_or_fact[] = {ret_1, fact};
return multiply(ret_1_or_fact[n > 1](sub_1(n)), n);
}
We still need to implement sub_1
and multiply
. Let's start with sub_1
, which is a simple recursion on the bits until the carry stops (if you don't understand this, the similar add_1
at the end is simpler to think about):
int identity(int n) {
return n;
}
int sub_1(int n) {
unary_fptr sub_1_or_identity[] = {sub_1, identity};
int lsb = n & 1;
int rest = sub_1_or_identity[lsb](n >> 1);
return (rest << 1) | (lsb ^ 1);
}
multiply
: The simplest I can think of is Russian Peasant multiplication, which reduces it to binary shifts and addition. With conditionals, a recursive formulation would look like this:
/* If we could use conditionals */
int multiply(int a, int b) {
int subproduct;
if(a <= 1) {
subproduct = 0;
} else {
subproduct = multiply(a >> 1, b << 1);
}
if(a & 1) {
return add(b, subproduct);
} else {
return subproduct;
}
}
Without conditionals, we have to use the dispatch array trick twice:
typedef int (*binary_fptr)(int, int);
int ret_0(int a, int b) {
return 0;
}
int multiply(int a, int b) {
binary_fptr ret_0_or_multiply = {ret_0, multiply};
int subproduct = ret_0_or_multiply[a >= 2](a >> 1, b << 1);
binary_fptr ret_0_or_add = {ret_0, add};
return ret_0_or_add[a & 1](subproduct, b);
}
Now all we miss is add
. You should by now guess how it will go - a simultaneous recursion over bits of the two numbers, which reduces the problem to shifts and add_1
:
int add(int a, int b) {
int lsb = (a & 1) ^ (b & 1);
int carry = (a & 1) & (b & 1);
binary_fptr ret_0_or_add = {ret_0, add};
int subsum = ret_0_or_add[(a >= 2) & (b >= 2)](a >> 1, b>> 1);
unary_fptr identity_or_add_1 = {identity, add_1};
return identity_or_add_1[carry](subsum << 1);
}
and add_1
is a simple recursion over bits until the carry stops:
int add_1(int n) {
unary_fptr identity_or_add_1[] = {identity, add_1};
int lsb = n & 1;
int rest = identity_or_add_1[lsb](n >> 1);
return (rest << 1) | (lsb ^ 1);
}
That's it I think! [As noted above all code is untested!]