my $class = "Math::BigInt";
use 5.006002;
-$VERSION = '1.82';
+$VERSION = '1.87';
@ISA = qw(Exporter);
@EXPORT_OK = qw(objectify bgcd blcm);
"$_[1]" cmp $_[0]->bstr() :
$_[0]->bstr() cmp "$_[1]" },
-# make cos()/sin()/atan2() "work" with BigInt's or subclasses
-'cos' => sub { cos($_[0]->numify()) },
-'sin' => sub { sin($_[0]->numify()) },
+'cos' => sub { $_[0]->copy->bcos(); },
+'sin' => sub { $_[0]->copy->bsin(); },
'atan2' => sub { $_[2] ?
atan2($_[1],$_[0]->numify()) :
atan2($_[0]->numify(),$_[1]) },
my $class = shift || 'Math::BigInt';
no strict 'refs';
- if (@_ > 0)
+ if (@_ > 1 || (@_ == 1 && (ref($_[0]) eq 'HASH')))
{
# try to set given options as arguments from hash
{
$cfg->{$key} = ${"${class}::$key"};
};
+ if (@_ == 1 && (ref($_[0]) ne 'HASH'))
+ {
+ # calls of the style config('lib') return just this value
+ return $cfg->{$_[0]};
+ }
$cfg;
}
sub copy
{
- my ($c,$x);
+ # if two arguments, the first one is the class to "swallow" subclasses
if (@_ > 1)
{
- # if two arguments, the first one is the class to "swallow" subclasses
- ($c,$x) = @_;
- }
- else
- {
- $x = shift;
- $c = ref($x);
+ my $self = bless {
+ sign => $_[1]->{sign},
+ value => $CALC->_copy($_[1]->{value}),
+ }, $_[0] if @_ > 1;
+
+ $self->{_a} = $_[1]->{_a} if defined $_[1]->{_a};
+ $self->{_p} = $_[1]->{_p} if defined $_[1]->{_p};
+ return $self;
}
- return unless ref($x); # only for objects
- my $self = bless {}, $c;
+ my $self = bless {
+ sign => $_[0]->{sign},
+ value => $CALC->_copy($_[0]->{value}),
+ }, ref($_[0]);
- $self->{sign} = $x->{sign};
- $self->{value} = $CALC->_copy($x->{value});
- $self->{_a} = $x->{_a} if defined $x->{_a};
- $self->{_p} = $x->{_p} if defined $x->{_p};
+ $self->{_a} = $_[0]->{_a} if defined $_[0]->{_a};
+ $self->{_p} = $_[0]->{_p} if defined $_[0]->{_p};
$self;
}
my $c = ref($self); # find out class of argument(s)
no strict 'refs';
+ # convert to normal scalar for speed and correctness in inner parts
+ $a = $a->can('numify') ? $a->numify() : "$a" if defined $a && ref($a);
+ $p = $p->can('numify') ? $p->numify() : "$p" if defined $p && ref($p);
+
# now pick $a or $p, but only if we have got "arguments"
if (!defined $a)
{
return $x if $x->modify('blog');
+ $base = $self->new($base) if defined $base && !ref $base;
+
# inf, -inf, NaN, <0 => NaN
return $x->bnan()
if $x->{sign} ne '+' || (defined $base && $base->{sign} ne '+');
$x->round(@r);
}
+sub bnok
+ {
+ # Calculate n over k (binomial coefficient or "choose" function) as integer.
+ # set up parameters
+ my ($self,$x,$y,@r) = (ref($_[0]),@_);
+
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,@r) = objectify(2,@_);
+ }
+
+ return $x if $x->modify('bnok');
+ return $x->bnan() if $x->{sign} eq 'NaN' || $y->{sign} eq 'NaN';
+ return $x->binf() if $x->{sign} eq '+inf';
+
+ # k > n or k < 0 => 0
+ my $cmp = $x->bacmp($y);
+ return $x->bzero() if $cmp < 0 || $y->{sign} =~ /^-/;
+ # k == n => 1
+ return $x->bone(@r) if $cmp == 0;
+
+ if ($CALC->can('_nok'))
+ {
+ $x->{value} = $CALC->_nok($x->{value},$y->{value});
+ }
+ else
+ {
+ # ( 7 ) 7! 7*6*5 * 4*3*2*1 7 * 6 * 5
+ # ( - ) = --------- = --------------- = ---------
+ # ( 3 ) 3! (7-3)! 3*2*1 * 4*3*2*1 3 * 2 * 1
+
+ # compute n - k + 2 (so we start with 5 in the example above)
+ my $z = $x - $y;
+ if (!$z->is_one())
+ {
+ $z->binc();
+ my $r = $z->copy(); $z->binc();
+ my $d = $self->new(2);
+ while ($z->bacmp($x) <= 0) # f < x ?
+ {
+ $r->bmul($z); $r->bdiv($d);
+ $z->binc(); $d->binc();
+ }
+ $x->{value} = $r->{value}; $x->{sign} = '+';
+ }
+ else { $x->bone(); }
+ }
+ $x->round(@r);
+ }
+
sub bexp
{
# Calculate e ** $x (Euler's number to the power of X), truncated to
my $u;
{
# run through Math::BigFloat unless told otherwise
+ require Math::BigFloat unless defined $upgrade;
local $upgrade = 'Math::BigFloat' unless defined $upgrade;
# calculate result, truncate it to integer
$u = $upgrade->bexp($upgrade->new($x),@r);
sub bmul
{
- # multiply two numbers -- stolen from Knuth Vol 2 pg 233
+ # multiply the first number by the second numbers
# (BINT or num_str, BINT or num_str) return BINT
# set up parameters
$x->round(@r);
}
+sub bmuladd
+ {
+ # multiply two numbers and then add the third to the result
+ # (BINT or num_str, BINT or num_str, BINT or num_str) return BINT
+
+ # set up parameters
+ my ($self,$x,$y,$z,@r) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,$z,@r) = objectify(3,@_);
+ }
+
+ return $x if $x->modify('bmuladd');
+
+ return $x->bnan() if ($x->{sign} eq $nan) ||
+ ($y->{sign} eq $nan) ||
+ ($z->{sign} eq $nan);
+
+ # inf handling of x and y
+ if (($x->{sign} =~ /^[+-]inf$/) || ($y->{sign} =~ /^[+-]inf$/))
+ {
+ return $x->bnan() if $x->is_zero() || $y->is_zero();
+ # result will always be +-inf:
+ # +inf * +/+inf => +inf, -inf * -/-inf => +inf
+ # +inf * -/-inf => -inf, -inf * +/+inf => -inf
+ return $x->binf() if ($x->{sign} =~ /^\+/ && $y->{sign} =~ /^\+/);
+ return $x->binf() if ($x->{sign} =~ /^-/ && $y->{sign} =~ /^-/);
+ return $x->binf('-');
+ }
+ # inf handling x*y and z
+ if (($z->{sign} =~ /^[+-]inf$/))
+ {
+ # something +-inf => +-inf
+ $x->{sign} = $z->{sign}, return $x if $z->{sign} =~ /^[+-]inf$/;
+ }
+
+ return $upgrade->bmuladd($x,$upgrade->new($y),$upgrade->new($z),@r)
+ if defined $upgrade && (!$y->isa($self) || !$z->isa($self) || !$x->isa($self));
+
+ # TODO: what it $y and $z have A or P set?
+ $r[3] = $z; # no push here
+
+ $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; # +1 * +1 or -1 * -1 => +
+
+ $x->{value} = $CALC->_mul($x->{value},$y->{value}); # do actual math
+ $x->{sign} = '+' if $CALC->_is_zero($x->{value}); # no -0
+
+ my ($sx, $sz) = ( $x->{sign}, $z->{sign} ); # get signs
+
+ if ($sx eq $sz)
+ {
+ $x->{value} = $CALC->_add($x->{value},$z->{value}); # same sign, abs add
+ }
+ else
+ {
+ my $a = $CALC->_acmp ($z->{value},$x->{value}); # absolute compare
+ if ($a > 0)
+ {
+ $x->{value} = $CALC->_sub($z->{value},$x->{value},1); # abs sub w/ swap
+ $x->{sign} = $sz;
+ }
+ elsif ($a == 0)
+ {
+ # speedup, if equal, set result to 0
+ $x->{value} = $CALC->_zero();
+ $x->{sign} = '+';
+ }
+ else # a < 0
+ {
+ $x->{value} = $CALC->_sub($x->{value}, $z->{value}); # abs sub
+ }
+ }
+ $x->round(@r);
+ }
+
sub _div_inf
{
# helper function that handles +-inf cases for bdiv()/bmod() to reuse code
sub bmodpow
{
# takes a very large number to a very large exponent in a given very
- # large modulus, quickly, thanks to binary exponentation. supports
+ # large modulus, quickly, thanks to binary exponentation. Supports
# negative exponents.
my ($self,$num,$exp,$mod,@r) = objectify(3,@_);
}
###############################################################################
+# trigonometric functions
+
+sub bpi
+ {
+ # Calculate PI to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer, that is, always returns '3'.
+ my ($self,$n) = @_;
+ if (@_ == 1)
+ {
+ # called like Math::BigInt::bpi(10);
+ $n = $self; $self = $class;
+ }
+ $self = ref($self) if ref($self);
+
+ return $upgrade->new($n) if defined $upgrade;
+
+ # hard-wired to "3"
+ $self->new(3);
+ }
+
+sub bcos
+ {
+ # Calculate cosinus(x) to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('bcos');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->bcos(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->bcos(@r)->as_int();
+
+ $x->bone() if $t->is_one();
+ $x->bzero() if $t->is_zero();
+ $x->round(@r);
+ }
+
+sub bsin
+ {
+ # Calculate sinus(x) to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('bsin');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->bsin(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->bsin(@r)->as_int();
+
+ $x->bone() if $t->is_one();
+ $x->bzero() if $t->is_zero();
+ $x->round(@r);
+ }
+
+sub batan
+ {
+ # Calculate arcus tangens of x to N digits. Unless upgrading is in effect, returns the
+ # result truncated to an integer.
+ my ($self,$x,@r) = ref($_[0]) ? (undef,@_) : objectify(1,@_);
+
+ return $x if $x->modify('batan');
+
+ return $x->bnan() if $x->{sign} !~ /^[+-]\z/; # -inf +inf or NaN => NaN
+
+ return $upgrade->new($x)->batan(@r) if defined $upgrade;
+
+ # calculate the result and truncate it to integer
+ my $t = Math::BigFloat->new($x)->batan(@r);
+
+ $x->{value} = $CALC->_new( $x->as_int()->bstr() );
+ $x->round(@r);
+ }
+
+###############################################################################
# this method returns 0 if the object can be modified, or 1 if not.
# We use a fast constant sub() here, to avoid costly calls. Subclasses
# may override it with special code (f.i. Math::BigInt::Constant does so)
my $n = 1; my $sign = '-';
# Number creation
- $x = Math::BigInt->new($str); # defaults to 0
- $y = $x->copy(); # make a true copy
- $nan = Math::BigInt->bnan(); # create a NotANumber
- $zero = Math::BigInt->bzero(); # create a +0
- $inf = Math::BigInt->binf(); # create a +inf
- $inf = Math::BigInt->binf('-'); # create a -inf
- $one = Math::BigInt->bone(); # create a +1
- $one = Math::BigInt->bone('-'); # create a -1
+ my $x = Math::BigInt->new($str); # defaults to 0
+ my $y = $x->copy(); # make a true copy
+ my $nan = Math::BigInt->bnan(); # create a NotANumber
+ my $zero = Math::BigInt->bzero(); # create a +0
+ my $inf = Math::BigInt->binf(); # create a +inf
+ my $inf = Math::BigInt->binf('-'); # create a -inf
+ my $one = Math::BigInt->bone(); # create a +1
+ my $mone = Math::BigInt->bone('-'); # create a -1
+
+ my $pi = Math::BigInt->bpi(); # returns '3'
+ # see Math::BigFloat::bpi()
$h = Math::BigInt->new('0x123'); # from hexadecimal
$b = Math::BigInt->new('0b101'); # from binary
$x->bdiv($y); # divide, set $x to quotient
# return (quo,rem) or quo if scalar
+ $x->bmuladd($y,$z); # $x = $x * $y + $z
+
$x->bmod($y); # modulus (x % y)
$x->bmodpow($exp,$mod); # modular exponentation (($num**$exp) % $mod))
$x->bmodinv($mod); # the inverse of $x in the given modulus $mod
$x->broot($y); # $y'th root of $x (e.g. $y == 3 => cubic root)
$x->bfac(); # factorial of $x (1*2*3*4*..$x)
+ $x->bnok($y); # x over y (binomial coefficient n over k)
+
$x->blog(); # logarithm of $x to base e (Euler's number)
$x->blog($base); # logarithm of $x to base $base (f.i. 2)
$x->bexp(); # calculate e ** $x where e is Euler's number
$x->babs();
-Set the number to it's absolute value, e.g. change the sign from '-' to '+'
+Set the number to its absolute value, e.g. change the sign from '-' to '+'
and from '-inf' to '+inf', respectively. Does nothing for NaN or positive
numbers.
$x->bnot();
-Two's complement (bit wise not). This is equivalent to
+Two's complement (bitwise not). This is equivalent to
$x->binc()->bneg();
$x->bmul($y); # multiplication (multiply $x by $y)
+=head2 bmuladd()
+
+ $x->bmuladd($y,$z);
+
+Multiply $x by $y, and then add $z to the result,
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
=head2 bdiv()
$x->bdiv($y); # divide, set $x to quotient
See also L<blog()>.
+=head2 bnok()
+
+ $x->bnok($y); # x over y (binomial coefficient n over k)
+
+Calculates the binomial coefficient n over k, also called the "choose"
+function. The result is equivalent to:
+
+ ( n ) n!
+ | - | = -------
+ ( k ) k!(n-k)!
+
+This method was added in v1.84 of Math::BigInt (April 2007).
+
+=head2 bpi()
+
+ print Math::BigInt->bpi(100), "\n"; # 3
+
+Returns PI truncated to an integer, with the argument being ignored. that
+is it always returns C<3>.
+
+If upgrading is in effect, returns PI to N digits (including the "3"
+before the dot):
+
+ use Math::BigFloat;
+ use Math::BigInt upgrade => Math::BigFloat;
+ print Math::BigInt->bpi(3), "\n"; # 3.14
+ print Math::BigInt->bpi(100), "\n"; # 3.1415....
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 bcos()
+
+ my $x = Math::BigFloat->new(1);
+ print $x->bcos(100), "\n";
+
+Calculate the cosinus of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 bsin()
+
+ my $x = Math::BigFloat->new(1);
+ print $x->bsin(100), "\n";
+
+Calculate the sinus of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
+=head2 batan()
+
+ my $x = Math::BigFloat->new(0.5);
+ print $x->batan(100), "\n";
+
+Calculate the arcus tanges of $x, modifying $x in place.
+
+This method was added in v1.87 of Math::BigInt (June 2007).
+
=head2 blsft()
$x->blsft($y); # left shift in base 2
=item Creating numbers
- * When you create a number, you can give it's desired A or P via:
+ * When you create a number, you can give the desired A or P via:
$x = Math::BigInt->new($number,$A,$P);
* Only one of A or P can be defined, otherwise the result is NaN
* If no A or P is give ($x = Math::BigInt->new($number) form), then the
$x will be what was in effect when $x was created)
* If given undef for A and P, B<no> rounding will occur, and the globals will
B<not> be used. This is used by subclasses to create numbers without
- suffering rounding in the parent. Thus a subclass is able to have it's own
+ suffering rounding in the parent. Thus a subclass is able to have its own
globals enforced upon creation of a number by using
C<< $x = Math::BigInt->new($number,undef,undef) >>:
All other object methods and overloaded functions can be directly inherited
from the parent class.
-At the very minimum, any subclass will need to provide it's own C<new()> and can
+At the very minimum, any subclass will need to provide its own C<new()> and can
store additional hash keys in the object. There are also some package globals
that must be defined, e.g.:
arguments are of the class mentioned in $upgrade (This might change in later
versions to a more sophisticated scheme):
+=head1 EXPORTS
+
+C<Math::BigInt> exports nothing by default, but can export the following methods:
+
+ bgcd
+ blcm
+
=head1 BUGS
=over 2
drop the leading '+'. The old code would return '+3', the new returns '3'.
This is to be consistent with Perl and to make C<cmp> (especially with
overloading) to work as you expect. It also solves problems with C<Test.pm>,
-because it's C<ok()> uses 'eq' internally.
+because its C<ok()> uses 'eq' internally.
Mark Biggar said, when asked about to drop the '+' altogether, or make only
C<cmp> work: