Commit 96ffa177 authored by Angel Abad's avatar Angel Abad

New upstream version 1.999801

parent 0bbaa75f
Please see <http://bloodgate.com/perl/bigint/benchmarks.html> for detailed and
up-todate benchmarks, as well as a program to generate them.
......@@ -22,7 +22,7 @@ Known bugs:
+ fdiv() using F (fallback) mode does not work properly in all cases of
local (aka $x's or $y's) A or P settings. Not definite test case yet, but
it might calculate not enough digits to satisfy the rounding needs.
* BigInt:
+ exponent on input is limited to a Perl int (bigger numbers won't fit
into your memory, anyway - use BigFloat)
......@@ -31,7 +31,7 @@ Known bugs:
+ Handling of undef arguments is somewhat broken (no proper warnings)
+ eval('use...') and use Math::BigInt qw/:constant/ fail on Perl prior 5.6.0
This is likely an Exporter bug, and causes Math::BigInt to eval require on
earlier Perls when loading the core math lib. Thus the loading can fail
earlier Perls when loading the core math lib. Thus the loading can fail
under older Perls on filesystems that can not cope with the
'Math/BigInt/Calc.pm'-style filenames.
......
......@@ -1896,13 +1896,51 @@ Fixes:
* Add more tests and use more verbose output in some tests.
* Fix typo in lib/Math/BigFloat.pm
* Fix typo in lib/Math/BigFloat.pm
* Fix documentation error in lib/Math/Calc.pm
* Use Config::Tiny and an .ini file to handle the library specific
configuration for the author-lib*.t test files.
2016-11-23 v1.999801 pjacklam
* Fix, hopefully once and for all, the longstanding problem of handling undef
as an operand to mathematical methods. The only method that accepts undef as
an operand is blog(), where the second operand might be undef, as in
$x->blog() or $x->blog($b), where $b is undef. The undef signifies that
Euler's number should be used as the base. With this fix, we should be able
to get Math::BigInt::Lite working again.
* Add least common multiple method _lcm() to Math::BigInt::Lib, and add
corresponding test file t/author-lib-arithmetic-binary-_lcm.t and test data
file t/author-lib-arithmetic-binary-_lcm.dat.
* Remove internal function __lcm() which has become redundant now that _lcm()
is in the library.
* Make it possible to use bgcd() and blcm() as class methods, since other
methods can be used as class methods. This applies to both Math::BigInt and
Math::BigFloat.
* Fix blcm() with negative input. The LCM should always be non-negative. This
applies to both Math::BigInt and Math::BigFloat.
* Add tests for bgcd() and blcm() in t/bigintpm.t and t/bigfltpm.t.
* Fix tests for blcm() assuming that LCM(0,0) should be a NaN. LCM(0,0) is 0
by convention.
* Prefer Class->config('option') over Class->config()->{option}. However, this
does not seem to be working for all options. It seems that this won't work
properly until we move the global variables into the OO interface.
* Explicitly specify the library in all test files that are shared between
Math-BigInt and the library distributions (FaatCalc, GMP, Pari, ...) with,
e.g., "use Math::BigInt only => 'Calc';". This way, it will fail immediately
if the specified library can't be loaded rather than using the fallback
library.
Please send us test-reports, your experiences with this and your ideas - we
love to hear about our work!
......
......@@ -21,7 +21,7 @@ I wish to thank the following people:
* Tim Rushing for reporting the bsqrt() hang and giving me the chance to
improve BigInt/BigFloat.
* cpan@ali.as for reporting the floor() bug with 0.1412024 and providing a
fix and testcase - thanx!
fix and testcase - thanx!
* Stephen Ross for finding the -2 ** Y with odd Y bug
Special thanx must go to John Peacock and Tom Roche, both have helped me a lot
......
......@@ -6,7 +6,7 @@ provided helpfull feedback.
* Favour correctness over speed
* Make your code maintable, so avoid Copy&Paste, unclear constructs, read-only
code and special hacks whenever possible
* Optimize more for the average case than the worst, while trying to avoid
* Optimize more for the average case than the worst, while trying to avoid
performance hits for the worst case.
The average case is more for longer numbers than short, based on the
assumption that if you wanted to add 1 and 2 _fast_ together, you wouldn't
......
......@@ -47,6 +47,8 @@ t/author-lib-arithmetic-binary-_div.dat
t/author-lib-arithmetic-binary-_div.t
t/author-lib-arithmetic-binary-_gcd.dat
t/author-lib-arithmetic-binary-_gcd.t
t/author-lib-arithmetic-binary-_lcm.dat
t/author-lib-arithmetic-binary-_lcm.t
t/author-lib-arithmetic-binary-_log_int.t
t/author-lib-arithmetic-binary-_mod.dat
t/author-lib-arithmetic-binary-_mod.t
......
......@@ -40,6 +40,6 @@
}
},
"release_status" : "stable",
"version" : "1.999800",
"version" : "1.999801",
"x_serialization_backend" : "JSON::PP version 2.27400"
}
......@@ -22,5 +22,5 @@ requires:
Math::Complex: '1.39'
Test::More: '0.94'
perl: '5.006001'
version: '1.999800'
version: '1.999801'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
This diff is collapsed.
......@@ -3,7 +3,7 @@ See also various .pm files.
General:
* Copy on write (helps for $x = -$x; cases etc) (seems to make it slower :/
* run config() and die_on_nan() tests under Subclass.pm
Math::BigFloat:
* finish upgrading and downgrading
* ! bround()/bfround(): some bugs may lurk in there
......
This diff is collapsed.
......@@ -13,7 +13,7 @@ use Math::BigInt qw/:constant/;
print "# Using Math::BigInt v",$Math::BigInt::VERSION,"\n";
# calculate some sample prime numbers from
# calculate some sample prime numbers from
# http://www.utm.edu/research/primes/largest.html
# also: http://www-stud.enst.fr/~bellard/mersenne.html
# (c takes 1 minute on 800 Mhz, so Perl will take..ages..)
......@@ -134,4 +134,3 @@ sub len
$x = "$x"; $x =~ s/^\+//;
return length($x);
}
......@@ -21,7 +21,7 @@ my $prime = Math::BigInt->new (3); # start
$r = 0; my $a = $amount->numify();
for ($i = 3; $i < $a; $i++) # int version
{
$primes[$i] = $r; $r = 1-$r;
$primes[$i] = $r; $r = 1-$r;
}
# find primes
......@@ -43,7 +43,7 @@ while ($prime < $amount)
$cur += $add;
while ($cur < $amount)
{
$primes[$cur] = 1; $cur += $add;
$primes[$cur] = 1; $cur += $add;
}
}
......@@ -75,10 +75,9 @@ close FILE;
my $i = 0;
foreach (@real_primes)
{
print "oups: $i: $test[$i] != $real_primes[$i]\n"
print "oups: $i: $test[$i] != $real_primes[$i]\n"
if $test[$i] != $real_primes[$i]; $i++;
last if $i >= 1000;
}
print "done\n";
......@@ -19,7 +19,7 @@ use warnings;
use Carp ();
use Math::BigInt ();
our $VERSION = '1.999800';
our $VERSION = '1.999801';
$VERSION = eval $VERSION;
require Exporter;
......@@ -218,7 +218,7 @@ $div_scale = 40;
$upgrade = undef;
$downgrade = undef;
# the package we are using for our private parts, defaults to:
# Math::BigInt->config()->{lib}
# Math::BigInt->config('lib')
my $MBI = 'Math::BigInt::Calc';
# are NaNs ok? (otherwise it dies when encountering an NaN) set w/ config()
......@@ -2126,7 +2126,7 @@ sub bpow {
}
sub blog {
my ($class, $x, $base, $a, $p, $r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
my ($class, $x, $base, $a, $p, $r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(2, @_);
# If called as $x -> blog() or $x -> blog(undef), don't objectify the
# undefined base, since undef signals that the base is Euler's number.
......@@ -3590,28 +3590,29 @@ sub bgcd {
# (BINT or num_str, BINT or num_str) return BINT
# does not modify arguments, but returns new object
my $y = shift;
$y = __PACKAGE__->new($y) if !ref($y);
my $class = ref($y);
my $x = $y->copy()->babs(); # keep arguments
unshift @_, __PACKAGE__
unless ref($_[0]) || $_[0] =~ /^[a-z]\w*(?:::[a-z]\w*)*$/i;
return $x->bnan() if $x->{sign} !~ /^[+-]$/ # x NaN?
|| !$x->is_int(); # only for integers now
my ($class, @args) = objectify(0, @_);
while (@_) {
my $t = shift;
$t = $class->new($t) if !ref($t);
$y = $t->copy()->babs();
my $x = shift @args;
$x = ref($x) && $x -> isa($class) ? $x -> copy() : $class -> new($x);
$x -> babs();
return $class->bnan() unless $x -> is_int();
return $x->bnan() if $y->{sign} !~ /^[+-]$/ # y NaN?
|| !$y->is_int(); # only for integers now
while (@args) {
my $y = shift @args;
$y = ref($y) && $y -> isa($class) ? $y -> copy() -> babs()
: $class -> new($y);
return $class->bnan() unless $y -> is_int();
$y -> babs();
# greatest common divisor
while (! $y->is_zero()) {
($x, $y) = ($y->copy(), $x->copy()->bmod($y));
}
last if $x->is_one();
last if $x -> is_one();
}
$x;
}
......@@ -3621,10 +3622,20 @@ sub blcm {
# does not modify arguments, but returns new object
# Lowest Common Multiplicator
my ($class, @arg) = objectify(0, @_);
my $x = $class->new(shift @arg);
while (@arg) {
$x = Math::BigInt::__lcm($x, shift @arg);
unshift @_, __PACKAGE__
unless ref($_[0]) || $_[0] =~ /^[a-z]\w*(?:::[a-z]\w*)*$/i;
my ($class, @args) = objectify(0, @_);
my $x = $class -> bone();
while (@args) {
my $y = shift @args;
$y = ref($y) && $y -> isa($class) ? $y -> copy() -> babs()
: $class -> new($y);
return $x->bnan() unless $y -> is_int();
my $gcd = $x -> bgcd($y);
$x -> bdiv($gcd) -> bmul($y);
}
$x;
}
......@@ -4091,7 +4102,7 @@ my @a;
$lib =~ tr/a-zA-Z0-9,://cd; # restrict to sane characters
# let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
my $mbilib = eval { Math::BigInt->config()->{lib} };
my $mbilib = eval { Math::BigInt->config('lib') };
if ((defined $mbilib) && ($MBI eq 'Math::BigInt::Calc')) {
# MBI already loaded
Math::BigInt->import($lib_kind, "$lib, $mbilib", 'objectify');
......@@ -4111,7 +4122,7 @@ my @a;
Carp::croak("Couldn't load $lib: $! $@");
}
# find out which one was actually loaded
$MBI = Math::BigInt->config()->{lib};
$MBI = Math::BigInt->config('lib');
# register us with MBI to get notified of future lib changes
Math::BigInt::_register_callback($class, sub { $MBI = $_[0]; });
......
......@@ -20,7 +20,7 @@ use warnings;
use Carp ();
our $VERSION = '1.999800';
our $VERSION = '1.999801';
$VERSION = eval $VERSION;
our @ISA = qw(Exporter);
......@@ -413,7 +413,7 @@ sub precision {
sub config {
# return (or set) configuration data as hash ref
my $class = shift || 'Math::BigInt';
my $class = shift || __PACKAGE__;
no strict 'refs';
if (@_ > 1 || (@_ == 1 && (ref($_[0]) eq 'HASH'))) {
......@@ -2342,13 +2342,13 @@ sub blog {
my ($class, $x, $base, @r) = (undef, @_);
# objectify is costly, so avoid it
if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) {
($class, $x, $base, @r) = objectify(1, @_);
($class, $x, $base, @r) = objectify(2, @_);
}
return $x if $x->modify('blog');
# Handle all exception cases and all trivial cases. I have used Wolfram Alpha
# (http://www.wolframalpha.com) as the reference for these cases.
# Handle all exception cases and all trivial cases. I have used Wolfram
# Alpha (http://www.wolframalpha.com) as the reference for these cases.
return $x -> bnan() if $x -> is_nan();
......@@ -3093,16 +3093,17 @@ sub bgcd {
# does not modify arguments, but returns new object
# GCD -- Euclid's algorithm, variant C (Knuth Vol 3, pg 341 ff)
my $y = shift;
$y = $class->new($y) if !ref($y);
my $class = ref($y);
my $x = $y->copy()->babs(); # keep arguments
return $x->bnan() if $x->{sign} !~ /^[+-]$/; # x NaN?
my ($class, @args) = objectify(0, @_);
while (@_) {
$y = shift;
$y = $class->new($y) if !ref($y);
return $x->bnan() if $y->{sign} !~ /^[+-]$/; # y NaN?
my $x = shift @args;
$x = ref($x) && $x -> isa($class) ? $x -> copy() : $class -> new($x);
$x -> babs();
return $class->bnan() if $x->{sign} !~ /^[+-]$/; # x NaN?
while (@args) {
my $y = shift @args;
$y = $class->new($y) unless ref($y) && $y -> isa($class);
return $class->bnan() if $y->{sign} !~ /^[+-]$/; # y NaN?
$x->{value} = $CALC->_gcd($x->{value}, $y->{value});
last if $CALC->_is_one($x->{value});
}
......@@ -3114,18 +3115,15 @@ sub blcm {
# does not modify arguments, but returns new object
# Lowest Common Multiple
my $y = shift;
my ($x);
if (ref($y)) {
$x = $y->copy();
} else {
$x = $class->new($y);
}
my $class = ref($x);
while (@_) {
my $y = shift;
$y = $class->new($y) if !ref ($y);
$x = __lcm($x, $y);
my ($class, @args) = objectify(0, @_);
my $x = $class -> bone();
while (@args) {
my $y = shift @args;
$y = $class -> new($y) unless ref($y) && $y -> isa($class);
return $x->bnan() if $y->{sign} !~ /^[+-]$/; # y not integer
$x -> {value} = $CALC->_lcm($x -> {value}, $y -> {value});
}
$x;
}
......@@ -3575,6 +3573,13 @@ sub objectify {
}
for my $i (1 .. $count) {
# Don't do anything with undefs. This special treatment is necessary
# because blog() might have a second operand which is undef, to signify
# that the default Euler base should be used.
next unless defined $a[$i];
my $ref = ref $a[$i];
# Perl scalars are fed to the appropriate constructor.
......@@ -4020,21 +4025,6 @@ sub _find_round_parameters {
($self, $a, $p, $r);
}
##############################################################################
# internal calculation routines (others are in Math::BigInt::Calc etc)
sub __lcm {
# (BINT or num_str, BINT or num_str) return BINT
# does modify first argument
# LCM
my ($x, $ty) = @_;
return $x->bnan() if ($x->{sign} eq $nan) || ($ty->{sign} eq $nan);
my $method = ref($x) . '::bgcd';
no strict 'refs';
$x * $ty / &$method($x, $ty);
}
###############################################################################
# this method returns 0 if the object can be modified, or 1 if not.
# We use a fast constant sub() here, to avoid costly calls. Subclasses
......@@ -4433,6 +4423,7 @@ This is used for instance by L<Math::BigInt::Constant>.
print Dumper ( Math::BigInt->config() );
print Math::BigInt->config()->{lib},"\n";
print Math::BigInt->config('lib')},"\n";
Returns a hash containing the configuration, e.g. the version number, lib
loaded etc. The following hash keys are currently filled in with the
......
......@@ -7,7 +7,7 @@ use warnings;
use Carp;
use Math::BigInt::Lib;
our $VERSION = '1.999800';
our $VERSION = '1.999801';
$VERSION = eval $VERSION;
our @ISA = ('Math::BigInt::Lib');
......
......@@ -4,7 +4,7 @@ use 5.006001;
use strict;
use warnings;
our $VERSION = '1.999800';
our $VERSION = '1.999801';
$VERSION = eval $VERSION;
package Math::BigInt;
......@@ -15,7 +15,7 @@ my $CALC_EMU;
BEGIN
{
$CALC_EMU = Math::BigInt->config()->{'lib'};
$CALC_EMU = Math::BigInt->config('lib');
# register us with MBI to get notified of future lib changes
Math::BigInt::_register_callback( __PACKAGE__, sub { $CALC_EMU = $_[0]; } );
}
......
......@@ -4,7 +4,7 @@ use 5.006001;
use strict;
use warnings;
our $VERSION = '1.999800';
our $VERSION = '1.999801';
$VERSION = eval $VERSION;
use Carp;
......@@ -1148,6 +1148,23 @@ sub _gcd {
}
}
sub _lcm {
# Least common multiple.
my ($class, $x, $y) = @_;
# lcm(0, x) = 0 for all x
return $class -> _zero()
if ($class -> _is_zero($x) ||
$class -> _is_zero($y));
my $gcd = $class -> _gcd($class -> _copy($x), $y);
$x = $class -> _div($x, $gcd);
$x = $class -> _mul($x, $y);
return $x;
}
##############################################################################
##############################################################################
......@@ -1365,6 +1382,10 @@ whether OBJ is the exact result.
Return the greatest common divisor of OBJ1 and OBJ2.
=item I<_lcm(OBJ1, OBJ2)>
Return the least common multiple of OBJ1 and OBJ2.
=back
=head3 Bitwise operators
......
......@@ -2,44 +2,16 @@
package Math::BigInt::BareCalc;
use 5.005;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
our $VERSION = '0.06';
sub api_version () { 1; }
our $VERSION = '1.999801';
# Package to to test Bigint's simulation of Calc
# Uses Calc, but only features the strictly neccessary methods.
use Math::BigInt::Calc '0.51';
BEGIN {
no strict 'refs';
foreach (qw/
base_len new zero one two ten copy str num add sub mul div mod inc dec
acmp alen len digit zeros
rsft lsft
fac pow gcd log_int sqrt root
is_zero is_one is_odd is_even is_one is_two is_ten check
as_hex as_bin as_oct from_hex from_bin from_oct
modpow modinv
and xor or
/)
{
my $name = "Math::BigInt::Calc::_$_";
*{"Math::BigInt::BareCalc::_$_"} = \&$name;
}
print "# BareCalc using Calc v$Math::BigInt::Calc::VERSION\n";
}
use Math::BigInt::Calc '1.9998';
our @ISA = qw(Math::BigInt::Calc);
# catch and throw away
sub import { }
print "# BareCalc using Calc v", Math::BigInt::Calc -> VERSION, "\n";
1;
......@@ -196,4 +196,3 @@ for (my $i = 0 ; $i <= $#data ; ++ $i) {
"'$test' second output arg is unmodified");
};
}
......@@ -183,4 +183,3 @@ for (my $i = 0 ; $i <= $#data ; ++ $i) {
"'$test' second output arg is unmodified");
};
}
......@@ -166,4 +166,3 @@ for (my $i = 0 ; $i <= $#data ; ++ $i) {
"'$test' second input arg is unmodified");
};
}
This diff is collapsed.
#!perl
BEGIN {
unless ($ENV{AUTHOR_TESTING}) {
require Test::More;
Test::More::plan(skip_all =>
'these tests are for testing by the author');
}
}
use strict;
use warnings;
use Test::More tests => 20205;
###############################################################################
# Read and load configuration file and backend library.
use Config::Tiny ();
my $config_file = 't/author-lib.ini';
my $config = Config::Tiny -> read('t/author-lib.ini')
or die Config::Tiny -> errstr();
# Read the library to test.
our $LIB = $config->{_}->{lib};
die "No library defined in file '$config_file'"
unless defined $LIB;
die "Invalid library name '$LIB' in file '$config_file'"
unless $LIB =~ /^[A-Za-z]\w*(::\w+)*\z/;
# Read the reference type(s) the library uses.
our $REF = $config->{_}->{ref};
die "No reference type defined in file '$config_file'"
unless defined $REF;
die "Invalid reference type '$REF' in file '$config_file'"
unless $REF =~ /^[A-Za-z]\w*(::\w+)*\z/;
# Load the library.
eval "require $LIB";
die $@ if $@;
###############################################################################
can_ok($LIB, '_lcm');
my $scalar_util_ok = eval { require Scalar::Util; };
Scalar::Util -> import('refaddr') if $scalar_util_ok;
diag "Skipping some tests since Scalar::Util is not installed."
unless $scalar_util_ok;
my @data;
# Add data in data file.
(my $datafile = $0) =~ s/\.t/.dat/;
open DATAFILE, $datafile or die "$datafile: can't open file for reading: $!";
while (<DATAFILE>) {
s/\s+\z//;
next if /^#/ || ! /\S/;
push @data, [ split /:/ ];
}
close DATAFILE or die "$datafile: can't close file after reading: $!";
# List context.
for (my $i = 0 ; $i <= $#data ; ++ $i) {
my ($in0, $in1, $out0) = @{ $data[$i] };
my ($x, $y, @got);
my $test = qq|\$x = $LIB->_new("$in0"); |
. qq|\$y = $LIB->_new("$in1"); |
. qq|\@got = $LIB->_lcm(\$x, \$y);|;
eval $test;
is($@, "", "'$test' gives emtpy \$\@");
subtest "_lcm() in list context: $test", sub {
plan tests => $scalar_util_ok ? 9 : 8;
cmp_ok(scalar @got, "==", 1,
"'$test' gives one output arg");
is(ref($got[0]), $REF,
"'$test' output arg is a $REF");
is($LIB->_check($got[0]), 0,
"'$test' output is valid");
is($LIB->_str($got[0]), $out0,
"'$test' output arg has the right value");
isnt(refaddr($got[0]), refaddr($y),
"'$test' output arg is not the second input arg")
if $scalar_util_ok;
is(ref($x), $REF,
"'$test' first input arg is still a $REF");
my $strx = $LIB->_str($x);
ok($strx eq $out0 || $strx eq $in0,
"'$test' first input arg has the right value")
or diag(" got: $strx\n", " expected: ",
$out0 eq $in0 ? $out0 : "$out0 or $in0");
is(ref($y), $REF,
"'$test' second input arg is still a $REF");
is($LIB->_str($y), $in1,
"'$test' second input arg is unmodified");
};
}
# Scalar context.
for (my $i = 0 ; $i <= $#data ; ++ $i) {
my ($in0, $in1, $out0) = @{ $data[$i] };
my ($got);
my ($x, $y);
my ($xo, $yo);
my $test = qq|\$x = $LIB->_new("$in0"); |
. qq|\$y = $LIB->_new("$in1"); |
. qq|\$got = $LIB->_lcm(\$x, \$y);|;
eval $test;
is($@, "", "'$test' gives emtpy \$\@");
subtest "_lcm() in scalar context: $test", sub {
plan tests => $scalar_util_ok ? 8 : 7;
is(ref($got), $REF,
"'$test' output arg is a $REF");