Blame mpz/bin_ui.c

Packit 5c3484
/* mpz_bin_ui - compute n over k.
Packit 5c3484
Packit 5c3484
Copyright 1998-2002, 2012, 2013 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* This is a poor implementation.  Look at bin_uiui.c for improvement ideas.
Packit 5c3484
   In fact consider calling mpz_bin_uiui() when the arguments fit, leaving
Packit 5c3484
   the code here only for big n.
Packit 5c3484
Packit 5c3484
   The identity bin(n,k) = (-1)^k * bin(-n+k-1,k) can be found in Knuth vol
Packit 5c3484
   1 section 1.2.6 part G. */
Packit 5c3484
Packit 5c3484
Packit 5c3484
#define DIVIDE()                                                              \
Packit 5c3484
  do {                                                                        \
Packit 5c3484
    ASSERT (SIZ(r) > 0);                                                      \
Packit 5c3484
    MPN_DIVREM_OR_DIVEXACT_1 (PTR(r), PTR(r), (mp_size_t) SIZ(r), kacc);      \
Packit 5c3484
    SIZ(r) -= (PTR(r)[SIZ(r)-1] == 0);                                        \
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpz_bin_ui (mpz_ptr r, mpz_srcptr n, unsigned long int k)
Packit 5c3484
{
Packit 5c3484
  mpz_t      ni;
Packit 5c3484
  mp_limb_t  i;
Packit 5c3484
  mpz_t      nacc;
Packit 5c3484
  mp_limb_t  kacc;
Packit 5c3484
  mp_size_t  negate;
Packit 5c3484
Packit 5c3484
  if (SIZ (n) < 0)
Packit 5c3484
    {
Packit 5c3484
      /* bin(n,k) = (-1)^k * bin(-n+k-1,k), and set ni = -n+k-1 - k = -n-1 */
Packit 5c3484
      mpz_init (ni);
Packit 5c3484
      mpz_add_ui (ni, n, 1L);
Packit 5c3484
      mpz_neg (ni, ni);
Packit 5c3484
      negate = (k & 1);   /* (-1)^k */
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* bin(n,k) == 0 if k>n
Packit 5c3484
	 (no test for this under the n<0 case, since -n+k-1 >= k there) */
Packit 5c3484
      if (mpz_cmp_ui (n, k) < 0)
Packit 5c3484
	{
Packit 5c3484
	  SIZ (r) = 0;
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* set ni = n-k */
Packit 5c3484
      mpz_init (ni);
Packit 5c3484
      mpz_sub_ui (ni, n, k);
Packit 5c3484
      negate = 0;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* Now wanting bin(ni+k,k), with ni positive, and "negate" is the sign (0
Packit 5c3484
     for positive, 1 for negative). */
Packit 5c3484
  SIZ (r) = 1; PTR (r)[0] = 1;
Packit 5c3484
Packit 5c3484
  /* Rewrite bin(n,k) as bin(n,n-k) if that is smaller.  In this case it's
Packit 5c3484
     whether ni+k-k < k meaning ni
Packit 5c3484
     = ni, and new ni of ni+k-ni = k.  */
Packit 5c3484
  if (mpz_cmp_ui (ni, k) < 0)
Packit 5c3484
    {
Packit 5c3484
      unsigned long  tmp;
Packit 5c3484
      tmp = k;
Packit 5c3484
      k = mpz_get_ui (ni);
Packit 5c3484
      mpz_set_ui (ni, tmp);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  kacc = 1;
Packit 5c3484
  mpz_init_set_ui (nacc, 1L);
Packit 5c3484
Packit 5c3484
  for (i = 1; i <= k; i++)
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t k1, k0;
Packit 5c3484
Packit 5c3484
#if 0
Packit 5c3484
      mp_limb_t nacclow;
Packit 5c3484
      int c;
Packit 5c3484
Packit 5c3484
      nacclow = PTR(nacc)[0];
Packit 5c3484
      for (c = 0; (((kacc | nacclow) & 1) == 0); c++)
Packit 5c3484
	{
Packit 5c3484
	  kacc >>= 1;
Packit 5c3484
	  nacclow >>= 1;
Packit 5c3484
	}
Packit 5c3484
      mpz_div_2exp (nacc, nacc, c);
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
      mpz_add_ui (ni, ni, 1L);
Packit 5c3484
      mpz_mul (nacc, nacc, ni);
Packit 5c3484
      umul_ppmm (k1, k0, kacc, i << GMP_NAIL_BITS);
Packit 5c3484
      if (k1 != 0)
Packit 5c3484
	{
Packit 5c3484
	  /* Accumulator overflow.  Perform bignum step.  */
Packit 5c3484
	  mpz_mul (r, r, nacc);
Packit 5c3484
	  SIZ (nacc) = 1; PTR (nacc)[0] = 1;
Packit 5c3484
	  DIVIDE ();
Packit 5c3484
	  kacc = i;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  /* Save new products in accumulators to keep accumulating.  */
Packit 5c3484
	  kacc = k0 >> GMP_NAIL_BITS;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  mpz_mul (r, r, nacc);
Packit 5c3484
  DIVIDE ();
Packit 5c3484
  SIZ(r) = (SIZ(r) ^ -negate) + negate;
Packit 5c3484
Packit 5c3484
  mpz_clear (nacc);
Packit 5c3484
  mpz_clear (ni);
Packit 5c3484
}