From: Rafael Garcia-Suarez Date: Tue, 30 Dec 2003 16:04:47 +0000 (+0000) Subject: Upgrade to Math::BigInt 1.68. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=3a427a117ca05296a5c3d5d88415dc040792253b;p=p5sagit%2Fp5-mst-13.2.git Upgrade to Math::BigInt 1.68. p4raw-id: //depot/perl@22007 --- diff --git a/MANIFEST b/MANIFEST index ca85bd4..d2543db 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1339,6 +1339,7 @@ lib/Math/BigInt/t/bigintpm.inc Shared tests for bigintpm.t and sub_mbi.t lib/Math/BigInt/t/bigintpm.t See if BigInt.pm works lib/Math/BigInt/t/bigints.t See if BigInt.pm works lib/Math/BigInt/t/biglog.t Test the log function +lib/Math/BigInt/t/bigroot.t Test the broot function lib/Math/BigInt/t/calling.t Test calling conventions lib/Math/BigInt/t/config.t Test Math::BigInt->config() lib/Math/BigInt/t/constant.t Test Math::BigInt/BigFloat under :constant diff --git a/lib/Math/BigFloat.pm b/lib/Math/BigFloat.pm index 9071648..90d4767 100644 --- a/lib/Math/BigFloat.pm +++ b/lib/Math/BigFloat.pm @@ -14,7 +14,8 @@ package Math::BigFloat; $VERSION = '1.42'; require 5.005; -use Exporter; + +require Exporter; @ISA = qw(Exporter Math::BigInt); use strict; @@ -1366,7 +1367,14 @@ sub bmod sub broot { # calculate $y'th root of $x - my ($self,$x,$y,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(2,@_); + + # set up parameters + my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_); + # objectify is costly, so avoid it + if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1]))) + { + ($self,$x,$y,$a,$p,$r) = objectify(2,@_); + } # NaN handling: $x ** 1/0, x or y NaN, or y inf/-inf or y == 0 return $x->bnan() if $x->{sign} !~ /^\+/ || $y->is_zero() || @@ -1425,9 +1433,31 @@ sub broot } else { - my $u = $self->bone()->bdiv($y,$scale+4); - delete $u->{_a}; delete $u->{_p}; # otherwise it conflicts - $x->bpow($u,$scale+4); # el cheapo + # calculate the broot() as integer result first, and if it fits, return + # it rightaway (but only if $x and $y are integer): + + my $done = 0; # not yet + if ($y->is_int() && $x->is_int()) + { + my $int = $x->{_m}->copy(); + $int->blsft($x->{_e},10) unless $x->{_e}->is_zero(); + $int->broot($y->as_number()); + # if ($exact) + if ($int->copy()->bpow($y) == $x) + { + # found result, return it + $x->{_m} = $int; + $x->{_e} = $MBI->bzero(); + $x->bnorm(); + $done = 1; + } + } + if ($done == 0) + { + my $u = $self->bone()->bdiv($y,$scale+4); + delete $u->{_a}; delete $u->{_p}; # otherwise it conflicts + $x->bpow($u,$scale+4); # el cheapo + } } $x->bneg() if $sign == 1; diff --git a/lib/Math/BigInt.pm b/lib/Math/BigInt.pm index aeee0e2..9a26f33 100644 --- a/lib/Math/BigInt.pm +++ b/lib/Math/BigInt.pm @@ -120,8 +120,8 @@ use overload 'bool' => sub { # this kludge is needed for perl prior 5.6.0 since returning 0 here fails :-/ # v5.6.1 dumps on this: return !$_[0]->is_zero() || undef; :-( - my $t = !$_[0]->is_zero(); - undef $t if $t == 0; + my $t = undef; + $t = 1 if !$_[0]->is_zero(); $t; }, @@ -2051,7 +2051,7 @@ sub broot # objectify is costly, so avoid it if ((!ref($x)) || (ref($x) ne ref($y))) { - ($self,$x,$y,@r) = $self->objectify(2,@_); + ($self,$x,$y,@r) = objectify(2,$self || $class,@_); } return $x if $x->modify('broot'); diff --git a/lib/Math/BigInt/Calc.pm b/lib/Math/BigInt/Calc.pm index e1cae77..1dd7619 100644 --- a/lib/Math/BigInt/Calc.pm +++ b/lib/Math/BigInt/Calc.pm @@ -1052,7 +1052,7 @@ sub __strip_zeros } ############################################################################### -# check routine to test internal state of corruptions +# check routine to test internal state for corruptions sub _check { @@ -1081,7 +1081,7 @@ sub _check $i++; } return "Illegal part '$e' at pos $i (tested: $try)" if $i < $j; - return 0; + 0; } @@ -1120,7 +1120,7 @@ sub _mod } elsif ($b == 1) { - # else need to go trough all elements: O(N), but loop is a bit simplified + # else need to go through all elements: O(N), but loop is a bit simplified my $r = 0; foreach (@$x) { @@ -1132,7 +1132,7 @@ sub _mod } else { - # else need to go trough all elements: O(N) + # else need to go through all elements: O(N) my $r = 0; my $bm = 1; foreach (@$x) { @@ -1396,7 +1396,7 @@ sub _log_int $trial = _pow ($c, _copy($c, $base), $x); my $a = _acmp($x,$trial,$x_org); return ($x,1) if $a == 0; - # we now that $res is too small + # we now know that $res is too small if ($res < 0) { _mul($c,$trial,$base); _add($c, $x, [1]); @@ -1551,11 +1551,12 @@ sub _root return $x; } - # X is more than one element + # we know now that X is more than one element long + # if $n is a power of two, we can repeatedly take sqrt($X) and find the # proper result, because sqrt(sqrt($x)) == root($x,4) my $b = _as_bin($c,$n); - if ($$b =~ /0b1(0+)/) + if ($$b =~ /0b1(0+)$/) { my $count = CORE::length($1); # 0b100 => len('00') => 2 my $cnt = $count; # counter for loop @@ -1577,42 +1578,54 @@ sub _root else { # trial computation by starting with 2,4,8,16 etc until we overstep - - my $step = _two(); + my $step; my $trial = _two(); - _mul($c, $trial, $step) - while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0); - - # hit exactly? - if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) == 0) + # while still to do more than X steps + do { - @$x = @$trial; # make copy while preserving ref to $x - return $x; - } - # overstepped, so go back on step - _div($c, $trial, $step); + $step = _two(); + while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0) + { + _mul ($c, $step, [2]); + _add ($c, $trial, $step); + } + + # hit exactly? + if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) == 0) + { + @$x = @$trial; # make copy while preserving ref to $x + return $x; + } + # overstepped, so go back on step + _sub($c, $trial, $step); + } while (scalar @$step > 1 || $step->[0] > 128); + # reset step to 2 + $step = _two(); # add two, because $trial cannot be exactly the result (otherwise we would # alrady have found it) _add($c, $trial, $step); - # and now add more and more (2,4,6,8, etc) - _add($c, $trial, $step) - while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0); + # and now add more and more (2,4,6,8,10 etc) + while (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) < 0) + { + _add ($c, $trial, $step); + } # hit not exactly? (overstepped) - # 80 too small, 81 slightly too big, 82 too big if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0) { _dec($c,$trial); } - # 80 too small, 81 slightly too big + + # hit not exactly? (overstepped) + # 80 too small, 81 slightly too big, 82 too big if (_acmp($c, _pow($c, _copy($c, $trial), $n), $x) > 0) { - _dec($c,$trial); + _dec ($c, $trial); } - + @$x = @$trial; # make copy while preserving ref to $x return $x; } diff --git a/lib/Math/BigInt/CalcEmu.pm b/lib/Math/BigInt/CalcEmu.pm index 4ec244e..d5d1734 100644 --- a/lib/Math/BigInt/CalcEmu.pm +++ b/lib/Math/BigInt/CalcEmu.pm @@ -6,7 +6,7 @@ use strict; use vars qw/$VERSION/; -$VERSION = '0.01'; +$VERSION = '0.02'; # See SYNOPSIS below. @@ -474,7 +474,7 @@ sub __emu_broot # proper result, because sqrt(sqrt($x)) == root($x,4) # See Calc.pm for more details my $b = $y->as_bin(); - if ($b =~ /0b1(0+)/) + if ($b =~ /0b1(0+)$/) { my $count = CORE::length($1); # 0b100 => len('00') => 2 my $cnt = $count; # counter for loop @@ -493,10 +493,48 @@ sub __emu_broot } else { - # Should compute a guess of the result (by rule of thumb), then improve it - # via Newton's method or something similiar. - # XXX TODO - warn ('broot() not fully implemented in BigInt.'); + # trial computation by starting with 2,4,6,8,10 etc until we overstep + my $step; + my $trial = $self->new(2); + my $two = $self->new(2); + my $s_128 = $self->new(128); + + local undef $Math::BigInt::accuracy; + local undef $Math::BigInt::precision; + + # while still to do more than X steps + do + { + $step = $self->new(2); + while ( $trial->copy->bpow($y)->bacmp($x) < 0) + { + $step->bmul($two); + $trial->badd($step); + } + + # hit exactly? + if ( $trial->copy->bpow($y)->bacmp($x) == 0) + { + $x->{value} = $trial->{value}; # make copy while preserving ref to $x + return $x->round(@r); + } + # overstepped, so go back on step + $trial->bsub($step); + } while ($step > $s_128); + + $step = $two->copy(); + while ( $trial->copy->bpow($y)->bacmp($x) < 0) + { + $trial->badd($step); + } + + # not hit exactly? + if ( $x->bacmp( $trial->copy()->bpow($y) ) < 0) + { + $trial->bdec(); + } + # copy result into $x (preserve ref) + $x->{value} = $trial->{value}; } $x->round(@r); } diff --git a/lib/Math/BigInt/t/alias.inc b/lib/Math/BigInt/t/alias.inc index 84310fc..b17f317 100644 --- a/lib/Math/BigInt/t/alias.inc +++ b/lib/Math/BigInt/t/alias.inc @@ -6,7 +6,7 @@ my $x = $CL->new(123); is ($x->is_pos(), 1, '123 is positive'); is ($x->is_neg(), 0, '123 is not negative'); is ($x->as_int(), 123, '123 is 123 as int'); -is (ref($x->as_int()), $CL, '123 is scalar as int'); +is (ref($x->as_int()), $CL, "as_int(123) is of class '$CL'"); $x->bneg(); is ($x->is_pos(), 0, '-123 is not positive'); is ($x->is_neg(), 1, '-123 is negative'); diff --git a/lib/Math/BigInt/t/bare_mbi.t b/lib/Math/BigInt/t/bare_mbi.t index 0af4c42..62911a5 100644 --- a/lib/Math/BigInt/t/bare_mbi.t +++ b/lib/Math/BigInt/t/bare_mbi.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 2760; + plan tests => 2766; } use Math::BigInt lib => 'BareCalc'; diff --git a/lib/Math/BigInt/t/bigfltpm.inc b/lib/Math/BigInt/t/bigfltpm.inc index 1a05a66..34d264a 100644 --- a/lib/Math/BigInt/t/bigfltpm.inc +++ b/lib/Math/BigInt/t/bigfltpm.inc @@ -1262,6 +1262,7 @@ NaN:inf:NaN # fourths root 16:4:2 81:4:3 +# see t/bigroot() for more tests &fsqrt +0:0 -1:NaN diff --git a/lib/Math/BigInt/t/bigintpm.inc b/lib/Math/BigInt/t/bigintpm.inc index 3cbb993..c3fbd78 100644 --- a/lib/Math/BigInt/t/bigintpm.inc +++ b/lib/Math/BigInt/t/bigintpm.inc @@ -2014,13 +2014,16 @@ NaN:inf:NaN # fourths root 16:4:2 81:4:3 -# 2 ** 32 +# 2 ** 64 18446744073709551616:4:65536 18446744073709551616:8:256 18446744073709551616:16:16 18446744073709551616:32:4 18446744073709551616:64:2 18446744073709551616:128:1 +# 213 ** 15 +84274086103068221283760416414557757:15:213 +# see t/bigroot for more tests &bsqrt 145:12 144:12 @@ -2040,6 +2043,9 @@ NaN:inf:NaN 152399026:12345 152399025:12345 152399024:12344 +# 2 ** 64 => 2 ** 32 +18446744073709551616:4294967296 +84274086103068221283760416414557757:290299993288095377 1:1 0:0 -2:NaN diff --git a/lib/Math/BigInt/t/bigintpm.t b/lib/Math/BigInt/t/bigintpm.t index d4e0772..c8c0f1d 100755 --- a/lib/Math/BigInt/t/bigintpm.t +++ b/lib/Math/BigInt/t/bigintpm.t @@ -10,7 +10,7 @@ BEGIN my $location = $0; $location =~ s/bigintpm.t//; unshift @INC, $location; # to locate the testing files chdir 't' if -d 't'; - plan tests => 2760; + plan tests => 2766; } use Math::BigInt; diff --git a/lib/Math/BigInt/t/bigroot.t b/lib/Math/BigInt/t/bigroot.t new file mode 100644 index 0000000..e48d659 --- /dev/null +++ b/lib/Math/BigInt/t/bigroot.t @@ -0,0 +1,71 @@ +#!/usr/bin/perl -w + +# Test broot function (and bsqrt() function, since it is used by broot()). + +# It is too slow to be simple included in bigfltpm.inc, where it would get +# executed 3 times. + +# But it is better to test the numerical functionality, instead of not testing +# it at all. + +use Test; +use strict; + +BEGIN + { + $| = 1; + # to locate the testing files + my $location = $0; $location =~ s/bigroot.t//i; + if ($ENV{PERL_CORE}) + { + # testing with the core distribution + @INC = qw(../lib); + } + unshift @INC, '../lib'; + if (-d 't') + { + chdir 't'; + require File::Spec; + unshift @INC, File::Spec->catdir(File::Spec->updir, $location); + } + else + { + unshift @INC, $location; + } + print "# INC = @INC\n"; + + plan tests => 4 * 2; + } + +use Math::BigFloat; +use Math::BigInt; + +my $cl = "Math::BigFloat"; +my $c = "Math::BigInt"; + +# 2 ** 240 = +# 1766847064778384329583297500742918515827483896875618958121606201292619776 + +# takes way too long +#test_broot ('2','240', 8, undef, '1073741824'); +#test_broot ('2','240', 9, undef, '106528681.3099908308759836475139583940127'); +#test_broot ('2','120', 9, undef, '10321.27324073880096577298929482324664787'); +#test_broot ('2','120', 17, undef, '133.3268493632747279600707813049418888729'); + +test_broot ('2','120', 8, undef, '32768'); +test_broot ('2','60', 8, undef, '181.0193359837561662466161566988413540569'); +test_broot ('2','60', 9, undef, '101.5936673259647663841091609134277286651'); +test_broot ('2','60', 17, undef, '11.54672461623965153271017217302844672562'); + +sub test_broot + { + my ($x,$n,$y,$scale,$result) = @_; + + my $s = $scale || 'undef'; + print "# Try: $cl $x->bpow($n)->broot($y,$s) == $result:\n"; + ok ($cl->new($x)->bpow($n)->broot($y,$scale),$result); + $result =~ s/\..*//; + print "# Try: $c $x->bpow($n)->broot($y,$s) == $result:\n"; + ok ($c->new($x)->bpow($n)->broot($y,$scale),$result); + } + diff --git a/lib/Math/BigInt/t/sub_mbi.t b/lib/Math/BigInt/t/sub_mbi.t index 3b3c6e6..238407a 100755 --- a/lib/Math/BigInt/t/sub_mbi.t +++ b/lib/Math/BigInt/t/sub_mbi.t @@ -26,7 +26,7 @@ BEGIN } print "# INC = @INC\n"; - plan tests => 2760 + plan tests => 2766 + 5; # +5 own tests }