diff --git a/arb/bin.c b/arb/bin.c index dc5d0cdb3..d75284683 100644 --- a/arb/bin.c +++ b/arb/bin.c @@ -1,5 +1,6 @@ /* Copyright (C) 2013 Fredrik Johansson + Copyright (C) 2021, 2022 Albin Ahlbäck This file is part of Arb. @@ -10,42 +11,106 @@ */ #include "arb.h" +#include "flint/fmpz.h" + +#define LOG2_BIN(n, k) \ + (-1.3257480647361592 \ + + ((n) + .5) * log2(n) \ + - ((k) + .5) * log2(k) \ + - ((n) - (k) + .5) * log2((n) - (k))) \ + +static void +_arb_bin_ui(arb_t x, const arb_t n, ulong k, slong prec) +{ + arb_t t, u; + + arb_init(t); + arb_init(u); + + arb_sub_ui(t, n, k - 1, prec); + arb_rising_ui(t, t, k, prec); + arb_fac_ui(u, k, prec); + arb_div(x, t, u, prec); + + arb_clear(t); + arb_clear(u); +} void arb_bin_ui(arb_t x, const arb_t n, ulong k, slong prec) { if (k == 0) - { arb_one(x); - } else if (k == 1) - { arb_set_round(x, n, prec); - } - else + else if (arb_is_int(n) + && arb_is_nonnegative(n) + && ARF_EXP(arb_midref(n)) <= FLINT_BITS) /* fits in ulong */ { - arb_t t, u; - - arb_init(t); - arb_init(u); - - arb_sub_ui(t, n, k - 1, prec); - arb_rising_ui(t, t, k, prec); - arb_fac_ui(u, k, prec); - arb_div(x, t, u, prec); - - arb_clear(t); - arb_clear(u); + fmpz_t tmp; + fmpz_init(tmp); + arb_get_unique_fmpz(tmp, n); + arb_bin_uiui(x, fmpz_get_ui(tmp), k, prec); } + else + _arb_bin_ui(x, n, k, prec); } void arb_bin_uiui(arb_t x, ulong n, ulong k, slong prec) { - arb_t t; - arb_init(t); - arb_set_ui(t, n); - arb_bin_ui(x, t, k, prec); - arb_clear(t); + k = FLINT_MIN(k, n - k); + +#if LONG_MAX == WORD_MAX + if (n <= (UWORD(1) << 12) + || k <= (UWORD(1) << 7) +#if FLINT_BITS == 64 + || (n <= (UWORD(1) << 32) && k <= (UWORD(1) << 8)) +#else + || (n <= UWORD_MAX && k <= (UWORD(1) << 8)) +#endif + || (n <= (UWORD(1) << 22) && k <= (UWORD(1) << 9)) + || (n <= (UWORD(1) << 16) && k <= (UWORD(1) << 10)) + || ((double) prec) < LOG2_BIN(n, k)) + { + mpz_t mres; + mpz_init(mres); + flint_mpz_bin_uiui(mres, n, k); + arb_set_round_mpz(x, mres, prec); + mpz_clear(mres); + } +#else + if (n <= (UWORD(1) << 12) + || k <= (UWORD(1) << 7) + || (n < UWORD_MAX && k <= (UWORD(1) << 8)) + || (n <= (UWORD(1) << 22) && k <= (UWORD(1) << 9)) + || (n <= (UWORD(1) << 16) && k <= (UWORD(1) << 10))) + { + mpz_t mres; + mpz_init(mres); + flint_mpz_bin_uiui(mres, n, k); + arb_set_round_mpz(x, mres, prec); + mpz_clear(mres); + } + else if (k < (UWORD(1) << 32) && ((double) prec) < LOG2_BIN(n, k)) + { + mpz_t mres; + __mpz_struct mn = {1, 1, NULL}; + mpz_init(mres); + mn._mp_d = &n; + mpz_bin_ui(mres, &mn, k); + arb_set_round_mpz(x, mres, prec); + mpz_clear(mres); + } +#endif + else + { + arf_struct atmp; + mag_struct mtmp = {0, 0}; + arb_struct tmp = {atmp, mtmp}; + arf_init_set_ui(&atmp, n); + arb_bin_ui(x, &tmp, k, prec); + } } +#undef LOG2_BIN diff --git a/doc/source/arb.rst b/doc/source/arb.rst index 039882bf9..9a4e09a34 100644 --- a/doc/source/arb.rst +++ b/doc/source/arb.rst @@ -1348,11 +1348,15 @@ Gamma function and factorials .. function:: void arb_bin_ui(arb_t z, const arb_t n, ulong k, slong prec) -.. function:: void arb_bin_uiui(arb_t z, ulong n, ulong k, slong prec) - Computes the binomial coefficient `z = {n \choose k}`, via the rising factorial as `{n \choose k} = (n-k+1)_k / k!`. +.. function:: void arb_bin_uiui(arb_t z, ulong n, ulong k, slong prec) + + Computes the binomial coefficient `z = {n \choose k}`. If `n` is small, + it is computed via Flint's implementation `fmpz_bin_uiui`. If `n` is big, + it is computed via rising factorial as `{n \choose k} = (n-k+1)_k / k!`. + .. function:: void arb_gamma(arb_t z, const arb_t x, slong prec) void arb_gamma_fmpq(arb_t z, const fmpq_t x, slong prec) void arb_gamma_fmpz(arb_t z, const fmpz_t x, slong prec)