diff options
author | Chris Williams <chris@bingosnet.co.uk> | 2009-09-10 21:30:20 +0100 |
---|---|---|
committer | Chris Williams <chris@bingosnet.co.uk> | 2009-09-10 21:30:20 +0100 |
commit | a3bef8ed8dece3b48e9e11993f255fbe719a0647 (patch) | |
tree | a1c54009ef1eb097495c49c44bb8b4133a93c5aa /ext | |
parent | 5711376d83710d8f98ce1dc447317524afb70055 (diff) | |
download | perl-a3bef8ed8dece3b48e9e11993f255fbe719a0647.tar.gz |
Moved bignum from lib/ to ext/
Diffstat (limited to 'ext')
-rw-r--r-- | ext/.gitignore | 1 | ||||
-rw-r--r-- | ext/bignum/lib/Math/BigFloat/Trace.pm | 58 | ||||
-rw-r--r-- | ext/bignum/lib/Math/BigInt/Trace.pm | 47 | ||||
-rw-r--r-- | ext/bignum/lib/bigint.pm | 675 | ||||
-rw-r--r-- | ext/bignum/lib/bignum.pm | 681 | ||||
-rw-r--r-- | ext/bignum/lib/bigrat.pm | 566 | ||||
-rw-r--r-- | ext/bignum/t/big_e_pi.t | 23 | ||||
-rw-r--r-- | ext/bignum/t/bigexp.t | 26 | ||||
-rw-r--r-- | ext/bignum/t/bigint.t | 123 | ||||
-rw-r--r-- | ext/bignum/t/bignum.t | 94 | ||||
-rw-r--r-- | ext/bignum/t/bigrat.t | 98 | ||||
-rw-r--r-- | ext/bignum/t/bii_e_pi.t | 24 | ||||
-rw-r--r-- | ext/bignum/t/biinfnan.t | 22 | ||||
-rw-r--r-- | ext/bignum/t/bir_e_pi.t | 25 | ||||
-rw-r--r-- | ext/bignum/t/bn_lite.t | 30 | ||||
-rw-r--r-- | ext/bignum/t/bninfnan.t | 39 | ||||
-rw-r--r-- | ext/bignum/t/br_lite.t | 30 | ||||
-rw-r--r-- | ext/bignum/t/brinfnan.t | 22 | ||||
-rw-r--r-- | ext/bignum/t/in_effect.t | 42 | ||||
-rw-r--r-- | ext/bignum/t/infnan.inc | 35 | ||||
-rw-r--r-- | ext/bignum/t/option_a.t | 31 | ||||
-rw-r--r-- | ext/bignum/t/option_l.t | 54 | ||||
-rw-r--r-- | ext/bignum/t/option_p.t | 29 | ||||
-rw-r--r-- | ext/bignum/t/ratopt_a.t | 34 | ||||
-rw-r--r-- | ext/bignum/t/scope_f.t | 41 | ||||
-rw-r--r-- | ext/bignum/t/scope_i.t | 42 | ||||
-rw-r--r-- | ext/bignum/t/scope_r.t | 41 |
27 files changed, 2933 insertions, 0 deletions
diff --git a/ext/.gitignore b/ext/.gitignore index 4e99370073..47543e17bd 100644 --- a/ext/.gitignore +++ b/ext/.gitignore @@ -18,6 +18,7 @@ ppport.h /autodie/Makefile.PL /autouse/Makefile.PL /base/Makefile.PL +/bignum/Makefile.PL /B-Debug/Makefile.PL /B-Deparse/Makefile.PL /B-Lint/Makefile.PL diff --git a/ext/bignum/lib/Math/BigFloat/Trace.pm b/ext/bignum/lib/Math/BigFloat/Trace.pm new file mode 100644 index 0000000000..871b2a969e --- /dev/null +++ b/ext/bignum/lib/Math/BigFloat/Trace.pm @@ -0,0 +1,58 @@ +#!/usr/bin/perl -w + +package Math::BigFloat::Trace; + +require 5.005_02; +use strict; + +use Exporter; +use Math::BigFloat; +use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK + $accuracy $precision $round_mode $div_scale); + +@ISA = qw(Exporter Math::BigFloat); + +$VERSION = 0.01; + +use overload; # inherit overload from BigFloat + +# Globals +$accuracy = $precision = undef; +$round_mode = 'even'; +$div_scale = 40; + +sub new +{ + my $proto = shift; + my $class = ref($proto) || $proto; + + my $value = shift; + my $a = $accuracy; $a = $_[0] if defined $_[0]; + my $p = $precision; $p = $_[1] if defined $_[1]; + my $self = Math::BigFloat->new($value,$a,$p,$round_mode); + +# remember, downgrading may return a BigInt, so don't meddle with class +# bless $self,$class; + + print "MBF new '$value' => '$self' (",ref($self),")"; + return $self; +} + +sub import + { + print "MBF import ",join(' ',@_); + my $self = shift; + + # we catch the constants, the rest goes go BigFloat + my @a = (); + foreach (@_) + { + push @a, $_ if $_ ne ':constant'; + } + overload::constant float => sub { $self->new(shift); }; + + Math::BigFloat->import(@a); # need it for subclasses +# $self->export_to_level(1,$self,@_); # need this ? + } + +1; diff --git a/ext/bignum/lib/Math/BigInt/Trace.pm b/ext/bignum/lib/Math/BigInt/Trace.pm new file mode 100644 index 0000000000..4733d22634 --- /dev/null +++ b/ext/bignum/lib/Math/BigInt/Trace.pm @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w + +package Math::BigInt::Trace; + +require 5.005_02; +use strict; + +use Exporter; +use Math::BigInt; +use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK + $accuracy $precision $round_mode $div_scale); + +@ISA = qw(Exporter Math::BigInt); + +$VERSION = 0.01; + +use overload; # inherit overload from BigInt + +# Globals +$accuracy = $precision = undef; +$round_mode = 'even'; +$div_scale = 40; + +sub new +{ + my $proto = shift; + my $class = ref($proto) || $proto; + + my $value = shift; + my $a = $accuracy; $a = $_[0] if defined $_[0]; + my $p = $precision; $p = $_[1] if defined $_[1]; + my $self = Math::BigInt->new($value,$a,$p,$round_mode); + bless $self,$class; + print "MBI new '$value' => '$self' (",ref($self),")"; + return $self; +} + +sub import + { + print "MBI import ",join(' ',@_); + my $self = shift; + Math::BigInt::import($self,@_); # need it for subclasses +# $self->export_to_level(1,$self,@_); # need this ? + @_ = (); + } + +1; diff --git a/ext/bignum/lib/bigint.pm b/ext/bignum/lib/bigint.pm new file mode 100644 index 0000000000..e143af61d0 --- /dev/null +++ b/ext/bignum/lib/bigint.pm @@ -0,0 +1,675 @@ +package bigint; +use 5.006; + +$VERSION = '0.23'; +use Exporter; +@ISA = qw( Exporter ); +@EXPORT_OK = qw( PI e bpi bexp ); +@EXPORT = qw( inf NaN ); + +use strict; +use overload; + +############################################################################## + +# These are all alike, and thus faked by AUTOLOAD + +my @faked = qw/round_mode accuracy precision div_scale/; +use vars qw/$VERSION $AUTOLOAD $_lite/; # _lite for testsuite + +sub AUTOLOAD + { + my $name = $AUTOLOAD; + + $name =~ s/.*:://; # split package + no strict 'refs'; + foreach my $n (@faked) + { + if ($n eq $name) + { + *{"bigint::$name"} = sub + { + my $self = shift; + no strict 'refs'; + if (defined $_[0]) + { + return Math::BigInt->$name($_[0]); + } + return Math::BigInt->$name(); + }; + return &$name; + } + } + + # delayed load of Carp and avoid recursion + require Carp; + Carp::croak ("Can't call bigint\-\>$name, not a valid method"); + } + +sub upgrade + { + $Math::BigInt::upgrade; + } + +sub _binary_constant + { + # this takes a binary/hexadecimal/octal constant string and returns it + # as string suitable for new. Basically it converts octal to decimal, and + # passes every thing else unmodified back. + my $string = shift; + + return Math::BigInt->new($string) if $string =~ /^0[bx]/; + + # so it must be an octal constant + Math::BigInt->from_oct($string); + } + +sub _float_constant + { + # this takes a floating point constant string and returns it truncated to + # integer. For instance, '4.5' => '4', '1.234e2' => '123' etc + my $float = shift; + + # some simple cases first + return $float if ($float =~ /^[+-]?[0-9]+$/); # '+123','-1','0' etc + return $float + if ($float =~ /^[+-]?[0-9]+\.?[eE]\+?[0-9]+$/); # 123e2, 123.e+2 + return '0' if ($float =~ /^[+-]?[0]*\.[0-9]+$/); # .2, 0.2, -.1 + if ($float =~ /^[+-]?[0-9]+\.[0-9]*$/) # 1., 1.23, -1.2 etc + { + $float =~ s/\..*//; + return $float; + } + my ($mis,$miv,$mfv,$es,$ev) = Math::BigInt::_split($float); + return $float if !defined $mis; # doesn't look like a number to me + my $ec = int($$ev); + my $sign = $$mis; $sign = '' if $sign eq '+'; + if ($$es eq '-') + { + # ignore fraction part entirely + if ($ec >= length($$miv)) # 123.23E-4 + { + return '0'; + } + return $sign . substr ($$miv,0,length($$miv)-$ec); # 1234.45E-2 = 12 + } + # xE+y + if ($ec >= length($$mfv)) + { + $ec -= length($$mfv); + return $sign.$$miv.$$mfv if $ec == 0; # 123.45E+2 => 12345 + return $sign.$$miv.$$mfv.'E'.$ec; # 123.45e+3 => 12345e1 + } + $mfv = substr($$mfv,0,$ec); + $sign.$$miv.$mfv; # 123.45e+1 => 1234 + } + +sub unimport + { + $^H{bigint} = undef; # no longer in effect + overload::remove_constant('binary','','float','','integer'); + } + +sub in_effect + { + my $level = shift || 0; + my $hinthash = (caller($level))[10]; + $hinthash->{bigint}; + } + +############################################################################# +# the following two routines are for "use bigint qw/hex oct/;": + +sub _hex_global + { + my $i = $_[0]; + $i = '0x'.$i unless $i =~ /^0x/; + Math::BigInt->new($i); + } + +sub _oct_global + { + my $i = $_[0]; + return Math::BigInt->from_oct($i) if $i =~ /^0[0-7]/; + Math::BigInt->new($i); + } + +############################################################################# +# the following two routines are for Perl 5.9.4 or later and are lexical + +sub _hex + { + return CORE::hex($_[0]) unless in_effect(1); + my $i = $_[0]; + $i = '0x'.$i unless $i =~ /^0x/; + Math::BigInt->new($i); + } + +sub _oct + { + return CORE::oct($_[0]) unless in_effect(1); + my $i = $_[0]; + return Math::BigInt->from_oct($i) if $i =~ /^0[0-7]/; + Math::BigInt->new($i); + } + +sub import + { + my $self = shift; + + $^H{bigint} = 1; # we are in effect + + my ($hex,$oct); + # for newer Perls always override hex() and oct() with a lexical version: + if ($] > 5.009004) + { + $oct = \&_oct; + $hex = \&_hex; + } + # some defaults + my $lib = ''; my $lib_kind = 'try'; + + my @import = ( ':constant' ); # drive it w/ constant + my @a = @_; my $l = scalar @_; my $j = 0; + my ($ver,$trace); # version? trace? + my ($a,$p); # accuracy, precision + for ( my $i = 0; $i < $l ; $i++,$j++ ) + { + if ($_[$i] =~ /^(l|lib|try|only)$/) + { + # this causes a different low lib to take care... + $lib_kind = $1; $lib_kind = 'lib' if $lib_kind eq 'l'; + $lib = $_[$i+1] || ''; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(a|accuracy)$/) + { + $a = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(p|precision)$/) + { + $p = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(v|version)$/) + { + $ver = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] =~ /^(t|trace)$/) + { + $trace = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] eq 'hex') + { + splice @a, $j, 1; $j --; + $hex = \&_hex_global; + } + elsif ($_[$i] eq 'oct') + { + splice @a, $j, 1; $j --; + $oct = \&_oct_global; + } + elsif ($_[$i] !~ /^(PI|e|bpi|bexp)\z/) + { + die ("unknown option $_[$i]"); + } + } + my $class; + $_lite = 0; # using M::BI::L ? + if ($trace) + { + require Math::BigInt::Trace; $class = 'Math::BigInt::Trace'; + } + else + { + # see if we can find Math::BigInt::Lite + if (!defined $a && !defined $p) # rounding won't work to well + { + eval 'require Math::BigInt::Lite;'; + if ($@ eq '') + { + @import = ( ); # :constant in Lite, not MBI + Math::BigInt::Lite->import( ':constant' ); + $_lite= 1; # signal okay + } + } + require Math::BigInt if $_lite == 0; # not already loaded? + $class = 'Math::BigInt'; # regardless of MBIL or not + } + push @import, $lib_kind => $lib if $lib ne ''; + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import); + + bigint->accuracy($a) if defined $a; + bigint->precision($p) if defined $p; + if ($ver) + { + print "bigint\t\t\t v$VERSION\n"; + print "Math::BigInt::Lite\t v$Math::BigInt::Lite::VERSION\n" if $_lite; + print "Math::BigInt\t\t v$Math::BigInt::VERSION"; + my $config = Math::BigInt->config(); + print " lib => $config->{lib} v$config->{lib_version}\n"; + exit; + } + # we take care of floating point constants, since BigFloat isn't available + # and BigInt doesn't like them: + overload::constant float => sub { Math::BigInt->new( _float_constant(shift) ); }; + # Take care of octal/hexadecimal constants + overload::constant binary => sub { _binary_constant(shift) }; + + # if another big* was already loaded: + my ($package) = caller(); + + no strict 'refs'; + if (!defined *{"${package}::inf"}) + { + $self->export_to_level(1,$self,@a); # export inf and NaN, e and PI + } + { + no warnings 'redefine'; + *CORE::GLOBAL::oct = $oct if $oct; + *CORE::GLOBAL::hex = $hex if $hex; + } + } + +sub inf () { Math::BigInt::binf(); } +sub NaN () { Math::BigInt::bnan(); } + +sub PI () { Math::BigInt->new(3); } +sub e () { Math::BigInt->new(2); } +sub bpi ($) { Math::BigInt->new(3); } +sub bexp ($$) { my $x = Math::BigInt->new($_[0]); $x->bexp($_[1]); } + +1; + +__END__ + +=head1 NAME + +bigint - Transparent BigInteger support for Perl + +=head1 SYNOPSIS + + use bigint; + + $x = 2 + 4.5,"\n"; # BigInt 6 + print 2 ** 512,"\n"; # really is what you think it is + print inf + 42,"\n"; # inf + print NaN * 7,"\n"; # NaN + print hex("0x1234567890123490"),"\n"; # Perl v5.9.4 or later + + { + no bigint; + print 2 ** 256,"\n"; # a normal Perl scalar now + } + + # Note that this will be global: + use bigint qw/hex oct/; + print hex("0x1234567890123490"),"\n"; + print oct("01234567890123490"),"\n"; + +=head1 DESCRIPTION + +All operators (including basic math operations) are overloaded. Integer +constants are created as proper BigInts. + +Floating point constants are truncated to integer. All parts and results of +expressions are also truncated. + +Unlike L<integer>, this pragma creates integer constants that are only +limited in their size by the available memory and CPU time. + +=head2 use integer vs. use bigint + +There is one small difference between C<use integer> and C<use bigint>: the +former will not affect assignments to variables and the return value of +some functions. C<bigint> truncates these results to integer too: + + # perl -Minteger -wle 'print 3.2' + 3.2 + # perl -Minteger -wle 'print 3.2 + 0' + 3 + # perl -Mbigint -wle 'print 3.2' + 3 + # perl -Mbigint -wle 'print 3.2 + 0' + 3 + + # perl -Mbigint -wle 'print exp(1) + 0' + 2 + # perl -Mbigint -wle 'print exp(1)' + 2 + # perl -Minteger -wle 'print exp(1)' + 2.71828182845905 + # perl -Minteger -wle 'print exp(1) + 0' + 2 + +In practice this makes seldom a difference as B<parts and results> of +expressions will be truncated anyway, but this can, for instance, affect the +return value of subroutines: + + sub three_integer { use integer; return 3.2; } + sub three_bigint { use bigint; return 3.2; } + + print three_integer(), " ", three_bigint(),"\n"; # prints "3.2 3" + +=head2 Options + +bigint recognizes some options that can be passed while loading it via use. +The options can (currently) be either a single letter form, or the long form. +The following options exist: + +=over 2 + +=item a or accuracy + +This sets the accuracy for all math operations. The argument must be greater +than or equal to zero. See Math::BigInt's bround() function for details. + + perl -Mbigint=a,2 -le 'print 12345+1' + +Note that setting precision and accurary at the same time is not possible. + +=item p or precision + +This sets the precision for all math operations. The argument can be any +integer. Negative values mean a fixed number of digits after the dot, and +are <B>ignored</B> since all operations happen in integer space. +A positive value rounds to this digit left from the dot. 0 or 1 mean round to +integer and are ignore like negative values. + +See Math::BigInt's bfround() function for details. + + perl -Mbignum=p,5 -le 'print 123456789+123' + +Note that setting precision and accurary at the same time is not possible. + +=item t or trace + +This enables a trace mode and is primarily for debugging bigint or +Math::BigInt. + +=item hex + +Override the built-in hex() method with a version that can handle big +integers. Note that under Perl v5.9.4 or ealier, this will be global +and cannot be disabled with "no bigint;". + +=item oct + +Override the built-in oct() method with a version that can handle big +integers. Note that under Perl v5.9.4 or ealier, this will be global +and cannot be disabled with "no bigint;". + +=item l, lib, try or only + +Load a different math lib, see L<Math Library>. + + perl -Mbigint=lib,GMP -e 'print 2 ** 512' + perl -Mbigint=try,GMP -e 'print 2 ** 512' + perl -Mbigint=only,GMP -e 'print 2 ** 512' + +Currently there is no way to specify more than one library on the command +line. This means the following does not work: + + perl -Mbignum=l,GMP,Pari -e 'print 2 ** 512' + +This will be hopefully fixed soon ;) + +=item v or version + +This prints out the name and version of all modules used and then exits. + + perl -Mbigint=v + +=back + +=head2 Math Library + +Math with the numbers is done (by default) by a module called +Math::BigInt::Calc. This is equivalent to saying: + + use bigint lib => 'Calc'; + +You can change this by using: + + use bignum lib => 'GMP'; + +The following would first try to find Math::BigInt::Foo, then +Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: + + use bigint lib => 'Foo,Math::BigInt::Bar'; + +Using C<lib> warns if none of the specified libraries can be found and +L<Math::BigInt> did fall back to one of the default libraries. +To supress this warning, use C<try> instead: + + use bignum try => 'GMP'; + +If you want the code to die instead of falling back, use C<only> instead: + + use bignum only => 'GMP'; + +Please see respective module documentation for further details. + +=head2 Internal Format + +The numbers are stored as objects, and their internals might change at anytime, +especially between math operations. The objects also might belong to different +classes, like Math::BigInt, or Math::BigInt::Lite. Mixing them together, even +with normal scalars is not extraordinary, but normal and expected. + +You should not depend on the internal format, all accesses must go through +accessor methods. E.g. looking at $x->{sign} is not a good idea since there +is no guaranty that the object in question has such a hash key, nor is a hash +underneath at all. + +=head2 Sign + +The sign is either '+', '-', 'NaN', '+inf' or '-inf'. +You can access it with the sign() method. + +A sign of 'NaN' is used to represent the result when input arguments are not +numbers or as a result of 0/0. '+inf' and '-inf' represent plus respectively +minus infinity. You will get '+inf' when dividing a positive number by 0, and +'-inf' when dividing any negative number by 0. + +=head2 Methods + +Since all numbers are now objects, you can use all functions that are part of +the BigInt API. You can only use the bxxx() notation, and not the fxxx() +notation, though. + +=over 2 + +=item inf() + +A shortcut to return Math::BigInt->binf(). Useful because Perl does not always +handle bareword C<inf> properly. + +=item NaN() + +A shortcut to return Math::BigInt->bnan(). Useful because Perl does not always +handle bareword C<NaN> properly. + +=item e + + # perl -Mbigint=e -wle 'print e' + +Returns Euler's number C<e>, aka exp(1). Note that under bigint, this is +truncated to an integer, and hence simple '2'. + +=item PI + + # perl -Mbigint=PI -wle 'print PI' + +Returns PI. Note that under bigint, this is truncated to an integer, and hence +simple '3'. + +=item bexp() + + bexp($power,$accuracy); + +Returns Euler's number C<e> raised to the appropriate power, to +the wanted accuracy. + +Note that under bigint, the result is truncated to an integer. + +Example: + + # perl -Mbigint=bexp -wle 'print bexp(1,80)' + +=item bpi() + + bpi($accuracy); + +Returns PI to the wanted accuracy. Note that under bigint, this is truncated +to an integer, and hence simple '3'. + +Example: + + # perl -Mbigint=bpi -wle 'print bpi(80)' + +=item upgrade() + +Return the class that numbers are upgraded to, is in fact returning +C<$Math::BigInt::upgrade>. + +=item in_effect() + + use bigint; + + print "in effect\n" if bigint::in_effect; # true + { + no bigint; + print "in effect\n" if bigint::in_effect; # false + } + +Returns true or false if C<bigint> is in effect in the current scope. + +This method only works on Perl v5.9.4 or later. + +=back + +=head2 MATH LIBRARY + +Math with the numbers is done (by default) by a module called + +=head2 Caveat + +But a warning is in order. When using the following to make a copy of a number, +only a shallow copy will be made. + + $x = 9; $y = $x; + $x = $y = 7; + +Using the copy or the original with overloaded math is okay, e.g. the +following work: + + $x = 9; $y = $x; + print $x + 1, " ", $y,"\n"; # prints 10 9 + +but calling any method that modifies the number directly will result in +B<both> the original and the copy being destroyed: + + $x = 9; $y = $x; + print $x->badd(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->binc(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->bmul(2), " ", $y,"\n"; # prints 18 18 + +Using methods that do not modify, but testthe contents works: + + $x = 9; $y = $x; + $z = 9 if $x->is_zero(); # works fine + +See the documentation about the copy constructor and C<=> in overload, as +well as the documentation in BigInt for further details. + +=head1 CAVAETS + +=over 2 + +=item in_effect() + +This method only works on Perl v5.9.4 or later. + +=item hex()/oct() + +C<bigint> overrides these routines with versions that can also handle +big integer values. Under Perl prior to version v5.9.4, however, this +will not happen unless you specifically ask for it with the two +import tags "hex" and "oct" - and then it will be global and cannot be +disabled inside a scope with "no bigint": + + use bigint qw/hex oct/; + + print hex("0x1234567890123456"); + { + no bigint; + print hex("0x1234567890123456"); + } + +The second call to hex() will warn about a non-portable constant. + +Compare this to: + + use bigint; + + # will warn only under Perl older than v5.9.4 + print hex("0x1234567890123456"); + +=back + +=head1 MODULES USED + +C<bigint> is just a thin wrapper around various modules of the Math::BigInt +family. Think of it as the head of the family, who runs the shop, and orders +the others to do the work. + +The following modules are currently used by bigint: + + Math::BigInt::Lite (for speed, and only if it is loadable) + Math::BigInt + +=head1 EXAMPLES + +Some cool command line examples to impress the Python crowd ;) You might want +to compare them to the results under -Mbignum or -Mbigrat: + + perl -Mbigint -le 'print sqrt(33)' + perl -Mbigint -le 'print 2*255' + perl -Mbigint -le 'print 4.5+2*255' + perl -Mbigint -le 'print 3/7 + 5/7 + 8/3' + perl -Mbigint -le 'print 123->is_odd()' + perl -Mbigint -le 'print log(2)' + perl -Mbigint -le 'print 2 ** 0.5' + perl -Mbigint=a,65 -le 'print 2 ** 0.2' + perl -Mbignum=a,65,l,GMP -le 'print 7 ** 7777' + +=head1 LICENSE + +This program is free software; you may redistribute it and/or modify it under +the same terms as Perl itself. + +=head1 SEE ALSO + +Especially L<bigrat> as in C<perl -Mbigrat -le 'print 1/3+1/4'> and +L<bignum> as in C<perl -Mbignum -le 'print sqrt(2)'>. + +L<Math::BigInt>, L<Math::BigRat> and L<Math::Big> as well +as L<Math::BigInt::BitVect>, L<Math::BigInt::Pari> and L<Math::BigInt::GMP>. + +=head1 AUTHORS + +(C) by Tels L<http://bloodgate.com/> in early 2002 - 2007. + +=cut diff --git a/ext/bignum/lib/bignum.pm b/ext/bignum/lib/bignum.pm new file mode 100644 index 0000000000..2e075c8b5a --- /dev/null +++ b/ext/bignum/lib/bignum.pm @@ -0,0 +1,681 @@ +package bignum; +use 5.006; + +$VERSION = '0.23'; +use Exporter; +@ISA = qw( bigint ); +@EXPORT_OK = qw( PI e bexp bpi ); +@EXPORT = qw( inf NaN ); + +use strict; +use overload; +require bigint; # no "use" to avoid import being called + +############################################################################## + +BEGIN + { + *inf = \&bigint::inf; + *NaN = \&bigint::NaN; + } + +# These are all alike, and thus faked by AUTOLOAD + +my @faked = qw/round_mode accuracy precision div_scale/; +use vars qw/$VERSION $AUTOLOAD $_lite/; # _lite for testsuite + +sub AUTOLOAD + { + my $name = $AUTOLOAD; + + $name =~ s/.*:://; # split package + no strict 'refs'; + foreach my $n (@faked) + { + if ($n eq $name) + { + *{"bignum::$name"} = sub + { + my $self = shift; + no strict 'refs'; + if (defined $_[0]) + { + Math::BigInt->$name($_[0]); + return Math::BigFloat->$name($_[0]); + } + return Math::BigInt->$name(); + }; + return &$name; + } + } + + # delayed load of Carp and avoid recursion + require Carp; + Carp::croak ("Can't call bignum\-\>$name, not a valid method"); + } + +sub unimport + { + $^H{bignum} = undef; # no longer in effect + overload::remove_constant('binary','','float','','integer'); + } + +sub in_effect + { + my $level = shift || 0; + my $hinthash = (caller($level))[10]; + $hinthash->{bignum}; + } + +############################################################################# +# the following two routines are for Perl 5.9.4 or later and are lexical + +sub _hex + { + return CORE::hex($_[0]) unless in_effect(1); + my $i = $_[0]; + $i = '0x'.$i unless $i =~ /^0x/; + Math::BigInt->new($i); + } + +sub _oct + { + return CORE::oct($_[0]) unless in_effect(1); + my $i = $_[0]; + return Math::BigInt->from_oct($i) if $i =~ /^0[0-7]/; + Math::BigInt->new($i); + } + +sub import + { + my $self = shift; + + $^H{bignum} = 1; # we are in effect + + my ($hex,$oct); + + # for newer Perls override hex() and oct() with a lexical version: + if ($] > 5.009003) + { + $hex = \&_hex; + $oct = \&_oct; + } + + # some defaults + my $lib = ''; my $lib_kind = 'try'; + my $upgrade = 'Math::BigFloat'; + my $downgrade = 'Math::BigInt'; + + my @import = ( ':constant' ); # drive it w/ constant + my @a = @_; my $l = scalar @_; my $j = 0; + my ($ver,$trace); # version? trace? + my ($a,$p); # accuracy, precision + for ( my $i = 0; $i < $l ; $i++,$j++ ) + { + if ($_[$i] eq 'upgrade') + { + # this causes upgrading + $upgrade = $_[$i+1]; # or undef to disable + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] eq 'downgrade') + { + # this causes downgrading + $downgrade = $_[$i+1]; # or undef to disable + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(l|lib|try|only)$/) + { + # this causes a different low lib to take care... + $lib_kind = $1; $lib_kind = 'lib' if $lib_kind eq 'l'; + $lib = $_[$i+1] || ''; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(a|accuracy)$/) + { + $a = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(p|precision)$/) + { + $p = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(v|version)$/) + { + $ver = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] =~ /^(t|trace)$/) + { + $trace = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] eq 'hex') + { + splice @a, $j, 1; $j --; + $hex = \&bigint::_hex_global; + } + elsif ($_[$i] eq 'oct') + { + splice @a, $j, 1; $j --; + $oct = \&bigint::_oct_global; + } + elsif ($_[$i] !~ /^(PI|e|bexp|bpi)\z/) + { + die ("unknown option $_[$i]"); + } + } + my $class; + $_lite = 0; # using M::BI::L ? + if ($trace) + { + require Math::BigInt::Trace; $class = 'Math::BigInt::Trace'; + $upgrade = 'Math::BigFloat::Trace'; + } + else + { + # see if we can find Math::BigInt::Lite + if (!defined $a && !defined $p) # rounding won't work to well + { + eval 'require Math::BigInt::Lite;'; + if ($@ eq '') + { + @import = ( ); # :constant in Lite, not MBI + Math::BigInt::Lite->import( ':constant' ); + $_lite= 1; # signal okay + } + } + require Math::BigInt if $_lite == 0; # not already loaded? + $class = 'Math::BigInt'; # regardless of MBIL or not + } + push @import, $lib_kind => $lib if $lib ne ''; + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import, upgrade => $upgrade); + + if ($trace) + { + require Math::BigFloat::Trace; $class = 'Math::BigFloat::Trace'; + $downgrade = 'Math::BigInt::Trace'; + } + else + { + require Math::BigFloat; $class = 'Math::BigFloat'; + } + $class->import(':constant','downgrade',$downgrade); + + bignum->accuracy($a) if defined $a; + bignum->precision($p) if defined $p; + if ($ver) + { + print "bignum\t\t\t v$VERSION\n"; + print "Math::BigInt::Lite\t v$Math::BigInt::Lite::VERSION\n" if $_lite; + print "Math::BigInt\t\t v$Math::BigInt::VERSION"; + my $config = Math::BigInt->config(); + print " lib => $config->{lib} v$config->{lib_version}\n"; + print "Math::BigFloat\t\t v$Math::BigFloat::VERSION\n"; + exit; + } + + # Take care of octal/hexadecimal constants + overload::constant binary => sub { bigint::_binary_constant(shift) }; + + # if another big* was already loaded: + my ($package) = caller(); + + no strict 'refs'; + if (!defined *{"${package}::inf"}) + { + $self->export_to_level(1,$self,@a); # export inf and NaN + } + { + no warnings 'redefine'; + *CORE::GLOBAL::oct = $oct if $oct; + *CORE::GLOBAL::hex = $hex if $hex; + } + } + +sub PI () { Math::BigFloat->new('3.141592653589793238462643383279502884197'); } +sub e () { Math::BigFloat->new('2.718281828459045235360287471352662497757'); } +sub bpi ($) { Math::BigFloat::bpi(@_); } +sub bexp ($$) { my $x = Math::BigFloat->new($_[0]); $x->bexp($_[1]); } + +1; + +__END__ + +=head1 NAME + +bignum - Transparent BigNumber support for Perl + +=head1 SYNOPSIS + + use bignum; + + $x = 2 + 4.5,"\n"; # BigFloat 6.5 + print 2 ** 512 * 0.1,"\n"; # really is what you think it is + print inf * inf,"\n"; # prints inf + print NaN * 3,"\n"; # prints NaN + + { + no bignum; + print 2 ** 256,"\n"; # a normal Perl scalar now + } + + # for older Perls, note that this will be global: + use bignum qw/hex oct/; + print hex("0x1234567890123490"),"\n"; + print oct("01234567890123490"),"\n"; + +=head1 DESCRIPTION + +All operators (including basic math operations) are overloaded. Integer and +floating-point constants are created as proper BigInts or BigFloats, +respectively. + +If you do + + use bignum; + +at the top of your script, Math::BigFloat and Math::BigInt will be loaded +and any constant number will be converted to an object (Math::BigFloat for +floats like 3.1415 and Math::BigInt for integers like 1234). + +So, the following line: + + $x = 1234; + +creates actually a Math::BigInt and stores a reference to in $x. +This happens transparently and behind your back, so to speak. + +You can see this with the following: + + perl -Mbignum -le 'print ref(1234)' + +Don't worry if it says Math::BigInt::Lite, bignum and friends will use Lite +if it is installed since it is faster for some operations. It will be +automatically upgraded to BigInt whenever necessary: + + perl -Mbignum -le 'print ref(2**255)' + +This also means it is a bad idea to check for some specific package, since +the actual contents of $x might be something unexpected. Due to the +transparent way of bignum C<ref()> should not be necessary, anyway. + +Since Math::BigInt and BigFloat also overload the normal math operations, +the following line will still work: + + perl -Mbignum -le 'print ref(1234+1234)' + +Since numbers are actually objects, you can call all the usual methods from +BigInt/BigFloat on them. This even works to some extent on expressions: + + perl -Mbignum -le '$x = 1234; print $x->bdec()' + perl -Mbignum -le 'print 1234->copy()->binc();' + perl -Mbignum -le 'print 1234->copy()->binc->badd(6);' + perl -Mbignum -le 'print +(1234)->copy()->binc()' + +(Note that print doesn't do what you expect if the expression starts with +'(' hence the C<+>) + +You can even chain the operations together as usual: + + perl -Mbignum -le 'print 1234->copy()->binc->badd(6);' + 1241 + +Under bignum (or bigint or bigrat), Perl will "upgrade" the numbers +appropriately. This means that: + + perl -Mbignum -le 'print 1234+4.5' + 1238.5 + +will work correctly. These mixed cases don't do always work when using +Math::BigInt or Math::BigFloat alone, or at least not in the way normal Perl +scalars work. + +If you do want to work with large integers like under C<use integer;>, try +C<use bigint;>: + + perl -Mbigint -le 'print 1234.5+4.5' + 1238 + +There is also C<use bigrat;> which gives you big rationals: + + perl -Mbigrat -le 'print 1234+4.1' + 12381/10 + +The entire upgrading/downgrading is still experimental and might not work +as you expect or may even have bugs. You might get errors like this: + + Can't use an undefined value as an ARRAY reference at + /usr/local/lib/perl5/5.8.0/Math/BigInt/Calc.pm line 864 + +This means somewhere a routine got a BigFloat/Lite but expected a BigInt (or +vice versa) and the upgrade/downgrad path was missing. This is a bug, please +report it so that we can fix it. + +You might consider using just Math::BigInt or Math::BigFloat, since they +allow you finer control over what get's done in which module/space. For +instance, simple loop counters will be Math::BigInts under C<use bignum;> and +this is slower than keeping them as Perl scalars: + + perl -Mbignum -le 'for ($i = 0; $i < 10; $i++) { print ref($i); }' + +Please note the following does not work as expected (prints nothing), since +overloading of '..' is not yet possible in Perl (as of v5.8.0): + + perl -Mbignum -le 'for (1..2) { print ref($_); }' + +=head2 Options + +bignum recognizes some options that can be passed while loading it via use. +The options can (currently) be either a single letter form, or the long form. +The following options exist: + +=over 2 + +=item a or accuracy + +This sets the accuracy for all math operations. The argument must be greater +than or equal to zero. See Math::BigInt's bround() function for details. + + perl -Mbignum=a,50 -le 'print sqrt(20)' + +Note that setting precision and accurary at the same time is not possible. + +=item p or precision + +This sets the precision for all math operations. The argument can be any +integer. Negative values mean a fixed number of digits after the dot, while +a positive value rounds to this digit left from the dot. 0 or 1 mean round to +integer. See Math::BigInt's bfround() function for details. + + perl -Mbignum=p,-50 -le 'print sqrt(20)' + +Note that setting precision and accurary at the same time is not possible. + +=item t or trace + +This enables a trace mode and is primarily for debugging bignum or +Math::BigInt/Math::BigFloat. + +=item l or lib + +Load a different math lib, see L<MATH LIBRARY>. + + perl -Mbignum=l,GMP -e 'print 2 ** 512' + +Currently there is no way to specify more than one library on the command +line. This means the following does not work: + + perl -Mbignum=l,GMP,Pari -e 'print 2 ** 512' + +This will be hopefully fixed soon ;) + +=item hex + +Override the built-in hex() method with a version that can handle big +integers. Note that under Perl older than v5.9.4, this will be global +and cannot be disabled with "no bigint;". + +=item oct + +Override the built-in oct() method with a version that can handle big +integers. Note that under Perl older than v5.9.4, this will be global +and cannot be disabled with "no bigint;". + +=item v or version + +This prints out the name and version of all modules used and then exits. + + perl -Mbignum=v + +=back + +=head2 Methods + +Beside import() and AUTOLOAD() there are only a few other methods. + +Since all numbers are now objects, you can use all functions that are part of +the BigInt or BigFloat API. It is wise to use only the bxxx() notation, and not +the fxxx() notation, though. This makes it possible that the underlying object +might morph into a different class than BigFloat. + +=head2 Caveats + +But a warning is in order. When using the following to make a copy of a number, +only a shallow copy will be made. + + $x = 9; $y = $x; + $x = $y = 7; + +If you want to make a real copy, use the following: + + $y = $x->copy(); + +Using the copy or the original with overloaded math is okay, e.g. the +following work: + + $x = 9; $y = $x; + print $x + 1, " ", $y,"\n"; # prints 10 9 + +but calling any method that modifies the number directly will result in +B<both> the original and the copy being destroyed: + + $x = 9; $y = $x; + print $x->badd(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->binc(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->bmul(2), " ", $y,"\n"; # prints 18 18 + +Using methods that do not modify, but test the contents works: + + $x = 9; $y = $x; + $z = 9 if $x->is_zero(); # works fine + +See the documentation about the copy constructor and C<=> in overload, as +well as the documentation in BigInt for further details. + +=over 2 + +=item inf() + +A shortcut to return Math::BigInt->binf(). Useful because Perl does not always +handle bareword C<inf> properly. + +=item NaN() + +A shortcut to return Math::BigInt->bnan(). Useful because Perl does not always +handle bareword C<NaN> properly. + +=item e + + # perl -Mbignum=e -wle 'print e' + +Returns Euler's number C<e>, aka exp(1). + +=item PI() + + # perl -Mbignum=PI -wle 'print PI' + +Returns PI. + +=item bexp() + + bexp($power,$accuracy); + +Returns Euler's number C<e> raised to the appropriate power, to +the wanted accuracy. + +Example: + + # perl -Mbignum=bexp -wle 'print bexp(1,80)' + +=item bpi() + + bpi($accuracy); + +Returns PI to the wanted accuracy. + +Example: + + # perl -Mbignum=bpi -wle 'print bpi(80)' + +=item upgrade() + +Return the class that numbers are upgraded to, is in fact returning +C<$Math::BigInt::upgrade>. + +=item in_effect() + + use bignum; + + print "in effect\n" if bignum::in_effect; # true + { + no bignum; + print "in effect\n" if bignum::in_effect; # false + } + +Returns true or false if C<bignum> is in effect in the current scope. + +This method only works on Perl v5.9.4 or later. + +=back + +=head2 Math Library + +Math with the numbers is done (by default) by a module called +Math::BigInt::Calc. This is equivalent to saying: + + use bignum lib => 'Calc'; + +You can change this by using: + + use bignum lib => 'GMP'; + +The following would first try to find Math::BigInt::Foo, then +Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: + + use bignum lib => 'Foo,Math::BigInt::Bar'; + +Please see respective module documentation for further details. + +Using C<lib> warns if none of the specified libraries can be found and +L<Math::BigInt> did fall back to one of the default libraries. +To supress this warning, use C<try> instead: + + use bignum try => 'GMP'; + +If you want the code to die instead of falling back, use C<only> instead: + + use bignum only => 'GMP'; + +=head2 INTERNAL FORMAT + +The numbers are stored as objects, and their internals might change at anytime, +especially between math operations. The objects also might belong to different +classes, like Math::BigInt, or Math::BigFLoat. Mixing them together, even +with normal scalars is not extraordinary, but normal and expected. + +You should not depend on the internal format, all accesses must go through +accessor methods. E.g. looking at $x->{sign} is not a bright idea since there +is no guaranty that the object in question has such a hashkey, nor is a hash +underneath at all. + +=head2 SIGN + +The sign is either '+', '-', 'NaN', '+inf' or '-inf' and stored seperately. +You can access it with the sign() method. + +A sign of 'NaN' is used to represent the result when input arguments are not +numbers or as a result of 0/0. '+inf' and '-inf' represent plus respectively +minus infinity. You will get '+inf' when dividing a positive number by 0, and +'-inf' when dividing any negative number by 0. + +=head1 CAVAETS + +=over 2 + +=item in_effect() + +This method only works on Perl v5.9.4 or later. + +=item hex()/oct() + +C<bigint> overrides these routines with versions that can also handle +big integer values. Under Perl prior to version v5.9.4, however, this +will not happen unless you specifically ask for it with the two +import tags "hex" and "oct" - and then it will be global and cannot be +disabled inside a scope with "no bigint": + + use bigint qw/hex oct/; + + print hex("0x1234567890123456"); + { + no bigint; + print hex("0x1234567890123456"); + } + +The second call to hex() will warn about a non-portable constant. + +Compare this to: + + use bigint; + + # will warn only under older than v5.9.4 + print hex("0x1234567890123456"); + +=back + +=head1 MODULES USED + +C<bignum> is just a thin wrapper around various modules of the Math::BigInt +family. Think of it as the head of the family, who runs the shop, and orders +the others to do the work. + +The following modules are currently used by bignum: + + Math::BigInt::Lite (for speed, and only if it is loadable) + Math::BigInt + Math::BigFloat + +=head1 EXAMPLES + +Some cool command line examples to impress the Python crowd ;) + + perl -Mbignum -le 'print sqrt(33)' + perl -Mbignum -le 'print 2*255' + perl -Mbignum -le 'print 4.5+2*255' + perl -Mbignum -le 'print 3/7 + 5/7 + 8/3' + perl -Mbignum -le 'print 123->is_odd()' + perl -Mbignum -le 'print log(2)' + perl -Mbignum -le 'print exp(1)' + perl -Mbignum -le 'print 2 ** 0.5' + perl -Mbignum=a,65 -le 'print 2 ** 0.2' + perl -Mbignum=a,65,l,GMP -le 'print 7 ** 7777' + +=head1 LICENSE + +This program is free software; you may redistribute it and/or modify it under +the same terms as Perl itself. + +=head1 SEE ALSO + +Especially L<bigrat> as in C<perl -Mbigrat -le 'print 1/3+1/4'>. + +L<Math::BigFloat>, L<Math::BigInt>, L<Math::BigRat> and L<Math::Big> as well +as L<Math::BigInt::BitVect>, L<Math::BigInt::Pari> and L<Math::BigInt::GMP>. + +=head1 AUTHORS + +(C) by Tels L<http://bloodgate.com/> in early 2002 - 2007. + +=cut diff --git a/ext/bignum/lib/bigrat.pm b/ext/bignum/lib/bigrat.pm new file mode 100644 index 0000000000..10e8057abd --- /dev/null +++ b/ext/bignum/lib/bigrat.pm @@ -0,0 +1,566 @@ +package bigrat; +use 5.006; + +$VERSION = '0.23'; +require Exporter; +@ISA = qw( bigint ); +@EXPORT_OK = qw( PI e bpi bexp ); +@EXPORT = qw( inf NaN ); + +use strict; +use overload; +require bigint; # no "use" to avoid callind import + +############################################################################## + +BEGIN + { + *inf = \&bigint::inf; + *NaN = \&bigint::NaN; + } + +# These are all alike, and thus faked by AUTOLOAD + +my @faked = qw/round_mode accuracy precision div_scale/; +use vars qw/$VERSION $AUTOLOAD $_lite/; # _lite for testsuite + +sub AUTOLOAD + { + my $name = $AUTOLOAD; + + $name =~ s/.*:://; # split package + no strict 'refs'; + foreach my $n (@faked) + { + if ($n eq $name) + { + *{"bigrat::$name"} = sub + { + my $self = shift; + no strict 'refs'; + if (defined $_[0]) + { + Math::BigInt->$name($_[0]); + Math::BigFloat->$name($_[0]); + return Math::BigRat->$name($_[0]); + } + return Math::BigInt->$name(); + }; + return &$name; + } + } + + # delayed load of Carp and avoid recursion + require Carp; + Carp::croak ("Can't call bigrat\-\>$name, not a valid method"); + } + +sub unimport + { + $^H{bigrat} = undef; # no longer in effect + overload::remove_constant('binary','','float','','integer'); + } + +sub in_effect + { + my $level = shift || 0; + my $hinthash = (caller($level))[10]; + $hinthash->{bigrat}; + } + +############################################################################# +# the following two routines are for Perl 5.9.4 or later and are lexical + +sub _hex + { + return CORE::hex($_[0]) unless in_effect(1); + my $i = $_[0]; + $i = '0x'.$i unless $i =~ /^0x/; + Math::BigInt->new($i); + } + +sub _oct + { + return CORE::oct($_[0]) unless in_effect(1); + my $i = $_[0]; + return Math::BigInt->from_oct($i) if $i =~ /^0[0-7]/; + Math::BigInt->new($i); + } + +sub import + { + my $self = shift; + + # see also bignum->import() for additional comments + + $^H{bigrat} = 1; # we are in effect + + my ($hex,$oct); + # for newer Perls always override hex() and oct() with a lexical version: + if ($] > 5.009004) + { + $oct = \&_oct; + $hex = \&_hex; + } + # some defaults + my $lib = ''; my $lib_kind = 'try'; my $upgrade = 'Math::BigFloat'; + + my @import = ( ':constant' ); # drive it w/ constant + my @a = @_; my $l = scalar @_; my $j = 0; + my ($a,$p); + my ($ver,$trace); # version? trace? + for ( my $i = 0; $i < $l ; $i++,$j++ ) + { + if ($_[$i] eq 'upgrade') + { + # this causes upgrading + $upgrade = $_[$i+1]; # or undef to disable + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; + } + elsif ($_[$i] =~ /^(l|lib|try|only)$/) + { + # this causes a different low lib to take care... + $lib_kind = $1; $lib_kind = 'lib' if $lib_kind eq 'l'; + $lib = $_[$i+1] || ''; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(a|accuracy)$/) + { + $a = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(p|precision)$/) + { + $p = $_[$i+1]; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; $i++; + } + elsif ($_[$i] =~ /^(v|version)$/) + { + $ver = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] =~ /^(t|trace)$/) + { + $trace = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] eq 'hex') + { + splice @a, $j, 1; $j --; + $hex = \&bigint::_hex_global; + } + elsif ($_[$i] eq 'oct') + { + splice @a, $j, 1; $j --; + $oct = \&bigint::_oct_global; + } + elsif ($_[$i] !~ /^(PI|e|bpi|bexp)\z/) + { + die ("unknown option $_[$i]"); + } + } + my $class; + $_lite = 0; # using M::BI::L ? + if ($trace) + { + require Math::BigInt::Trace; $class = 'Math::BigInt::Trace'; + $upgrade = 'Math::BigFloat::Trace'; + } + else + { + # see if we can find Math::BigInt::Lite + if (!defined $a && !defined $p) # rounding won't work to well + { + eval 'require Math::BigInt::Lite;'; + if ($@ eq '') + { + @import = ( ); # :constant in Lite, not MBI + Math::BigInt::Lite->import( ':constant' ); + $_lite= 1; # signal okay + } + } + require Math::BigInt if $_lite == 0; # not already loaded? + $class = 'Math::BigInt'; # regardless of MBIL or not + } + push @import, $lib_kind => $lib if $lib ne ''; + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import, upgrade => $upgrade); + + require Math::BigFloat; + Math::BigFloat->import( upgrade => 'Math::BigRat', ':constant' ); + require Math::BigRat; + + bigrat->accuracy($a) if defined $a; + bigrat->precision($p) if defined $p; + if ($ver) + { + print "bigrat\t\t\t v$VERSION\n"; + print "Math::BigInt::Lite\t v$Math::BigInt::Lite::VERSION\n" if $_lite; + print "Math::BigInt\t\t v$Math::BigInt::VERSION"; + my $config = Math::BigInt->config(); + print " lib => $config->{lib} v$config->{lib_version}\n"; + print "Math::BigFloat\t\t v$Math::BigFloat::VERSION\n"; + print "Math::BigRat\t\t v$Math::BigRat::VERSION\n"; + exit; + } + + # Take care of octal/hexadecimal constants + overload::constant binary => sub { bigint::_binary_constant(shift) }; + + # if another big* was already loaded: + my ($package) = caller(); + + no strict 'refs'; + if (!defined *{"${package}::inf"}) + { + $self->export_to_level(1,$self,@a); # export inf and NaN + } + { + no warnings 'redefine'; + *CORE::GLOBAL::oct = $oct if $oct; + *CORE::GLOBAL::hex = $hex if $hex; + } + } + +sub PI () { Math::BigFloat->new('3.141592653589793238462643383279502884197'); } +sub e () { Math::BigFloat->new('2.718281828459045235360287471352662497757'); } + +sub bpi ($) { local $Math::BigFloat::upgrade; Math::BigFloat::bpi(@_); } + +sub bexp ($$) + { + local $Math::BigFloat::upgrade; + my $x = Math::BigFloat->new($_[0]); $x->bexp($_[1]); + } + +1; + +__END__ + +=head1 NAME + +bigrat - Transparent BigNumber/BigRational support for Perl + +=head1 SYNOPSIS + + use bigrat; + + print 2 + 4.5,"\n"; # BigFloat 6.5 + print 1/3 + 1/4,"\n"; # produces 7/12 + + { + no bigrat; + print 1/3,"\n"; # 0.33333... + } + + # Note that this will make hex() and oct() be globally overriden: + use bigrat qw/hex oct/; + print hex("0x1234567890123490"),"\n"; + print oct("01234567890123490"),"\n"; + +=head1 DESCRIPTION + +All operators (including basic math operations) are overloaded. Integer and +floating-point constants are created as proper BigInts or BigFloats, +respectively. + +Other than L<bignum>, this module upgrades to Math::BigRat, meaning that +instead of 2.5 you will get 2+1/2 as output. + +=head2 Modules Used + +C<bigrat> is just a thin wrapper around various modules of the Math::BigInt +family. Think of it as the head of the family, who runs the shop, and orders +the others to do the work. + +The following modules are currently used by bignum: + + Math::BigInt::Lite (for speed, and only if it is loadable) + Math::BigInt + Math::BigFloat + Math::BigRat + +=head2 Math Library + +Math with the numbers is done (by default) by a module called +Math::BigInt::Calc. This is equivalent to saying: + + use bigrat lib => 'Calc'; + +You can change this by using: + + use bignum lib => 'GMP'; + +The following would first try to find Math::BigInt::Foo, then +Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc: + + use bigrat lib => 'Foo,Math::BigInt::Bar'; + +Using C<lib> warns if none of the specified libraries can be found and +L<Math::BigInt> did fall back to one of the default libraries. +To supress this warning, use C<try> instead: + + use bignum try => 'GMP'; + +If you want the code to die instead of falling back, use C<only> instead: + + use bignum only => 'GMP'; + +Please see respective module documentation for further details. + +=head2 Sign + +The sign is either '+', '-', 'NaN', '+inf' or '-inf'. + +A sign of 'NaN' is used to represent the result when input arguments are not +numbers or as a result of 0/0. '+inf' and '-inf' represent plus respectively +minus infinity. You will get '+inf' when dividing a positive number by 0, and +'-inf' when dividing any negative number by 0. + +=head2 Methods + +Since all numbers are not objects, you can use all functions that are part of +the BigInt or BigFloat API. It is wise to use only the bxxx() notation, and not +the fxxx() notation, though. This makes you independed on the fact that the +underlying object might morph into a different class than BigFloat. + +=over 2 + +=item inf() + +A shortcut to return Math::BigInt->binf(). Useful because Perl does not always +handle bareword C<inf> properly. + +=item NaN() + +A shortcut to return Math::BigInt->bnan(). Useful because Perl does not always +handle bareword C<NaN> properly. + +=item e + + # perl -Mbigrat=e -wle 'print e' + +Returns Euler's number C<e>, aka exp(1). + +=item PI + + # perl -Mbigrat=PI -wle 'print PI' + +Returns PI. + +=item bexp() + + bexp($power,$accuracy); + + +Returns Euler's number C<e> raised to the appropriate power, to +the wanted accuracy. + +Example: + + # perl -Mbigrat=bexp -wle 'print bexp(1,80)' + +=item bpi() + + bpi($accuracy); + +Returns PI to the wanted accuracy. + +Example: + + # perl -Mbigrat=bpi -wle 'print bpi(80)' + +=item upgrade() + +Return the class that numbers are upgraded to, is in fact returning +C<$Math::BigInt::upgrade>. + +=item in_effect() + + use bigrat; + + print "in effect\n" if bigrat::in_effect; # true + { + no bigrat; + print "in effect\n" if bigrat::in_effect; # false + } + +Returns true or false if C<bigrat> is in effect in the current scope. + +This method only works on Perl v5.9.4 or later. + +=back + +=head2 MATH LIBRARY + +Math with the numbers is done (by default) by a module called + +=head2 Cavaet + +But a warning is in order. When using the following to make a copy of a number, +only a shallow copy will be made. + + $x = 9; $y = $x; + $x = $y = 7; + +If you want to make a real copy, use the following: + + $y = $x->copy(); + +Using the copy or the original with overloaded math is okay, e.g. the +following work: + + $x = 9; $y = $x; + print $x + 1, " ", $y,"\n"; # prints 10 9 + +but calling any method that modifies the number directly will result in +B<both> the original and the copy being destroyed: + + $x = 9; $y = $x; + print $x->badd(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->binc(1), " ", $y,"\n"; # prints 10 10 + + $x = 9; $y = $x; + print $x->bmul(2), " ", $y,"\n"; # prints 18 18 + +Using methods that do not modify, but testthe contents works: + + $x = 9; $y = $x; + $z = 9 if $x->is_zero(); # works fine + +See the documentation about the copy constructor and C<=> in overload, as +well as the documentation in BigInt for further details. + +=head2 Options + +bignum recognizes some options that can be passed while loading it via use. +The options can (currently) be either a single letter form, or the long form. +The following options exist: + +=over 2 + +=item a or accuracy + +This sets the accuracy for all math operations. The argument must be greater +than or equal to zero. See Math::BigInt's bround() function for details. + + perl -Mbigrat=a,50 -le 'print sqrt(20)' + +Note that setting precision and accurary at the same time is not possible. + +=item p or precision + +This sets the precision for all math operations. The argument can be any +integer. Negative values mean a fixed number of digits after the dot, while +a positive value rounds to this digit left from the dot. 0 or 1 mean round to +integer. See Math::BigInt's bfround() function for details. + + perl -Mbigrat=p,-50 -le 'print sqrt(20)' + +Note that setting precision and accurary at the same time is not possible. + +=item t or trace + +This enables a trace mode and is primarily for debugging bignum or +Math::BigInt/Math::BigFloat. + +=item l or lib + +Load a different math lib, see L<MATH LIBRARY>. + + perl -Mbigrat=l,GMP -e 'print 2 ** 512' + +Currently there is no way to specify more than one library on the command +line. This means the following does not work: + + perl -Mbignum=l,GMP,Pari -e 'print 2 ** 512' + +This will be hopefully fixed soon ;) + +=item hex + +Override the built-in hex() method with a version that can handle big +integers. Note that under Perl v5.9.4 or ealier, this will be global +and cannot be disabled with "no bigint;". + +=item oct + +Override the built-in oct() method with a version that can handle big +integers. Note that under Perl v5.9.4 or ealier, this will be global +and cannot be disabled with "no bigint;". + +=item v or version + +This prints out the name and version of all modules used and then exits. + + perl -Mbigrat=v + +=back + +=head1 CAVAETS + +=over 2 + +=item in_effect() + +This method only works on Perl v5.9.4 or later. + +=item hex()/oct() + +C<bigint> overrides these routines with versions that can also handle +big integer values. Under Perl prior to version v5.9.4, however, this +will not happen unless you specifically ask for it with the two +import tags "hex" and "oct" - and then it will be global and cannot be +disabled inside a scope with "no bigint": + + use bigint qw/hex oct/; + + print hex("0x1234567890123456"); + { + no bigint; + print hex("0x1234567890123456"); + } + +The second call to hex() will warn about a non-portable constant. + +Compare this to: + + use bigint; + + # will warn only under Perl older than v5.9.4 + print hex("0x1234567890123456"); + +=back + +=head1 EXAMPLES + + perl -Mbigrat -le 'print sqrt(33)' + perl -Mbigrat -le 'print 2*255' + perl -Mbigrat -le 'print 4.5+2*255' + perl -Mbigrat -le 'print 3/7 + 5/7 + 8/3' + perl -Mbigrat -le 'print 12->is_odd()'; + perl -Mbignum=l,GMP -le 'print 7 ** 7777' + +=head1 LICENSE + +This program is free software; you may redistribute it and/or modify it under +the same terms as Perl itself. + +=head1 SEE ALSO + +Especially L<bignum>. + +L<Math::BigFloat>, L<Math::BigInt>, L<Math::BigRat> and L<Math::Big> as well +as L<Math::BigInt::BitVect>, L<Math::BigInt::Pari> and L<Math::BigInt::GMP>. + +=head1 AUTHORS + +(C) by Tels L<http://bloodgate.com/> in early 2002 - 2007. + +=cut diff --git a/ext/bignum/t/big_e_pi.t b/ext/bignum/t/big_e_pi.t new file mode 100644 index 0000000000..819e22528a --- /dev/null +++ b/ext/bignum/t/big_e_pi.t @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +############################################################################### +# test for e() and PI() exports + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bignum qw/e PI bexp bpi/; + +is (e, "2.718281828459045235360287471352662497757", 'e'); +is (PI, "3.141592653589793238462643383279502884197", 'PI'); + +is (bexp(1,10), "2.718281828", 'e'); +is (bpi(10), "3.141592654", 'PI'); diff --git a/ext/bignum/t/bigexp.t b/ext/bignum/t/bigexp.t new file mode 100644 index 0000000000..2fc631fff1 --- /dev/null +++ b/ext/bignum/t/bigexp.t @@ -0,0 +1,26 @@ +#!/usr/bin/perl -w + +############################################################################### +# test for bug #18025: bignum/bigrat can lead to a number that is both 1 and 0 + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bignum; + +my $lnev = -7 / (10**17); +my $ev=exp($lnev); + +is( sprintf('%0.5f',$ev) , '1.00000', '($ev) is approx. 1' ); +is( sprintf('%0.5f',1-$ev) , '0.00000', '(1-$ev) is approx. 0' ); +is( sprintf('%0.5f',1-"$ev") , '0.00000', '(1-"$ev") is approx. 0' ); + +cmp_ok( $ev, '!=', 0, '$ev should not equal 0'); diff --git a/ext/bignum/t/bigint.t b/ext/bignum/t/bigint.t new file mode 100644 index 0000000000..baf76a3005 --- /dev/null +++ b/ext/bignum/t/bigint.t @@ -0,0 +1,123 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 51; + } + +use bigint qw/hex oct/; + +############################################################################### +# _constant tests + +foreach (qw/ + 123:123 + 123.4:123 + 1.4:1 + 0.1:0 + -0.1:0 + -1.1:-1 + -123.4:-123 + -123:-123 + 123e2:123e2 + 123e-1:12 + 123e-4:0 + 123e-3:0 + 123.345e-1:12 + 123.456e+2:12345 + 1234.567e+3:1234567 + 1234.567e+4:1234567E1 + 1234.567e+6:1234567E3 + /) + { + my ($x,$y) = split /:/; + print "# Try $x\n"; + ok (bigint::_float_constant("$x"),"$y"); + } + +foreach (qw/ + 0100:64 + 0200:128 + 0x100:256 + 0b1001:9 + /) + { + my ($x,$y) = split /:/; + print "# Try $x\n"; + ok (bigint::_binary_constant("$x"),"$y"); + } + +############################################################################### +# general tests + +my $x = 5; ok (ref($x) =~ /^Math::BigInt/); # :constant + +# todo: ok (2 + 2.5,4.5); # should still work +# todo: $x = 2 + 3.5; ok (ref($x),'Math::BigFloat'); + +$x = 2 ** 255; ok (ref($x) =~ /^Math::BigInt/); + +ok (12->bfac(),479001600); +ok (9/4,2); + +ok (4.5+4.5,8); # truncate +ok (ref(4.5+4.5) =~ /^Math::BigInt/); + + +############################################################################### +# accurarcy and precision + +ok_undef (bigint->accuracy()); +ok (bigint->accuracy(12),12); +ok (bigint->accuracy(),12); + +ok_undef (bigint->precision()); +ok (bigint->precision(12),12); +ok (bigint->precision(),12); + +ok (bigint->round_mode(),'even'); +ok (bigint->round_mode('odd'),'odd'); +ok (bigint->round_mode(),'odd'); + +############################################################################### +# hex() and oct() + +my $c = 'Math::BigInt'; + +ok (ref(hex(1)), $c); +ok (ref(hex(0x1)), $c); +ok (ref(hex("af")), $c); +ok (hex("af"), Math::BigInt->new(0xaf)); +ok (ref(hex("0x1")), $c); + +ok (ref(oct("0x1")), $c); +ok (ref(oct("01")), $c); +ok (ref(oct("0b01")), $c); +ok (ref(oct("1")), $c); +ok (ref(oct(" 1")), $c); +ok (ref(oct(" 0x1")), $c); + +ok (ref(oct(0x1)), $c); +ok (ref(oct(01)), $c); +ok (ref(oct(0b01)), $c); +ok (ref(oct(1)), $c); + +############################################################################### +############################################################################### +# Perl 5.005 does not like ok ($x,undef) + +sub ok_undef + { + my $x = shift; + + ok (1,1) and return if !defined $x; + ok ($x,'undef'); + } diff --git a/ext/bignum/t/bignum.t b/ext/bignum/t/bignum.t new file mode 100644 index 0000000000..fe299a2e24 --- /dev/null +++ b/ext/bignum/t/bignum.t @@ -0,0 +1,94 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 35; + } + +use bignum qw/oct hex/; + +############################################################################### +# general tests + +my $x = 5; ok (ref($x) =~ /^Math::BigInt/); # :constant + +ok (2 + 2.5,4.5); +$x = 2 + 3.5; ok (ref($x),'Math::BigFloat'); +ok (2 * 2.1,4.2); +$x = 2 + 2.1; ok (ref($x),'Math::BigFloat'); + +$x = 2 ** 255; ok (ref($x) =~ /^Math::BigInt/); + +# see if Math::BigInt constant and upgrading works +ok (Math::BigInt::bsqrt('12'),'3.464101615137754587054892683011744733886'); +ok (sqrt(12),'3.464101615137754587054892683011744733886'); + +ok (2/3,"0.6666666666666666666666666666666666666667"); + +#ok (2 ** 0.5, 'NaN'); # should be sqrt(2); + +ok (12->bfac(),479001600); + +# see if Math::BigFloat constant works + +# 0123456789 0123456789 <- default 40 +# 0123456789 0123456789 +ok (1/3, '0.3333333333333333333333333333333333333333'); + +############################################################################### +# accurarcy and precision + +ok_undef (bignum->accuracy()); +ok (bignum->accuracy(12),12); +ok (bignum->accuracy(),12); + +ok_undef (bignum->precision()); +ok (bignum->precision(12),12); +ok (bignum->precision(),12); + +ok (bignum->round_mode(),'even'); +ok (bignum->round_mode('odd'),'odd'); +ok (bignum->round_mode(),'odd'); + +############################################################################### +# hex() and oct() + +my $c = 'Math::BigInt'; + +ok (ref(hex(1)), $c); +ok (ref(hex(0x1)), $c); +ok (ref(hex("af")), $c); +ok (hex("af"), Math::BigInt->new(0xaf)); +ok (ref(hex("0x1")), $c); + +ok (ref(oct("0x1")), $c); +ok (ref(oct("01")), $c); +ok (ref(oct("0b01")), $c); +ok (ref(oct("1")), $c); +ok (ref(oct(" 1")), $c); +ok (ref(oct(" 0x1")), $c); + +ok (ref(oct(0x1)), $c); +ok (ref(oct(01)), $c); +ok (ref(oct(0b01)), $c); +ok (ref(oct(1)), $c); + +############################################################################### +############################################################################### +# Perl 5.005 does not like ok ($x,undef) + +sub ok_undef + { + my $x = shift; + + ok (1,1) and return if !defined $x; + ok ($x,'undef'); + } diff --git a/ext/bignum/t/bigrat.t b/ext/bignum/t/bigrat.t new file mode 100644 index 0000000000..972b83cde2 --- /dev/null +++ b/ext/bignum/t/bigrat.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 40; + } + +use bigrat qw/oct hex/; + +############################################################################### +# general tests + +my $x = 5; ok (ref($x) =~ /^Math::BigInt/); # :constant + +# todo: ok (2 + 2.5,4.5); # should still work +# todo: $x = 2 + 3.5; ok (ref($x),'Math::BigFloat'); + +$x = 2 ** 255; ok (ref($x) =~ /^Math::BigInt/); + +# see if Math::BigRat constant works +ok (1/3, '1/3'); +ok (1/4+1/3,'7/12'); +ok (5/7+3/7,'8/7'); + +ok (3/7+1,'10/7'); +ok (3/7+1.1,'107/70'); +ok (3/7+3/7,'6/7'); + +ok (3/7-1,'-4/7'); +ok (3/7-1.1,'-47/70'); +ok (3/7-2/7,'1/7'); + +# fails ? +# ok (1+3/7,'10/7'); + +ok (1.1+3/7,'107/70'); +ok (3/7*5/7,'15/49'); +ok (3/7 / (5/7),'3/5'); +ok (3/7 / 1,'3/7'); +ok (3/7 / 1.5,'2/7'); + +############################################################################### +# accurarcy and precision + +ok_undef (bigrat->accuracy()); +ok (bigrat->accuracy(12),12); +ok (bigrat->accuracy(),12); + +ok_undef (bigrat->precision()); +ok (bigrat->precision(12),12); +ok (bigrat->precision(),12); + +ok (bigrat->round_mode(),'even'); +ok (bigrat->round_mode('odd'),'odd'); +ok (bigrat->round_mode(),'odd'); + +############################################################################### +# hex() and oct() + +my $c = 'Math::BigInt'; + +ok (ref(hex(1)), $c); +ok (ref(hex(0x1)), $c); +ok (ref(hex("af")), $c); +ok (hex("af"), Math::BigInt->new(0xaf)); +ok (ref(hex("0x1")), $c); + +ok (ref(oct("0x1")), $c); +ok (ref(oct("01")), $c); +ok (ref(oct("0b01")), $c); +ok (ref(oct("1")), $c); +ok (ref(oct(" 1")), $c); +ok (ref(oct(" 0x1")), $c); + +ok (ref(oct(0x1)), $c); +ok (ref(oct(01)), $c); +ok (ref(oct(0b01)), $c); +ok (ref(oct(1)), $c); + +############################################################################### +############################################################################### +# Perl 5.005 does not like ok ($x,undef) + +sub ok_undef + { + my $x = shift; + + ok (1,1) and return if !defined $x; + ok ($x,'undef'); + } diff --git a/ext/bignum/t/bii_e_pi.t b/ext/bignum/t/bii_e_pi.t new file mode 100644 index 0000000000..76ee07ae29 --- /dev/null +++ b/ext/bignum/t/bii_e_pi.t @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +############################################################################### +# test for e() and PI() exports + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 5; + } + +use bigint qw/e PI bpi bexp/; + +is (e, "2", 'e'); +is (PI, "3", 'PI'); + +is (bexp(1,10), "2", 'e'); +is (bexp(3,10), "20", 'e'); +is (bpi(10), "3", 'PI'); diff --git a/ext/bignum/t/biinfnan.t b/ext/bignum/t/biinfnan.t new file mode 100644 index 0000000000..f136c1e66e --- /dev/null +++ b/ext/bignum/t/biinfnan.t @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + unshift @INC, '../lib/bignum/t' if $ENV{PERL_CORE}; + plan tests => 26; + } + +use bigint; + +my ($x); + +require "infnan.inc"; + diff --git a/ext/bignum/t/bir_e_pi.t b/ext/bignum/t/bir_e_pi.t new file mode 100644 index 0000000000..88342b05bd --- /dev/null +++ b/ext/bignum/t/bir_e_pi.t @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +############################################################################### +# test for e() and PI() exports + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bigrat qw/e PI bexp bpi/; + +is (e, "2.718281828459045235360287471352662497757", 'e'); +is (PI, "3.141592653589793238462643383279502884197", 'PI'); + +# these tests should actually produce big rationals, but this is not yet +# implemented: +is (bexp(1,10), "2.718281828", 'e'); +is (bpi(10), "3.141592654", 'PI'); diff --git a/ext/bignum/t/bn_lite.t b/ext/bignum/t/bn_lite.t new file mode 100644 index 0000000000..21247c1ee7 --- /dev/null +++ b/ext/bignum/t/bn_lite.t @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 1; + } + +eval 'require Math::BigInt::Lite;'; +if ($@ eq '') + { + # can use Lite, so let bignum try it + require bignum; bignum->import(); + # can't get to work a ref(1+1) here, presumable because :constant phase + # already done + ok ($bignum::_lite,1); + } +else + { + print "ok 1 # skipped, no Math::BigInt::Lite\n"; + } + + diff --git a/ext/bignum/t/bninfnan.t b/ext/bignum/t/bninfnan.t new file mode 100644 index 0000000000..fbadb068b9 --- /dev/null +++ b/ext/bignum/t/bninfnan.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + my $location = $0; $location =~ s/bninfnan.t//i; + if ($ENV{PERL_CORE}) + { + #@INC = qw(../lib ../lib/bignum/t); # testing with the core distribution + } + else + { + unshift @INC, '../lib'; # for testing manually + } + 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 => 26; + } + +use bignum; + +my ($x); + +require "infnan.inc"; + diff --git a/ext/bignum/t/br_lite.t b/ext/bignum/t/br_lite.t new file mode 100644 index 0000000000..2bf77d4037 --- /dev/null +++ b/ext/bignum/t/br_lite.t @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 1; + } + +eval 'require Math::BigInt::Lite;'; +if ($@ eq '') + { + # can use Lite, so let bignum try it + require bigrat; bigrat->import(); + # can't get to work a ref(1+1) here, presumable because :constant phase + # already done + ok ($bigrat::_lite,1); + } +else + { + print "ok 1 # skipped, no Math::BigInt::Lite\n"; + } + + diff --git a/ext/bignum/t/brinfnan.t b/ext/bignum/t/brinfnan.t new file mode 100644 index 0000000000..286adbc0ca --- /dev/null +++ b/ext/bignum/t/brinfnan.t @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + unshift @INC, '../lib/bignum/t' if $ENV{PERL_CORE}; + plan tests => 26; + } + +use bigrat; + +my ($x); + +require "infnan.inc"; + diff --git a/ext/bignum/t/in_effect.t b/ext/bignum/t/in_effect.t new file mode 100644 index 0000000000..b163f12b8f --- /dev/null +++ b/ext/bignum/t/in_effect.t @@ -0,0 +1,42 @@ +#!/usr/bin/perl -w + +############################################################################### +# Test in_effect() + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 9; + } + +use bigint; +use bignum; +use bigrat; + +can_ok ('bigint', qw/in_effect/); +can_ok ('bignum', qw/in_effect/); +can_ok ('bigrat', qw/in_effect/); + +SKIP: { + skip ('Need at least Perl v5.9.4', 3) if $] < "5.009005"; + + is (bigint::in_effect(), 1, 'bigint in effect'); + is (bignum::in_effect(), 1, 'bignum in effect'); + is (bigrat::in_effect(), 1, 'bigrat in effect'); + } + +{ + no bigint; + no bignum; + no bigrat; + + is (bigint::in_effect(), undef, 'bigint not in effect'); + is (bignum::in_effect(), undef, 'bignum not in effect'); + is (bigrat::in_effect(), undef, 'bigrat not in effect'); +} + diff --git a/ext/bignum/t/infnan.inc b/ext/bignum/t/infnan.inc new file mode 100644 index 0000000000..771b94e748 --- /dev/null +++ b/ext/bignum/t/infnan.inc @@ -0,0 +1,35 @@ + +use strict; + +my ($x); + +############################################################################### +# inf tests + +$x = 1+inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'inf'); +$x = 1*inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'inf'); + +# these don't work without exporting inf() +$x = inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'inf'); +$x = inf+inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'inf'); +$x = inf*inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'inf'); + +############################################################################### +# NaN tests + +$x = 1+NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); +$x = 1*NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); + +# these don't work without exporting NaN() +$x = NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); +$x = NaN+NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); +$x = NaN*NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); + +############################################################################### +# mixed tests + +# these don't work without exporting NaN() or inf() +$x = NaN+inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); +$x = NaN*inf; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); +$x = inf*NaN; ok (ref($x) =~ /^Math::BigInt/); ok ($x->bstr(),'NaN'); + diff --git a/ext/bignum/t/option_a.t b/ext/bignum/t/option_a.t new file mode 100644 index 0000000000..2086f9a8d8 --- /dev/null +++ b/ext/bignum/t/option_a.t @@ -0,0 +1,31 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bignum a => '12'; + +my @C = qw/Math::BigInt Math::BigFloat/; + +foreach my $c (@C) + { + is ($c->accuracy(),12, "$c accuracy = 12"); + } + +bignum->import( accuracy => '23'); + +foreach my $c (@C) + { + is ($c->accuracy(), 23, "$c accuracy = 23"); + } + diff --git a/ext/bignum/t/option_l.t b/ext/bignum/t/option_l.t new file mode 100644 index 0000000000..cfa8033f22 --- /dev/null +++ b/ext/bignum/t/option_l.t @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# test the "l", "lib", "try" and "only" options: + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 19; + } + +use bignum; + +my @W; +{ +# catch warnings: +require Carp; +no warnings 'redefine'; +*Carp::carp = sub { push @W, $_[0]; }; +} + +my $rc = eval ('bignum->import( "l" => "foo" );'); +is ($@,''); # shouldn't die +is (scalar @W, 1, 'one warning'); +like ($W[0], qr/fallback to Math::/, 'got fallback'); + +$rc = eval ('bignum->import( "lib" => "foo" );'); +is ($@,''); # ditto +is (scalar @W, 2, 'two warnings'); +like ($W[1], qr/fallback to Math::/, 'got fallback'); + +$rc = eval ('bignum->import( "try" => "foo" );'); +is ($@,''); # shouldn't die +$rc = eval ('bignum->import( "try" => "foo" );'); +is ($@,''); # ditto + +$rc = eval ('bignum->import( "foo" => "bar" );'); +like ($@, qr/^Unknown option foo/i, 'died'); # should die + +$rc = eval ('bignum->import( "only" => "bar" );'); +like ($@, qr/fallback disallowed/i, 'died'); # should die + +# test that options are only lowercase (don't see a reason why allow UPPER) + +foreach (qw/L LIB Lib T Trace TRACE V Version VERSION/) + { + $rc = eval ('bignum->import( "$_" => "bar" );'); + like ($@, qr/^Unknown option $_/i, 'died'); # should die + } + diff --git a/ext/bignum/t/option_p.t b/ext/bignum/t/option_p.t new file mode 100644 index 0000000000..b84883be4b --- /dev/null +++ b/ext/bignum/t/option_p.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +my @C = qw/Math::BigInt Math::BigFloat/; + +use bignum p => '12'; + +foreach my $c (@C) + { + is ($c->precision(),12, "$c precision = 12"); + } + +bignum->import( p => '42' ); + +foreach my $c (@C) + { + is ($c->precision(),42, "$c precision = 42"); + } + diff --git a/ext/bignum/t/ratopt_a.t b/ext/bignum/t/ratopt_a.t new file mode 100644 index 0000000000..f004afe428 --- /dev/null +++ b/ext/bignum/t/ratopt_a.t @@ -0,0 +1,34 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 7; + } + +my @C = qw/Math::BigInt Math::BigFloat Math::BigRat/; + +# bigrat (bug until v0.15) +use bigrat a => 2; + +foreach my $c (@C) + { + is ($c->accuracy(), 2, "$c accuracy = 2"); + } + +eval { bigrat->import( accuracy => '42') }; + +is ($@, '', 'no error'); + +foreach my $c (@C) + { + is ($c->accuracy(), 42, "$c accuracy = 42"); + } + diff --git a/ext/bignum/t/scope_f.t b/ext/bignum/t/scope_f.t new file mode 100644 index 0000000000..dd748e11c8 --- /dev/null +++ b/ext/bignum/t/scope_f.t @@ -0,0 +1,41 @@ +#!/usr/bin/perl -w + +############################################################################### +# Test "no bignum;" and overloading of hex()/oct() for newer Perls + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 10; + } + +# no :hex and :oct means these do not get overloaded for older Perls: +use bignum; + +isnt (ref(1), '', 'is in effect'); +isnt (ref(2.0), '', 'is in effect'); +isnt (ref(0x20), '', 'is in effect'); + +SKIP: { + skip ('Need at least Perl v5.9.4', 2) if $] < 5.009004; + + is (ref(hex(9)), 'Math::BigInt', 'hex is overloaded'); + is (ref(oct(07)), 'Math::BigInt', 'oct is overloaded'); + } + +{ + no bignum; + + is (ref(1), '', 'is not in effect'); + is (ref(2.0), '', 'is not in effect'); + is (ref(0x20), '', 'is not in effect'); + + isnt (ref(hex(9)), 'Math::BigInt', 'hex is not overloaded'); + isnt (ref(oct(07)), 'Math::BigInt', 'oct is not overloaded'); +} + diff --git a/ext/bignum/t/scope_i.t b/ext/bignum/t/scope_i.t new file mode 100644 index 0000000000..4e2f1e6e7f --- /dev/null +++ b/ext/bignum/t/scope_i.t @@ -0,0 +1,42 @@ +#!/usr/bin/perl -w + +############################################################################### +# Test "no bigint;" and overloading of hex()/oct() for newer Perls + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 10; + } + +# no :hex and :oct means these do not get overloaded for older Perls: +use bigint; + +isnt (ref(1), '', 'is in effect'); +isnt (ref(2.0), '', 'is in effect'); +isnt (ref(0x20), '', 'is in effect'); + +SKIP: { + skip ('Need at least Perl v5.9.4', 2) if $] < "5.009004"; # quote due to "use bigint;" + + is (ref(hex(9)), 'Math::BigInt', 'hex is overloaded'); + is (ref(oct(07)), 'Math::BigInt', 'oct is overloaded'); + } + +{ + no bigint; + + is (ref(1), '', 'is not in effect'); + is (ref(2.0), '', 'is not in effect'); + is (ref(0x20), '', 'is not in effect'); + + isnt (ref(hex(9)), 'Math::BigInt', 'hex is not overloaded'); + isnt (ref(oct(07)), 'Math::BigInt', 'oct is not overloaded'); + +} + diff --git a/ext/bignum/t/scope_r.t b/ext/bignum/t/scope_r.t new file mode 100644 index 0000000000..784fe0e6b0 --- /dev/null +++ b/ext/bignum/t/scope_r.t @@ -0,0 +1,41 @@ +#!/usr/bin/perl -w + +############################################################################### +# Test "no bigrat;" and overloading of hex()/oct() for newer Perls + +use Test::More; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 10; + } + +# no :hex and :oct means these do not get overloaded for older Perls: +use bigrat; + +isnt (ref(1), '', 'is in effect'); +isnt (ref(2.0), '', 'is in effect'); +isnt (ref(0x20), '', 'is in effect'); + +SKIP: { + skip ('Need at least Perl v5.9.4', 2) if $] < 5.009004; + + is (ref(hex(9)), 'Math::BigInt', 'hex is overloaded'); + is (ref(oct(07)), 'Math::BigInt', 'oct is overloaded'); + } + +{ + no bigrat; + + is (ref(1), '', 'is not in effect'); + is (ref(2.0), '', 'is not in effect'); + is (ref(0x20), '', 'is not in effect'); + + isnt (ref(hex(9)), 'Math::BigInt', 'hex is not overloaded'); + isnt (ref(oct(07)), 'Math::BigInt', 'oct is not overloaded'); +} + |