⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

#include <stdio.h>
#include <stdlib.h>

/*  adapted from
        http://www-cs-faculty.stanford.edu/~knuth/programs/achain4.w
    with some severe loss of efficiency
 */
#define ASK_KNUTH 0
#if ASK_KNUTH
unsigned char knuth[20001] = {0};
#endif

#define N 32
#define NMAX 40000

/* u: upper bounds; l: lower bounds */
int	u[N] = {1, 2}, l[N] = {1, 2}, out[N], sum[N], tail[N],
	cache[NMAX + 1] = {0, 0, 1}, known = 2, stack;

typedef struct { int *p, v; } save_t;
save_t undo[N*N];
#define replace(u, l, n)\
	{ undo[stack].v = *(undo[stack].p = u + l); u[l] = n; stack++;}
#define restore(n)\
	while (stack > n) { stack--;*(undo[stack].p) = undo[stack].v; }

void show(int len)
{
	int i, n, j;
	printf("\033[H");
	for (i = 0; i <= len; i++) {
		n = l[i], j = 1;
		printf("%s%5d %5d\033[m %2d ",
			out[i] ? "\033[32m" : "", l[i], u[i], i);
		while (j <= n) {
			putchar((n & j) ? '#':'_');
			j <<= 1;
		}
		while (j < (1 << 16)) putchar(' '), j *= 2;
		n = u[i];
		for (j = 1; j <= n; j *= 2)
			putchar((n & j) ? '#':'_');

		while (j < (1 << 16)) putchar(' '), j*= 2;
		putchar('\n');
	}
}

/* lower and upper bounds */
int lower(int n, int *up)
{
	int i, o, q;
	if (n <= 2 || (n <= NMAX && cache[n])) {
		if (up) *up = cache[n];
		return cache[n];
	}

	for (i = -1, o = 0; n; n >>= 1, i++)
		if (n & 1) o++;

	if (up) *up = o + --i;
	do { i++; } while (o >>= 1);
	if (!up) return i;

	for (o = 2; o * o < n; o++) {
		if (n % o) continue;
		q = cache[o] + cache[n / o];
		if (q < *up)
			if ((*up = q) == i) break;
	}
	if (n > 2) {
		if (*up > cache[n-2] + 1) *up = cache[n-1] + 1;
		if (*up > cache[n-2] + 1) *up = cache[n-2] + 1;
	}

	return i;
}

int insert(int x, int pos)
{
	int save = stack, i, t;

	if (l[pos] > x || u[pos] < x) return 0;

	if (l[pos] == x) goto repl_u;
	replace(l, pos, x);
	for (i = pos - 1; u[i] * 2 < u[i+1]; i--) {
		if ((t = l[i+1] + 1) * 2 > u[i]) goto bail;
		replace(l, i, t);
	}

	for (i = pos + 1; l[i] <= l[i-1]; i++) {
		if ((t = l[i-1] + 1) > u[i]) goto bail;
		replace(l, i, t);
	}

repl_u:	if (u[pos] == x) return 1;
	replace(u, pos, x);
	for (i = pos - 1; u[i] >= u[i+1]; i--) {
		if ((t = u[i+1] - 1) < l[i]) goto bail;
		replace(u, i, t);
	}

	for (i = pos + 1; u[i] > u[i-1] * 2; i++) {
		if ((t = u[i-1] * 2) < l[i]) goto bail;
		replace(u, i, t);
	}

	return 1;

bail:	restore(save);
	return 0;
}

int seq_recur(int);
int seq_len(int);

inline int try_pq(int p, int q, int len) {
	int ps, qs, pl, ql, pu, qu, j;

	pl = cache[p];
	if (pl >= len) return 0;

	ql = cache[q];
	if (ql >= len) return 0;

	while (pl < len && u[pl] < p) pl++;
	for (pu = pl - 1; pu < len - 1 && u[pu + 1] >= p; pu++);

	while (ql < len && u[ql] < q) ql++;
	for (qu = ql - 1; qu < len - 1 && u[qu + 1] >= q; qu++);

	if (p != q && pl <= ql) pl = ql + 1;
	if (pl > pu || ql > qu || ql > pu) return 0;
	if (!out[len]) pl = pu = len - 1;

	ps = stack;

	for (; pu >= pl; pu--) {
		if (!insert(p, pu)) continue;
		out[pu]++, sum[pu] += len;
		if (p != q) {
			qs = stack;
			j = qu;
			if (j >= pu) j = pu - 1;
			for (; j >= ql; j--) {
				if (!insert(q, j)) continue;
				out[j]++, sum[j] += len, tail[len] = q;

				if (seq_recur(len - 1)) return 1;

				restore(qs);
				out[j]--, sum[j] -= len;
			}
		} else {
			out[pu]++, sum[pu] += len, tail[len] = p;

			if (seq_recur(len - 1)) return 1;

			out[pu]--, sum[pu] -= len;
		}

		out[pu]--, sum[pu] -= len;
		restore(ps);
	}
	return 0;
}

int seq_recur(int len)
{
	int p, q, n, limit;
	n = l[len];

	if (len < 2) return 1;

	limit = out[len] == 1 ? n - tail[sum[len]] : n - 1;
	if (limit > u[len - 1]) limit = u[len - 1];

	/* try to break n into p + q, and see if we can insert p, q into
	   list while satisfying bounds */
	for (p = limit, q = n - p; q <= p; q++, p--)
		if (try_pq(p, q, len)) return 1;

	return 0;
}

int seq(int n, int len, int *buf)
{
	int i;

	if (!len) len = seq_len(n);

	stack = 0;
	u[len] = l[len] = n;

	for (i = 0; i <= len; i++) out[i] = sum[i] = 0;
	for (i = 2; i < len; i++) {
		l[i] = l[i-1] + 1;
		u[i] = u[i-1] * 2;
	}
	for (i = len - 1; i > 2; i--) {
		if (l[i] * 2 < l[i+1]) l[i] = (1 + l[i+1]) / 2;
		if (u[i] >= u[i+1]) u[i] = u[i+1] - 1;
	}

	if (!seq_recur(len)) return 0;
	if (buf) for (i = 0; i <= len; i++) buf[i] = u[i];
	return len;
}

int seq_len(int n)
{
	int lb, ub;

	if (n <= known) return cache[n];

	/* need all lower n to compute sequence */
	while (known + 1 < n)
		seq_len(known + 1);

	lb = lower(n, &ub);
	while (lb < ub && !seq(n, lb, 0)) ++lb;

#if ASK_KNUTH
	if (n < 20000 && lb != knuth[n] - 32) {
		printf("bad %d: %d %d\n", n, lb, knuth[n] - 32);
		fflush(stdout);
		abort();
	}
#endif

	known = n;
	if (!(n & 1023))
		fprintf(stderr, "cached %d\n", known);

	return cache[n] = lb;
}

void init(void) {
#if ASK_KNUTH
	FILE *fp = fopen("out", "r");
	fread(knuth + 1, 1, 20000, fp);
	fclose(fp);
#endif
}