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
mpz_bin_ui(mpz_ptr r,mpz_srcptr n,unsigned long int k)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