xref: /dragonfly/contrib/gmp/mpz/bin_ui.c (revision 78478697)
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