#include <gmp.h>
#include "nmod.h"
#include "fmpz.h"
#include "arith.h"
static ulong
arith_bell_number_nmod2(unsigned int * divtab, nn_ptr facs, nn_ptr pows, ulong n, nmod_t mod)
{
ulong s, t, u, v, s2, s1, s0, t1, t0;
ulong qq[3];
slong i;
facs[n] = 1;
for (i = n - 1; i >= 0; i--)
facs[i] = _nmod_mul_fullword(facs[i + 1], i + 1, mod);
pows[0] = nmod_pow_ui(0, n, mod);
pows[1] = nmod_pow_ui(1, n, mod);
for (i = 2; i <= n; i++)
{
if (divtab[2 * i] == 1)
pows[i] = nmod_pow_ui(i, n, mod);
else
pows[i] = _nmod_mul_fullword(pows[divtab[2 * i]], pows[divtab[2 * i + 1]], mod);
}
s2 = s1 = s0 = 0;
for (t = i = 0; i <= n; i++)
{
if (i % 2 == 0)
t = nmod_add(t, facs[i], mod);
else
t = nmod_sub(t, facs[i], mod);
u = pows[n - i];
v = facs[n - i];
u = _nmod_mul_fullword(u, v, mod);
umul_ppmm(t1, t0, u, t);
add_sssaaaaaa(s2, s1, s0, s2, s1, s0, 0, t1, t0);
}
qq[2] = s2;
qq[1] = s1;
qq[0] = s0;
s = mpn_mod_1(qq, 3, mod.n);
u = nmod_inv(facs[0], mod);
u = _nmod_mul_fullword(u, u, mod);
s = _nmod_mul_fullword(s, u, mod);
return s;
}
static void
divisor_table(unsigned int * tab, slong len)
{
slong i, j;
for (i = 0; i < len; i++)
{
tab[2 * i] = 1;
tab[2 * i + 1] = i;
}
for (i = 2; i < len; i++)
{
for (j = 2; j <= i && i * j < len; j++)
{
tab[2 * i * j] = j;
tab[2 * i * j + 1] = i;
}
}
}
void
arith_bell_number_multi_mod(fmpz_t res, ulong n)
{
fmpz_comb_temp_t temp;
fmpz_comb_t comb;
nmod_t mod;
nn_ptr primes, residues, t, u;
slong k, num_primes;
flint_bitcnt_t size, prime_bits;
unsigned int * divtab;
if (n <= 1)
{
fmpz_one(res);
return;
}
size = arith_bell_number_size(n) + 1;
prime_bits = FLINT_BITS - 1;
num_primes = (size + prime_bits - 1) / prime_bits;
primes = flint_malloc(num_primes * sizeof(ulong));
residues = flint_malloc(num_primes * sizeof(ulong));
divtab = flint_malloc(2 * sizeof(unsigned int) * (n + 1));
t = flint_malloc((n + 1) * sizeof(ulong));
u = flint_malloc((n + 1) * sizeof(ulong));
divisor_table(divtab, n + 1);
primes[0] = n_nextprime(UWORD(1) << prime_bits, 0);
for (k = 1; k < num_primes; k++)
primes[k] = n_nextprime(primes[k-1], 0);
for (k = 0; k < num_primes; k++)
{
nmod_init(&mod, primes[k]);
residues[k] = arith_bell_number_nmod2(divtab, t, u, n, mod);
}
fmpz_comb_init(comb, primes, num_primes);
fmpz_comb_temp_init(temp, comb);
fmpz_multi_CRT_ui(res, residues, comb, temp, 0);
fmpz_comb_clear(comb);
fmpz_comb_temp_clear(temp);
flint_free(primes);
flint_free(residues);
flint_free(divtab);
flint_free(t);
flint_free(u);
}