📄 bigint.pm
字号:
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# _a : accuracy# _p : precision# _f : flags, used by MBF to flag parts of a float as untouchable# Remember not to take shortcuts ala $xs = $x->{value}; $CALC->foo($xs); since# underlying lib might change the reference!my $class = "Math::BigInt";require 5.005;$VERSION = '1.63';use Exporter;@ISA = qw( Exporter );@EXPORT_OK = qw( objectify _swap bgcd blcm); use vars qw/$round_mode $accuracy $precision $div_scale $rnd_mode/;use vars qw/$upgrade $downgrade/;use strict;# Inside overload, the first arg is always an object. If the original code had# it reversed (like $x = 2 * $y), then the third paramater indicates this# swapping. To make it work, we use a helper routine which not only reswaps the# params, but also makes a new object in this case. See _swap() for details,# especially the cases of operators with different classes.# For overloaded ops with only one argument we simple use $_[0]->copy() to# preserve the argument.# Thus inheritance of overload operators becomes possible and transparent for# our subclasses without the need to repeat the entire overload section there.use overload'=' => sub { $_[0]->copy(); },# '+' and '-' do not use _swap, since it is a triffle slower. If you want to# override _swap (if ever), then override overload of '+' and '-', too!# for sub it is a bit tricky to keep b: b-a => -a+b'-' => sub { my $c = $_[0]->copy; $_[2] ? $c->bneg()->badd($_[1]) : $c->bsub( $_[1]) },'+' => sub { $_[0]->copy()->badd($_[1]); },# some shortcuts for speed (assumes that reversed order of arguments is routed# to normal '+' and we thus can always modify first arg. If this is changed,# this breaks and must be adjusted.)'+=' => sub { $_[0]->badd($_[1]); },'-=' => sub { $_[0]->bsub($_[1]); },'*=' => sub { $_[0]->bmul($_[1]); },'/=' => sub { scalar $_[0]->bdiv($_[1]); },'%=' => sub { $_[0]->bmod($_[1]); },'^=' => sub { $_[0]->bxor($_[1]); },'&=' => sub { $_[0]->band($_[1]); },'|=' => sub { $_[0]->bior($_[1]); },'**=' => sub { $_[0]->bpow($_[1]); },# not supported by Perl yet'..' => \&_pointpoint,'<=>' => sub { $_[2] ? ref($_[0])->bcmp($_[1],$_[0]) : $_[0]->bcmp($_[1])},'cmp' => sub { $_[2] ? "$_[1]" cmp $_[0]->bstr() : $_[0]->bstr() cmp "$_[1]" },'log' => sub { $_[0]->copy()->blog(); }, 'int' => sub { $_[0]->copy(); }, 'neg' => sub { $_[0]->copy()->bneg(); }, 'abs' => sub { $_[0]->copy()->babs(); },'sqrt' => sub { $_[0]->copy()->bsqrt(); },'~' => sub { $_[0]->copy()->bnot(); },'*' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->bmul($a[1]); },'/' => sub { my @a = ref($_[0])->_swap(@_);scalar $a[0]->bdiv($a[1]);},'%' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->bmod($a[1]); },'**' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->bpow($a[1]); },'<<' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->blsft($a[1]); },'>>' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->brsft($a[1]); },'&' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->band($a[1]); },'|' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->bior($a[1]); },'^' => sub { my @a = ref($_[0])->_swap(@_); $a[0]->bxor($a[1]); },# can modify arg of ++ and --, so avoid a new-copy for speed, but don't# use $_[0]->__one(), it modifies $_[0] to be 1!'++' => sub { $_[0]->binc() },'--' => sub { $_[0]->bdec() },# if overloaded, O(1) instead of O(N) and twice as fast for small numbers'bool' => sub { # this kludge is needed for perl prior 5.6.0 since returning 0 here fails :-/ # v5.6.1 dumps on that: return !$_[0]->is_zero() || undef; :-( my $t = !$_[0]->is_zero(); undef $t if $t == 0; $t; },# the original qw() does not work with the TIESCALAR below, why?# Order of arguments unsignificant'""' => sub { $_[0]->bstr(); },'0+' => sub { $_[0]->numify(); };############################################################################### global constants, flags and accessoryuse constant MB_NEVER_ROUND => 0x0001;my $NaNOK=1; # are NaNs ok?my $nan = 'NaN'; # constants for easier lifemy $CALC = 'Math::BigInt::Calc'; # module to do low level mathmy $IMPORT = 0; # did import() yet?$round_mode = 'even'; # one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'$accuracy = undef;$precision = undef;$div_scale = 40;$upgrade = undef; # default is no upgrade$downgrade = undef; # default is no downgrade############################################################################### the old code had $rnd_mode, so we need to support it, too$rnd_mode = 'even';sub TIESCALAR { my ($class) = @_; bless \$round_mode, $class; }sub FETCH { return $round_mode; }sub STORE { $rnd_mode = $_[0]->round_mode($_[1]); }BEGIN { tie $rnd_mode, 'Math::BigInt'; }############################################################################## sub round_mode { no strict 'refs'; # make Class->round_mode() work my $self = shift; my $class = ref($self) || $self || __PACKAGE__; if (defined $_[0]) { my $m = shift; die "Unknown round mode $m" if $m !~ /^(even|odd|\+inf|\-inf|zero|trunc)$/; return ${"${class}::round_mode"} = $m; } return ${"${class}::round_mode"}; }sub upgrade { no strict 'refs'; # make Class->upgrade() work my $self = shift; my $class = ref($self) || $self || __PACKAGE__; # 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'; # make Class->round_mode() work my $self = shift; my $class = ref($self) || $self || __PACKAGE__; if (defined $_[0]) { die ('div_scale must be greater than zero') if $_[0] < 0; ${"${class}::div_scale"} = shift; } return ${"${class}::div_scale"}; }sub accuracy { # $x->accuracy($a); ref($x) $a # $x->accuracy(); ref($x) # Class->accuracy(); class # Class->accuracy($a); class $a my $x = shift; my $class = ref($x) || $x || __PACKAGE__; no strict 'refs'; # need to set new value? if (@_ > 0) { my $a = shift; die ('accuracy must not be zero') if defined $a && $a == 0; if (ref($x)) { # $object->accuracy() or fallback to global $x->bround($a) if defined $a; $x->{_a} = $a; # set/overwrite, even if not rounded $x->{_p} = undef; # clear P } else { # set global ${"${class}::accuracy"} = $a; ${"${class}::precision"} = undef; # clear P } return $a; # shortcut } my $r; # $object->accuracy() or fallback to global $r = $x->{_a} if ref($x); # but don't return global undef, when $x's accuracy is 0! $r = ${"${class}::accuracy"} if !defined $r; $r; } sub precision { # $x->precision($p); ref($x) $p # $x->precision(); ref($x) # Class->precision(); class # Class->precision($p); class $p my $x = shift; my $class = ref($x) || $x || __PACKAGE__; no strict 'refs'; # need to set new value? if (@_ > 0) { my $p = shift; if (ref($x)) { # $object->precision() or fallback to global $x->bfround($p) if defined $p; $x->{_p} = $p; # set/overwrite, even if not rounded $x->{_a} = undef; # clear A } else { # set global ${"${class}::precision"} = $p; ${"${class}::accuracy"} = undef; # clear A } return $p; # shortcut } my $r; # $object->precision() or fallback to global $r = $x->{_p} if ref($x); # but don't return global undef, when $x's precision is 0! $r = ${"${class}::precision"} if !defined $r; $r; } sub config { # return (later set?) configuration data as hash ref my $class = shift || 'Math::BigInt'; no strict 'refs'; my $lib = $CALC; my $cfg = { lib => $lib, lib_version => ${"${lib}::VERSION"}, class => $class, }; foreach ( qw/upgrade downgrade precision accuracy round_mode VERSION div_scale/) { $cfg->{lc($_)} = ${"${class}::$_"}; }; $cfg; }sub _scale_a { # select accuracy parameter based on precedence, # used by bround() and bfround(), may return undef for scale (means no op) my ($x,$s,$m,$scale,$mode) = @_; $scale = $x->{_a} if !defined $scale; $scale = $s if (!defined $scale); $mode = $m if !defined $mode; return ($scale,$mode); }sub _scale_p { # select precision parameter based on precedence, # used by bround() and bfround(), may return undef for scale (means no op) my ($x,$s,$m,$scale,$mode) = @_; $scale = $x->{_p} if !defined $scale; $scale = $s if (!defined $scale); $mode = $m if !defined $mode; return ($scale,$mode); }############################################################################### constructorssub copy { my ($c,$x); if (@_ > 1) { # if two arguments, the first one is the class to "swallow" subclasses ($c,$x) = @_; } else { $x = shift; $c = ref($x); } return unless ref($x); # only for objects my $self = {}; bless $self,$c; my $r; foreach my $k (keys %$x) { if ($k eq 'value') { $self->{value} = $CALC->_copy($x->{value}); next; } if (!($r = ref($x->{$k}))) { $self->{$k} = $x->{$k}; next; } if ($r eq 'SCALAR') { $self->{$k} = \${$x->{$k}}; } elsif ($r eq 'ARRAY') { $self->{$k} = [ @{$x->{$k}} ]; } elsif ($r eq 'HASH') { # only one level deep! foreach my $h (keys %{$x->{$k}}) { $self->{$k}->{$h} = $x->{$k}->{$h}; } } else # normal ref { my $xk = $x->{$k}; if ($xk->can('copy')) { $self->{$k} = $xk->copy(); } else { $self->{$k} = $xk->new($xk); } } } $self; }sub new { # create a new BigInt object from a string or another BigInt object. # see hash keys documented at top # the argument could be an object, so avoid ||, && etc on it, this would # cause costly overloaded code to be called. The only allowed ops are # ref() and defined. my ($class,$wanted,$a,$p,$r) = @_; # 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) && $wanted->isa($class); # MBI or subclass $class->import() if $IMPORT == 0; # make require work my $self = bless {}, $class; # shortcut for "normal" numbers if ((!ref $wanted) && ($wanted =~ /^([+-]?)[1-9][0-9]*\z/)) { $self->{sign} = $1 || '+'; my $ref = \$wanted; if ($wanted =~ /^[+-]/) { # remove sign without touching wanted to make it work with constants 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$/) { $self->{value} = $CALC->_zero(); $self->{sign} = $wanted; $self->{sign} = '+inf' if $self->{sign} eq 'inf'; return $self; } # split str in m mantissa, e exponent, i integer, f fraction, v value, s sign my ($mis,$miv,$mfv,$es,$ev) = _split(\$wanted); if (!ref $mis) { die "$wanted is not a number initialized to $class" if !$NaNOK; #print "NaN 1\n"; $self->{value} = $CALC->_zero(); $self->{sign} = $nan; return $self; } if (!ref $miv) { # _from_hex or _from_bin $self->{value} = $mis->{value}; $self->{sign} = $mis->{sign}; return $self; # throw away $mis } # make integer from mantissa by adjusting exp, then convert to bigint $self->{sign} = $$mis; # store sign $self->{value} = $CALC->_zero(); # for all the NaN cases
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -