Commit f51fa1c8 authored by Angel Abad's avatar Angel Abad

Merge tag 'upstream/1.999808'

Upstream version 1.999808

# gpg: Firmado el jue 12 ene 2017 08:39:04 CET
# gpg:                usando RSA clave 4A0897191621CCF7FBEEF752A6D58816010A1096
# gpg:                issuer "angelabad@gmail.com"
# gpg: Firma correcta de "Angel Abad <angelabad@gmail.com>" [desconocido]
# gpg:                 alias "Angel Abad <angel@debian.org>" [desconocido]
# gpg:                 alias "Angel Abad <angelabad@ubuntu.com>" [desconocido]
# gpg: ATENCIÓN: ¡Esta clave no está certificada por una firma de confianza!
# gpg:           No hay indicios de que la firma pertenezca al propietario.
# Huellas dactilares de la clave primaria: 4A08 9719 1621 CCF7 FBEE  F752 A6D5 8816 010A 1096
parents 359d26ac 17b55f34
2017-01-11 v1.999808 pjacklam
* In Math::BigInt and Math::BigFloat, add methods bdfac() for double
factorial. Add tests for this method.
* In Math::BigInt and Math::BigFloat, add methods to_hex(), to_oct(), and
to_bin() for hexadecimal, octal, and binary string output without prefix.
Even for Math::BigFloat there is still only support for integer output. Add
tests for these methods.
* Add test for as_oct() corresponding to the old tests for as_hex() and
as_bin().
* In Math::BigInt::Lib, add method _dfac() for double factorial. Add
corresponding tests.
* In Math::BigInt::Lib, fix bug in overloaded "int".
* In Math::BigInt::Lib, implement much faster versions of _from_hex(),
_from_oct(), and _from_bin().
* In Makefile.PL, improve the wording in the message displayed if some of
the installed backend libraries are not a subclass of Math::BigInt::Lib (and
hence will not provide
* Fix minor bugs in some of the author library test files (t/author-lib*.t).
* Allow leading and trailing whitespace in the input to from_hex(),
from_oct(), and from_bin(). Add tests to verify. This is a regressions
(CPAN RT #119805).
2016-12-23 v1.999807 pjacklam
* Add a message to Makefile.PL recommending upgrade if old libraries are
......
......@@ -62,6 +62,8 @@ t/author-lib-arithmetic-ternary-_rsft.dat
t/author-lib-arithmetic-ternary-_rsft.t
t/author-lib-arithmetic-unary-_dec.dat
t/author-lib-arithmetic-unary-_dec.t
t/author-lib-arithmetic-unary-_dfac.dat
t/author-lib-arithmetic-unary-_dfac.t
t/author-lib-arithmetic-unary-_fac.dat
t/author-lib-arithmetic-unary-_fac.t
t/author-lib-arithmetic-unary-_inc.dat
......
......@@ -39,6 +39,6 @@
}
},
"release_status" : "stable",
"version" : "1.999807",
"version" : "1.999808",
"x_serialization_backend" : "JSON::PP version 2.27400"
}
......@@ -21,5 +21,5 @@ requires:
Math::Complex: '1.39'
Test::More: '0.94'
perl: '5.006001'
version: '1.999807'
version: '1.999808'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
......@@ -32,9 +32,9 @@ if (@$recommend_update) {
print <<"EOF";
##########################################################################
#
# Certain methods will not work unless the following installed modules are
# updated. It is recommended that after installing this distribution, you
# update these modules to at least the recommended version listed below.
# Some of the new methods will not work unless the following installed
# modules are updated. It is therefore recommended that the modules listed
# below are upgraded after installing this distribution.
#
# Module Recommended Installed
# ------ ----------- ---------
......
This diff is collapsed.
......@@ -19,7 +19,7 @@ use warnings;
use Carp ();
use Math::BigInt ();
our $VERSION = '1.999807';
our $VERSION = '1.999808';
require Exporter;
our @ISA = qw/Math::BigInt/;
......@@ -529,6 +529,7 @@ sub from_hex {
if ($str =~ s/
^
\s*
# sign
( [+-]? )
......@@ -555,6 +556,7 @@ sub from_hex {
( \d+ (?: _ \d+ )* )
)?
\s*
$
//x)
{
......@@ -618,6 +620,7 @@ sub from_oct {
if ($str =~ s/
^
\s*
# sign
( [+-]? )
......@@ -641,6 +644,7 @@ sub from_oct {
( \d+ (?: _ \d+ )* )
)?
\s*
$
//x)
{
......@@ -704,6 +708,7 @@ sub from_bin {
if ($str =~ s/
^
\s*
# sign
( [+-]? )
......@@ -730,6 +735,7 @@ sub from_bin {
( \d+ (?: _ \d+ )* )
)?
\s*
$
//x)
{
......@@ -3236,6 +3242,31 @@ sub bfac {
$x->bnorm()->round(@r); # norm again and round result
}
sub bdfac {
# compute double factorial
# set up parameters
my ($class, $x, @r) = (ref($_[0]), @_);
# objectify is costly, so avoid it
($class, $x, @r) = objectify(1, @_) if !ref($x);
# inf => inf
return $x if $x->modify('bfac') || $x->{sign} eq '+inf';
return $x->bnan()
if (($x->{sign} ne '+') || # inf, NaN, <0 etc => NaN
($x->{_es} ne '+')); # digits after dot?
# use BigInt's bdfac() for faster calc
if (! $MBI->_is_zero($x->{_e})) {
$x->{_m} = $MBI->_lsft($x->{_m}, $x->{_e}, 10); # change 12e1 to 120e0
$x->{_e} = $MBI->_zero(); # normalize
$x->{_es} = '+';
}
$x->{_m} = $MBI->_dfac($x->{_m}); # calculate factorial
$x->bnorm()->round(@r); # norm again and round result
}
sub blsft {
# shift left by $y (multiply by $b ** $y)
......@@ -3999,6 +4030,57 @@ sub bestr {
return $mant . 'e' . $esgn . $eabs;
}
sub to_hex {
# return number as hexadecimal string (only for integers defined)
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
return '0x0' if $x->is_zero();
return $nan if $x->{_es} ne '+'; # how to do 1e-1 in hex?
my $z = $MBI->_copy($x->{_m});
if (! $MBI->_is_zero($x->{_e})) { # > 0
$z = $MBI->_lsft($z, $x->{_e}, 10);
}
$z = Math::BigInt->new($x->{sign} . $MBI->_num($z));
$z->to_hex();
}
sub to_oct {
# return number as octal digit string (only for integers defined)
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
return '0' if $x->is_zero();
return $nan if $x->{_es} ne '+'; # how to do 1e-1 in octal?
my $z = $MBI->_copy($x->{_m});
if (! $MBI->_is_zero($x->{_e})) { # > 0
$z = $MBI->_lsft($z, $x->{_e}, 10);
}
$z = Math::BigInt->new($x->{sign} . $MBI->_num($z));
$z->to_oct();
}
sub to_bin {
# return number as binary digit string (only for integers defined)
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
return '0b0' if $x->is_zero();
return $nan if $x->{_es} ne '+'; # how to do 1e-1 in binary?
my $z = $MBI->_copy($x->{_m});
if (! $MBI->_is_zero($x->{_e})) { # > 0
$z = $MBI->_lsft($z, $x->{_e}, 10);
}
$z = Math::BigInt->new($x->{sign} . $MBI->_num($z));
$z->to_bin();
}
sub as_hex {
# return number as hexadecimal string (only for integers defined)
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
......
......@@ -20,7 +20,7 @@ use warnings;
use Carp ();
our $VERSION = '1.999807';
our $VERSION = '1.999808';
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(objectify bgcd blcm);
......@@ -731,12 +731,14 @@ sub from_hex {
if ($str =~ s/
^
\s*
( [+-]? )
(0?x)?
(
[0-9a-fA-F]*
( _ [0-9a-fA-F]+ )*
)
\s*
$
//x)
{
......@@ -785,11 +787,13 @@ sub from_oct {
if ($str =~ s/
^
\s*
( [+-]? )
(
[0-7]*
( _ [0-7]+ )*
)
\s*
$
//x)
{
......@@ -838,12 +842,14 @@ sub from_bin {
if ($str =~ s/
^
\s*
( [+-]? )
(0?b)?
(
[01]*
( _ [01]+ )*
)
\s*
$
//x)
{
......@@ -2711,6 +2717,17 @@ sub bfac {
$x->round(@r);
}
sub bdfac {
# compute factorial number from $x, modify $x in place
my ($class, $x, @r) = ref($_[0]) ? (undef, @_) : objectify(1, @_);
return $x if $x->modify('bdfac') || $x->{sign} eq '+inf'; # inf => inf
return $x->bnan() if $x->{sign} ne '+'; # NaN, <0 etc => NaN
$x->{value} = $CALC->_dfac($x->{value});
$x->round(@r);
}
sub blsft {
# (BINT or num_str, BINT or num_str) return BINT
# compute x << y, base n, y >= 0
......@@ -3488,55 +3505,88 @@ sub bdstr {
return $x->{sign} eq '-' ? "-$str" : $str;
}
sub as_hex {
sub to_hex {
# return as hex string, with prefixed 0x
my $x = shift;
$x = $class->new($x) if !ref($x);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $s = '';
$s = $x->{sign} if $x->{sign} eq '-';
$s . $CALC->_as_hex($x->{value});
my $hex = $CALC->_to_hex($x->{value});
return $x->{sign} eq '-' ? "-$hex" : $hex;
}
sub as_oct {
sub to_oct {
# return as octal string, with prefixed 0
my $x = shift;
$x = $class->new($x) if !ref($x);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $oct = $CALC->_as_oct($x->{value});
my $oct = $CALC->_to_oct($x->{value});
return $x->{sign} eq '-' ? "-$oct" : $oct;
}
sub as_bin {
sub to_bin {
# return as binary string, with prefixed 0b
my $x = shift;
$x = $class->new($x) if !ref($x);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $s = '';
$s = $x->{sign} if $x->{sign} eq '-';
return $s . $CALC->_as_bin($x->{value});
my $bin = $CALC->_to_bin($x->{value});
return $x->{sign} eq '-' ? "-$bin" : $bin;
}
sub as_bytes {
sub to_bytes {
# return a byte string
my $x = shift;
$x = $class->new($x) if !ref($x);
Carp::croak("as_bytes() requires a finite, non-negative integer")
Carp::croak("to_bytes() requires a finite, non-negative integer")
if $x -> is_neg() || ! $x -> is_int();
Carp::croak("as_bytes() requires a newer version of the $CALC library.")
unless $CALC->can('_as_bytes');
Carp::croak("to_bytes() requires a newer version of the $CALC library.")
unless $CALC->can('_to_bytes');
return $CALC->_to_bytes($x->{value});
}
sub as_hex {
# return as hex string, with prefixed 0x
my $x = shift;
$x = $class->new($x) if !ref($x);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $hex = $CALC->_as_hex($x->{value});
return $x->{sign} eq '-' ? "-$hex" : $hex;
}
sub as_oct {
# return as octal string, with prefixed 0
my $x = shift;
$x = $class->new($x) if !ref($x);
return $CALC->_as_bytes($x->{value});
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $oct = $CALC->_as_oct($x->{value});
return $x->{sign} eq '-' ? "-$oct" : $oct;
}
sub as_bin {
# return as binary string, with prefixed 0b
my $x = shift;
$x = $class->new($x) if !ref($x);
return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, nan etc
my $bin = $CALC->_as_bin($x->{value});
return $x->{sign} eq '-' ? "-$bin" : $bin;
}
*as_bytes = \&to_bytes;
###############################################################################
# Other conversion methods
###############################################################################
......@@ -4275,10 +4325,15 @@ Math::BigInt - Arbitrary size integer/float math package
$x->bnstr(); # string in normalized notation
$x->bestr(); # string in engineering notation
$x->bdstr(); # string in decimal notation
$x->to_hex(); # as signed hexadecimal string
$x->to_bin(); # as signed binary string
$x->to_oct(); # as signed octal string
$x->to_bytes(); # as byte string
$x->as_hex(); # as signed hexadecimal string with prefixed 0x
$x->as_bin(); # as signed binary string with prefixed 0b
$x->as_oct(); # as signed octal string with prefixed 0
$x->as_bytes(); # as byte string
# Other conversion methods
......@@ -5110,6 +5165,18 @@ Calculates the N'th root of C<$x>.
$x->bfac(); # factorial of $x (1*2*3*4*..*$x)
Returns the factorial of C<$x>, i.e., the product of all positive integers up
to and including C<$x>.
=item bdfac()
$x->bdfac(); # double factorial of $x (1*2*3*4*..*$x)
Returns the double factorial of C<$x>. If C<$x> is an even integer, returns the
product of all positive, even integers up to and including C<$x>, i.e.,
2*4*6*...*$x. If C<$x> is an odd integer, returns the product of all positive,
odd integers, i.e., 1*3*5*...*$x.
=item brsft()
$x->brsft($n); # right shift $n places in base 2
......@@ -5385,34 +5452,53 @@ corresponds to the output from C<dparts()>.
12000 is returned as "12000"
10000 is returned as "10000"
=item to_hex()
$x->to_hex();
Returns a hexadecimal string representation of the number.
=item to_bin()
$x->to_bin();
Returns a binary string representation of the number.
=item to_oct()
$x->to_oct();
Returns an octal string representation of the number.
=item to_bytes()
$x = Math::BigInt->new("1667327589");
$s = $x->to_bytes(); # $s = "cafe"
Returns a byte string representation of the number using big endian byte
order. The invocand must be a non-negative, finite integer.
=item as_hex()
$x->as_hex();
Returns a string representing the number using hexadecimal notation. The output
is prefixed by "0x".
As, C<to_hex()>, but with a "0x" prefix.
=item as_bin()
$x->as_bin();
Returns a string representing the number using binary notation. The output is
prefixed by "0b".
As, C<to_bin()>, but with a "0b" prefix.
=item as_oct()
$x->as_oct();
Returns a string representing the number using octal notation. The output is
prefixed by "0".
As, C<to_oct()>, but with a "0" prefix.
=item as_bytes()
$x = Math::BigInt->new("1667327589");
$s = $x->as_bytes(); # $s = "cafe"
Returns a byte string representing the number using big endian byte order. The
invocand must be a non-negative, finite integer.
This is just an alias for C<to_bytes()>.
=back
......
......@@ -7,7 +7,7 @@ use warnings;
use Carp;
use Math::BigInt::Lib;
our $VERSION = '1.999807';
our $VERSION = '1.999808';
our @ISA = ('Math::BigInt::Lib');
......@@ -2485,13 +2485,8 @@ sub _gcd {
return $x;
}
##############################################################################
##############################################################################
1;
__END__
=pod
=head1 NAME
......
......@@ -4,7 +4,7 @@ use 5.006001;
use strict;
use warnings;
our $VERSION = '1.999807';
our $VERSION = '1.999808';
package Math::BigInt;
......
......@@ -4,7 +4,7 @@ use 5.006001;
use strict;
use warnings;
our $VERSION = '1.999807';
our $VERSION = '1.999808';
use Carp;
......@@ -237,7 +237,7 @@ use overload
return $class -> _sqrt($class -> _copy($_[0]));
},
'int' => sub { $_[0] -> copy() -> bint(); },
'int' => sub { $_[0] },
# overload key: conversion
......@@ -585,6 +585,25 @@ sub _fac {
return $x;
}
sub _dfac {
# double factorial
my ($class, $x) = @_;
my $two = $class -> _two();
if ($class -> _acmp($x, $two) < 0) {
return $class -> _one();
}
my $i = $class -> _copy($x);
while ($class -> _acmp($i, $two) > 0) {
$i = $class -> _sub($i, $two);
$x = $class -> _mul($x, $i);
}
return $x;
}
sub _log_int {
# calculate integer log of $x to base $base
# calculate integer log of $x to base $base
......@@ -1062,7 +1081,7 @@ sub _to_bin {
my ($class, $x) = @_;
my $str = '';
my $tmp = $class -> _copy($x);
my $chunk = $class -> _new("16777216");
my $chunk = $class -> _new("16777216"); # 2^24 = 24 binary digits
my $rem;
until ($class -> _acmp($tmp, $chunk) < 0) {
($tmp, $rem) = $class -> _div($tmp, $chunk);
......@@ -1079,7 +1098,7 @@ sub _to_oct {
my ($class, $x) = @_;
my $str = '';
my $tmp = $class -> _copy($x);
my $chunk = $class -> _new("16777216"); # 2^24 / 8 octal digits
my $chunk = $class -> _new("16777216"); # 2^24 = 8 octal digits
my $rem;
until ($class -> _acmp($tmp, $chunk) < 0) {
($tmp, $rem) = $class -> _div($tmp, $chunk);
......@@ -1096,7 +1115,7 @@ sub _to_hex {
my ($class, $x) = @_;
my $str = '';
my $tmp = $class -> _copy($x);
my $chunk = $class -> _new("16777216");
my $chunk = $class -> _new("16777216"); # 2^24 = 6 hexadecimal digits
my $rem;
until ($class -> _acmp($tmp, $chunk) < 0) {
($tmp, $rem) = $class -> _div($tmp, $chunk);
......@@ -1143,46 +1162,92 @@ sub _to_bytes {
*_as_bytes = \&_to_bytes;
sub _from_oct {
# convert a string of octal digits to a number
my ($class, $str) = @_;
$str =~ s/^0+//;
my $x = $class -> _zero();
my $base = $class -> _new("8");
my $n = length($str);
for (my $i = 0 ; $i < $n ; ++$i) {
$x = $class -> _mul($x, $base);
$x = $class -> _add($x, $class -> _new(substr($str, $i, 1)));
sub _from_hex {
# Convert a string of hexadecimal digits to a number.
my ($class, $hex) = @_;
$hex =~ s/^0[xX]//;
# Find the largest number of hexadecimal digits that we can safely use with
# 32 bit integers. There are 4 bits pr hexadecimal digit, and we use only
# 31 bits to play safe. This gives us int(31 / 4) = 7.
my $len = length $hex;
my $rem = 1 + ($len - 1) % 7;
# Do the first chunk.
my $ret = $class -> _new(int hex substr $hex, 0, $rem);
return $ret if $rem == $len;
# Do the remaining chunks, if any.
my $shift = $class -> _new(1 << (4 * 7));
for (my $offset = $rem ; $offset < $len ; $offset += 7) {
my $part = int hex substr $hex, $offset, 7;
$ret = $class -> _mul($ret, $shift);
$ret = $class -> _add($ret, $class -> _new($part));
}
return $x;
return $ret;
}
sub _from_hex {
# convert a string of hexadecimal digits to a number
my ($class, $str) = @_;
$str =~ s/^0[Xx]//;
my $x = $class -> _zero();
my $base = $class -> _new("16");
my $n = length($str);
for (my $i = 0 ; $i < $n ; ++$i) {
$x = $class -> _mul($x, $base);
$x = $class -> _add($x, $class -> _new(hex substr($str, $i, 1)));
sub _from_oct {
# Convert a string of octal digits to a number.
my ($class, $oct) = @_;
# Find the largest number of octal digits that we can safely use with 32
# bit integers. There are 3 bits pr octal digit, and we use only 31 bits to
# play safe. This gives us int(31 / 3) = 10.
my $len = length $oct;
my $rem = 1 + ($len - 1) % 10;
# Do the first chunk.
my $ret = $class -> _new(int oct substr $oct, 0, $rem);
return $ret if $rem == $len;
# Do the remaining chunks, if any.
my $shift = $class -> _new(1 << (3 * 10));
for (my $offset = $rem ; $offset < $len ; $offset += 10) {
my $part = int oct substr $oct, $offset, 10;
$ret = $class -> _mul($ret, $shift);
$ret = $class -> _add($ret, $class -> _new($part));
}
return $x;
return $ret;
}
sub _from_bin {
# convert a string of binary digits to a number
my ($class, $str) = @_;
$str =~ s/^0[Bb]//;
my $x = $class -> _zero();
my $base = $class -> _new("2");
my $n = length($str);
for (my $i = 0 ; $i < $n ; ++$i) {
$x = $class -> _mul($x, $base);
$x = $class -> _add($x, $class -> _new(substr($str, $i, 1)));
# Convert a string of binary digits to a number.
my ($class, $bin) = @_;
$bin =~ s/^0[bB]//;
# The largest number of binary digits that we can safely use with 32 bit
# integers is 31. We use only 31 bits to play safe.
my $len = length $bin;
my $rem = 1 + ($len - 1) % 31;
# Do the first chunk.
my $ret = $class -> _new(int oct '0b' . substr $bin, 0, $rem);
return $ret if $rem == $len;
# Do the remaining chunks, if any.
my $shift = $class -> _new(1 << 31);
for (my $offset = $rem ; $offset < $len ; $offset += 31) {
my $part = int oct '0b' . substr $bin, $offset, 31;
$ret = $class -> _mul($ret, $shift);
$ret = $class -> _add($ret, $class -> _new($part));
}
return $x;
return $ret;
}
sub _from_bytes {
......@@ -1527,8 +1592,15 @@ Returns the Nth root of OBJ, truncated to an integer.
=item CLASS-E<gt>_fac(OBJ)
Returns factorial of OBJ, i.e., the product of all positive integers up to and
including OBJ.
Returns the factorial of OBJ, i.e., the product of all positive integers up to
and including OBJ.
=item CLASS-E<gt>_dfac(OBJ)
Returns the double factorial of OBJ. If OBJ is an even integer, returns the
product of all positive, even integers up to and including OBJ, i.e.,
2*4*6*...*OBJ. If OBJ is an odd integer, returns the product of all positive,
odd integers, i.e., 1*3*5*...*OBJ.
=item CLASS-E<gt>_pow(OBJ1, OBJ2)
......@@ -1583,7 +1655,7 @@ Returns the greatest common divisor of OBJ1 and OBJ2.
=item CLASS-E<gt>_lcm(OBJ1, OBJ2)
Rseturn the least common multiple of OBJ1 and OBJ2.
Return the least common multiple of OBJ1 and OBJ2.
=back
......@@ -1653,32 +1725,32 @@ should have no leading zeros, i.e., it should match C<^(0|[1-9]\d*)$>.