[libmath-prime-util-perl] 40/55: Extend binomial to negative arguments
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:53:42 UTC 2015
This is an automated email from the git hooks/post-receive script.
ppm-guest pushed a commit to annotated tag v0.41
in repository libmath-prime-util-perl.
commit 8f60e4120025c38686c43c760baed82a845be542
Author: Dana Jacobsen <dana at acm.org>
Date: Tue May 13 19:45:50 2014 -0700
Extend binomial to negative arguments
---
XS.xs | 22 ++++++++++++++++------
lib/Math/Prime/Util.pm | 10 ++++++++--
lib/Math/Prime/Util/PP.pm | 19 ++++++++++++++++---
lib/Math/Prime/Util/PPFE.pm | 4 ++--
t/19-moebius.t | 8 ++++++++
5 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/XS.xs b/XS.xs
index 3cdacc0..4c589dc 100644
--- a/XS.xs
+++ b/XS.xs
@@ -766,18 +766,27 @@ znorder(IN SV* sva, IN SV* svn)
PREINIT:
int astatus, nstatus;
PPCODE:
- astatus = _validate_int(aTHX_ sva, 0);
- nstatus = _validate_int(aTHX_ svn, 0);
- if (astatus == 1 && nstatus == 1) {
+ astatus = _validate_int(aTHX_ sva, (ix==1) ? 2 : 0);
+ nstatus = _validate_int(aTHX_ svn, (ix==1) ? 2 : 0);
+ if (astatus != 0 && nstatus != 0) {
UV a = my_svuv(sva);
UV n = my_svuv(svn);
UV ret;
switch (ix) {
case 0: ret = znorder(a, n);
break;
- case 1: ret = binomial(a, n);
- if (ret == 0 && n <= a)
+ case 1: if (nstatus == -1) {
+ ret = 0;
+ } else if (astatus == -1) {
+ ret = binomial( -my_sviv(sva)+n-1, n );
+ if (ret > 0 && ret <= (UV)IV_MAX)
+ XSRETURN_IV( (IV)ret * ((n&1) ? -1 : 1) );
goto overflow;
+ } else {
+ ret = binomial(a, n);
+ if (ret == 0 && n <= a)
+ goto overflow;
+ }
break;
case 2: ret = jordan_totient(a, n);
if (ret == 0 && n > 1)
@@ -844,7 +853,8 @@ kronecker(IN SV* sva, IN SV* svb)
} else if (ix == 1) {
UV n = (astatus == -1) ? (UV)(-(my_sviv(sva))) : my_svuv(sva);
UV k = (bstatus == -1) ? (UV)(-(my_sviv(svb))) : my_svuv(svb);
- XSRETURN_UV( valuation(n, k) );
+ /* valuation of 0-2 is very common, so return a constant if possible */
+ RETURN_NPARITY( valuation(n, k) );
} else {
UV a, n, ret = 0;
n = (bstatus != -1) ? my_svuv(svb) : (UV)(-(my_sviv(svb)));
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index 77945ff..660b81c 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -2201,11 +2201,17 @@ C<mpz_legendre(a,n)> functions.
=head2 binomial
-Given non-negative arguments C<n> and C<k>, returns the binomial
-coefficient C<n*(n-1)*...*(n-k+1)/k!>, also known as the choose function.
+Given integer arguments C<n> and C<k>, returns the binomial coefficient
+C<n*(n-1)*...*(n-k+1)/k!>, also known as the choose function. Negative
+arguments use the L<Kronenburg extensions|http://arxiv.org/abs/1105.3689/>.
This corresponds to Pari's C<binomial(n,k)> function, Mathematica's
C<Binomial[n,k]> function, and GMP's C<mpz_bin_ui> function.
+The rules for negative C<n> follow the same rules as the functions mentioned,
+which does not match Math::BigInt. Additionally, Pari does not use the
+extension for C<n E<lt>0 ; k E<lt>= n>, while GMP's API doesn't allow this
+case at all.
+
=head2 znorder
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index d795a7e..4e5d244 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -1787,9 +1787,22 @@ sub kronecker {
}
sub binomial {
my($n, $k) = @_;
- my $r = Math::BigInt->new("$n")->bnok("$k");
- $r = _bigint_to_int($r) if $r->bacmp(''.~0) <= 0;
- return $r;
+ if ($k <= 0) { return ($k == 0) ? 1 : 0; }
+ my $r;
+ if ($n >= 0) {
+ $r = Math::BigInt->new("$n")->bnok("$k");
+ $r = _bigint_to_int($r) if $r->bacmp(''.~0) <= 0;
+ } else {
+ # Math::BigInt is incorrect for negative n
+ $r = Math::BigInt->new( ''.(-$n+$k-1) )->bnok("$k");
+ if ($k & 1) {
+ $r->bneg;
+ $r = _bigint_to_int($r) if $r->bacmp(''.(~0>>1)) <= 0;
+ } else {
+ $r = _bigint_to_int($r) if $r->bacmp(''.~0) <= 0;
+ }
+ }
+ $r;
}
sub _is_perfect_square {
diff --git a/lib/Math/Prime/Util/PPFE.pm b/lib/Math/Prime/Util/PPFE.pm
index 5b0f901..cf0ecb9 100644
--- a/lib/Math/Prime/Util/PPFE.pm
+++ b/lib/Math/Prime/Util/PPFE.pm
@@ -212,8 +212,8 @@ sub kronecker {
sub binomial {
my($n, $k) = @_;
- _validate_positive_integer($n);
- _validate_positive_integer($k);
+ _validate_integer($n);
+ _validate_integer($k);
return Math::Prime::Util::PP::binomial($n, $k);
}
diff --git a/t/19-moebius.t b/t/19-moebius.t
index 5be67ca..b9409b0 100644
--- a/t/19-moebius.t
+++ b/t/19-moebius.t
@@ -410,6 +410,14 @@ my @binomials = (
[ 67,31, "11923179284862717872" ], # ...and this
[ 228,12, "30689926618143230620" ],# But the result of this is too big.
[ 177,78, "3314450882216440395106465322941753788648564665022000" ],
+ [ -10,5, -2002 ],
+ [ -11,22, 64512240 ],
+ [ -12,23, -286097760 ],
+ [ -12,-23, 0 ],
+ [ 12,-23, 0 ],
+ [ 12,-12, 0 ],
+ [ -12,0, 1 ],
+ [ 0,-1, 0 ],
);
# These are slow with XS, and *really* slow with PP.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git
More information about the Pkg-perl-cvs-commits
mailing list