arb/bernoulli/fmpq_ui_multi_mod.c

212 lines
4.7 KiB
C
Raw Normal View History

/*
Copyright (C) 2021 Fredrik Johansson
This file is part of Arb.
Arb is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See <http://www.gnu.org/licenses/>.
*/
#include "bernoulli.h"
#include "arb.h"
#define TIMING 0
#define DEBUG 0
static void
_fmpz_crt_combine(fmpz_t r1r2, fmpz_t m1m2, const fmpz_t r1, const fmpz_t m1, const fmpz_t r2, const fmpz_t m2)
{
fmpz_invmod(m1m2, m1, m2);
fmpz_mul(m1m2, m1m2, m1);
fmpz_sub(r1r2, r2, r1);
fmpz_mul(r1r2, r1r2, m1m2);
fmpz_add(r1r2, r1r2, r1);
fmpz_mul(m1m2, m1, m2);
fmpz_mod(r1r2, r1r2, m1m2);
}
static void
tree_crt(fmpz_t r, fmpz_t m, mp_srcptr residues, mp_srcptr primes, slong len)
{
if (len == 0)
{
fmpz_zero(r);
fmpz_one(m);
}
else if (len == 1)
{
fmpz_set_ui(r, residues[0]);
fmpz_set_ui(m, primes[0]);
}
else
{
fmpz_t r1, m1, r2, m2;
fmpz_init(r1);
fmpz_init(m1);
fmpz_init(r2);
fmpz_init(m2);
tree_crt(r1, m1, residues, primes, len / 2);
tree_crt(r2, m2, residues + len / 2, primes + len / 2, len - len / 2);
_fmpz_crt_combine(r, m, r1, m1, r2, m2);
fmpz_clear(r1);
fmpz_clear(m1);
fmpz_clear(r2);
fmpz_clear(m2);
}
}
void
_bernoulli_fmpq_ui_multi_mod(fmpz_t num, fmpz_t den, ulong n, double alpha)
{
slong i, bits, mod_bits, zeta_bits, num_primes;
ulong p;
mp_ptr primes, residues;
mag_t primes_product;
fmpz_t M;
#if TIMING
double t1, t2;
#endif
if (n < 10 || n % 2 != 0)
{
_bernoulli_fmpq_ui_zeta(num, den, n);
return;
}
if (alpha < 0)
{
if (n < 18000)
alpha = 0.0;
else if (n < 60000)
alpha = 0.005 + 3.6e-6 * n;
else
alpha = FLINT_MIN(0.18 + 0.5e-6 * n, 0.28);
}
#if TIMING
t1 = clock();
#endif
arith_bernoulli_number_denom(den, n);
bits = arith_bernoulli_number_size(n) + fmpz_bits(den) + 2;
mod_bits = bits * alpha;
zeta_bits = bits - mod_bits;
num_primes = 0;
mag_init(primes_product);
mag_one(primes_product);
for (p = 5; mag_cmp_2exp_si(primes_product, mod_bits) < 0; p = n_nextprime(p, 1))
{
if (n % (p - 1) != 0)
{
mag_mul_ui_lower(primes_product, primes_product, p);
num_primes++;
}
}
#if DEBUG
printf("\nn = %lu, bits = %lu, num_primes = %ld\n", n, bits, num_primes);
#endif
primes = flint_malloc(sizeof(mp_limb_t) * num_primes);
residues = flint_malloc(sizeof(mp_limb_t) * num_primes);
for (p = 5, i = 0; i < num_primes; p = n_nextprime(p, 1))
{
if (n % (p - 1) != 0)
{
primes[i] = p;
i++;
}
}
#if TIMING
t2 = clock();
printf("init time = %f\n", (t2 - t1) / (double) CLOCKS_PER_SEC);
printf("num_primes = %ld\n", num_primes);
#endif
for (i = 0; i < num_primes; i++)
{
#if TIMING
if (i % 10000 == 0)
printf("%ld / %ld\n", i, num_primes);
#endif
residues[i] = bernoulli_mod_p_harvey(n, primes[i]);
}
#if TIMING
t2 = clock();
printf("mod time = %f\n", (t2 - t1) / (double) CLOCKS_PER_SEC);
printf("start CRT\n");
t1 = clock();
#endif
fmpz_init(M);
tree_crt(num, M, residues, primes, num_primes);
fmpz_mul(num, num, den);
fmpz_mod(num, num, M);
if (n % 4 == 0)
{
fmpz_sub(num, M, num);
fmpz_neg(num, num);
}
#if TIMING
printf("end CRT\n");
t2 = clock();
printf("CRT time = %f\n", (t2 - t1) / (double) CLOCKS_PER_SEC);
t1 = clock();
#endif
if (zeta_bits > 0)
{
slong prec;
arb_t b;
fmpz_t t;
arb_init(b);
fmpz_init(t);
for (prec = zeta_bits + 10; ; prec += 32)
{
arb_bernoulli_ui_zeta(b, n, prec);
arb_mul_fmpz(b, b, den, prec);
arb_sub_fmpz(b, b, num, prec);
arb_div_fmpz(b, b, M, prec);
if (arb_get_unique_fmpz(t, b))
{
fmpz_addmul(num, t, M);
break;
}
flint_printf("bernoulli: n = %wu, bits = %wd, mod = %wd, zeta = %wd: get_unique_fmpz failed!\n", n, bits, mod_bits, zeta_bits);
}
arb_clear(b);
fmpz_clear(t);
}
#if TIMING
printf("end zeta\n");
t2 = clock();
printf("zeta time = %f\n", (t2 - t1) / (double) CLOCKS_PER_SEC);
#endif
flint_free(primes);
flint_free(residues);
fmpz_clear(M);
mag_clear(primes_product);
}