#include "pari.h"
#include "paripriv.h"
static void
err_direuler(GEN x)
{ pari_err_DOMAIN("direuler","constant term","!=", gen_1,x); }
static long
dirmuleuler_small(GEN V, GEN v, long n, ulong p, GEN s, long d)
{
long i, j, m = n, D = minss(d+2, lg(s));
ulong q = 1, X = lg(V)-1;
for (i = 3, q = p; i < D; i++, q *= p)
{
GEN aq = gel(s,i);
if (gequal0(aq)) continue;
gel(V,q) = aq;
v[++n] = q;
for (j = 2; j <= m; j++)
{
ulong nj = umuluu_le(uel(v,j), q, X);
if (!nj) continue;
gel(V,nj) = gmul(aq, gel(V,v[j]));
v[++n] = nj;
}
}
return n;
}
static void
dirmuleuler_large(GEN V, ulong p, GEN ap)
{
long j, jp, X = lg(V)-1;
gel(V,p) = ap;
for (j = 2, jp = 2*p; jp <= X; j++, jp += p) gel(V,jp) = gmul(ap, gel(V,j));
}
static ulong
direulertou(GEN a, GEN fl(GEN))
{
if (typ(a) != t_INT)
{
a = fl(a);
if (typ(a) != t_INT) pari_err_TYPE("direuler", a);
}
return signe(a)<=0 ? 0: itou(a);
}
GEN
direuler_bad(void *E, GEN (*eval)(void *,GEN,long), GEN a,GEN b,GEN c, GEN Sbad)
{
ulong au, bu, X, sqrtX, n, p;
pari_sp av0 = avma;
GEN gp, v, V;
forprime_t T;
long i;
au = direulertou(a, gceil);
bu = direulertou(b, gfloor);
X = c ? direulertou(c, gfloor): bu;
if (X == 0) return cgetg(1,t_VEC);
if (bu > X) bu = X;
if (!u_forprime_init(&T, au, bu)) { avma = av0; return mkvec(gen_1); }
v = vecsmall_ei(X, 1);
V = vec_ei(X, 1);
n = 1;
if (Sbad)
{
long l = lg(Sbad);
GEN pbad = gen_1;
for (i = 1; i < l; i++)
{
GEN ai = gel(Sbad,i);
ulong q;
if (typ(ai) != t_VEC || lg(ai) != 3)
pari_err_TYPE("direuler [bad primes]",ai);
q = gtou(gel(ai,1));
if (q <= X)
{
long d = ulogint(X, q) + 1;
GEN s = direuler_factor(gel(ai,2), d);
n = dirmuleuler_small(V, v, n, q, s, d);
pbad = muliu(pbad, q);
}
}
Sbad = pbad;
}
p = 1; gp = cgetipos(3); sqrtX = usqrt(X);
while (p <= sqrtX && (p = u_forprime_next(&T)))
if (!Sbad || umodiu(Sbad, p))
{
long d = ulogint(X, p) + 1;
GEN s;
gp[2] = p; s = eval(E, gp, d);
n = dirmuleuler_small(V, v, n, p, s, d);
}
while ((p = u_forprime_next(&T)))
if (!Sbad || umodiu(Sbad, p))
{
GEN s;
gp[2] = p; s = eval(E, gp, 2);
if (degpol(s) && !gequal0(gel(s,3))) dirmuleuler_large(V, p, gel(s,3));
}
return gerepilecopy(av0,V);
}
GEN
direuler_factor(GEN s, long n)
{
long t = typ(s);
if (is_scalar_t(t))
{
if (!gequal1(s)) err_direuler(s);
return scalarpol_shallow(s,0);
}
switch(t)
{
case t_POL: break;
case t_RFRAC:
{
GEN p = gel(s,1), q = gel(s,2);
q = RgXn_red_shallow(q,n);
s = RgXn_inv(q, n);
if (typ(p) == t_POL && varn(p) == varn(q))
{
p = RgXn_red_shallow(p, n);
s = RgXn_mul(s, p, n);
}
else
if (!gequal1(p)) s = RgX_Rg_mul(s, p);
if (!signe(s) || !gequal1(gel(s,2))) err_direuler(s);
break;
}
case t_SER:
if (!signe(s) || valp(s) || !gequal1(gel(s,2))) err_direuler(s);
break;
default: pari_err_TYPE("direuler", s);
}
return s;
}
struct eval_bad
{
void *E;
GEN (*eval)(void *, GEN);
};
static GEN
eval_bad(void *E, GEN p, long n)
{
struct eval_bad *d = (struct eval_bad*) E;
return direuler_factor(d->eval(d->E, p), n);
}
GEN
direuler(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, GEN c)
{
struct eval_bad d;
d.E= E; d.eval = eval;
return direuler_bad((void*)&d, eval_bad, a, b, c, NULL);
}