1 /* mpz_bin_ui - compute n over k. 2 3 Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 4 5 This file is part of the GNU MP Library. 6 7 The GNU MP Library is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or (at your 10 option) any later version. 11 12 The GNU MP Library is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ 19 20 #include "gmp.h" 21 #include "gmp-impl.h" 22 #include "longlong.h" 23 24 25 /* This is a poor implementation. Look at bin_uiui.c for improvement ideas. 26 In fact consider calling mpz_bin_uiui() when the arguments fit, leaving 27 the code here only for big n. 28 29 The identity bin(n,k) = (-1)^k * bin(-n+k-1,k) can be found in Knuth vol 30 1 section 1.2.6 part G. */ 31 32 33 #define DIVIDE() \ 34 do { \ 35 ASSERT (SIZ(r) > 0); \ 36 MPN_DIVREM_OR_DIVEXACT_1 (PTR(r), PTR(r), (mp_size_t) SIZ(r), kacc); \ 37 SIZ(r) -= (PTR(r)[SIZ(r)-1] == 0); \ 38 } while (0) 39 40 void 41 mpz_bin_ui (mpz_ptr r, mpz_srcptr n, unsigned long int k) 42 { 43 mpz_t ni; 44 mp_limb_t i; 45 mpz_t nacc; 46 mp_limb_t kacc; 47 mp_size_t negate; 48 49 if (mpz_sgn (n) < 0) 50 { 51 /* bin(n,k) = (-1)^k * bin(-n+k-1,k), and set ni = -n+k-1 - k = -n-1 */ 52 mpz_init (ni); 53 mpz_neg (ni, n); 54 mpz_sub_ui (ni, ni, 1L); 55 negate = (k & 1); /* (-1)^k */ 56 } 57 else 58 { 59 /* bin(n,k) == 0 if k>n 60 (no test for this under the n<0 case, since -n+k-1 >= k there) */ 61 if (mpz_cmp_ui (n, k) < 0) 62 { 63 mpz_set_ui (r, 0L); 64 return; 65 } 66 67 /* set ni = n-k */ 68 mpz_init (ni); 69 mpz_sub_ui (ni, n, k); 70 negate = 0; 71 } 72 73 /* Now wanting bin(ni+k,k), with ni positive, and "negate" is the sign (0 74 for positive, 1 for negative). */ 75 mpz_set_ui (r, 1L); 76 77 /* Rewrite bin(n,k) as bin(n,n-k) if that is smaller. In this case it's 78 whether ni+k-k < k meaning ni<k, and if so change to denominator ni+k-k 79 = ni, and new ni of ni+k-ni = k. */ 80 if (mpz_cmp_ui (ni, k) < 0) 81 { 82 unsigned long tmp; 83 tmp = k; 84 k = mpz_get_ui (ni); 85 mpz_set_ui (ni, tmp); 86 } 87 88 kacc = 1; 89 mpz_init_set_ui (nacc, 1L); 90 91 for (i = 1; i <= k; i++) 92 { 93 mp_limb_t k1, k0; 94 95 #if 0 96 mp_limb_t nacclow; 97 int c; 98 99 nacclow = PTR(nacc)[0]; 100 for (c = 0; (((kacc | nacclow) & 1) == 0); c++) 101 { 102 kacc >>= 1; 103 nacclow >>= 1; 104 } 105 mpz_div_2exp (nacc, nacc, c); 106 #endif 107 108 mpz_add_ui (ni, ni, 1L); 109 mpz_mul (nacc, nacc, ni); 110 umul_ppmm (k1, k0, kacc, i << GMP_NAIL_BITS); 111 k0 >>= GMP_NAIL_BITS; 112 if (k1 != 0) 113 { 114 /* Accumulator overflow. Perform bignum step. */ 115 mpz_mul (r, r, nacc); 116 mpz_set_ui (nacc, 1L); 117 DIVIDE (); 118 kacc = i; 119 } 120 else 121 { 122 /* Save new products in accumulators to keep accumulating. */ 123 kacc = k0; 124 } 125 } 126 127 mpz_mul (r, r, nacc); 128 DIVIDE (); 129 SIZ(r) = (SIZ(r) ^ -negate) + negate; 130 131 mpz_clear (nacc); 132 mpz_clear (ni); 133 } 134