# Project Euler 574
# Verifying Primes — CRT meet-in-the-middle for V(p); primorials in i128.
extern {
function calloc(n: i64, size: i64) -> ptr<void>
function free(p: ptr<void>) -> void
}
function egcd(a0: i128, b0: i128, outx: ptr<i128>, outy: ptr<i128>) -> i128 {
let mut a: i128 = a0
let mut b: i128 = b0
let mut x0: i128 = 1
let mut y0: i128 = 0
let mut x1: i128 = 0
let mut y1: i128 = 1
while b != 0 {
let q: i128 = a / b
let na: i128 = b
b = a - q * b
a = na
let nx: i128 = x1
x1 = x0 - q * x1
x0 = nx
let ny: i128 = y1
y1 = y0 - q * y1
y0 = ny
}
outx[0] = x0
outy[0] = y0
return a
}
function modinv128(a0: i128, m: i128) -> i128 {
let xy: ptr<i128> = calloc(2, 16)
let g: i128 = egcd(a0 % m, m, xy, xy + 1)
let x: i128 = xy[0]
free(xy)
if g != 1 { return 0 }
let mut r: i128 = x % m
if r < 0 { r = r + m }
return r
}
function sieve(limit: i64, primes: ptr<i64>) -> i64 {
let is_prime: ptr<i8> = calloc(limit + 1, 1)
if is_prime == null { return 0 }
let mut i: i64 = 0
while i <= limit {
is_prime[i] = 1
i = i + 1
}
is_prime[0] = 0
is_prime[1] = 0
i = 2
while i * i <= limit {
if is_prime[i] != 0 {
let mut j: i64 = i * i
while j <= limit {
is_prime[j] = 0
j = j + i
}
}
i = i + 1
}
let mut m: i64 = 0
i = 2
while i <= limit {
if is_prime[i] != 0 {
primes[m] = i
m = m + 1
}
i = i + 1
}
free(is_prime)
return m
}
function subset_sums128(terms: ptr<i128>, k: i64, modv: i128, out: ptr<i128>) -> i64 {
out[0] = 0
let mut n: i64 = 1
let mut t: i64 = 0
while t < k {
let term: i128 = terms[t]
let mut i: i64 = 0
let old: i64 = n
while i < old {
out[n] = (out[i] + term) % modv
n = n + 1
i = i + 1
}
t = t + 1
}
return n
}
function sort_i128(a: ptr<i128>, n: i64) -> void {
let mut i: i64 = 1
while i < n {
let key: i128 = a[i]
let mut j: i64 = i - 1
while j >= 0 && a[j] > key {
a[j + 1] = a[j]
j = j - 1
}
a[j + 1] = key
i = i + 1
}
}
function bisect_left128(a: ptr<i128>, n: i64, x: i128) -> i64 {
let mut lo: i64 = 0
let mut hi: i64 = n
while lo < hi {
let mid: i64 = (lo + hi) / 2
if a[mid] < x {
lo = mid + 1
} else {
hi = mid
}
}
return lo
}
function min_B_difference(p: i64, primes: ptr<i64>, pk: i64, M: i128, bases: ptr<i128>) -> i128 {
if pk == 0 { return 1 }
let terms: ptr<i128> = calloc(pk, 16)
let mut i: i64 = 0
while i < pk {
let r: i128 = primes[i] as i128
let mut negp: i128 = (-(p as i128)) % r
if negp < 0 { negp = negp + r }
terms[i] = (negp * bases[i]) % M
i = i + 1
}
let mid: i64 = pk / 2
let L: ptr<i128> = calloc(1 << mid, 16)
let R: ptr<i128> = calloc(1 << (pk - mid), 16)
let nL: i64 = subset_sums128(terms, mid, M, L)
# copy second half terms
let termsR: ptr<i128> = calloc(pk - mid, 16)
i = 0
while i < pk - mid {
termsR[i] = terms[mid + i]
i = i + 1
}
let nR: i64 = subset_sums128(termsR, pk - mid, M, R)
free(termsR)
sort_i128(R, nR)
let mut best: i128 = M
let p128: i128 = p as i128
i = 0
while i < nL {
let x: i128 = L[i]
if x != 0 && (x % p128) != 0 {
if x < best { best = x }
}
i = i + 1
}
i = 0
while i < nR {
let x: i128 = R[i]
if x != 0 && (x % p128) != 0 {
if x < best { best = x }
}
i = i + 1
}
i = 0
while i < nL {
let l: i128 = L[i]
let target: i128 = M - l
let mut j: i64 = bisect_left128(R, nR, target)
while j < nR {
let s: i128 = l + R[j]
if s == M {
j = j + 1
} else {
if s < M {
break
}
let res: i128 = s - M
if res >= best {
break
}
if (res % p128) != 0 {
best = res
break
}
j = j + 1
}
}
i = i + 1
}
free(terms)
free(L)
free(R)
return best
}
function max_B_sum(p: i64, primes: ptr<i64>, pk: i64, M: i128, bases: ptr<i128>) -> i128 {
let limit: i128 = (p / 2) as i128
if pk == 0 { return limit }
let Amax: i128 = (p as i128) - limit
if M > Amax * limit { return -1 }
let terms: ptr<i128> = calloc(pk, 16)
let mut i: i64 = 0
while i < pk {
let r: i128 = primes[i] as i128
terms[i] = (((p as i128) % r) * bases[i]) % M
i = i + 1
}
let residues: ptr<i128> = calloc(1 << pk, 16)
let nr: i64 = subset_sums128(terms, pk, M, residues)
let mut bestB: i128 = 0
i = 0
while i < nr {
let b0: i128 = residues[i]
if b0 == 0 {
if M <= limit {
let B: i128 = (limit / M) * M
if B > bestB { bestB = B }
}
} else {
if b0 <= limit {
let B: i128 = b0 + ((limit - b0) / M) * M
if B > bestB { bestB = B }
}
}
i = i + 1
}
free(terms)
free(residues)
if bestB == 0 { return -1 }
return bestB
}
function q_for_p(p: i64, primes: ptr<i64>, np: i64) -> i64 {
let mut i: i64 = 0
while i < np {
let q: i64 = primes[i]
if q * q > p { return q }
i = i + 1
}
return 0
}
function S(n: i64) -> i64 {
let primes: ptr<i64> = calloc(10000, 8)
let np: i64 = sieve(n + 200, primes)
let mut max_q: i64 = 0
let mut i: i64 = 0
while i < np {
let p: i64 = primes[i]
if p < n {
let q: i64 = q_for_p(p, primes, np)
if q > max_q { max_q = q }
}
i = i + 1
}
let mut nq: i64 = 0
i = 0
while i < np {
if primes[i] <= max_q { nq = nq + 1 }
i = i + 1
}
let Ms: ptr<i128> = calloc(nq, 16)
let base_offs: ptr<i64> = calloc(nq, 8)
let base_lens: ptr<i64> = calloc(nq, 8)
let all_bases: ptr<i128> = calloc(nq * nq + 8, 16)
let mut bcap: i64 = 0
i = 0
while i < nq {
let mut M: i128 = 1
let mut j: i64 = 0
while j < i {
M = M * (primes[j] as i128)
j = j + 1
}
Ms[i] = M
base_offs[i] = bcap
base_lens[i] = i
j = 0
while j < i {
let r: i128 = primes[j] as i128
let Mr: i128 = M / r
let inv: i128 = modinv128(Mr % r, r)
all_bases[bcap] = (Mr * inv) % M
bcap = bcap + 1
j = j + 1
}
i = i + 1
}
let mut total: i128 = 0
i = 0
while i < np {
let p: i64 = primes[i]
if p < n {
let q: i64 = q_for_p(p, primes, np)
let mut qidx: i64 = 0
while primes[qidx] != q { qidx = qidx + 1 }
let M: i128 = Ms[qidx]
let pk: i64 = base_lens[qidx]
let bases: ptr<i128> = calloc(pk + 1, 16)
let mut j: i64 = 0
while j < pk {
bases[j] = all_bases[base_offs[qidx] + j]
j = j + 1
}
let Adiff: i128 = (p as i128) + min_B_difference(p, primes, pk, M, bases)
let Bsum: i128 = max_B_sum(p, primes, pk, M, bases)
if Bsum < 0 {
total = total + Adiff
} else {
let Asum: i128 = (p as i128) - Bsum
if Asum < Adiff {
total = total + Asum
} else {
total = total + Adiff
}
}
free(bases)
}
i = i + 1
}
free(primes)
free(Ms)
free(base_offs)
free(base_lens)
free(all_bases)
return total as i64
}
function main() -> i32 {
printf("%lld\n", S(3800))
return 0
}
Generated C
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Flow runtime helpers */
typedef struct flow_temp_node { struct flow_temp_node* next; } flow_temp_node;
static flow_temp_node* flow_temp_head = NULL;
static int flow_temp_atexit_set = 0;
__attribute__((unused)) static void flow_temp_free_all(void) {
while (flow_temp_head) {
flow_temp_node* n = flow_temp_head;
flow_temp_head = n->next;
free(n);
}
}
__attribute__((unused)) static void* flow_temp_alloc(size_t nbytes) {
flow_temp_node* node = (flow_temp_node*)malloc(sizeof(flow_temp_node) + nbytes);
if (!node) return NULL;
node->next = flow_temp_head;
flow_temp_head = node;
if (!flow_temp_atexit_set) {
flow_temp_atexit_set = 1;
atexit(flow_temp_free_all);
}
return (void*)(node + 1);
}
#ifndef FLOW_DIAG
#define FLOW_DIAG(msg) fprintf(stderr, "%s", (msg))
#endif
#ifndef FLOW_LOG
#define FLOW_LOG(fmt, ...) printf(fmt, __VA_ARGS__)
#endif
#ifndef FLOW_LOG_EMPTY
#define FLOW_LOG_EMPTY(fmt) printf(fmt)
#endif
static char* flow_strcat(const char* a, const char* b) {
size_t la = strlen(a ? a : ""), lb = strlen(b ? b : "");
char* r = (char*)flow_temp_alloc(la + lb + 1);
if (!r) return NULL;
if (la) memcpy(r, a, la);
if (lb) memcpy(r + la, b, lb);
r[la + lb] = '\0';
return r;
}
#define __flow_in_arr(arr, val) __extension__ ({ \
int _found = 0; \
size_t _n = sizeof(arr)/sizeof((arr)[0]); \
for (size_t _i = 0; _i < _n; _i++) { \
if ((arr)[_i] == (val)) { _found = 1; break; } \
} _found; })
/* Unified fault handler (MISRA #279) — override with -DFLOW_FAULT_HANDLER=fn */
#ifndef FLOW_FAULT_HANDLER
__attribute__((unused)) static inline void flow_fault_handler(const char* msg) {
fprintf(stderr, "flow: %s\n", msg ? msg : "fault");
abort();
#if defined(__GNUC__) || defined(__clang__)
__builtin_unreachable();
#endif
}
#else
#define flow_fault_handler FLOW_FAULT_HANDLER
#endif
#define flow_div_by_zero_handler() flow_fault_handler("division by zero")
#define flow_shift_ub_handler() flow_fault_handler("invalid shift (amount out of range or left-shift of negative)")
#ifndef FLOW_CHECKED_DIV
#define FLOW_CHECKED_DIV(L, R) (((R) != 0) ? ((L) / (R)) : (flow_div_by_zero_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_MOD
#define FLOW_CHECKED_MOD(L, R) (((R) != 0) ? ((L) % (R)) : (flow_div_by_zero_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_SHL
#define FLOW_CHECKED_SHL(L, R) ((((R) >= 0) && ((unsigned long long)(R) < (sizeof(L) * 8ull)) && ((L) >= 0)) ? ((L) << (R)) : (flow_shift_ub_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_SHR
#define FLOW_CHECKED_SHR(L, R) ((((R) >= 0) && ((unsigned long long)(R) < (sizeof(L) * 8ull))) ? ((L) >> (R)) : (flow_shift_ub_handler(), (L) * 0))
#endif
#include <math.h>
void* _ui_state = NULL;
static inline float i32_to_f32(int32_t v) { return (float)v; }
/* Host stub for @gpu kernels (device codegen replaces this). */
static inline int32_t gpu_thread_id(void) { return 0; }
__int128 egcd_i128_i128_ptr_i128_ptr_i128(__int128 a0, __int128 b0, __int128* outx, __int128* outy);
__int128 modinv128_i128_i128(__int128 a0, __int128 m);
int64_t sieve_i64_ptr_i64(int64_t limit, int64_t* primes);
int64_t subset_sums128_ptr_i128_i64_i128_ptr_i128(__int128* terms, int64_t k, __int128 modv, __int128* out);
void sort_i128_ptr_i128_i64(__int128* a, int64_t n);
int64_t bisect_left128_ptr_i128_i64_i128(__int128* a, int64_t n, __int128 x);
__int128 min_B_difference_i64_ptr_i64_i64_i128_ptr_i128(int64_t p, int64_t* primes, int64_t pk, __int128 M, __int128* bases);
__int128 max_B_sum_i64_ptr_i64_i64_i128_ptr_i128(int64_t p, int64_t* primes, int64_t pk, __int128 M, __int128* bases);
int64_t q_for_p_i64_ptr_i64_i64(int64_t p, int64_t* primes, int64_t np);
int64_t S_i64(int64_t n);
int32_t main(void);
__int128 egcd_i128_i128_ptr_i128_ptr_i128(__int128 a0, __int128 b0, __int128* outx, __int128* outy) {
__int128 a = a0;
__int128 b = b0;
__int128 x0 = 1;
__int128 y0 = 0;
__int128 x1 = 0;
__int128 y1 = 1;
while (b != 0) {
__int128 q = FLOW_CHECKED_DIV((a), (b));
__int128 na = b;
b = (a - (q * b));
a = na;
__int128 nx = x1;
x1 = (x0 - (q * x1));
x0 = nx;
__int128 ny = y1;
y1 = (y0 - (q * y1));
y0 = ny;
}
outx[0] = x0;
outy[0] = y0;
return a;
}
__int128 modinv128_i128_i128(__int128 a0, __int128 m) {
__int128* xy = (__int128*)(calloc(2, 16));
__int128 g = egcd_i128_i128_ptr_i128_ptr_i128(FLOW_CHECKED_MOD((a0), (m)), m, xy, (xy + 1));
__int128 x = xy[0];
free(xy);
if (g != 1) {
return 0;
}
__int128 r = FLOW_CHECKED_MOD((x), (m));
if (r < 0) {
r = (r + m);
}
return r;
}
int64_t sieve_i64_ptr_i64(int64_t limit, int64_t* primes) {
int8_t* is_prime = (int8_t*)(calloc((limit + 1), 1));
if (is_prime == NULL) {
return 0;
}
int64_t i = 0;
while (i <= limit) {
is_prime[i] = 1;
i = (i + 1);
}
is_prime[0] = 0;
is_prime[1] = 0;
i = 2;
while ((i * i) <= limit) {
if (is_prime[i] != 0) {
int64_t j = (i * i);
while (j <= limit) {
is_prime[j] = 0;
j = (j + i);
}
}
i = (i + 1);
}
int64_t m = 0;
i = 2;
while (i <= limit) {
if (is_prime[i] != 0) {
primes[m] = i;
m = (m + 1);
}
i = (i + 1);
}
free(is_prime);
return m;
}
int64_t subset_sums128_ptr_i128_i64_i128_ptr_i128(__int128* terms, int64_t k, __int128 modv, __int128* out) {
out[0] = 0;
int64_t n = 1;
int64_t t = 0;
while (t < k) {
__int128 term = terms[t];
int64_t i = 0;
int64_t old = n;
while (i < old) {
out[n] = FLOW_CHECKED_MOD(((out[i] + term)), (modv));
n = (n + 1);
i = (i + 1);
}
t = (t + 1);
}
return n;
}
void sort_i128_ptr_i128_i64(__int128* a, int64_t n) {
int64_t i = 1;
while (i < n) {
__int128 key = a[i];
int64_t j = (i - 1);
while ((j >= 0 && a[j] > key)) {
a[(j + 1)] = a[j];
j = (j - 1);
}
a[(j + 1)] = key;
i = (i + 1);
}
}
int64_t bisect_left128_ptr_i128_i64_i128(__int128* a, int64_t n, __int128 x) {
int64_t lo = 0;
int64_t hi = n;
while (lo < hi) {
int64_t mid = FLOW_CHECKED_DIV(((lo + hi)), (2));
if (a[mid] < x) {
lo = (mid + 1);
} else {
hi = mid;
}
}
return lo;
}
__int128 min_B_difference_i64_ptr_i64_i64_i128_ptr_i128(int64_t p, int64_t* primes, int64_t pk, __int128 M, __int128* bases) {
if (pk == 0) {
return 1;
}
__int128* terms = (__int128*)(calloc(pk, 16));
int64_t i = 0;
while (i < pk) {
__int128 r = ((__int128)(primes[i]));
__int128 negp = FLOW_CHECKED_MOD(((-((__int128)(p)))), (r));
if (negp < 0) {
negp = (negp + r);
}
terms[i] = FLOW_CHECKED_MOD(((negp * bases[i])), (M));
i = (i + 1);
}
int64_t mid = FLOW_CHECKED_DIV((pk), (2));
__int128* L = (__int128*)(calloc(FLOW_CHECKED_SHL((1), (mid)), 16));
__int128* R = (__int128*)(calloc(FLOW_CHECKED_SHL((1), ((pk - mid))), 16));
int64_t nL = subset_sums128_ptr_i128_i64_i128_ptr_i128(terms, mid, M, L);
__int128* termsR = (__int128*)(calloc((pk - mid), 16));
i = 0;
while (i < (pk - mid)) {
termsR[i] = terms[(mid + i)];
i = (i + 1);
}
int64_t nR = subset_sums128_ptr_i128_i64_i128_ptr_i128(termsR, (pk - mid), M, R);
free(termsR);
sort_i128_ptr_i128_i64(R, nR);
__int128 best = M;
__int128 p128 = ((__int128)(p));
i = 0;
while (i < nL) {
__int128 x = L[i];
if ((x != 0 && FLOW_CHECKED_MOD((x), (p128)) != 0)) {
if (x < best) {
best = x;
}
}
i = (i + 1);
}
i = 0;
while (i < nR) {
__int128 x = R[i];
if ((x != 0 && FLOW_CHECKED_MOD((x), (p128)) != 0)) {
if (x < best) {
best = x;
}
}
i = (i + 1);
}
i = 0;
while (i < nL) {
__int128 l = L[i];
__int128 target = (M - l);
int64_t j = bisect_left128_ptr_i128_i64_i128(R, nR, target);
while (j < nR) {
__int128 s = (l + R[j]);
if (s == M) {
j = (j + 1);
} else {
if (s < M) {
break;
}
__int128 res = (s - M);
if (res >= best) {
break;
}
if (FLOW_CHECKED_MOD((res), (p128)) != 0) {
best = res;
break;
}
j = (j + 1);
}
}
i = (i + 1);
}
free(terms);
free(L);
free(R);
return best;
}
__int128 max_B_sum_i64_ptr_i64_i64_i128_ptr_i128(int64_t p, int64_t* primes, int64_t pk, __int128 M, __int128* bases) {
__int128 limit = ((__int128)(FLOW_CHECKED_DIV((p), (2))));
if (pk == 0) {
return limit;
}
__int128 Amax = (((__int128)(p)) - limit);
if (M > (Amax * limit)) {
return (-1);
}
__int128* terms = (__int128*)(calloc(pk, 16));
int64_t i = 0;
while (i < pk) {
__int128 r = ((__int128)(primes[i]));
terms[i] = FLOW_CHECKED_MOD(((FLOW_CHECKED_MOD((((__int128)(p))), (r)) * bases[i])), (M));
i = (i + 1);
}
__int128* residues = (__int128*)(calloc(FLOW_CHECKED_SHL((1), (pk)), 16));
int64_t nr = subset_sums128_ptr_i128_i64_i128_ptr_i128(terms, pk, M, residues);
__int128 bestB = 0;
i = 0;
while (i < nr) {
__int128 b0 = residues[i];
if (b0 == 0) {
if (M <= limit) {
__int128 B = (FLOW_CHECKED_DIV((limit), (M)) * M);
if (B > bestB) {
bestB = B;
}
}
} else {
if (b0 <= limit) {
__int128 B = (b0 + (FLOW_CHECKED_DIV(((limit - b0)), (M)) * M));
if (B > bestB) {
bestB = B;
}
}
}
i = (i + 1);
}
free(terms);
free(residues);
if (bestB == 0) {
return (-1);
}
return bestB;
}
int64_t q_for_p_i64_ptr_i64_i64(int64_t p, int64_t* primes, int64_t np) {
int64_t i = 0;
while (i < np) {
int64_t q = primes[i];
if ((q * q) > p) {
return q;
}
i = (i + 1);
}
return 0;
}
int64_t S_i64(int64_t n) {
int64_t* primes = (int64_t*)(calloc(10000, 8));
int64_t np = sieve_i64_ptr_i64((n + 200), primes);
int64_t max_q = 0;
int64_t i = 0;
while (i < np) {
int64_t p = primes[i];
if (p < n) {
int64_t q = q_for_p_i64_ptr_i64_i64(p, primes, np);
if (q > max_q) {
max_q = q;
}
}
i = (i + 1);
}
int64_t nq = 0;
i = 0;
while (i < np) {
if (primes[i] <= max_q) {
nq = (nq + 1);
}
i = (i + 1);
}
__int128* Ms = (__int128*)(calloc(nq, 16));
int64_t* base_offs = (int64_t*)(calloc(nq, 8));
int64_t* base_lens = (int64_t*)(calloc(nq, 8));
__int128* all_bases = (__int128*)(calloc(((nq * nq) + 8), 16));
int64_t bcap = 0;
i = 0;
while (i < nq) {
__int128 M = 1;
int64_t j = 0;
while (j < i) {
M = (M * ((__int128)(primes[j])));
j = (j + 1);
}
Ms[i] = M;
base_offs[i] = bcap;
base_lens[i] = i;
j = 0;
while (j < i) {
__int128 r = ((__int128)(primes[j]));
__int128 Mr = FLOW_CHECKED_DIV((M), (r));
__int128 inv = modinv128_i128_i128(FLOW_CHECKED_MOD((Mr), (r)), r);
all_bases[bcap] = FLOW_CHECKED_MOD(((Mr * inv)), (M));
bcap = (bcap + 1);
j = (j + 1);
}
i = (i + 1);
}
__int128 total = 0;
i = 0;
while (i < np) {
int64_t p = primes[i];
if (p < n) {
int64_t q = q_for_p_i64_ptr_i64_i64(p, primes, np);
int64_t qidx = 0;
while (primes[qidx] != q) {
qidx = (qidx + 1);
}
__int128 M = Ms[qidx];
int64_t pk = base_lens[qidx];
__int128* bases = (__int128*)(calloc((pk + 1), 16));
int64_t j = 0;
while (j < pk) {
bases[j] = all_bases[(base_offs[qidx] + j)];
j = (j + 1);
}
__int128 Adiff = (((__int128)(p)) + min_B_difference_i64_ptr_i64_i64_i128_ptr_i128(p, primes, pk, M, bases));
__int128 Bsum = max_B_sum_i64_ptr_i64_i64_i128_ptr_i128(p, primes, pk, M, bases);
if (Bsum < 0) {
total = (total + Adiff);
} else {
__int128 Asum = (((__int128)(p)) - Bsum);
if (Asum < Adiff) {
total = (total + Asum);
} else {
total = (total + Adiff);
}
}
free(bases);
}
i = (i + 1);
}
free(primes);
free(Ms);
free(base_offs);
free(base_lens);
free(all_bases);
return ((int64_t)(total));
}
int32_t main(void) {
printf("%lld\n", S_i64(3800));
return 0;
}