From: Jarkko Hietaniemi Date: Sun, 24 Mar 2002 00:21:07 +0000 (+0000) Subject: Merge bignum 0.10, from Tels. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=126f3c5f1f20a40de59db499161effaa9e24e4fc;p=p5sagit%2Fp5-mst-13.2.git Merge bignum 0.10, from Tels. p4raw-id: //depot/perl@15455 --- diff --git a/MANIFEST b/MANIFEST index 1e226c6..95b908b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -892,6 +892,20 @@ lib/autouse.t See if autouse works lib/base.pm Establish IS-A relationship at compile time lib/Benchmark.pm Measure execution time lib/Benchmark.t See if Benchmark works +lib/Math/BigFloat/Trace.pm bignum tracing +lib/Math/BigInt/Trace.pm bignum tracing +lib/bigint.pm bignum +lib/bignum.pm bignum +lib/bignum/t/bigint.t See if bignum works +lib/bignum/t/bignum.t See if bignum works +lib/bignum/t/bigrat.t See if bignum works +lib/bignum/t/bn_lite.t See if bignum works +lib/bignum/t/br_lite.t See if bignum works +lib/bignum/t/option_a.t See if bignum works +lib/bignum/t/option_l.t See if bignum works +lib/bignum/t/option_p.t See if bignum works +lib/bignum/t/trace.t See if bignum works +lib/bigrat.pm bignum lib/bigfloat.pl An arbitrary precision floating point package lib/bigfloatpl.t See if bigfloat.pl works lib/bigint.pl An arbitrary precision integer arithmetic package diff --git a/lib/Math/BigFloat/Trace.pm b/lib/Math/BigFloat/Trace.pm new file mode 100644 index 0000000..871b2a9 --- /dev/null +++ b/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/lib/Math/BigInt/Trace.pm b/lib/Math/BigInt/Trace.pm new file mode 100644 index 0000000..4733d22 --- /dev/null +++ b/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/lib/bigint.pm b/lib/bigint.pm new file mode 100644 index 0000000..9539fd5 --- /dev/null +++ b/lib/bigint.pm @@ -0,0 +1,343 @@ +package bigint; +require 5.005; + +$VERSION = '0.02'; +use Exporter; +@ISA = qw( Exporter ); +@EXPORT_OK = qw( ); + +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]) + { + 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 + { + my $self = shift; + no strict 'refs'; +# if (defined $_[0]) +# { +# $Math::BigInt::upgrade = $_[0]; +# } + return $Math::BigInt::upgrade; + } + +sub _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); + return $sign.$$miv.$mfv; # 123.45e+1 => 1234 + } + +sub import + { + my $self = shift; + + # some defaults + my $lib = 'Calc'; + + 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)$/) + { + # this causes a different low lib to take care... + $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 --; + } + else { die "unknown option $_[$i]"; } + } + my $class; + $_lite = 0; # using M::BI::L ? + if ($trace) + { + require Math::BigInt::Trace; $class = 'Math::BigInt::Trace'; + print STDERR "Loading $class"; + } + 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 + } + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import, lib => $lib); + + 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( _constant(shift) ); }; + } + +1; + +__END__ + +=head1 NAME + +bigint - Transparent big integer support for Perl + +=head1 SYNOPSIS + + use bignt; + + $x = 2 + 4.5,"\n"; # BigInt 6 + print 2 ** 512; # really is what you think it is + +=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 results are also +trunctaed. + +=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' + +=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 ignored 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' + +=item t or trace + +This enables a trace mode and is primarily for debugging bigint or +Math::BigInt. + +=item l or lib + +Load a different math lib, see L. + + perl -Mbigint=l,GMP -e 'print 2 ** 512' + +Currently there is no way to specify more than one library on the command +line. 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 -e '' + +=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 bigint lib => 'BitVect'; + +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'; + +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 bright 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' 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. + +=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. + +=head1 MODULES USED + +C 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' + +=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 as in C and +L as in C. + +L, L and L as well +as L, L and L. + +=head1 AUTHORS + +(C) by Tels L in early 2002. + +=cut diff --git a/lib/bignum.pm b/lib/bignum.pm new file mode 100644 index 0000000..6e619ac --- /dev/null +++ b/lib/bignum.pm @@ -0,0 +1,326 @@ +package bignum; +require 5.005; + +$VERSION = '0.10'; +use Exporter; +@ISA = qw( Exporter ); +@EXPORT_OK = qw( ); + +use strict; + +############################################################################## + +# 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]); + 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 upgrade + { + my $self = shift; + no strict 'refs'; +# if (defined $_[0]) +# { +# $Math::BigInt::upgrade = $_[0]; +# $Math::BigFloat::upgrade = $_[0]; +# } + return $Math::BigInt::upgrade; + } + +sub import + { + my $self = shift; + + # some defaults + my $lib = 'Calc'; + 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)$/) + { + # this causes a different low lib to take care... + $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 --; + } + else { 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'; + print STDERR "Loading $class"; + } + 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 + } + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import, upgrade => $upgrade, lib => $lib); + + if ($trace) + { + require Math::BigFloat::Trace; $class = 'Math::BigFloat::Trace'; + $downgrade = 'Math::BigInt::Trace'; + print STDERR "Loading $class"; + } + 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; + } + } + +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; # really is what you think it is + +=head1 DESCRIPTION + +All operators (including basic math operations) are overloaded. Integer and +floating-point constants are created as proper BigInts or BigFloats, +respectively. + +=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)' + +=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)' + +=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. + + perl -Mbignum=l,GMP -e 'print 2 ** 512' + +Currently there is no way to specify more than one library on the command +line. 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 -Mbignum=v -e '' + +=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 => 'BitVect'; + +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. + +=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. + +=head2 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. + +=head1 MODULES USED + +C 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 2 ** 0.5' + perl -Mbignum=a,65 -le 'print 2 ** 0.2' + +=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 as in C. + +L, L, L and L as well +as L, L and L. + +=head1 AUTHORS + +(C) by Tels L in early 2002. + +=cut diff --git a/lib/bignum/t/bigint.t b/lib/bignum/t/bigint.t new file mode 100755 index 0000000..6133f7b --- /dev/null +++ b/lib/bignum/t/bigint.t @@ -0,0 +1,86 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 28; + } + +use bigint; + +############################################################################### +# _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::_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 + +# this might change! + +ok_undef ($Math::BigInt::accuracy); +ok_undef ($Math::BigInt::precision); +bigint->accuracy(5); +ok ($Math::BigInt::accuracy,5); +bigint->precision(-2); +ok_undef ($Math::BigInt::accuracy); +ok ($Math::BigInt::precision,-2); + +############################################################################### +############################################################################### +# 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/lib/bignum/t/bignum.t b/lib/bignum/t/bignum.t new file mode 100755 index 0000000..a804a26 --- /dev/null +++ b/lib/bignum/t/bignum.t @@ -0,0 +1,72 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 17; + } + +use bignum; + +############################################################################### +# 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::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 + +# this might change! + +ok_undef ($Math::BigInt::accuracy); +ok_undef ($Math::BigInt::precision); +ok_undef ($Math::BigFloat::accuracy); +ok_undef ($Math::BigFloat::precision); +bignum->accuracy(5); +ok ($Math::BigInt::accuracy,5); +ok ($Math::BigFloat::accuracy,5); +bignum->precision(-2); +ok_undef ($Math::BigInt::accuracy); +ok_undef ($Math::BigFloat::accuracy); +ok ($Math::BigInt::precision,-2); +ok ($Math::BigFloat::precision,-2); + +############################################################################### +############################################################################### +# 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/lib/bignum/t/bigrat.t b/lib/bignum/t/bigrat.t new file mode 100755 index 0000000..3664e8b --- /dev/null +++ b/lib/bignum/t/bigrat.t @@ -0,0 +1,59 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bigrat; + +############################################################################### +# 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'); + +############################################################################### +# accurarcy and precision + +# this might change! +#ok_undef ($Math::BigInt::accuracy); +#ok_undef ($Math::BigInt::precision); +#ok_undef ($Math::BigFloat::accuracy); +#ok_undef ($Math::BigFloat::precision); +#bigrat->accuracy(5); +#ok ($Math::BigInt::accuracy,5); +#ok ($Math::BigFloat::accuracy,5); +#bigrat->precision(-2); +#ok_undef ($Math::BigInt::accuracy); +#ok_undef ($Math::BigFloat::accuracy); +#ok ($Math::BigInt::precision,-2); +#ok ($Math::BigFloat::precision,-2); + +############################################################################### +############################################################################### +# 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/lib/bignum/t/bn_lite.t b/lib/bignum/t/bn_lite.t new file mode 100755 index 0000000..21247c1 --- /dev/null +++ b/lib/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/lib/bignum/t/br_lite.t b/lib/bignum/t/br_lite.t new file mode 100755 index 0000000..2bf77d4 --- /dev/null +++ b/lib/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/lib/bignum/t/option_a.t b/lib/bignum/t/option_a.t new file mode 100755 index 0000000..2ab00bb --- /dev/null +++ b/lib/bignum/t/option_a.t @@ -0,0 +1,25 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 4; + } + +use bignum a => '12'; + +ok (Math::BigInt->accuracy(),12); +ok (Math::BigFloat->accuracy(),12); + +bignum->import( accuracy => '23'); + +ok (Math::BigInt->accuracy(),23); +ok (Math::BigFloat->accuracy(),23); + diff --git a/lib/bignum/t/option_l.t b/lib/bignum/t/option_l.t new file mode 100755 index 0000000..134dd7c --- /dev/null +++ b/lib/bignum/t/option_l.t @@ -0,0 +1,33 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 12; + } + +use bignum; + +my $rc = eval ('bignum->import( "l" => "foo" );'); +ok ($@,''); # shouldn't die +$rc = eval ('bignum->import( "lib" => "foo" );'); +ok ($@,''); # ditto + +$rc = eval ('bignum->import( "foo" => "bar" );'); +ok ($@ =~ /^Unknown option foo/i,1); # 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" );'); + ok ($@ =~ /^Unknown option $_/i,1); # should die + } + diff --git a/lib/bignum/t/option_p.t b/lib/bignum/t/option_p.t new file mode 100755 index 0000000..c6df4ad --- /dev/null +++ b/lib/bignum/t/option_p.t @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 2; + } + +use bignum p => '12'; + +ok (Math::BigInt->precision(),12); +ok (Math::BigFloat->precision(),12); + diff --git a/lib/bignum/t/trace.t b/lib/bignum/t/trace.t new file mode 100755 index 0000000..891101b --- /dev/null +++ b/lib/bignum/t/trace.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w + +############################################################################### + +use Test; +use strict; + +BEGIN + { + $| = 1; + chdir 't' if -d 't'; + unshift @INC, '../lib'; + plan tests => 1; + } + +BEGIN + { + print "# "; # for testsuite + } +use bignum qw/ trace /; + +############################################################################### +# general tests + +my $x = 5; +print "\n"; +ok (ref($x),'Math::BigInt::Trace'); # :constant via trace + +############################################################################### +############################################################################### +# 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/lib/bigrat.pm b/lib/bigrat.pm new file mode 100644 index 0000000..ed37875 --- /dev/null +++ b/lib/bigrat.pm @@ -0,0 +1,242 @@ +package bigrat; +require 5.005; + +$VERSION = '0.04'; +use Exporter; +@ISA = qw( Exporter ); +@EXPORT_OK = qw( ); + +use strict; + +############################################################################## + +# 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::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 upgrade + { + my $self = shift; + no strict 'refs'; +# if (defined $_[0]) +# { +# $Math::BigInt::upgrade = $_[0]; +# $Math::BigFloat::upgrade = $_[0]; +# } + return $Math::BigInt::upgrade; + } + +sub import + { + my $self = shift; + + # see also bignum->import() for additional comments + + # some defaults + my $lib = 'Calc'; 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)$/) + { + # this causes a different low lib to take care... + $lib = $_[$i+1] || ''; + my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..." + splice @a, $j, $s; $j -= $s; + } + elsif ($_[$i] =~ /^(v|version)$/) + { + $ver = 1; + splice @a, $j, 1; $j --; + } + elsif ($_[$i] =~ /^(t|trace)$/) + { + $trace = 1; + splice @a, $j, 1; $j --; + } + else + { + 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'; + print STDERR "Loading $class"; + } + 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 + } + # Math::BigInt::Trace or plain Math::BigInt + $class->import(@import, upgrade => $upgrade, lib => $lib); + + require Math::BigFloat; + Math::BigFloat->import( upgrade => 'Math::BigRat', ':constant' ); + require Math::BigRat; + 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; + } + } + +1; + +__END__ + +=head1 NAME + +bigrat - Transparent BigNumber/BigRationale support for Perl + +=head1 SYNOPSIS + + use bigrat; + + $x = 2 + 4.5,"\n"; # BigFloat 6.5 + print 1/3 + 1/4,"\n"; # produces 7/12 + +=head1 DESCRIPTION + +All operators (inlcuding basic math operations) are overloaded. Integer and +floating-point constants are created as proper BigInts or BigFloats, +respectively. + +Other than L, this module upgrades to Math::BigRat, meaning that +instead of 2.5 you will get 2+1/2 as output. + +=head2 MODULES USED + +C 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 bigrat lib => 'BitVect'; + +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'; + +Please see respective module documentation for further details. + +=head2 SIGN + +The sign is either '+', '-', 'NaN', '+inf' or '-inf' and stored seperately. + +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. + +=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()'; + +=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. + +L, L, L and L as well +as L, L and L. + +=head1 AUTHORS + +(C) by Tels L in early 2002. + +=cut