/*
Copyright (C) 2008, 2009 David Harvey
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 .
*/
#include
#include
#include "flint/fmpz_vec.h"
#include "flint/arith.h"
#include "bernoulli.h"
/* test this internal function which should really be in FLINT */
ulong _bernoulli_n_muldivrem_precomp(ulong * q, ulong a, ulong b, ulong n, double bnpre);
ulong _bernoulli_mod_p_harvey_powg(ulong p, ulong pinv, ulong k);
ulong _bernoulli_mod_p_harvey_pow2(ulong p, ulong pinv, ulong k);
void test_bern_modp_pow2(ulong p, ulong k)
{
ulong x, y;
ulong pinv = n_preinvert_limb(p);
if (n_powmod2_preinv(2, k, p, pinv) == 1)
return;
x = _bernoulli_mod_p_harvey_powg(p, pinv, k);
y = _bernoulli_mod_p_harvey_pow2(p, pinv, k);
if (x != y)
{
flint_printf("FAIL\n");
flint_printf("p = %wu\n", p);
flint_printf("k = %wu\n", k);
flint_printf("x = %wu\n", x);
flint_printf("y = %wu\n", y);
flint_abort();
}
}
int main()
{
slong iter;
flint_rand_t state;
flint_printf("mod_p_harvey....");
fflush(stdout);
flint_randinit(state);
for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++)
{
ulong a, b, n, q1, r1, r2;
mp_limb_t q2[2];
double bnpre;
a = n_randtest_bits(state, FLINT_D_BITS);
b = n_randtest_bits(state, FLINT_D_BITS);
do {
n = n_randtest_bits(state, FLINT_D_BITS);
} while (n == 0);
a = a % n;
b = b % n;
bnpre = (double) b / (double) n;
r1 = _bernoulli_n_muldivrem_precomp(&q1, a, b, n, bnpre);
umul_ppmm(q2[1], q2[0], a, b);
r2 = mpn_divrem_1(q2, 0, q2, 2, n);
if (q2[0] != q1 || r1 != r2)
{
flint_printf("FAIL\n");
flint_printf("a = %wu\n", a);
flint_printf("b = %wu\n", b);
flint_printf("n = %wu\n", n);
flint_printf("q1 = %wu\n", q1);
flint_printf("r1 = %wu\n", r1);
flint_printf("q2 = %wu\n", q2);
flint_printf("r2 = %wu\n", r2);
flint_abort();
}
}
{
slong n, N, iter;
ulong x, y, z, p, pinv;
fmpz * num;
fmpz * den;
N = 300;
num = _fmpz_vec_init(N);
den = _fmpz_vec_init(N);
_arith_bernoulli_number_vec_recursive(num, den, N);
for (n = 2; n < N; n += 2)
{
for (iter = 0; iter < 10 * arb_test_multiplier(); iter++)
{
if (n_randint(state, 100) == 0)
{
p = 1000003;
}
else
{
p = n + 2 + n_randint(state, 2 * N);
p = n_nextprime(p, 1);
}
pinv = n_preinvert_limb(p);
x = _bernoulli_mod_p_harvey_powg(p, pinv, n);
y = fmpz_fdiv_ui(num + n, p);
z = fmpz_fdiv_ui(den + n, p);
if (y != n_mulmod2_preinv(z, n_mulmod2_preinv(x, n, p, pinv), p, pinv))
{
flint_printf("FAIL\n");
flint_printf("n = %wu\n", n);
flint_printf("x = %wu\n", x);
flint_printf("y = %wu\n", y);
flint_printf("z = %wu\n", z);
flint_abort();
}
}
}
_fmpz_vec_clear(num, N);
_fmpz_vec_clear(den, N);
}
{
ulong p, k;
/* exhaustive comparison over some small p and k */
for (p = 5; p < 1000; p = n_nextprime(p, 1))
{
for (k = 2; k <= p - 3; k += 2)
test_bern_modp_pow2(p, k);
}
/* a few larger values of p */
for (p = n_nextprime(1000000, 1);
p < 1000000 + 1000 * FLINT_MIN(10, arb_test_multiplier()); p = n_nextprime(p, 1))
{
k = 2 * (rand() % ((p-3)/2)) + 2;
test_bern_modp_pow2(p, k);
}
/* these are slow */
if (FLINT_BITS == 64 && arb_test_multiplier() >= 10)
{
test_bern_modp_pow2(2147483647, 10);
test_bern_modp_pow2(2147483629, 10);
test_bern_modp_pow2(2147483659, 10);
}
for (p = n_nextprime((1 << 15) - 1000, 1); p < (1 << 15); p = n_nextprime(p, 1))
{
for (iter = 0; iter < 10 * arb_test_multiplier(); iter++)
{
test_bern_modp_pow2(p, 2 * (n_randlimb(state) % ((p - 3)/2)) + 2);
}
}
}
flint_randclear(state);
flint_cleanup();
flint_printf("PASS\n");
return 0;
}