files / problems / landau-n2-plus-1 / compute / q3 / verify_n2p1.c
/*
* Independent check of the prime and P2 lists. Does not use the residue sieve.
*
* * Miller–Rabin every even n (and n=1)
* * multiply every claimed P2 factorization back
* * trial (primes <= 2003) + Pollard of every even n^2+1, and require
* the P2 file to be exactly the Ω=2 composites
*
* Build: gcc -O3 -std=c11 -Wall -Wextra verify_n2p1.c -o verify_n2p1
* Run: ./verify_n2p1 OUTDIR
*
* Even-n membership is stored as bitsets (~6.25 MB each at n_max=1e8)
* rather than a byte per even n.
*/
#define _POSIX_C_SOURCE 200809L
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static const uint64_t MR_WITNESSES[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
static const int N_MR = 9;
static const uint64_t MR_BOUND = UINT64_C(3825123056546413051);
static const int TRIAL_BOUND = 2003;
#define BIT_GET(a, i) (((a)[(i) >> 3] >> ((i) & 7)) & 1u)
#define BIT_SET(a, i) ((a)[(i) >> 3] |= (uint8_t)(1u << ((i) & 7)))
static double now_sec(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_nsec * 1e-9 + (double)ts.tv_sec;
}
static uint64_t mulmod(uint64_t a, uint64_t b, uint64_t m) {
return (uint64_t)((__uint128_t)a * b % m);
}
static uint64_t modpow(uint64_t a, uint64_t e, uint64_t m) {
uint64_t r = 1;
a %= m;
while (e) {
if (e & 1)
r = mulmod(r, a, m);
a = mulmod(a, a, m);
e >>= 1;
}
return r;
}
static uint64_t gcd_u64(uint64_t a, uint64_t b) {
while (b) {
uint64_t t = a % b;
a = b;
b = t;
}
return a;
}
static int miller_rabin(uint64_t n) {
if (n < 2)
return 0;
static const uint64_t small[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
for (size_t i = 0; i < sizeof small / sizeof small[0]; i++) {
if (n == small[i])
return 1;
if (n % small[i] == 0)
return 0;
}
if (n >= MR_BOUND) {
fprintf(stderr, "n=%" PRIu64 " exceeds deterministic MR bound\n", n);
exit(2);
}
uint64_t d = n - 1;
int s = 0;
while ((d & 1) == 0) {
d >>= 1;
s++;
}
for (int i = 0; i < N_MR; i++) {
uint64_t a = MR_WITNESSES[i];
if (a % n == 0)
continue;
uint64_t x = modpow(a, d, n);
if (x == 1 || x == n - 1)
continue;
int ok = 0;
for (int r = 0; r < s - 1; r++) {
x = mulmod(x, x, n);
if (x == n - 1) {
ok = 1;
break;
}
}
if (!ok)
return 0;
}
return 1;
}
static uint64_t pollard_rho(uint64_t n) {
if ((n & 1) == 0)
return 2;
if (n % 3 == 0)
return 3;
for (uint64_t c = 1; c < 96; c++) {
uint64_t y = 2, d = 1, r = 1, q = 1, ys = y, x = 0;
while (d == 1) {
x = y;
for (uint64_t i = 0; i < r; i++)
y = (mulmod(y, y, n) + c) % n;
uint64_t k = 0;
while (k < r && d == 1) {
ys = y;
uint64_t mstep = r - k;
if (mstep > 128)
mstep = 128;
for (uint64_t i = 0; i < mstep; i++) {
y = (mulmod(y, y, n) + c) % n;
uint64_t diff = x > y ? x - y : y - x;
q = mulmod(q, diff, n);
}
d = gcd_u64(q, n);
k += mstep;
}
r *= 2;
}
if (d != n)
return d;
y = ys;
for (;;) {
y = (mulmod(y, y, n) + c) % n;
uint64_t diff = x > y ? x - y : y - x;
d = gcd_u64(diff, n);
if (d > 1) {
if (d != n)
return d;
break;
}
}
}
fprintf(stderr, "pollard rho failed on %" PRIu64 "\n", n);
exit(1);
}
static void factor_rec(uint64_t m, uint64_t *out, int *nout) {
if (m == 1)
return;
if (miller_rabin(m)) {
out[(*nout)++] = m;
return;
}
uint64_t d = pollard_rho(m);
factor_rec(d, out, nout);
factor_rec(m / d, out, nout);
}
static uint64_t *trial_primes(int bound, int *nprimes) {
uint8_t *s = malloc((size_t)bound + 1);
memset(s, 1, (size_t)bound + 1);
s[0] = s[1] = 0;
for (int p = 2; p * p <= bound; p++)
if (s[p])
for (int k = p * p; k <= bound; k += p)
s[k] = 0;
int cnt = 0;
for (int i = 2; i <= bound; i++)
if (s[i])
cnt++;
uint64_t *pr = malloc((size_t)cnt * sizeof *pr);
int j = 0;
for (int i = 2; i <= bound; i++)
if (s[i])
pr[j++] = (uint64_t)i;
free(s);
*nprimes = cnt;
return pr;
}
static int factor_omega(uint64_t m, const uint64_t *pr, int npr) {
int om = 0;
for (int i = 0; i < npr; i++) {
uint64_t p = pr[i];
if (p * p > m)
break;
while (m % p == 0) {
om++;
m /= p;
}
}
if (m == 1)
return om;
uint64_t fs[16];
int nfs = 0;
factor_rec(m, fs, &nfs);
return om + nfs;
}
static uint64_t parse_nmax(const char *path) {
FILE *f = fopen(path, "r");
if (!f)
return 0;
char line[256];
uint64_t n = 0;
while (fgets(line, sizeof line, f)) {
if (sscanf(line, " \"n_max\": %" SCNu64, &n) == 1) {
fclose(f);
return n;
}
}
fclose(f);
return 0;
}
static void write_verify_ok(
const char *dir,
uint64_t n_max,
uint64_t nclaimed,
uint64_t nfound,
uint64_t extra,
uint64_t missing,
uint64_t np2,
uint64_t miss_p2,
uint64_t extra_p2,
double seconds
) {
char path[4096];
snprintf(path, sizeof path, "%s/verify_ok.json", dir);
FILE *f = fopen(path, "w");
if (!f) {
perror(path);
exit(1);
}
fprintf(f, "{\n");
fprintf(f, " \"n_max\": %" PRIu64 ",\n", n_max);
fprintf(f, " \"producer\": \"verify_n2p1.c\",\n");
fprintf(f, " \"algorithm\": \"miller-rabin every even n; trial<=2003 plus Pollard Omega\",\n");
fprintf(f, " \"seconds\": %.6f,\n", seconds);
fprintf(f, " \"primes_claimed\": %" PRIu64 ",\n", nclaimed);
fprintf(f, " \"primes_found\": %" PRIu64 ",\n", nfound);
fprintf(f, " \"extra\": %" PRIu64 ",\n", extra);
fprintf(f, " \"missing\": %" PRIu64 ",\n", missing);
fprintf(f, " \"p2_rows\": %" PRIu64 ",\n", np2);
fprintf(f, " \"complete_miss\": %" PRIu64 ",\n", miss_p2);
fprintf(f, " \"complete_extra\": %" PRIu64 "\n", extra_p2);
fprintf(f, "}\n");
fclose(f);
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s OUTDIR\n", argv[0]);
return 2;
}
const char *dir = argv[1];
char path[4096];
snprintf(path, sizeof path, "%s/n2p1.json", dir);
uint64_t n_max = parse_nmax(path);
if (!n_max) {
snprintf(path, sizeof path, "%s/sieve_meta.json", dir);
n_max = parse_nmax(path);
}
if (!n_max) {
fprintf(stderr, "n_max not found in n2p1.json or sieve_meta.json\n");
return 1;
}
uint64_t size = n_max / 2;
size_t nbytes = (size_t)((size + 7) / 8);
uint8_t *is_prime = calloc(nbytes, 1);
uint8_t *is_p2 = calloc(nbytes, 1);
if (!is_prime || !is_p2) {
fprintf(stderr, "bitset calloc failed (%zu bytes each)\n", nbytes);
return 1;
}
setvbuf(stderr, NULL, _IONBF, 0);
double t0 = now_sec();
snprintf(path, sizeof path, "%s/prime_n.txt", dir);
FILE *fp = fopen(path, "r");
if (!fp) {
perror(path);
return 1;
}
char *line = NULL;
size_t cap = 0;
uint64_t nclaimed = 0, has1 = 0;
while (getline(&line, &cap, fp) != -1) {
if (line[0] == '#' || line[0] == '\n')
continue;
uint64_t n = strtoull(line, NULL, 10);
if (n == 1) {
has1 = 1;
nclaimed++;
continue;
}
if (n < 2 || n > n_max || (n & 1)) {
fprintf(stderr, "bad prime n=%" PRIu64 "\n", n);
return 1;
}
BIT_SET(is_prime, n / 2 - 1);
nclaimed++;
}
fclose(fp);
snprintf(path, sizeof path, "%s/p2_omega2.txt", dir);
FILE *f2 = fopen(path, "r");
if (!f2) {
perror(path);
return 1;
}
uint64_t np2 = 0, bad = 0;
while (getline(&line, &cap, f2) != -1) {
if (line[0] == '#' || line[0] == '\n')
continue;
uint64_t n = 0, p = 0, q = 0, extra = 0;
int got = sscanf(line, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &n, &p, &q, &extra);
if (got != 3) {
bad++;
if (bad <= 5)
fprintf(stderr, "p2 parse fail: %s", line);
continue;
}
if (n < 2 || n > n_max || (n & 1) || p > q ||
(__uint128_t)p * q != (__uint128_t)n * n + 1 ||
!miller_rabin(p) || !miller_rabin(q) || miller_rabin(n * n + 1)) {
bad++;
if (bad <= 5)
fprintf(stderr, "p2 fail n=%" PRIu64 " p=%" PRIu64 " q=%" PRIu64 "\n", n, p, q);
continue;
}
BIT_SET(is_p2, n / 2 - 1);
np2++;
}
fclose(f2);
if (bad) {
fprintf(stderr, "P2 factorization failures: %" PRIu64 "\n", bad);
return 1;
}
printf("P2 factorizations OK (%" PRIu64 " rows) %.1fs\n", np2, now_sec() - t0);
uint64_t nfound = has1 && miller_rabin(2) ? 1 : 0;
uint64_t extra = 0, missing = 0;
for (uint64_t n = 2; n <= n_max; n += 2) {
if ((n & ((1u << 18) - 1)) == 0)
fprintf(stderr, " prime scan n=%" PRIu64 " found=%" PRIu64 "\n", n, nfound);
int pr = miller_rabin(n * n + 1);
uint64_t i = n / 2 - 1;
if (pr)
nfound++;
if (pr && !BIT_GET(is_prime, i))
missing++;
if (!pr && BIT_GET(is_prime, i))
extra++;
}
printf("primes n_max=%" PRIu64 " claimed=%" PRIu64 " found=%" PRIu64 " extra=%" PRIu64 " missing=%" PRIu64 "\n",
n_max, nclaimed, nfound, extra, missing);
if (extra || missing || nclaimed != nfound)
return 1;
printf("primes OK %.1fs\n", now_sec() - t0);
int npr = 0;
uint64_t *pr = trial_primes(TRIAL_BOUND, &npr);
uint64_t miss_p2 = 0, extra_p2 = 0, scanned = 0;
for (uint64_t n = 2; n <= n_max; n += 2) {
scanned++;
if (scanned % 200000 == 0)
fprintf(stderr, " completeness scanned %" PRIu64 " even n\n", scanned);
uint64_t i = n / 2 - 1;
if (BIT_GET(is_prime, i))
continue;
int om = factor_omega(n * n + 1, pr, npr);
if (om == 2) {
if (!BIT_GET(is_p2, i)) {
miss_p2++;
if (miss_p2 <= 5)
fprintf(stderr, "missing P2 n=%" PRIu64 "\n", n);
}
} else if (BIT_GET(is_p2, i)) {
extra_p2++;
if (extra_p2 <= 5)
fprintf(stderr, "extra P2 n=%" PRIu64 " omega=%d\n", n, om);
}
}
printf("complete P2 scan miss=%" PRIu64 " extra=%" PRIu64 " %.1fs\n", miss_p2, extra_p2, now_sec() - t0);
if (miss_p2 || extra_p2)
return 1;
printf("P2 completeness OK\n");
write_verify_ok(dir, n_max, nclaimed, nfound, extra, missing, np2, miss_p2, extra_p2, now_sec() - t0);
printf("OK\n");
free(line);
free(is_prime);
free(is_p2);
free(pr);
return 0;
}