+package Math::BigInt;
+
+#
+# "Mike had an infinite amount to do and a negative amount of time in which
+# to do it." - Before and After
+#
+
# The following hash values are used:
# value: unsigned int with actual value (as a Math::BigInt::Calc or similiar)
# sign : +,-,NaN,+inf,-inf
# Remember not to take shortcuts ala $xs = $x->{value}; $CALC->foo($xs); since
# underlying lib might change the reference!
-package Math::BigInt;
my $class = "Math::BigInt";
require 5.005;
-$VERSION = '1.51';
+$VERSION = '1.56';
use Exporter;
@ISA = qw( Exporter );
@EXPORT_OK = qw( objectify _swap bgcd blcm);
sub upgrade
{
no strict 'refs';
- # make Class->round_mode() work
+ # make Class->upgrade() work
my $self = shift;
my $class = ref($self) || $self || __PACKAGE__;
- if (defined $_[0])
+ # need to set new value?
+ if (@_ > 0)
{
my $u = shift;
return ${"${class}::upgrade"} = $u;
return ${"${class}::upgrade"};
}
+sub downgrade
+ {
+ no strict 'refs';
+ # make Class->downgrade() work
+ my $self = shift;
+ my $class = ref($self) || $self || __PACKAGE__;
+ # need to set new value?
+ if (@_ > 0)
+ {
+ my $u = shift;
+ return ${"${class}::downgrade"} = $u;
+ }
+ return ${"${class}::downgrade"};
+ }
+
sub div_scale
{
no strict 'refs';
class => $class,
};
foreach (
- qw/upgrade downgrade precisison accuracy round_mode VERSION div_scale/)
+ qw/upgrade downgrade precision accuracy round_mode VERSION div_scale/)
{
$cfg->{lc($_)} = ${"${class}::$_"};
};
# avoid numify-calls by not using || on $wanted!
return $class->bzero($a,$p) if !defined $wanted; # default to 0
- return $class->copy($wanted,$a,$p,$r) if ref($wanted);
+ return $class->copy($wanted,$a,$p,$r)
+ if ref($wanted) && $wanted->isa($class); # MBI or subclass
$class->import() if $IMPORT == 0; # make require work
- my $self = {}; bless $self, $class;
+ my $self = bless {}, $class;
+
+ # shortcut for "normal" numbers
+ if ((!ref $wanted) && ($wanted =~ /^([+-]?)[1-9][0-9]*$/))
+ {
+ $self->{sign} = $1 || '+';
+ my $ref = \$wanted;
+ if ($wanted =~ /^[+-]/)
+ {
+ # remove sign without touching wanted
+ my $t = $wanted; $t =~ s/^[+-]//; $ref = \$t;
+ }
+ $self->{value} = $CALC->_new($ref);
+ no strict 'refs';
+ if ( (defined $a) || (defined $p)
+ || (defined ${"${class}::precision"})
+ || (defined ${"${class}::accuracy"})
+ )
+ {
+ $self->round($a,$p,$r) unless (@_ == 4 && !defined $a && !defined $p);
+ }
+ return $self;
+ }
+
# handle '+inf', '-inf' first
if ($wanted =~ /^[+-]?inf$/)
{
# do not round for new($x,undef,undef) since that is used by MBF to signal
# no rounding
$self->round($a,$p,$r) unless @_ == 4 && !defined $a && !defined $p;
- # print "mbi new $self\n";
- return $self;
+ $self;
}
sub bnan
}
$self->import() if $IMPORT == 0; # make require work
return if $self->modify('bnan');
- $self->{value} = $CALC->_zero();
+ my $c = ref($self);
+ if ($self->can('_bnan'))
+ {
+ # use subclass to initialize
+ $self->_bnan();
+ }
+ else
+ {
+ # otherwise do our own thing
+ $self->{value} = $CALC->_zero();
+ }
$self->{sign} = $nan;
delete $self->{_a}; delete $self->{_p}; # rounding NaN is silly
return $self;
# create a bigint '+-inf', if given a BigInt, set it to '+-inf'
# the sign is either '+', or if given, used from there
my $self = shift;
- my $sign = shift; $sign = '+' if !defined $sign || $sign ne '-';
+ my $sign = shift; $sign = '+' if !defined $sign || $sign !~ /^-(inf)?$/;
$self = $class if !defined $self;
if (!ref($self))
{
}
$self->import() if $IMPORT == 0; # make require work
return if $self->modify('binf');
- $self->{value} = $CALC->_zero();
- $self->{sign} = $sign.'inf';
+ my $c = ref($self);
+ if ($self->can('_binf'))
+ {
+ # use subclass to initialize
+ $self->_binf();
+ }
+ else
+ {
+ # otherwise do our own thing
+ $self->{value} = $CALC->_zero();
+ }
+ $sign = $sign . 'inf' if $sign !~ /inf$/; # - => -inf
+ $self->{sign} = $sign;
($self->{_a},$self->{_p}) = @_; # take over requested rounding
return $self;
}
}
$self->import() if $IMPORT == 0; # make require work
return if $self->modify('bzero');
- $self->{value} = $CALC->_zero();
+
+ if ($self->can('_bzero'))
+ {
+ # use subclass to initialize
+ $self->_bzero();
+ }
+ else
+ {
+ # otherwise do our own thing
+ $self->{value} = $CALC->_zero();
+ }
$self->{sign} = '+';
if (@_ > 0)
{
}
$self->import() if $IMPORT == 0; # make require work
return if $self->modify('bone');
- $self->{value} = $CALC->_one();
+
+ if ($self->can('_bone'))
+ {
+ # use subclass to initialize
+ $self->_bone();
+ }
+ else
+ {
+ # otherwise do our own thing
+ $self->{value} = $CALC->_one();
+ }
$self->{sign} = $sign;
if (@_ > 0)
{
# make a string from bigint object
my $x = shift; $class = ref($x) || $x; $x = $class->new(shift) if !ref($x);
# my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
+
if ($x->{sign} !~ /^[+-]$/)
{
return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN
return $x->{sign} if $x->{sign} !~ /^[+-]$/;
my $num = $CALC->_num($x->{value});
return -$num if $x->{sign} eq '-';
- return $num;
+ $num;
}
##############################################################################
sub sign
{
- # return the sign of the number: +/-/NaN
+ # return the sign of the number: +/-/-inf/+inf/NaN
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return $x->{sign};
+ $x->{sign};
}
sub _find_round_parameters
# post-normalized compare for internal use (honors signs)
if ($x->{sign} eq '+')
{
- return 1 if $y->{sign} eq '-'; # 0 check handled above
+ # $x and $y both > 0
return $CALC->_acmp($x->{value},$y->{value});
}
- # $x->{sign} eq '-'
- return -1 if $y->{sign} eq '+';
+ # $x && $y both < 0
$CALC->_acmp($y->{value},$x->{value}); # swaped (lib does only 0,1,-1)
}
my ($self,$x,$y,@r) = objectify(2,@_);
return $x if $x->modify('badd');
-# print "mbi badd ",join(' ',caller()),"\n";
-# print "upgrade => ",$upgrade||'undef',
-# " \$x (",ref($x),") \$y (",ref($y),")\n";
-# return $upgrade->badd($x,$y,@r) if defined $upgrade &&
-# ((ref($x) eq $upgrade) || (ref($y) eq $upgrade));
-# print "still badd\n";
+ return $upgrade->badd($x,$y,@r) if defined $upgrade &&
+ ((!$x->isa($self)) || (!$y->isa($self)));
$r[3] = $y; # no push!
# inf and NaN handling
{
# NaN first
return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
- # inf handline
- if (($x->{sign} =~ /^[+-]inf$/) && ($y->{sign} =~ /^[+-]inf$/))
+ # inf handling
+ if (($x->{sign} =~ /^[+-]inf$/) && ($y->{sign} =~ /^[+-]inf$/))
{
# +inf++inf or -inf+-inf => same, rest is NaN
return $x if $x->{sign} eq $y->{sign};
my ($self,$x,$y,@r) = objectify(2,@_);
return $x if $x->modify('bsub');
+
+# upgrade done by badd():
# return $upgrade->badd($x,$y,@r) if defined $upgrade &&
-# ((ref($x) eq $upgrade) || (ref($y) eq $upgrade));
+# ((!$x->isa($self)) || (!$y->isa($self)));
if ($y->is_zero())
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
return 1 if $x->{sign} eq $nan;
- return 0;
+ 0;
}
sub is_inf
my ($self,$x,$sign) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
$sign = '' if !defined $sign;
+ return 1 if $sign eq $x->{sign}; # match ("+inf" eq "+inf")
return 0 if $sign !~ /^([+-]|)$/;
if ($sign eq '')
}
$sign = quotemeta($sign.'inf');
return 1 if ($x->{sign} =~ /^$sign$/);
- return 0;
+ 0;
}
sub is_one
return $x if $x->modify('bmul');
- $r[3] = $y; # no push here
-
return $x->bnan() if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
# inf handling
return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
return $x->binf('-');
}
+
+ return $upgrade->bmul($x,$y,@r)
+ if defined $upgrade && $y->isa($upgrade);
+
+ $r[3] = $y; # no push here
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; # +1 * +1 or -1 * -1 => +
return $self->_div_inf($x,$y)
if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
+ return $upgrade->bdiv($upgrade->new($x),$y,@r)
+ if defined $upgrade && !$y->isa($self);
+
$r[3] = $y; # no push!
# 0 / something
my $cmp = $CALC->_acmp($x->{value},$y->{value});
if (($cmp < 0) and (($x->{sign} eq $y->{sign}) or !wantarray))
{
- return $upgrade->bdiv($x,$y,@r) if defined $upgrade;
+ return $upgrade->bdiv($upgrade->new($x),$upgrade->new($y),@r)
+ if defined $upgrade;
return $x->bzero()->round(@r) unless wantarray;
my $t = $x->copy(); # make copy first, because $x->bzero() clobbers $x
return $x unless wantarray;
return ($x->round(@r),$self->bzero(@r));
}
+ return $upgrade->bdiv($upgrade->new($x),$upgrade->new($y),@r)
+ if defined $upgrade;
# calc new sign and in case $y == +/- 1, return $x
my $xsign = $x->{sign}; # keep
return wantarray ? ($x->round(@r),$self->bzero(@r)) : $x->round(@r);
}
- my $rem;
if (wantarray)
{
my $rem = $self->bzero();
$x->{value} = $CALC->_div($x->{value},$y->{value});
$x->{sign} = '+' if $CALC->_is_zero($x->{value});
$x->round(@r);
- $x;
}
sub bmod
# modulus (or remainder)
# (BINT or num_str, BINT or num_str) return BINT
my ($self,$x,$y,@r) = objectify(2,@_);
-
+
return $x if $x->modify('bmod');
$r[3] = $y; # no push!
if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero())
my ($self,$x,$y,@r) = objectify(2,@_);
return $x if $x->modify('bpow');
-
+
+ return $upgrade->bpow($upgrade->new($x),$y,@r)
+ if defined $upgrade && !$y->isa($self);
+
$r[3] = $y; # no push!
return $x if $x->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
$x->bmul($x);
}
$x->bmul($pow2) unless $pow2->is_one();
- return $x->round(@r);
+ $x->round(@r);
}
sub blsft
$bin =~ s/^-0b//; # strip '-0b' prefix
$bin =~ tr/10/01/; # flip bits
# now shift
- if (length($bin) <= $y)
+ if (CORE::length($bin) <= $y)
{
$bin = '0'; # shifting to far right creates -1
# 0, because later increment makes
sub digit
{
# return the nth decimal digit, negative values count backward, 0 is right
- my $x = shift;
- my $n = shift || 0;
+ my ($self,$x,$n) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+ $n = 0 if !defined $n;
- return $CALC->_digit($x->{value},$n);
+ $CALC->_digit($x->{value},$n);
}
sub _trailing_zeros
my $e = $class->bzero();
return $e->binc() if $x->is_zero();
$e += $x->_trailing_zeros();
- return $e;
+ $e;
}
sub mantissa
if ($x->{sign} !~ /^[+-]$/)
{
- my $s = $x->{sign}; $s =~ s/^[+]//;
- return $self->new($s); # +inf => inf
+ return $self->new($x->{sign}); # keep + or - sign
}
my $m = $x->copy();
# that's inefficient
my $zeros = $m->_trailing_zeros();
- $m /= 10 ** $zeros if $zeros != 0;
- return $m;
+ $m->brsft($zeros,10) if $zeros != 0;
+# $m /= 10 ** $zeros if $zeros != 0;
+ $m;
}
sub parts
# currently it tries 'Math::BigInt' + 1, which will not work.
# some shortcut for the common cases
-
# $x->unary_op();
return (ref($_[1]),$_[1]) if (@_ == 2) && ($_[0]||0 == 1) && ref($_[1]);
- # $x->binary_op($y);
- #return (ref($_[1]),$_[1],$_[2]) if (@_ == 3) && ($_[0]||0 == 2)
- # && ref($_[1]) && ref($_[2]);
my $count = abs(shift || 0);
- my @a; # resulting array
+ my (@a,$k,$d); # resulting array, temp, and downgrade
if (ref $_[0])
{
# okay, got object as first
$a[0] = $class;
$a[0] = shift if $_[0] =~ /^[A-Z].*::/; # classname as first?
}
+
+ no strict 'refs';
+ # disable downgrading, because Math::BigFLoat->foo('1.0','2.0') needs floats
+ if (defined ${"$a[0]::downgrade"})
+ {
+ $d = ${"$a[0]::downgrade"};
+ ${"$a[0]::downgrade"} = undef;
+ }
+
# print "Now in objectify, my class is today $a[0]\n";
- my $k;
if ($count == 0)
{
while (@_)
push @a,@_; # return other params, too
}
die "$class objectify needs list context" unless wantarray;
+ ${"$a[0]::downgrade"} = $d;
@a;
}
my $self = shift;
$IMPORT++;
- my @a = @_; my $l = scalar @_; my $j = 0;
- for ( my $i = 0; $i < $l ; $i++,$j++ )
+ my @a; my $l = scalar @_;
+ for ( my $i = 0; $i < $l ; $i++ )
{
+# print "at $_[$i]\n";
if ($_[$i] eq ':constant')
{
# this causes overlord er load to step in
overload::constant integer => sub { $self->new(shift) };
- splice @a, $j, 1; $j --;
+ overload::constant binary => sub { $self->new(shift) };
}
elsif ($_[$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] =~ /^lib$/i)
{
# this causes a different low lib to take care...
$CALC = $_[$i+1] || '';
- my $s = 2; $s = 1 if @a-$j < 2; # avoid "can not modify non-existant..."
- splice @a, $j, $s; $j -= $s;
+ $i++;
+ }
+ else
+ {
+ push @a, $_[$i];
}
}
+# print "a @a\n";
# any non :constant stuff is handled by our parent, Exporter
# even if @_ is empty, to give it a chance
$self->SUPER::import(@a); # need it for subclasses
$mul *= $x65536 if $len >= 0; # skip last mul
}
}
- $x->{sign} = $sign if !$x->is_zero(); # no '-0'
- return $x;
+ $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0'
+ $x;
}
sub __from_bin
$$bs =~ s/([01])_([01])/$1$2/g;
return $x->bnan() if $$bs !~ /^[+-]?0b[01]+$/;
- my $mul = Math::BigInt->bzero(); $mul++;
- my $x256 = Math::BigInt->new(256);
-
my $sign = '+'; $sign = '-' if ($$bs =~ /^\-/);
$$bs =~ s/^[+-]//; # strip sign
if ($CALC->can('_from_bin'))
}
else
{
+ my $mul = Math::BigInt->bzero(); $mul++;
+ my $x256 = Math::BigInt->new(256);
my $len = CORE::length($$bs)-2;
$len = int($len/8); # 8-digit parts, w/o '0b'
my $val; my $i = -8;
$mul *= $x256 if $len >= 0; # skip last mul
}
}
- $x->{sign} = $sign if !$x->is_zero();
- return $x;
+ $x->{sign} = $sign unless $CALC->_is_zero($x->{value}); # no '-0'
+ $x;
}
sub _split
$x->bsstr(); # normalized string in scientific notation
$x->as_hex(); # as signed hexadecimal string with prefixed 0x
$x->as_bin(); # as signed binary string with prefixed 0b
+
+ Math::BigInt->config(); # return hash containing configuration/version
=head1 DESCRIPTION
$A, $P and $R are accuracy, precision and round_mode. Please see more in the
section about ACCURACY and ROUNDIND.
+=head2 config
+
+ use Data::Dumper;
+
+ print Dumper ( Math::BigInt->config() );
+
+Returns a hash containing the configuration, e.g. the version number, lib
+loaded etc.
+
+=head2 accuracy
+
+ $x->accuracy(5); # local for $x
+ $class->accuracy(5); # global for all members of $class
+
+Set or get the global or local accuracy, aka how many significant digits the
+results have. Please see the section about L<ACCURACY AND PRECISION> for
+further details.
+
+Value must be greater than zero. Pass an undef value to disable it:
+
+ $x->accuracy(undef);
+ Math::BigInt->accuracy(undef);
+
+Returns the current accuracy. For C<$x->accuracy()> it will return either the
+local accuracy, or if not defined, the global. This means the return value
+represents the accuracy that will be in effect for $x:
+
+ $y = Math::BigInt->new(1234567); # unrounded
+ print Math::BigInt->accuracy(4),"\n"; # set 4, print 4
+ $x = Math::BigInt->new(123456); # will be automatically rounded
+ print "$x $y\n"; # '123500 1234567'
+ print $x->accuracy(),"\n"; # will be 4
+ print $y->accuracy(),"\n"; # also 4, since global is 4
+ print Math::BigInt->accuracy(5),"\n"; # set to 5, print 5
+ print $x->accuracy(),"\n"; # still 4
+ print $y->accuracy(),"\n"; # 5, since global is 5
+
=head2 brsft
$x->brsft($y,$n);
$x->bone(); # +1
$x->bone('-'); # -1
-=head2 is_one()/is_zero()/is_nan()/is_positive()/is_negative()/is_inf()/is_odd()/is_even()/is_int()
+=head2 is_one()/is_zero()/is_nan()/is_inf()
+
$x->is_zero(); # true if arg is +0
$x->is_nan(); # true if arg is NaN
$x->is_one(); # true if arg is +1
$x->is_one('-'); # true if arg is -1
- $x->is_odd(); # true if odd, false for even
- $x->is_even(); # true if even, false for odd
- $x->is_positive(); # true if >= 0
- $x->is_negative(); # true if < 0
$x->is_inf(); # true if +inf
$x->is_inf('-'); # true if -inf (sign is default '+')
+
+These methods all test the BigInt for beeing one specific value and return
+true or false depending on the input. These are faster than doing something
+like:
+
+ if ($x == 0)
+
+=head2 is_positive()/is_negative()
+
+ $x->is_positive(); # true if >= 0
+ $x->is_negative(); # true if < 0
+
+The methods return true if the argument is positive or negative, respectively.
+C<NaN> is neither positive nor negative, while C<+inf> counts as positive, and
+C<-inf> is negative. A C<zero> is positive.
+
+These methods are only testing the sign, and not the value.
+
+=head2 is_odd()/is_even()/is_int()
+
+ $x->is_odd(); # true if odd, false for even
+ $x->is_even(); # true if even, false for odd
$x->is_int(); # true if $x is an integer
-These methods all test the BigInt for one condition and return true or false
-depending on the input.
+The return true when the argument satisfies the condition. C<NaN>, C<+inf>,
+C<-inf> are not integers and are neither odd nor even.
=head2 bcmp
- $x->bcmp($y); # compare numbers (undef,<0,=0,>0)
+ $x->bcmp($y);
+
+Compares $x with $y and takes the sign into account.
+Returns -1, 0, 1 or undef.
=head2 bacmp
- $x->bacmp($y); # compare absolutely (undef,<0,=0,>0)
+ $x->bacmp($y);
+
+Compares $x with $y while ignoring their. Returns -1, 0, 1 or undef.
=head2 sign
- $x->sign(); # return the sign, either +,- or NaN
+ $x->sign();
+
+Return the sign, of $x, meaning either C<+>, C<->, C<-inf>, C<+inf> or NaN.
=head2 bcmp
=head1 Autocreating constants
-After C<use Math::BigInt ':constant'> all the B<integer> decimal constants
-in the given scope are converted to C<Math::BigInt>. This conversion
-happens at compile time.
+After C<use Math::BigInt ':constant'> all the B<integer> decimal, hexadecimal
+and binary constants in the given scope are converted to C<Math::BigInt>.
+This conversion happens at compile time.
In particular,
perl -MMath::BigInt=:constant -e 'print 2**100,"\n"'
-prints the integer value of C<2**100>. Note that without conversion of
+prints the integer value of C<2**100>. Note that without conversion of
constants the expression 2**100 will be calculated as perl scalar.
Please note that strings and floating point constants are not affected,
constant at compile time and then hand the result to BigInt, which results in
an truncated result or a NaN.
+This also applies to integers that look like floating point constants:
+
+ use Math::BigInt ':constant';
+
+ print ref(123e2),"\n";
+ print ref(123.2e2),"\n";
+
+will print nothing but newlines. Use either L<bignum> or L<Math::BigFloat>
+to get this to work.
+
=head1 PERFORMANCE
Using the form $x += $y; etc over $x = $x + $y is faster, since a copy of $x