1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
/* mpz_bin_uiui - compute n over k.
Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2006 Free Software Foundation,
Inc.
This file is part of the GNU MP Library.
The GNU MP Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
The GNU MP Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"
/* Enhancement: It ought to be possible to calculate the size of the final
result in advance, to a rough approximation at least, and use it to do
just one realloc. Stirling's approximation n! ~= sqrt(2*pi*n)*(n/e)^n
(Knuth section 1.2.5) might be of use. */
/* "inc" in the main loop allocates a chunk more space if not already
enough, so as to avoid repeated reallocs. The final step on the other
hand requires only one more limb. */
#define MULDIV(inc) \
do { \
ASSERT (rsize <= ralloc); \
\
if (rsize == ralloc) \
{ \
mp_size_t new_ralloc = ralloc + (inc); \
rp = __GMP_REALLOCATE_FUNC_LIMBS (rp, ralloc, new_ralloc); \
ralloc = new_ralloc; \
} \
\
rp[rsize] = mpn_mul_1 (rp, rp, rsize, nacc); \
MPN_DIVREM_OR_DIVEXACT_1 (rp, rp, rsize+1, kacc); \
rsize += (rp[rsize] != 0); \
\
} while (0)
void
mpz_bin_uiui (mpz_ptr r, unsigned long int n, unsigned long int k)
{
unsigned long int i, j;
mp_limb_t nacc, kacc;
unsigned long int cnt;
mp_size_t rsize, ralloc;
mp_ptr rp;
/* bin(n,k) = 0 if k>n. */
if (n < k)
{
SIZ(r) = 0;
return;
}
rp = PTR(r);
/* Rewrite bin(n,k) as bin(n,n-k) if that is smaller. */
k = MIN (k, n-k);
/* bin(n,0) = 1 */
if (k == 0)
{
SIZ(r) = 1;
rp[0] = 1;
return;
}
j = n - k + 1;
rp[0] = j;
rsize = 1;
ralloc = ALLOC(r);
/* Initialize accumulators. */
nacc = 1;
kacc = 1;
for (i = 2; i <= k; i++)
{
mp_limb_t n1, n0;
/* Remove common 2 factors. */
cnt = ((nacc | kacc) & 1) ^ 1;
nacc >>= cnt;
kacc >>= cnt;
j++;
/* Accumulate next multiples. */
umul_ppmm (n1, n0, nacc, (mp_limb_t) j << GMP_NAIL_BITS);
n0 >>= GMP_NAIL_BITS;
if (n1 == 0)
{
/* Save new products in accumulators to keep accumulating. */
nacc = n0;
kacc = kacc * i;
}
else
{
/* Accumulator overflow. Perform bignum step. */
MULDIV (32);
nacc = j;
kacc = i;
}
}
/* Take care of whatever is left in accumulators. */
MULDIV (1);
ALLOC(r) = ralloc;
SIZ(r) = rsize;
PTR(r) = rp;
}
|