diff options
author | Peter John Acklam <pjacklam@online.no> | 2011-02-18 07:39:40 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2011-02-18 07:39:40 -0800 |
commit | de1ac46b8df09e847eed38152e03f6742b4af9de (patch) | |
tree | ce5ebfab9da328b03f108b114e0c7128b50fb138 /dist | |
parent | bd0e971a15332cc9726bb012b97be7b0f6cadd6f (diff) | |
download | perl-de1ac46b8df09e847eed38152e03f6742b4af9de.tar.gz |
Update Math::BigInt to CPAN version 1.992
dist/Math-BigInt/lib/Math/BigFloat.pm:
- Increment version number.
dist/Math-BigInt/lib/Math/BigInt.pm:
- Increment version number.
- Make from_hex(), from_oct(), and behave more like hex() and oct()
in the Perl core, and make from_bin() consistent with from_hex()
and from_oct(). This is related to RT #58954.
dist/Math-BigInt/lib/Math/BigInt/Calc.pm:
- Increment version number.
- Make _rem() modify first input arg always, not just sometimes.
- Make _modinv() more consistent with the _modinv() method in other
libraries (Math::BigInt::GMP, etc.)
- In _nok(), use symmetry property nok(n,k) = nok(n,n-k). This cuts
computation time tremendously when n and k are large.
- In _gcd(), quickly handle zero cases, avoid code duplication, and
always modify the first input argument in-place.
- Clean up code and add more code comments.
- Fix typos.
dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm:
- Increment version number.
dist/Math-BigInt/t/bigintpm.inc:
- Modify tests to something that still fails.
dist/Math-BigInt/t/upgrade.inc:
- Modify tests to something that still fails.
Diffstat (limited to 'dist')
-rw-r--r-- | dist/Math-BigInt/lib/Math/BigFloat.pm | 2 | ||||
-rw-r--r-- | dist/Math-BigInt/lib/Math/BigInt.pm | 194 | ||||
-rw-r--r-- | dist/Math-BigInt/lib/Math/BigInt/Calc.pm | 202 | ||||
-rw-r--r-- | dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm | 2 | ||||
-rw-r--r-- | dist/Math-BigInt/t/bigintpm.inc | 4 | ||||
-rw-r--r-- | dist/Math-BigInt/t/upgrade.inc | 4 |
6 files changed, 264 insertions, 144 deletions
diff --git a/dist/Math-BigInt/lib/Math/BigFloat.pm b/dist/Math-BigInt/lib/Math/BigFloat.pm index 20045a6459..a39d7866b7 100644 --- a/dist/Math-BigInt/lib/Math/BigFloat.pm +++ b/dist/Math-BigInt/lib/Math/BigFloat.pm @@ -12,7 +12,7 @@ package Math::BigFloat; # _a : accuracy # _p : precision -$VERSION = '1.991'; +$VERSION = '1.992'; require 5.006002; require Exporter; diff --git a/dist/Math-BigInt/lib/Math/BigInt.pm b/dist/Math-BigInt/lib/Math/BigInt.pm index 9ce39f45e4..a9de794d7e 100644 --- a/dist/Math-BigInt/lib/Math/BigInt.pm +++ b/dist/Math-BigInt/lib/Math/BigInt.pm @@ -18,7 +18,7 @@ package Math::BigInt; my $class = "Math::BigInt"; use 5.006002; -$VERSION = '1.991'; +$VERSION = '1.992'; @ISA = qw(Exporter); @EXPORT_OK = qw(objectify bgcd blcm); @@ -2854,93 +2854,145 @@ sub import # import done } -sub from_hex - { - # create a bigint from a hexadecimal string - my ($self, $hs) = @_; +sub from_hex { + # Create a bigint from a hexadecimal string. - my $rc = __from_hex($hs); + my ($self, $str) = @_; - return $self->bnan() unless defined $rc; + if ($str =~ s/ + ^ + ( [+-]? ) + (0?x)? + ( + [0-9a-fA-F]* + ( _ [0-9a-fA-F]+ )* + ) + $ + //x) + { + # Get a "clean" version of the string, i.e., non-emtpy and with no + # underscores or invalid characters. - $rc; - } + my $sign = $1; + my $chrs = $3; + $chrs =~ tr/_//d; + $chrs = '0' unless CORE::length $chrs; -sub from_bin - { - # create a bigint from a hexadecimal string - my ($self, $bs) = @_; + # Initialize output. - my $rc = __from_bin($bs); + my $x = Math::BigInt->bzero(); - return $self->bnan() unless defined $rc; + # The library method requires a prefix. - $rc; - } + $x->{value} = $CALC->_from_hex('0x' . $chrs); -sub from_oct - { - # create a bigint from a hexadecimal string - my ($self, $os) = @_; + # Place the sign. - my $x = $self->bzero(); - - # strip underscores - $os =~ s/([0-7])_([0-7])/$1$2/g; - $os =~ s/([0-7])_([0-7])/$1$2/g; - - return $x->bnan() if $os !~ /^[\-\+]?0[0-7]+\z/; + if ($sign eq '-' && ! $CALC->_is_zero($x->{value})) { + $x->{sign} = '-'; + } - my $sign = '+'; $sign = '-' if $os =~ /^-/; + return $x; + } - $os =~ s/^[+-]//; # strip sign - $x->{value} = $CALC->_from_oct($os); - $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0' - $x; - } + # CORE::hex() parses as much as it can, and ignores any trailing garbage. + # For backwards compatibility, we return NaN. -sub __from_hex - { - # internal - # convert a (ref to) big hex string to BigInt, return undef for error - my $hs = shift; + return $self->bnan(); +} - my $x = Math::BigInt->bzero(); - - # strip underscores - $hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g; - $hs =~ s/([0-9a-fA-F])_([0-9a-fA-F])/$1$2/g; - - return $x->bnan() if $hs !~ /^[\-\+]?0x[0-9A-Fa-f]+$/; +sub from_oct { + # Create a bigint from an octal string. - my $sign = '+'; $sign = '-' if $hs =~ /^-/; + my ($self, $str) = @_; - $hs =~ s/^[+-]//; # strip sign - $x->{value} = $CALC->_from_hex($hs); - $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0' - $x; - } + if ($str =~ s/ + ^ + ( [+-]? ) + ( + [0-7]* + ( _ [0-7]+ )* + ) + $ + //x) + { + # Get a "clean" version of the string, i.e., non-emtpy and with no + # underscores or invalid characters. -sub __from_bin - { - # internal - # convert a (ref to) big binary string to BigInt, return undef for error - my $bs = shift; + my $sign = $1; + my $chrs = $2; + $chrs =~ tr/_//d; + $chrs = '0' unless CORE::length $chrs; - my $x = Math::BigInt->bzero(); + # Initialize output. - # strip underscores - $bs =~ s/([01])_([01])/$1$2/g; - $bs =~ s/([01])_([01])/$1$2/g; - return $x->bnan() if $bs !~ /^[+-]?0b[01]+$/; + my $x = Math::BigInt->bzero(); - my $sign = '+'; $sign = '-' if $bs =~ /^\-/; - $bs =~ s/^[+-]//; # strip sign + # The library method requires a prefix. - $x->{value} = $CALC->_from_bin($bs); - $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0' - $x; - } + $x->{value} = $CALC->_from_oct('0' . $chrs); + + # Place the sign. + + if ($sign eq '-' && ! $CALC->_is_zero($x->{value})) { + $x->{sign} = '-'; + } + + return $x; + } + + # CORE::oct() parses as much as it can, and ignores any trailing garbage. + # For backwards compatibility, we return NaN. + + return $self->bnan(); +} + +sub from_bin { + # Create a bigint from a binary string. + + my ($self, $str) = @_; + + if ($str =~ s/ + ^ + ( [+-]? ) + (0?b)? + ( + [01]* + ( _ [01]+ )* + ) + $ + //x) + { + # Get a "clean" version of the string, i.e., non-emtpy and with no + # underscores or invalid characters. + + my $sign = $1; + my $chrs = $3; + $chrs =~ tr/_//d; + $chrs = '0' unless CORE::length $chrs; + + # Initialize output. + + my $x = Math::BigInt->bzero(); + + # The library method requires a prefix. + + $x->{value} = $CALC->_from_bin('0b' . $chrs); + + # Place the sign. + + if ($sign eq '-' && ! $CALC->_is_zero($x->{value})) { + $x->{sign} = '-'; + } + + return $x; + } + + # For consistency with from_hex() and from_oct(), we return NaN when the + # input is invalid. + + return $self->bnan(); +} sub _split { @@ -2966,9 +3018,9 @@ sub _split # invalid starting char? return if $x !~ /^[+-]?(\.?[0-9]|0b[0-1]|0x[0-9a-fA-F])/; - return __from_hex($x) if $x =~ /^[\-\+]?0x/; # hex string - return __from_bin($x) if $x =~ /^[\-\+]?0b/; # binary string - + return Math::BigInt->from_hex($x) if $x =~ /^[+-]?0x/; # hex string + return Math::BigInt->from_bin($x) if $x =~ /^[+-]?0b/; # binary string + # strip underscores between digits $x =~ s/([0-9])_([0-9])/$1$2/g; $x =~ s/([0-9])_([0-9])/$1$2/g; # do twice for 1_2_3 diff --git a/dist/Math-BigInt/lib/Math/BigInt/Calc.pm b/dist/Math-BigInt/lib/Math/BigInt/Calc.pm index e4c3e060d6..a84786ed29 100644 --- a/dist/Math-BigInt/lib/Math/BigInt/Calc.pm +++ b/dist/Math-BigInt/lib/Math/BigInt/Calc.pm @@ -4,7 +4,7 @@ use 5.006002; use strict; # use warnings; # dont use warnings for older Perls -our $VERSION = '1.991'; +our $VERSION = '1.992'; # Package to store unsigned big integers in decimal and do math with them @@ -1355,22 +1355,24 @@ sub _mod # if possible, use mod shortcut my ($c,$x,$yo) = @_; - # slow way since $y to big + # slow way since $y too big if (scalar @$yo > 1) { my ($xo,$rem) = _div($c,$x,$yo); - return $rem; + @$x = @$rem; + return $x; } my $y = $yo->[0]; - # both are single element arrays + + # if both are single element arrays if (scalar @$x == 1) { $x->[0] %= $y; return $x; } - # @y is a single element, but @x has more than one element + # if @$x has more than one element, but @$y is a single element my $b = $BASE % $y; if ($b == 0) { @@ -1381,7 +1383,8 @@ sub _mod } elsif ($b == 1) { - # else need to go through all elements: O(N), but loop is a bit simplified + # else need to go through all elements in @$x: O(N), but loop is a bit + # simplified my $r = 0; foreach (@$x) { @@ -1393,8 +1396,9 @@ sub _mod } else { - # else need to go through all elements: O(N) - my $r = 0; my $bm = 1; + # else need to go through all elements in @$x: O(N) + my $r = 0; + my $bm = 1; foreach (@$x) { $r = ($_ * $bm + $r) % $y; @@ -1408,8 +1412,8 @@ sub _mod $r = 0 if $r == $y; $x->[0] = $r; } - splice (@$x,1); # keep one element of $x - $x; + @$x = $x->[0]; # keep one element of @$x + return $x; } ############################################################################## @@ -1533,38 +1537,68 @@ sub _pow $cx; } -sub _nok - { - # n over k - # ref to array, return ref to array - my ($c,$n,$k) = @_; +sub _nok { + # Return binomial coefficient (n over k). + # Given refs to arrays, return ref to array. + # First input argument is modified. - # ( 7 ) 7! 1*2*3*4 * 5*6*7 5 * 6 * 7 6 7 - # ( - ) = --------- = --------------- = --------- = 5 * - * - - # ( 3 ) (7-3)! 3! 1*2*3*4 * 1*2*3 1 * 2 * 3 2 3 + my ($c, $n, $k) = @_; + + # If k > n/2, or, equivalently, 2*k > n, compute nok(n, k) as + # nok(n, n-k), to minimize the number if iterations in the loop. - if (!_is_zero($c,$k)) { - my $x = _copy($c,$n); - _sub($c,$n,$k); - _inc($c,$n); - my $f = _copy($c,$n); _inc($c,$f); # n = 5, f = 6, d = 2 - my $d = _two($c); - while (_acmp($c,$f,$x) <= 0) # f <= n ? - { - # n = (n * f / d) == 5 * 6 / 2 - $n = _mul($c,$n,$f); $n = _div($c,$n,$d); - # f = 7, d = 3 - _inc($c,$f); _inc($c,$d); - } + my $twok = _mul($c, _two($c), _copy($c, $k)); # 2 * k + if (_acmp($c, $twok, $n) > 0) { # if 2*k > n + $k = _sub($c, _copy($c, $n), $k); # k = n - k + } } - else - { - # keep ref to $n and set it to 1 - splice (@$n,1); $n->[0] = 1; + + # Example: + # + # / 7 \ 7! 1*2*3*4 * 5*6*7 5 * 6 * 7 6 7 + # | | = --------- = --------------- = --------- = 5 * - * - + # \ 3 / (7-3)! 3! 1*2*3*4 * 1*2*3 1 * 2 * 3 2 3 + + if (_is_zero($c, $k)) { + @$n = 1; } - $n; - } + + else { + + # Make a copy of the original n, since we'll be modifing n in-place. + + my $n_orig = _copy($c, $n); + + # n = 5, f = 6, d = 2 (cf. example above) + + _sub($c, $n, $k); + _inc($c, $n); + + my $f = _copy($c, $n); + _inc($c, $f); + + my $d = _two($c); + + # while f <= n (the original n, that is) ... + + while (_acmp($c, $f, $n_orig) <= 0) { + + # n = (n * f / d) == 5 * 6 / 2 (cf. example above) + + _mul($c, $n, $f); + _div($c, $n, $d); + + # f = 7, d = 3 (cf. example above) + + _inc($c, $f); + _inc($c, $d); + } + + } + + return $n; +} my @factorials = ( 1, @@ -2349,32 +2383,45 @@ sub _from_bin sub _modinv { - # modular inverse + # modular multiplicative inverse my ($c,$x,$y) = @_; - my $u = _zero($c); my $u1 = _one($c); - my $a = _copy($c,$y); my $b = _copy($c,$x); + # modulo zero + if (_is_zero($c, $y)) { + return (undef, undef); + } + + # modulo one + if (_is_one($c, $y)) { + return (_zero($c), '+'); + } + + my $u = _zero($c); + my $v = _one($c); + my $a = _copy($c,$y); + my $b = _copy($c,$x); - # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the - # result ($u) at the same time. See comments in BigInt for why this works. + # Euclid's Algorithm for bgcd(), only that we calc bgcd() ($a) and the result + # ($u) at the same time. See comments in BigInt for why this works. my $q; - ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1 my $sign = 1; - while (!_is_zero($c,$b)) - { - my $t = _add($c, # step 2: - _mul($c,_copy($c,$u1), $q) , # t = u1 * q - $u ); # + u - $u = $u1; # u = u1, u1 = t - $u1 = $t; - $sign = -$sign; - ($a, $q, $b) = ($b, _div($c,$a,$b)); # step 1 - } + { + ($a, $q, $b) = ($b, _div($c, $a, $b)); # step 1 + last if _is_zero($c, $b); + + my $t = _add($c, # step 2: + _mul($c, _copy($c, $v), $q) , # t = v * q + $u ); # + u + $u = $v; # u = v + $v = $t; # v = t + $sign = -$sign; + redo; + } # if the gcd is not 1, then return NaN - return (undef,undef) unless _is_one($c,$a); - - ($u1, $sign == 1 ? '+' : '-'); + return (undef, undef) unless _is_one($c, $a); + + ($v, $sign == 1 ? '+' : '-'); } sub _modpow @@ -2420,19 +2467,40 @@ sub _modpow $num; } -sub _gcd - { - # greatest common divisor - my ($c,$x,$y) = @_; +sub _gcd { + # Greatest common divisor. - while ( (scalar @$y != 1) || ($y->[0] != 0) ) # while ($y != 0) - { - my $t = _copy($c,$y); - $y = _mod($c, $x, $y); - $x = $t; + my ($c, $x, $y) = @_; + + # gcd(0,0) = 0 + # gcd(0,a) = a, if a != 0 + + if (@$x == 1 && $x->[0] == 0) { + if (@$y == 1 && $y->[0] == 0) { + @$x = 0; + } else { + @$x = @$y; + } + return $x; } - $x; - } + + # Until $y is zero ... + + until (@$y == 1 && $y->[0] == 0) { + + # Compute remainder. + + _mod($c, $x, $y); + + # Swap $x and $y. + + my $tmp = [ @$x ]; + @$x = @$y; + $y = $tmp; # no deref here; that would modify input $y + } + + return $x; +} ############################################################################## ############################################################################## diff --git a/dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm b/dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm index 810665d55f..3f2aa3bdbe 100644 --- a/dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm +++ b/dist/Math-BigInt/lib/Math/BigInt/CalcEmu.pm @@ -5,7 +5,7 @@ use strict; # use warnings; # dont use warnings for older Perls use vars qw/$VERSION/; -$VERSION = '1.991'; +$VERSION = '1.992'; package Math::BigInt; diff --git a/dist/Math-BigInt/t/bigintpm.inc b/dist/Math-BigInt/t/bigintpm.inc index da127c3ae1..f5f0fd2812 100644 --- a/dist/Math-BigInt/t/bigintpm.inc +++ b/dist/Math-BigInt/t/bigintpm.inc @@ -895,7 +895,7 @@ NaN:-inf: 0b100000000000000000000000000000001:4294967297 0b1000000000000000000000000000000001:8589934593 0b10000000000000000000000000000000001:17179869185 -0b_101:NaN +0b__101:NaN 0b1_0_1:5 0b0_0_0_1:1 # hex input @@ -908,7 +908,7 @@ NaN:-inf: 0x12345678:305419896 0x1_2_3_4_56_78:305419896 0xa_b_c_d_e_f:11259375 -0x_123:NaN +0x__123:NaN 0x9:9 0x11:17 0x21:33 diff --git a/dist/Math-BigInt/t/upgrade.inc b/dist/Math-BigInt/t/upgrade.inc index 088c567762..c7ecc26434 100644 --- a/dist/Math-BigInt/t/upgrade.inc +++ b/dist/Math-BigInt/t/upgrade.inc @@ -395,7 +395,7 @@ NaN:-inf: 0b100000000000000000000000000000001:4294967297 0b1000000000000000000000000000000001:8589934593 0b10000000000000000000000000000000001:17179869185 -0b_101:NaN +0b__101:NaN 0b1_0_1:5 0b0_0_0_1:1 # hex input @@ -408,7 +408,7 @@ NaN:-inf: 0x12345678:305419896 0x1_2_3_4_56_78:305419896 0xa_b_c_d_e_f:11259375 -0x_123:NaN +0x__123:NaN 0x9:9 0x11:17 0x21:33 |