/* 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; }