#include "pari.h"
#include "paripriv.h"
static ulong _maxprime = 0;
static ulong diffptrlen;
static void
initprimes1(ulong size, long *lenp, ulong *lastp, byteptr p1)
{
pari_sp av = avma;
long k;
byteptr q, r, s, p = (byteptr)stack_calloc(size+2), fin = p + size;
for (r=q=p,k=1; r<=fin; )
{
do { r+=k; k+=2; r+=k; } while (*++q);
for (s=r; s<=fin; s+=k) *s = 1;
}
r = p1; *r++ = 2; *r++ = 1;
for (s=q=p+1; ; s=q)
{
do q++; while (*q);
if (q > fin) break;
*r++ = (unsigned char) ((q-s) << 1);
}
*r++ = 0;
*lenp = r - p1;
*lastp = ((s - p) << 1) + 1;
avma = av;
}
#ifndef SLOW2_IN_ROOTS
# ifdef i386
# define SLOW2_IN_ROOTS 0.36
# else
# define SLOW2_IN_ROOTS 2.6
# endif
#endif
#ifndef CACHE_ARENA
# ifdef i386
# define CACHE_ARENA (63 * 1024UL)
# else
# define CACHE_ARENA (200 * 1024UL)
# endif
#endif
#define CACHE_ALPHA (0.38)
#define CACHE_CUTOFF (0.018)
static double slow2_in_roots = SLOW2_IN_ROOTS;
typedef struct {
ulong arena;
double power;
double cutoff;
} cache_model_t;
static cache_model_t cache_model = { CACHE_ARENA, CACHE_ALPHA, CACHE_CUTOFF };
static ulong
good_arena_size(ulong slow2_size, ulong total, ulong fixed_to_cache,
cache_model_t *cache_model)
{
ulong asize, cache_arena = cache_model->arena;
double Xmin, Xmax, A, B, C1, C2, D, V;
double alpha = cache_model->power, cut_off = cache_model->cutoff;
asize = cache_arena - fixed_to_cache;
if (total <= asize) return total;
if (asize > 10 * slow2_size) return asize;
B = (1 - ((double)fixed_to_cache)/cache_arena);
A = B + ((double)slow2_size)/cache_arena;
C2 = A*B;
C1 = (A + B - 1/alpha*(A - B))/2;
D = C1*C1 - C2;
if (D > 0)
V = cut_off*cut_off + 2*C1*cut_off + C2;
else
V = 0;
Xmin = cut_off;
Xmax = ((double)total - fixed_to_cache)/cache_arena;
if ( D <= 0 || (V >= 0 && C1 + cut_off >= 0) )
Xmax = cut_off;
else if (V >= 0 &&
((Xmax + C1) <= 0 || (Xmax*Xmax + 2*C1*Xmax + C2) <= 0))
;
else if (V <= 0 && (Xmax*Xmax + 2*C1*Xmax + C2) <= 0)
Xmin = cut_off;
else
Xmax = sqrt(D) - C1;
if (Xmax != Xmin) {
double v1 = (cut_off + A)/(cut_off + B);
double v2 = 2.33 * (Xmax + A)/(Xmax + B) * pow(Xmax, alpha);
if (1.1 * v2 >= v1)
V = v1;
else
{ Xmin = Xmax; V = v2; }
} else if (B > 0)
V = 2.33 * (Xmin + A)/(Xmin + B) * pow(Xmin, alpha);
if (B > 0 && 1.1 * V > A/B)
Xmin = 0;
asize = (ulong)((1 + Xmin)*cache_arena - fixed_to_cache);
if (asize > total) asize = total;
return asize;
}
long
set_optimize(long what, GEN g)
{
long ret = 0;
switch (what) {
case 1:
ret = (long)cache_model.arena;
break;
case 2:
ret = (long)(slow2_in_roots * 1000);
break;
case 3:
ret = (long)(cache_model.power * 1000);
break;
case 4:
ret = (long)(cache_model.cutoff * 1000);
break;
default:
pari_err_BUG("set_optimize");
break;
}
if (g != NULL) {
ulong val = itou(g);
switch (what) {
case 1: cache_model.arena = val; break;
case 2: slow2_in_roots = (double)val / 1000.; break;
case 3: cache_model.power = (double)val / 1000.; break;
case 4: cache_model.cutoff = (double)val / 1000.; break;
}
}
return ret;
}
static void
sieve_chunk(byteptr known_primes, ulong s, byteptr data, ulong n)
{
ulong p, cnt = n-1, start = s, delta = 1;
byteptr q;
memset(data, 0, n);
start >>= 1;
start += n;
for (q = known_primes + 1, p = 3; delta; delta = *++q, p += delta)
{
long off = cnt - ((start+(p>>1)) % p);
while (off >= 0) { data[off] = 1; off -= p; }
}
}
static void
initprimes0(ulong maxnum, long *lenp, ulong *lastp, byteptr p1)
{
pari_sp av = avma, bot = pari_mainstack->bot;
long alloced, psize;
byteptr q, end, p, end1, plast, curdiff;
ulong last, remains, curlow, rootnum, asize;
ulong prime_above;
byteptr p_prime_above;
maxnum |= 1;
if (maxnum < 1ul<<17) { initprimes1(maxnum>>1, lenp, lastp, p1); return; }
rootnum = usqrt(maxnum) | 1;
initprimes1(rootnum>>1, &psize, &last, p1);
end1 = p1 + psize - 1;
remains = (maxnum - last) >> 1;
asize = good_arena_size((ulong)(rootnum * slow2_in_roots), remains+1, 0,
&cache_model) - 1;
alloced = (((byteptr)avma) <= ((byteptr)bot) + asize);
if (alloced)
p = (byteptr)pari_malloc(asize+1);
else
p = (byteptr)stack_malloc(asize+1);
end = p + asize;
curlow = last + 2;
curdiff = end1;
plast = p - 1;
p_prime_above = p1 + 2;
prime_above = 3;
while (remains)
{
unsigned char was_delta;
if (asize > remains) { asize = remains; end = p + asize; }
while (prime_above*prime_above <= curlow + (asize << 1) && *p_prime_above)
prime_above += *p_prime_above++;
was_delta = *p_prime_above;
*p_prime_above = 0;
sieve_chunk(p1, curlow, p, asize);
*p_prime_above = was_delta;
p[asize] = 0;
for (q = p; ; plast = q++)
{
while (*q) q++;
if (q >= end) break;
*curdiff++ = (unsigned char)(q-plast) << 1;
}
plast -= asize;
remains -= asize;
curlow += (asize<<1);
}
last = curlow - ((p - plast) << 1);
*curdiff++ = 0;
*lenp = curdiff - p1;
*lastp = last;
if (alloced) pari_free(p); else avma = av;
}
ulong
maxprime(void) { return diffptr ? _maxprime : 0; }
void
maxprime_check(ulong c) { if (_maxprime < c) pari_err_MAXPRIME(c); }
byteptr
initprimes(ulong maxnum, long *lenp, ulong *lastp)
{
byteptr t;
if (maxnum < 65537)
maxnum = 65537;
else if (maxnum > 436273289)
maxnum = 436273289;
t = (byteptr)pari_malloc((size_t) (1.09 * maxnum/log((double)maxnum)) + 146);
initprimes0(maxnum, lenp, lastp, t);
return (byteptr)pari_realloc(t, *lenp);
}
void
initprimetable(ulong maxnum)
{
long len;
ulong last;
byteptr p = initprimes(maxnum, &len, &last), old = diffptr;
diffptrlen = minss(diffptrlen, len);
_maxprime = minss(_maxprime,last);
diffptr = p; diffptrlen = len; _maxprime = last;
if (old) free(old);
}
ulong
init_primepointer_geq(ulong a, byteptr *pd)
{
ulong n, p;
prime_table_next_p(a, pd, &p, &n);
return p;
}
ulong
init_primepointer_lt(ulong a, byteptr *pd)
{
ulong n, p;
prime_table_next_p(a, pd, &p, &n);
PREC_PRIME_VIADIFF(p, *pd);
return p;
}
ulong
init_primepointer_leq(ulong a, byteptr *pd)
{
ulong n, p;
prime_table_next_p(a, pd, &p, &n);
if (p != a) PREC_PRIME_VIADIFF(p, *pd);
return p;
}
ulong
init_primepointer_gt(ulong a, byteptr *pd)
{
ulong n, p;
prime_table_next_p(a, pd, &p, &n);
if (p == a) NEXT_PRIME_VIADIFF(p, *pd);
return p;
}
static ulong
optimize_chunk(ulong a, ulong b)
{
ulong chunk = 0x80000UL;
ulong tmp = (b - a) / chunk + 1;
if (tmp == 1)
chunk = b - a + 16;
else
chunk = (b - a) / tmp + 15;
return (((chunk + 2)>>4)<<4) - 2;
}
static void
sieve_init(forprime_t *T, ulong a, ulong b)
{
T->sieveb = b;
T->chunk = optimize_chunk(a, b);
T->isieve = (unsigned char*)stack_malloc(((T->chunk+2) >> 4) + 1);
T->cache[0] = 0;
T->a = a;
T->end = minuu(a + T->chunk, b);
T->pos = T->maxpos = 0;
}
enum {PRST_none, PRST_diffptr, PRST_sieve, PRST_unextprime, PRST_nextprime};
static void
u_forprime_set_prime_table(forprime_t *T, ulong a)
{
T->strategy = PRST_diffptr;
if (a < 3)
{
T->p = 0;
T->d = diffptr;
}
else
T->p = init_primepointer_lt(a, &T->d);
}
static void
arith_set(forprime_t *T)
{
ulong r = T->p % T->q;
pari_sp av = avma;
GEN d = adduu(T->p - r, T->c);
if (T->c > r) d = subiu(d, T->q);
T->p = itou_or_0(d); avma = av;
}
static int
u_forprime_sieve_arith_init(forprime_t *T, struct pari_sieve *psieve,
ulong a, ulong b, ulong c, ulong q)
{
ulong maxp, maxp2;
if (!odd(b) && b > 2) b--;
if (a > b || b < 2)
{
T->strategy = PRST_diffptr;
T->p = 0;
T->b = 0;
T->d = diffptr;
return 0;
}
maxp = maxprime();
if (q != 1)
{
c %= q;
if (ugcd(c,q) != 1) { a = maxuu(a,c); b = minuu(b,c); }
if (odd(q) && (a > 2 || c != 2))
{
if (!odd(c)) c += q;
q <<= 1;
}
}
T->q = q;
T->c = c;
T->strategy = PRST_none;
T->psieve = psieve;
T->isieve = NULL;
T->b = b;
if (maxp >= b) {
u_forprime_set_prime_table(T, a);
return 1;
}
if (a >= maxp)
{
T->p = a - 1;
if (T->q > 1) arith_set(T);
}
else
u_forprime_set_prime_table(T, a);
maxp2 = (maxp & HIGHMASK)? 0 : maxp*maxp;
if (q != 1 || (maxp2 && maxp2 <= a)
|| T->b - maxuu(a,maxp) < maxp / expu(b))
{ if (T->strategy==PRST_none) T->strategy = PRST_unextprime; }
else
{
#ifdef LONG_IS_64BIT
const ulong UPRIME_MAX = 18446744073709551557UL;
#else
const ulong UPRIME_MAX = 4294967291UL;
#endif
ulong sieveb;
if (b > UPRIME_MAX) b = UPRIME_MAX;
sieveb = b;
if (maxp2 && maxp2 < b) sieveb = maxp2;
if (T->strategy==PRST_none) T->strategy = PRST_sieve;
sieve_init(T, maxuu(maxp+2, a), sieveb);
}
return 1;
}
int
u_forprime_arith_init(forprime_t *T, ulong a, ulong b, ulong c, ulong q)
{ return u_forprime_sieve_arith_init(T, NULL, a, b, c, q); }
int
u_forprime_init(forprime_t *T, ulong a, ulong b)
{ return u_forprime_arith_init(T, a,b, 0,1); }
static int
u_forprime_sieve_init(forprime_t *T, struct pari_sieve *s, ulong b)
{ return u_forprime_sieve_arith_init(T, s, s->start, b, s->c, s->q); }
void
u_forprime_restrict(forprime_t *T, ulong c) { T->b = c; }
int
forprimestep_init(forprime_t *T, GEN a, GEN b, GEN q)
{
long lb;
a = gceil(a); if (typ(a) != t_INT) pari_err_TYPE("forprime_init",a);
if (signe(a) <= 0) a = gen_1;
if (b && typ(b) != t_INFINITY)
{
b = gfloor(b);
if (typ(b) != t_INT) pari_err_TYPE("forprime_init",b);
if (signe(b) < 0 || cmpii(a,b) > 0)
{
T->strategy = PRST_nextprime;
T->bb = T->pp = gen_0; return 0;
}
lb = lgefint(b);
T->bb = b;
}
else if (!b || inf_get_sign(b) > 0)
{
lb = lgefint(a) + 4;
T->bb = NULL;
}
else
{
T->strategy = PRST_nextprime;
T->bb = T->pp = gen_0; return 0;
}
T->pp = cgeti(lb);
T->c = 0;
T->q = 1;
if (q)
{
switch(typ(q))
{
case t_INT: break;
case t_INTMOD: a = addii(a, modii(subii(gel(q,2),a), gel(q,1)));
q = gel(q,1); break;
default: pari_err_TYPE("forprimestep_init",q);
}
if (signe(q) <= 0) pari_err_TYPE("forprimestep_init (q <= 0)",q);
if (equali1(q)) q = NULL;
else
{
T->q = itou(q);
T->c = umodiu(a, T->q);
}
}
if (lgefint(a) == 3)
return u_forprime_arith_init(T, uel(a,2), lb == 3? uel(b,2): ULONG_MAX,
T->c, T->q);
T->strategy = PRST_nextprime;
affii(subiu(a,T->q), T->pp);
return 1;
}
int
forprime_init(forprime_t *T, GEN a, GEN b)
{ return forprimestep_init(T,a,b,NULL); }
static void
sieve_block(ulong a, ulong b, ulong maxpos, unsigned char* sieve)
{
ulong p = 2, lim = usqrt(b), sz = (b-a) >> 1;
byteptr d = diffptr+1;
(void)memset(sieve, 0, maxpos+1);
for (;;)
{
ulong k, r;
NEXT_PRIME_VIADIFF(p, d);
if (p > lim) break;
r = a % p;
if (r == 0)
k = 0;
else
{
k = p - r;
if (odd(k)) k += p;
k >>= 1;
}
while (k <= sz) { sieve[k>>3] |= 1 << (k&7); k += p; }
}
}
static void
pari_sieve_init(struct pari_sieve *s, ulong a, ulong b)
{
ulong maxpos= (b - a) >> 4;
s->start = a; s->end = b;
s->sieve = (unsigned char*) pari_malloc(maxpos+1);
s->c = 0; s->q = 1;
sieve_block(a, b, maxpos, s->sieve);
s->maxpos = maxpos;
}
static struct pari_sieve pari_sieve_modular;
#ifdef LONG_IS_64BIT
#define PARI_MODULAR_BASE ((1UL<<((BITS_IN_LONG-2)>>1))+1)
#else
#define PARI_MODULAR_BASE ((1UL<<(BITS_IN_LONG-1))+1)
#endif
void
pari_init_primes(ulong maxprime)
{
ulong a = PARI_MODULAR_BASE, b = a + (1UL<<20)-2;
initprimetable(maxprime);
pari_sieve_init(&pari_sieve_modular, a, b);
}
void
pari_close_primes(void)
{
pari_free(diffptr);
pari_free(pari_sieve_modular.sieve);
}
void
init_modular_small(forprime_t *S)
{
#ifdef LONG_IS_64BIT
u_forprime_sieve_init(S, &pari_sieve_modular, ULONG_MAX);
#else
ulong a = (1UL<<((BITS_IN_LONG-2)>>1))+1;
u_forprime_init(S, a, ULONG_MAX);
#endif
}
void
init_modular_big(forprime_t *S)
{
#ifdef LONG_IS_64BIT
ulong a = (1UL<<(BITS_IN_LONG-1))+1;
u_forprime_init(S, a, ULONG_MAX);
#else
u_forprime_sieve_init(S, &pari_sieve_modular, ULONG_MAX);
#endif
}
static ulong
shift_cache(forprime_t *T)
{
long i;
T->p = T->cache[0];
for (i = 1;; i++)
if (! (T->cache[i-1] = T->cache[i]) ) break;
return T->p;
}
ulong
u_forprime_next(forprime_t *T)
{
if (T->strategy == PRST_diffptr)
{
for(;;)
{
if (!*(T->d))
{
T->strategy = T->isieve? PRST_sieve: PRST_unextprime;
if (T->q != 1) { arith_set(T); if (!T->p) return 0; }
break;
}
else
{
NEXT_PRIME_VIADIFF(T->p, T->d);
if (T->p > T->b) return 0;
if (T->q == 1 || T->p % T->q == T->c) return T->p;
}
}
}
if (T->strategy == PRST_sieve)
{
ulong n;
if (T->cache[0]) return shift_cache(T);
NEXT_CHUNK:
if (T->psieve)
{
T->sieve = T->psieve->sieve;
T->end = T->psieve->end;
if (T->end > T->sieveb) T->end = T->sieveb;
T->maxpos = T->psieve->maxpos;
T->pos = 0;
T->psieve = NULL;
}
for (n = T->pos; n < T->maxpos; n++)
if (T->sieve[n] != 0xFF)
{
unsigned char mask = T->sieve[n];
ulong p = T->a + (n<<4);
long i = 0;
T->pos = n;
if (!(mask & 1)) T->cache[i++] = p;
if (!(mask & 2)) T->cache[i++] = p+2;
if (!(mask & 4)) T->cache[i++] = p+4;
if (!(mask & 8)) T->cache[i++] = p+6;
if (!(mask & 16)) T->cache[i++] = p+8;
if (!(mask & 32)) T->cache[i++] = p+10;
if (!(mask & 64)) T->cache[i++] = p+12;
if (!(mask &128)) T->cache[i++] = p+14;
T->cache[i] = 0;
T->pos = n+1;
return shift_cache(T);
}
if (T->maxpos && n == T->maxpos && T->sieve[n] != 0xFF)
{
unsigned char mask = T->sieve[n];
ulong p = T->a + (n<<4);
long i = 0;
T->pos = n;
if (!(mask & 1) && p <= T->sieveb) T->cache[i++] = p;
if (!(mask & 2) && p <= T->sieveb-2) T->cache[i++] = p+2;
if (!(mask & 4) && p <= T->sieveb-4) T->cache[i++] = p+4;
if (!(mask & 8) && p <= T->sieveb-6) T->cache[i++] = p+6;
if (!(mask & 16) && p <= T->sieveb-8) T->cache[i++] = p+8;
if (!(mask & 32) && p <= T->sieveb-10) T->cache[i++] = p+10;
if (!(mask & 64) && p <= T->sieveb-12) T->cache[i++] = p+12;
if (!(mask &128) && p <= T->sieveb-14) T->cache[i++] = p+14;
if (i)
{
T->cache[i] = 0;
T->pos = n+1;
return shift_cache(T);
}
}
if (T->maxpos && T->end >= T->sieveb)
{
if (T->sieveb == T->b && T->b != ULONG_MAX) return 0;
T->strategy = PRST_unextprime;
}
else
{
T->sieve = T->isieve;
if (T->maxpos == 0)
T->a |= 1;
else
T->a = (T->end + 2) | 1;
T->end = T->a + T->chunk;
if (T->end < T->a || T->end > T->sieveb) T->end = T->sieveb;
T->pos = 0;
T->maxpos = (T->end - T->a) >> 4;
sieve_block(T->a, T->end, T->maxpos, T->sieve);
goto NEXT_CHUNK;
}
}
if (T->strategy == PRST_unextprime)
{
if (T->q == 1)
{
#ifdef LONG_IS_64BIT
if (T->p == (1UL<<63)) return T->p = 9223372036854775837UL;
if (T->p == 9223372036854775837UL) return T->p = 9223372036854775907UL;
#endif
T->p = unextprime(T->p + 1);
}
else do {
T->p += T->q;
if (T->p < T->q || T->p > T->b) { T->p = 0; break; }
} while (!uisprime(T->p));
if (T->p && T->p <= T->b) return T->p;
T->strategy = PRST_nextprime;
}
return 0;
}
GEN
forprime_next(forprime_t *T)
{
pari_sp av;
GEN p;
if (T->strategy != PRST_nextprime)
{
ulong u = u_forprime_next(T);
if (u) { affui(u, T->pp); return T->pp; }
if (T->strategy != PRST_nextprime) return NULL;
u = ULONG_MAX;
if (T->q > 1) u -= (ULONG_MAX-T->c) % T->q;
affui(u, T->pp);
}
av = avma; p = T->pp;
if (T->q == 1)
{
p = nextprime(addiu(p, 1));
if (T->bb && abscmpii(p, T->bb) > 0) { avma = av; return NULL; }
} else do {
p = addiu(p, T->q);
if (T->bb && abscmpii(p, T->bb) > 0) { avma = av; return NULL; }
} while (!BPSW_psp(p));
affii(p, T->pp); avma = av; return T->pp;
}
void
forprimestep(GEN a, GEN b, GEN q, GEN code)
{
pari_sp av = avma;
forprime_t T;
if (!forprimestep_init(&T, a,b,q)) { avma = av; return; }
push_lex(T.pp,code);
while(forprime_next(&T))
{
closure_evalvoid(code); if (loop_break()) break;
if (get_lex(-1) != T.pp)
pari_err(e_MISC,"prime index read-only: was changed to %Ps", get_lex(-1));
}
pop_lex(1); avma = av;
}
void
forprime(GEN a, GEN b, GEN code) { return forprimestep(a,b,NULL,code); }
int
forcomposite_init(forcomposite_t *C, GEN a, GEN b)
{
pari_sp av = avma;
a = gceil(a);
if (typ(a)!=t_INT) pari_err_TYPE("forcomposite",a);
if (b) {
if (typ(b) == t_INFINITY) b = NULL;
else
{
b = gfloor(b);
if (typ(b)!=t_INT) pari_err_TYPE("forcomposite",b);
}
}
if (signe(a) < 0) pari_err_DOMAIN("forcomposite", "a", "<", gen_0, a);
if (abscmpiu(a, 4) < 0) a = utoipos(4);
C->first = 1;
if (!forprime_init(&C->T, a,b) && cmpii(a,b) > 0)
{
C->n = gen_1;
C->b = gen_0;
avma = av; return 0;
}
C->n = setloop(a);
C->b = b;
C->p = NULL; return 1;
}
GEN
forcomposite_next(forcomposite_t *C)
{
if (C->first)
{
C->first = 0;
C->p = forprime_next(&C->T);
}
else
C->n = incloop(C->n);
if (C->p)
{
if (cmpii(C->n, C->p) < 0) return C->n;
C->n = incloop(C->n);
C->p = forprime_next(&C->T);
if (C->p) return C->n;
}
if (!C->b || cmpii(C->n, C->b) <= 0) return C->n;
return NULL;
}
void
forcomposite(GEN a, GEN b, GEN code)
{
pari_sp av = avma;
forcomposite_t T;
GEN n;
if (!forcomposite_init(&T,a,b)) return;
push_lex(T.n,code);
while((n = forcomposite_next(&T)))
{
closure_evalvoid(code); if (loop_break()) break;
if (get_lex(-1) != n)
pari_err(e_MISC,"index read-only: was changed to %Ps", get_lex(-1));
}
pop_lex(1); avma = av;
}