// gcc -Wall -o ccollatz ccollatz.c
// ./ccollatz 837799
// ./ccollatz 8400511
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
// https://www.reddit.com/r/C_Programming/comments/1eoc0v3/does_any_c_compiler_other_than_gcc_support_nested/
// See the accompanying collatz.imp file for an example of a language where
// nested procedures and mutual recursion are a normal part of the language.
// Although not frequently used in C, nested subroutines were once common
// in early programming languages, especially those in the Algol family.
// Translations of that old source code into readable and maintainable C read
// much better if use is made of gcc's extension to support nested subroutines.
// clang also has a related extension but in clang's case, the lambda-expression
// anonymous subroutines, inspired by Apple's blocks extension, have a major
// limitation - they cannot support mutual recursion because there is no
// syntax to allow forward references to the anonymous blocks that take the
// place of nested subroutines.
// This program demonstrates mutual recursion of a nested subroutine.
// If anyone can show me this code working on some compiler other than gcc,
// and without restructuring to avoid the use of nested procedures, while
// maintaining the mutual-recursion, I would very much like to know which
// compiler you used and what changes to the syntax if any were necessary.
// (you can email me at gtoal@gtoal.com)
// Obviously this extremely artificial example could be easily restructured to avoid
// this problem, but the real life programs which make use of this are not so
// trivially fixed, but unfortunately they are too large to be used as an example here.
// The example also demonstrates the use of variables from parent scopes (although
// again, very artificially and avoidably). By the way there is no check here for
// overflow, which can easily occur. The old Algol-style languages would routinely
// have caught overflows, array bounds exceeded, unassigned variables, etc., plus
// would have given a backtrace showing the variables in each stack frame incarnation!
// I've thrown in an exception generation and trap using setjmp/longjmp to simulate
// the old-school behaviour.
// Hopefully this example can inspire writers of C compilers other than gcc to add
// gcc's nested subroutine extension to their compilers - preferably using the same
// syntax as gcc.
unsigned long odd1 = 1UL, odd2 = 2UL;
static const unsigned long final_value = 0UL, MAX_FACTOR = (((unsigned long)-1L)-1UL)/3UL;
int main(int argc, char **argv) {
static const unsigned long terminating_value = 1UL;
void show_collatz_length(unsigned long i, unsigned long *max) {
// Returns 0 for the length if there was an integer overflow.
jmp_buf errbuf;
unsigned long base_value = final_value + 1UL;
auto int odd_collatz(unsigned long i); // mutual recursion requires a forward declaration!
int even_collatz(unsigned long i) {
if (i < terminating_value) return final_value;
if (i > *max) *max = i;
if (i&odd2) { // 'optimization' :-)
return base_value+odd_collatz(i>>1UL); // (really just an excuse to justify this use of mutual recursion)
} else {
return base_value+even_collatz(i>>1UL);
}
}
int odd_collatz(unsigned long i) {
if (i <= terminating_value) return final_value;
if (i > *max) *max = i;
if (i > MAX_FACTOR) longjmp(errbuf, 1); // next call would cause integer overflow...
return base_value+even_collatz((i<<1)+i+1UL);
}
fprintf(stdout, "The Collatz sequence for %lu takes ", i);
if (setjmp(errbuf)) {
fprintf(stdout, "an unknown number of steps (we hit an integer overflow)\n");
return;
}
fprintf(stdout, "%d steps", i&odd1 ? odd_collatz(i) : even_collatz(i));
fprintf(stdout, " and the largest value reached was %lu\n", *max);
}
unsigned long highest;
if (argc != 2) exit(EXIT_FAILURE);
if ((unsigned long)atol(argv[1]) < 0) {
fprintf(stderr, "Parameter must be a positive integer.\n");
exit(1);
}
highest = 0UL; show_collatz_length((unsigned long)atol(argv[1])-1UL, &highest);
highest = 0UL; show_collatz_length((unsigned long)atol(argv[1]), &highest);
highest = 0UL; show_collatz_length((unsigned long)atol(argv[1])+1UL, &highest);
exit(EXIT_SUCCESS);
return(EXIT_FAILURE);
}