The GNU MP Library is free software; you can redistribute it and/or modify it under the terms of either:
* 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.
or
* the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
or both in parallel, as here.
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 General Public License for more details.
You should have received copies of the GNU General Public License and the GNU Lesser General Public License along with the GNU MP Library. If not,
see https://www.gnu.org/licenses/. */
#include"gmp-impl.h"
/* How many special cases? Minimum is 2: 0 and 1; * also 3 {0,1,2} and 5 {0,1,2,3,4} are implemented.
*/ #define APARTAJ_KALKULOJ 2
/* Whether to use (1) or not (0) the function mpz_bin_uiui whenever * the operands fit.
*/ #define UZU_BIN_UIUI 0
/* Whether to use a shortcut to precompute the product of four * elements (1), or precompute only the product of a couple (0). * * In both cases the precomputed product is then updated with some * linear operations to obtain the product of the next four (1) * [or two (0)] operands.
*/ #define KVAROPE 1
staticvoid
posmpz_init (mpz_ptr r)
{
mp_ptr rp;
ASSERT (SIZ (r) > 0);
rp = SIZ (r) + MPZ_REALLOC (r, SIZ (r) + 2);
*rp = 0;
*++rp = 0;
}
/* Equivalent to mpz_add_ui (r, r, in), but faster when
0 < SIZ (r) < ALLOC (r) and limbs above SIZ (r) contain 0. */ staticvoid
posmpz_inc_ui (mpz_ptr r, unsignedlong in)
{ #if BITS_PER_ULONG > GMP_NUMB_BITS
mpz_add_ui (r, r, in); #else
ASSERT (SIZ (r) > 0);
MPN_INCR_U (PTR (r), SIZ (r) + 1, in);
SIZ (r) += PTR (r)[SIZ (r)]; #endif
}
/* Equivalent to mpz_sub_ui (r, r, in), but faster when
0 < SIZ (r) and we know in advance that the result is positive. */ staticvoid
posmpz_dec_ui (mpz_ptr r, unsignedlong in)
{ #if BITS_PER_ULONG > GMP_NUMB_BITS
mpz_sub_ui (r, r, in); #else
ASSERT (mpz_cmp_ui (r, in) >= 0);
MPN_DECR_U (PTR (r), SIZ (r), in);
SIZ (r) -= (PTR (r)[SIZ (r)-1] == 0); #endif
}
/* Equivalent to mpz_tdiv_q_2exp (r, r, 1), but faster when
0 < SIZ (r) and we know in advance that the result is positive. */ staticvoid
posmpz_rsh1 (mpz_ptr r)
{
mp_ptr rp;
mp_size_t rn;
rn = SIZ (r);
rp = PTR (r);
ASSERT (rn > 0);
mpn_rshift (rp, rp, rn, 1);
SIZ (r) -= rp[rn - 1] == 0;
}
/* Computes r = n(n+(2*k-1))/2 It uses a sqare instead of a product, computing r = ((n+k-1)^2 + n - (k-1)^2)/2 As a side effect, sets t = n+k-1
*/ staticvoid
mpz_hmul_nbnpk (mpz_ptr r, mpz_srcptr n, unsignedlongint k, mpz_ptr t)
{
ASSERT (k > 0 && SIZ(n) > 0);
--k;
mpz_add_ui (t, n, k);
mpz_mul (r, t, t);
mpz_add (r, r, n);
posmpz_rsh1 (r); if (LIKELY (k <= (1UL << (BITS_PER_ULONG / 2))))
posmpz_dec_ui (r, (k + (k & 1))*(k >> 1)); else
{
mpz_t tmp;
mpz_init_set_ui (tmp, (k + (k & 1)));
mpz_mul_ui (tmp, tmp, k >> 1);
mpz_sub (r, r, tmp);
mpz_clear (tmp);
}
}
/* Computes (n+1)(n+2)...(n+k)/2^(k/2 +k/4) using the helper function rek_raising_fac4, and exploiting an idea inspired by a piece of code that Fredrik Johansson wrote and by a comment by Niels Möller.
Assume k = 4i then compute: p = (n+1)(n+4i)/2 - i (n+1+1)(n+4i)/2 = p + i + (n+4i)/2 (n+1+1)(n+4i-1)/2 = p + i + ((n+4i)-(n+1+1))/2 = p + i + (n-n+4i-2)/2 = p + 3i-1 P = (p + i)*(p+3i-1)/2 = (n+1)(n+2)(n+4i-1)(n+4i)/8 n' = n + 2 i' = i - 1 (n'-1)(n')(n'+4i'+1)(n'+4i'+2)/8 = P (n'-1)(n'+4i'+2)/2 - i' - 1 = p (n'-1+2)(n'+4i'+2)/2 - i' - 1 = p + (n'+4i'+2) (n'-1+2)(n'+4i'+2-2)/2 - i' - 1 = p + (n'+4i'+2) - (n'-1+2) = p + 4i' + 1 (n'-1+2)(n'+4i'+2-2)/2 - i' = p + 4i' + 2 p' = p + 4i' + 2 = (n'+1)(n'+4i')/2 - i' p' - 4i' - 2 = p (p' - 4i' - 2 + i)*(p' - 4i' - 2+3i-1)/2 = P (p' - 4i' - 2 + i' + 1)*(p' - 4i' - 2 + 3i' + 3 - 1)/2 = P (p' - 3i' - 1)*(p' - i')/2 = P (p' - 3i' - 1 + 4i' + 1)*(p' - i' + 4i' - 1)/2 = P + (4i' + 1)*(p' - i')/2 + (p' - 3i' - 1 + 4i' + 1)*(4i' - 1)/2 (p' + i')*(p' + 3i' - 1)/2 = P + (4i')*(p' + p')/2 + (p' - i' - (p' + i'))/2 (p' + i')*(p' + 3i' - 1)/2 = P + 4i'p' + (p' - i' - p' - i')/2 (p' + i')*(p' + 3i' - 1)/2 = P + 4i'p' - i' P' = P + 4i'p' - i'
And compute the product P * P' * P" ...
*/
staticvoid
mpz_raising_fac4 (mpz_ptr r, mpz_ptr n, unsignedlongint k, mpz_ptr t, mpz_ptr p)
{
ASSERT ((k >= APARTAJ_KALKULOJ) && (APARTAJ_KALKULOJ > 0));
posmpz_init (n);
posmpz_inc_ui (n, 1);
SIZ (r) = 0; if (k & 1)
{
mpz_set (r, n);
posmpz_inc_ui (n, 1);
}
k >>= 1; if (APARTAJ_KALKULOJ < 2 && k == 0) return;
mpz_hmul_nbnpk (p, n, k, t);
posmpz_init (p);
if (k & 1)
{ if (SIZ (r))
mpz_mul (r, r, p); else
mpz_set (r, p);
posmpz_inc_ui (p, k - 1);
}
k >>= 1; if (APARTAJ_KALKULOJ < 4 && k == 0) return;
/* Computes (n+1)(n+2)...(n+k)/2^(k/2) using the helper function rek_raising_fac, and exploiting an idea inspired by a piece of code that Fredrik Johansson wrote.
Force an even k = 2i then compute: p = (n+1)(n+2i)/2 i' = i - 1 p == (n+1)(n+2i'+2)/2 p' = p + i' == (n+2)(n+2i'+1)/2 n' = n + 1 p'== (n'+1)(n'+2i')/2 == (n+1 +1)(n+2i -1)/2
And compute the product p * p' * p" ...
*/
staticvoid
mpz_raising_fac (mpz_ptr r, mpz_ptr n, unsignedlongint k, mpz_ptr t, mpz_ptr p)
{ unsignedlongint hk;
ASSERT ((k >= APARTAJ_KALKULOJ) && (APARTAJ_KALKULOJ > 1));
mpz_add_ui (n, n, 1);
hk = k >> 1;
mpz_hmul_nbnpk (p, n, hk, t);
/* This is a poor implementation. Look at bin_uiui.c for improvement ideas. In fact consider calling mpz_bin_uiui() when the arguments fit, leaving the code here only for big n.
The identity bin(n,k) = (-1)^k * bin(-n+k-1,k) can be found in Knuth vol
1 section 1.2.6 part G. */
if (SIZ (n) < 0)
{ /* bin(n,k) = (-1)^k * bin(-n+k-1,k), and set ni = -n+k-1 - k = -n-1 */
mpz_init (ni);
mpz_add_ui (ni, n, 1L);
mpz_neg (ni, ni);
negate = (k & 1); /* (-1)^k */
} else
{ /* bin(n,k) == 0 if k>n
(no test for this under the n<0 case, since -n+k-1 >= k there) */ if (mpz_cmp_ui (n, k) < 0)
{
SIZ (r) = 0; return;
}
/* set ni = n-k */
mpz_init (ni);
mpz_sub_ui (ni, n, k);
negate = 0;
}
/* Now wanting bin(ni+k,k), with ni positive, and "negate" is the sign (0
for positive, 1 for negative). */
/* Rewrite bin(n,k) as bin(n,n-k) if that is smaller. In this case it's whether ni+k-k < k meaning ni<k, and if so change to denominator ni+k-k
= ni, and new ni of ni+k-ni = k. */ if (mpz_cmp_ui (ni, k) < 0)
{ unsignedlong tmp;
tmp = k;
k = mpz_get_ui (ni);
mpz_set_ui (ni, tmp);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.