-#!/usr/bin/perl -w
+
+#
+# "Tax the rat farms." - Lord Vetinari
+#
# The following hash values are used:
# sign : +,-,NaN,+inf,-inf
# _n : numeraotr (value = _n/_d)
# _a : accuracy
# _p : precision
-# _f : flags, used by MBR to flag parts of a rationale as untouchable
+# You should not look at the innards of a BigRat - use the methods for this.
package Math::BigRat;
-require 5.005_02;
+# anythig older is untested, and unlikely to work
+use 5.006;
use strict;
-use Exporter;
use Math::BigFloat;
-use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
- $accuracy $precision $round_mode $div_scale);
+use vars qw($VERSION @ISA $upgrade $downgrade
+ $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf);
-@ISA = qw(Exporter Math::BigFloat);
-@EXPORT_OK = qw();
+@ISA = qw(Math::BigFloat);
-$VERSION = '0.05';
+$VERSION = '0.21';
-use overload; # inherit from Math::BigFloat
+use overload; # inherit overload from Math::BigFloat
-##############################################################################
-# global constants, flags and accessory
+BEGIN
+ {
+ *objectify = \&Math::BigInt::objectify; # inherit this from BigInt
+ *AUTOLOAD = \&Math::BigFloat::AUTOLOAD; # can't inherit AUTOLOAD
+ # we inherit these from BigFloat because currently it is not possible
+ # that MBF has a different $MBI variable than we, because MBF also uses
+ # Math::BigInt::config->('lib'); (there is always only one library loaded)
+ *_e_add = \&Math::BigFloat::_e_add;
+ *_e_sub = \&Math::BigFloat::_e_sub;
+ *as_int = \&as_number;
+ *is_pos = \&is_positive;
+ *is_neg = \&is_negative;
+ }
-use constant MB_NEVER_ROUND => 0x0001;
+##############################################################################
+# Global constants and flags. Access these only via the accessor methods!
$accuracy = $precision = undef;
$round_mode = 'even';
$upgrade = undef;
$downgrade = undef;
+# These are internally, and not to be used from the outside at all!
+
+$_trap_nan = 0; # are NaNs ok? set w/ config()
+$_trap_inf = 0; # are infs ok? set w/ config()
+
+# the package we are using for our private parts, defaults to:
+# Math::BigInt->config()->{lib}
+my $MBI = 'Math::BigInt::Calc';
+
my $nan = 'NaN';
my $class = 'Math::BigRat';
UNIVERSAL::isa(@_);
}
+##############################################################################
+
sub _new_from_float
{
- # turn a single float input into a rationale (like '0.1')
+ # turn a single float input into a rational number (like '0.1')
my ($self,$f) = @_;
return $self->bnan() if $f->is_nan();
- return $self->binf('-inf') if $f->{sign} eq '-inf';
- return $self->binf('+inf') if $f->{sign} eq '+inf';
+ return $self->binf($f->{sign}) if $f->{sign} =~ /^[+-]inf$/;
- #print "f $f caller", join(' ',caller()),"\n";
- $self->{_n} = $f->{_m}->copy(); # mantissa
- $self->{_d} = Math::BigInt->bone();
- $self->{sign} = $f->{sign}; $self->{_n}->{sign} = '+';
- if ($f->{_e}->{sign} eq '-')
+ $self->{_n} = $MBI->_copy( $f->{_m} ); # mantissa
+ $self->{_d} = $MBI->_one();
+ $self->{sign} = $f->{sign} || '+';
+ if ($f->{_es} eq '-')
{
# something like Math::BigRat->new('0.1');
- $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
+ # 1 / 1 => 1/10
+ $MBI->_lsft ( $self->{_d}, $f->{_e} ,10);
}
else
{
# something like Math::BigRat->new('10');
# 1 / 1 => 10/1
- $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
+ $MBI->_lsft ( $self->{_n}, $f->{_e} ,10) unless
+ $MBI->_is_zero($f->{_e});
}
-# print "float new $self->{_n} / $self->{_d}\n";
$self;
}
# create a Math::BigRat
my $class = shift;
- my ($n,$d) = shift;
+ my ($n,$d) = @_;
my $self = { }; bless $self,$class;
-# print "ref ",ref($d),"\n";
-# if (ref($d))
-# {
-# print "isa float ",$d->isa('Math::BigFloat'),"\n";
-# print "isa int ",$d->isa('Math::BigInt'),"\n";
-# print "isa rat ",$d->isa('Math::BigRat'),"\n";
-# }
-
- # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
-
- if ((ref $n) && (!$n->isa('Math::BigRat')))
+ # input like (BigInt) or (BigFloat):
+ if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat')))
{
-# print "is ref, but not rat\n";
if ($n->isa('Math::BigFloat'))
{
- # print "is ref, and float\n";
- return $self->_new_from_float($n)->bnorm();
+ $self->_new_from_float($n);
}
if ($n->isa('Math::BigInt'))
{
-# print "is ref, and int\n";
- $self->{_n} = $n->copy(); # "mantissa" = $n
- $self->{_d} = Math::BigInt->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- return $self->bnorm();
+ # TODO: trap NaN, inf
+ $self->{_n} = $MBI->_copy($n->{value}); # "mantissa" = N
+ $self->{_d} = $MBI->_one(); # d => 1
+ $self->{sign} = $n->{sign};
}
if ($n->isa('Math::BigInt::Lite'))
{
-# print "is ref, and lite\n";
- $self->{_n} = Math::BigInt->new($$n); # "mantissa" = $n
- $self->{_d} = Math::BigInt->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- return $self->bnorm();
+ # TODO: trap NaN, inf
+ $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0;
+ $self->{_n} = $MBI->_new(abs($$n)); # "mantissa" = N
+ $self->{_d} = $MBI->_one(); # d => 1
+ }
+ return $self->bnorm(); # normalize (120/1 => 12/10)
+ }
+
+ # input like (BigInt,BigInt) or (BigLite,BigLite):
+ if (ref($d) && ref($n))
+ {
+ # do N first (for $self->{sign}):
+ if ($n->isa('Math::BigInt'))
+ {
+ # TODO: trap NaN, inf
+ $self->{_n} = $MBI->_copy($n->{value}); # "mantissa" = N
+ $self->{sign} = $n->{sign};
+ }
+ elsif ($n->isa('Math::BigInt::Lite'))
+ {
+ # TODO: trap NaN, inf
+ $self->{sign} = '+'; $self->{sign} = '-' if $$n < 0;
+ $self->{_n} = $MBI->_new(abs($$n)); # "mantissa" = $n
+ }
+ else
+ {
+ require Carp;
+ Carp::croak(ref($n) . " is not a recognized object format for Math::BigRat->new");
+ }
+ # now D:
+ if ($d->isa('Math::BigInt'))
+ {
+ # TODO: trap NaN, inf
+ $self->{_d} = $MBI->_copy($d->{value}); # "mantissa" = D
+ # +/+ or -/- => +, +/- or -/+ => -
+ $self->{sign} = $d->{sign} ne $self->{sign} ? '-' : '+';
+ }
+ elsif ($d->isa('Math::BigInt::Lite'))
+ {
+ # TODO: trap NaN, inf
+ $self->{_d} = $MBI->_new(abs($$d)); # "mantissa" = D
+ my $ds = '+'; $ds = '-' if $$d < 0;
+ # +/+ or -/- => +, +/- or -/+ => -
+ $self->{sign} = $ds ne $self->{sign} ? '-' : '+';
+ }
+ else
+ {
+ require Carp;
+ Carp::croak(ref($d) . " is not a recognized object format for Math::BigRat->new");
}
+ return $self->bnorm(); # normalize (120/1 => 12/10)
}
- return $n->copy() if ref $n;
-
-# print "is string\n";
+ return $n->copy() if ref $n; # already a BigRat
if (!defined $n)
{
- $self->{_n} = Math::BigInt->bzero(); # undef => 0
- $self->{_d} = Math::BigInt->bone();
+ $self->{_n} = $MBI->_zero(); # undef => 0
+ $self->{_d} = $MBI->_one();
$self->{sign} = '+';
- return $self->bnorm();
+ return $self;
}
+
# string input with / delimiter
if ($n =~ /\s*\/\s*/)
{
- return Math::BigRat->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
- return Math::BigRat->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
+ return $class->bnan() if $n =~ /\/.*\//; # 1/2/3 isn't valid
+ return $class->bnan() if $n =~ /\/\s*$/; # 1/ isn't valid
($n,$d) = split (/\//,$n);
# try as BigFloats first
if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
{
+ local $Math::BigFloat::accuracy = undef;
+ local $Math::BigFloat::precision = undef;
+
# one of them looks like a float
- $self->_new_from_float(Math::BigFloat->new($n));
+ my $nf = Math::BigFloat->new($n,undef,undef);
+ $self->{sign} = '+';
+ return $self->bnan() if $nf->is_nan();
+
+ $self->{_n} = $MBI->_copy( $nf->{_m} ); # get mantissa
+
# now correct $self->{_n} due to $n
- my $f = Math::BigFloat->new($d);
- if ($f->{_e}->{sign} eq '-')
+ my $f = Math::BigFloat->new($d,undef,undef);
+ return $self->bnan() if $f->is_nan();
+ $self->{_d} = $MBI->_copy( $f->{_m} );
+
+ # calculate the difference between nE and dE
+ my $diff_e = $nf->exponent()->bsub( $f->exponent);
+ if ($diff_e->is_negative())
+ {
+ # < 0: mul d with it
+ $MBI->_lsft( $self->{_d}, $MBI->_new( $diff_e->babs()), 10);
+ }
+ elsif (!$diff_e->is_zero())
{
- # 10 / 0.1 => 100/1
- $self->{_n}->blsft($f->{_e}->copy()->babs(),10);
+ # > 0: mul n with it
+ $MBI->_lsft( $self->{_n}, $MBI->_new( $diff_e), 10);
}
- else
- {
- $self->{_d}->blsft($f->{_e},10); # 1 / 1 => 10/1
- }
}
else
{
- $self->{_n} = Math::BigInt->new($n);
- $self->{_d} = Math::BigInt->new($d);
- return $self->bnan() if $self->{_n}->is_nan() || $self->{_d}->is_nan();
- # inf handling is missing here
-
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- # if $d is negative, flip sign
- $self->{sign} =~ tr/+-/-+/ if $self->{_d}->{sign} eq '-';
- $self->{_d}->{sign} = '+'; # normalize
+ # both d and n look like (big)ints
+
+ $self->{sign} = '+'; # no sign => '+'
+ $self->{_n} = undef;
+ $self->{_d} = undef;
+ if ($n =~ /^([+-]?)0*([0-9]+)\z/) # first part ok?
+ {
+ $self->{sign} = $1 || '+'; # no sign => '+'
+ $self->{_n} = $MBI->_new($2 || 0);
+ }
+
+ if ($d =~ /^([+-]?)0*([0-9]+)\z/) # second part ok?
+ {
+ $self->{sign} =~ tr/+-/-+/ if ($1 || '') eq '-'; # negate if second part neg.
+ $self->{_d} = $MBI->_new($2 || 0);
+ }
+
+ if (!defined $self->{_n} || !defined $self->{_d})
+ {
+ $d = Math::BigInt->new($d,undef,undef) unless ref $d;
+ $n = Math::BigInt->new($n,undef,undef) unless ref $n;
+
+ if ($n->{sign} =~ /^[+-]$/ && $d->{sign} =~ /^[+-]$/)
+ {
+ # both parts are ok as integers (wierd things like ' 1e0'
+ $self->{_n} = $MBI->_copy($n->{value});
+ $self->{_d} = $MBI->_copy($d->{value});
+ $self->{sign} = $n->{sign};
+ $self->{sign} =~ tr/+-/-+/ if $d->{sign} eq '-'; # -1/-2 => 1/2
+ return $self->bnorm();
+ }
+
+ $self->{sign} = '+'; # a default sign
+ return $self->bnan() if $n->is_nan() || $d->is_nan();
+
+ # handle inf cases:
+ if ($n->is_inf() || $d->is_inf())
+ {
+ if ($n->is_inf())
+ {
+ return $self->bnan() if $d->is_inf(); # both are inf => NaN
+ my $s = '+'; # '+inf/+123' or '-inf/-123'
+ $s = '-' if substr($n->{sign},0,1) ne $d->{sign};
+ # +-inf/123 => +-inf
+ return $self->binf($s);
+ }
+ # 123/inf => 0
+ return $self->bzero();
+ }
+ }
}
+
return $self->bnorm();
}
# simple string input
if (($n =~ /[\.eE]/))
{
- # looks like a float
-# print "float-like string $d\n";
- $self->_new_from_float(Math::BigFloat->new($n));
+ # looks like a float, quacks like a float, so probably is a float
+ $self->{sign} = 'NaN';
+ local $Math::BigFloat::accuracy = undef;
+ local $Math::BigFloat::precision = undef;
+ $self->_new_from_float(Math::BigFloat->new($n,undef,undef));
}
else
{
- $self->{_n} = Math::BigInt->new($n);
- $self->{_d} = Math::BigInt->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
+ # for simple forms, use $MBI directly
+ if ($n =~ /^([+-]?)0*([0-9]+)\z/)
+ {
+ $self->{sign} = $1 || '+';
+ $self->{_n} = $MBI->_new($2 || 0);
+ $self->{_d} = $MBI->_one();
+ }
+ else
+ {
+ my $n = Math::BigInt->new($n,undef,undef);
+ $self->{_n} = $MBI->_copy($n->{value});
+ $self->{_d} = $MBI->_one();
+ $self->{sign} = $n->{sign};
+ return $self->bnan() if $self->{sign} eq 'NaN';
+ return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/;
+ }
}
$self->bnorm();
}
-###############################################################################
+sub copy
+ {
+ # if two arguments, the first one is the class to "swallow" subclasses
+ my ($c,$x) = @_;
+
+ if (scalar @_ == 1)
+ {
+ $x = $_[0];
+ $c = ref($x);
+ }
+ return unless ref($x); # only for objects
+
+ my $self = bless {}, $c;
+
+ $self->{sign} = $x->{sign};
+ $self->{_d} = $MBI->_copy($x->{_d});
+ $self->{_n} = $MBI->_copy($x->{_n});
+ $self->{_a} = $x->{_a} if defined $x->{_a};
+ $self->{_p} = $x->{_p} if defined $x->{_p};
+ $self;
+ }
+
+##############################################################################
+
+sub config
+ {
+ # return (later set?) configuration data as hash ref
+ my $class = shift || 'Math::BigRat';
+
+ if (@_ == 1 && ref($_[0]) ne 'HASH')
+ {
+ my $cfg = $class->SUPER::config();
+ return $cfg->{$_[0]};
+ }
+
+ my $cfg = $class->SUPER::config(@_);
+
+ # now we need only to override the ones that are different from our parent
+ $cfg->{class} = $class;
+ $cfg->{with} = $MBI;
+ $cfg;
+ }
+
+##############################################################################
sub bstr
{
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
{
return $s;
}
-# print "bstr $x->{sign} $x->{_n} $x->{_d}\n";
- my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
+ my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2'
- return $s.$x->{_n}->bstr() if $x->{_d}->is_one();
- return $s.$x->{_n}->bstr() . '/' . $x->{_d}->bstr();
+ return $s . $MBI->_str($x->{_n}) if $MBI->_is_one($x->{_d});
+ $s . $MBI->_str($x->{_n}) . '/' . $MBI->_str($x->{_d});
}
sub bsstr
{
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
{
}
my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
- return $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
+ $s . $MBI->_str($x->{_n}) . '/' . $MBI->_str($x->{_d});
}
sub bnorm
{
- # reduce the number to the shortest form and remember this (so that we
- # don't reduce again)
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ # reduce the number to the shortest form
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ # Both parts must be objects of whatever we are using today.
+ if ( my $c = $MBI->_check($x->{_n}) )
+ {
+ require Carp; Carp::croak ("n did not pass the self-check ($c) in bnorm()");
+ }
+ if ( my $c = $MBI->_check($x->{_d}) )
+ {
+ require Carp; Carp::croak ("d did not pass the self-check ($c) in bnorm()");
+ }
- # this is to prevent automatically rounding when MBI's globals are set
- $x->{_d}->{_f} = MB_NEVER_ROUND;
- $x->{_n}->{_f} = MB_NEVER_ROUND;
- # 'forget' that parts were rounded via MBI::bround() in MBF's bfround()
- $x->{_d}->{_a} = undef; $x->{_n}->{_a} = undef;
- $x->{_d}->{_p} = undef; $x->{_n}->{_p} = undef;
+ # no normalize for NaN, inf etc.
+ return $x if $x->{sign} !~ /^[+-]$/;
# normalize zeros to 0/1
- if (($x->{sign} =~ /^[+-]$/) &&
- ($x->{_n}->is_zero()))
+ if ($MBI->_is_zero($x->{_n}))
{
- $x->{sign} = '+'; # never -0
- $x->{_d} = Math::BigInt->bone() unless $x->{_d}->is_one();
+ $x->{sign} = '+'; # never leave a -0
+ $x->{_d} = $MBI->_one() unless $MBI->_is_one($x->{_d});
return $x;
}
-# print "$x->{_n} / $x->{_d} => ";
- # reduce other numbers
- # print "bgcd $x->{_n} (",ref($x->{_n}),") $x->{_d} (",ref($x->{_d}),")\n";
- # disable upgrade in BigInt, otherwise deep recursion
- local $Math::BigInt::upgrade = undef;
- my $gcd = $x->{_n}->bgcd($x->{_d});
+ return $x if $MBI->_is_one($x->{_d}); # no need to reduce
- if (!$gcd->is_one())
+ # reduce other numbers
+ my $gcd = $MBI->_copy($x->{_n});
+ $gcd = $MBI->_gcd($gcd,$x->{_d});
+
+ if (!$MBI->_is_one($gcd))
{
- $x->{_n}->bdiv($gcd);
- $x->{_d}->bdiv($gcd);
+ $x->{_n} = $MBI->_div($x->{_n},$gcd);
+ $x->{_d} = $MBI->_div($x->{_d},$gcd);
}
-# print "$x->{_n} / $x->{_d}\n";
+ $x;
+ }
+
+##############################################################################
+# sign manipulation
+
+sub bneg
+ {
+ # (BRAT or num_str) return BRAT
+ # negate number or make a negated number from string
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $x if $x->modify('bneg');
+
+ # for +0 dont negate (to have always normalized +0). Does nothing for 'NaN'
+ $x->{sign} =~ tr/+-/-+/ unless ($x->{sign} eq '+' && $MBI->_is_zero($x->{_n}));
$x;
}
sub _bnan
{
- # used by parent class bone() to initialize number to 1
+ # used by parent class bnan() to initialize number to NaN
my $self = shift;
- $self->{_n} = Math::BigInt->bzero();
- $self->{_d} = Math::BigInt->bzero();
+
+ if ($_trap_nan)
+ {
+ require Carp;
+ my $class = ref($self);
+ # "$self" below will stringify the object, this blows up if $self is a
+ # partial object (happens under trap_nan), so fix it beforehand
+ $self->{_d} = $MBI->_zero() unless defined $self->{_d};
+ $self->{_n} = $MBI->_zero() unless defined $self->{_n};
+ Carp::croak ("Tried to set $self to NaN in $class\::_bnan()");
+ }
+ $self->{_n} = $MBI->_zero();
+ $self->{_d} = $MBI->_zero();
}
sub _binf
{
- # used by parent class bone() to initialize number to 1
+ # used by parent class bone() to initialize number to +inf/-inf
my $self = shift;
- $self->{_n} = Math::BigInt->bzero();
- $self->{_d} = Math::BigInt->bzero();
+
+ if ($_trap_inf)
+ {
+ require Carp;
+ my $class = ref($self);
+ # "$self" below will stringify the object, this blows up if $self is a
+ # partial object (happens under trap_nan), so fix it beforehand
+ $self->{_d} = $MBI->_zero() unless defined $self->{_d};
+ $self->{_n} = $MBI->_zero() unless defined $self->{_n};
+ Carp::croak ("Tried to set $self to inf in $class\::_binf()");
+ }
+ $self->{_n} = $MBI->_zero();
+ $self->{_d} = $MBI->_zero();
}
sub _bone
{
- # used by parent class bone() to initialize number to 1
+ # used by parent class bone() to initialize number to +1/-1
my $self = shift;
- $self->{_n} = Math::BigInt->bone();
- $self->{_d} = Math::BigInt->bone();
+ $self->{_n} = $MBI->_one();
+ $self->{_d} = $MBI->_one();
}
sub _bzero
{
- # used by parent class bone() to initialize number to 1
+ # used by parent class bzero() to initialize number to 0
my $self = shift;
- $self->{_n} = Math::BigInt->bzero();
- $self->{_d} = Math::BigInt->bone();
+ $self->{_n} = $MBI->_zero();
+ $self->{_d} = $MBI->_one();
}
##############################################################################
sub badd
{
- # add two rationales
- my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
+ # add two rational numbers
+
+ # 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,@_);
+ }
- $x = $class->new($x) unless $x->isa($class);
- $y = $class->new($y) unless $y->isa($class);
+ # +inf + +inf => +inf, -inf + -inf => -inf
+ return $x->binf(substr($x->{sign},0,1))
+ if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
- return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
+ # +inf + -inf or -inf + +inf => NaN
+ return $x->bnan() if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/);
# 1 1 gcd(3,4) = 1 1*3 + 1*4 7
# - + - = --------- = --
# 4 3 4*3 12
- my $gcd = $x->{_d}->bgcd($y->{_d});
+ # we do not compute the gcd() here, but simple do:
+ # 5 7 5*3 + 7*4 43
+ # - + - = --------- = --
+ # 4 3 4*3 12
+
+ # and bnorm() will then take care of the rest
- my $aa = $x->{_d}->copy();
- my $bb = $y->{_d}->copy();
- if ($gcd->is_one())
- {
- $bb->bdiv($gcd); $aa->bdiv($gcd);
- }
- $x->{_n}->bmul($bb); $x->{_n}->{sign} = $x->{sign};
- my $m = $y->{_n}->copy()->bmul($aa);
- $m->{sign} = $y->{sign}; # 2/1 - 2/1
- $x->{_n}->badd($m);
+ # 5 * 3
+ $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_d});
+
+ # 7 * 4
+ my $m = $MBI->_mul( $MBI->_copy( $y->{_n} ), $x->{_d} );
- $x->{_d}->bmul($y->{_d});
+ # 5 * 3 + 7 * 4
+ ($x->{_n}, $x->{sign}) = _e_add( $x->{_n}, $m, $x->{sign}, $y->{sign});
- # calculate new sign
- $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
+ # 4 * 3
+ $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_d});
- $x->bnorm()->round($a,$p,$r);
+ # normalize result, and possible round
+ $x->bnorm()->round(@r);
}
sub bsub
{
- # subtract two rationales
- my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
-
- $x = $class->new($x) unless $x->isa($class);
- $y = $class->new($y) unless $y->isa($class);
-
- return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
- # TODO: inf handling
-
- # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
- # - + - = --------- = --
- # 4 3 4*3 12
-
- my $gcd = $x->{_d}->bgcd($y->{_d});
+ # subtract two rational numbers
- my $aa = $x->{_d}->copy();
- my $bb = $y->{_d}->copy();
- if ($gcd->is_one())
+ # set up parameters
+ my ($self,$x,$y,@r) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
{
- $bb->bdiv($gcd); $aa->bdiv($gcd);
+ ($self,$x,$y,@r) = objectify(2,@_);
}
- $x->{_n}->bmul($bb); $x->{_n}->{sign} = $x->{sign};
- my $m = $y->{_n}->copy()->bmul($aa);
- $m->{sign} = $y->{sign}; # 2/1 - 2/1
- $x->{_n}->bsub($m);
-
- $x->{_d}->bmul($y->{_d});
-
- # calculate new sign
- $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
- $x->bnorm()->round($a,$p,$r);
+ # flip sign of $x, call badd(), then flip sign of result
+ $x->{sign} =~ tr/+-/-+/
+ unless $x->{sign} eq '+' && $MBI->_is_zero($x->{_n}); # not -0
+ $x->badd($y,@r); # does norm and round
+ $x->{sign} =~ tr/+-/-+/
+ unless $x->{sign} eq '+' && $MBI->_is_zero($x->{_n}); # not -0
+ $x;
}
sub bmul
{
- # multiply two rationales
- my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
-
- $x = $class->new($x) unless $x->isa($class);
- $y = $class->new($y) unless $y->isa($class);
+ # multiply two rational numbers
+
+ # 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->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
# x== 0 # also: or y == 1 or y == -1
return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
- # According to Knuth, this can be optimized by doingtwice gcd (for d and n)
- # and reducing in one step)
+ # XXX TODO:
+ # According to Knuth, this can be optimized by doing gcd twice (for d and n)
+ # and reducing in one step. This would save us the bnorm() at the end.
- # 1 1 2 1
- # - * - = - = -
- # 4 3 12 6
- $x->{_n}->bmul($y->{_n});
- $x->{_d}->bmul($y->{_d});
+ # 1 2 1 * 2 2 1
+ # - * - = ----- = - = -
+ # 4 3 4 * 3 12 6
+
+ $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_n});
+ $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_d});
# compute new sign
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
- $x->bnorm()->round($a,$p,$r);
+ $x->bnorm()->round(@r);
}
sub bdiv
{
# (dividend: BRAT or num_str, divisor: BRAT or num_str) return
# (BRAT,BRAT) (quo,rem) or BRAT (only rem)
- my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
- $x = $class->new($x) unless $x->isa($class);
- $y = $class->new($y) unless $y->isa($class);
+ # 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 $self->_div_inf($x,$y)
if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
# x== 0 # also: or y == 1 or y == -1
return wantarray ? ($x,$self->bzero()) : $x if $x->is_zero();
- # TODO: list context, upgrade
+ # XXX TODO: list context, upgrade
+ # According to Knuth, this can be optimized by doing gcd twice (for d and n)
+ # and reducing in one step. This would save us the bnorm() at the end.
# 1 1 1 3
# - / - == - * -
# 4 3 4 1
- $x->{_n}->bmul($y->{_d});
- $x->{_d}->bmul($y->{_n});
+
+ $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_d});
+ $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_n});
# compute new sign
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
- $x->bnorm()->round($a,$p,$r);
+ $x->bnorm()->round(@r);
+ $x;
+ }
+
+sub bmod
+ {
+ # compute "remainder" (in Perl way) of $x / $y
+
+ # 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 $self->_div_inf($x,$y)
+ if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/) || $y->is_zero());
+
+ return $x if $x->is_zero(); # 0 / 7 = 0, mod 0
+
+ # compute $x - $y * floor($x/$y), keeping the sign of $x
+
+ # copy x to u, make it positive and then do a normal division ($u/$y)
+ my $u = bless { sign => '+' }, $self;
+ $u->{_n} = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d} );
+ $u->{_d} = $MBI->_mul( $MBI->_copy($x->{_d}), $y->{_n} );
+
+ # compute floor(u)
+ if (! $MBI->_is_one($u->{_d}))
+ {
+ $u->{_n} = $MBI->_div($u->{_n},$u->{_d}); # 22/7 => 3/1 w/ truncate
+ # no need to set $u->{_d} to 1, since below we set it to $y->{_d} anyway
+ }
+
+ # now compute $y * $u
+ $u->{_d} = $MBI->_copy($y->{_d}); # 1 * $y->{_d}, see floor above
+ $u->{_n} = $MBI->_mul($u->{_n},$y->{_n});
+
+ my $xsign = $x->{sign}; $x->{sign} = '+'; # remember sign and make x positive
+ # compute $x - $u
+ $x->bsub($u);
+ $x->{sign} = $xsign; # put sign back
+
+ $x->bnorm()->round(@r);
+ }
+
+##############################################################################
+# bdec/binc
+
+sub bdec
+ {
+ # decrement value (subtract 1)
+ my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
+
+ if ($x->{sign} eq '-')
+ {
+ $x->{_n} = $MBI->_add( $x->{_n}, $x->{_d}); # -5/2 => -7/2
+ }
+ else
+ {
+ if ($MBI->_acmp($x->{_n},$x->{_d}) < 0) # n < d?
+ {
+ # 1/3 -- => -2/3
+ $x->{_n} = $MBI->_sub( $MBI->_copy($x->{_d}), $x->{_n});
+ $x->{sign} = '-';
+ }
+ else
+ {
+ $x->{_n} = $MBI->_sub($x->{_n}, $x->{_d}); # 5/2 => 3/2
+ }
+ }
+ $x->bnorm()->round(@r);
+ }
+
+sub binc
+ {
+ # increment value (add 1)
+ my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf
+
+ if ($x->{sign} eq '-')
+ {
+ if ($MBI->_acmp($x->{_n},$x->{_d}) < 0)
+ {
+ # -1/3 ++ => 2/3 (overflow at 0)
+ $x->{_n} = $MBI->_sub( $MBI->_copy($x->{_d}), $x->{_n});
+ $x->{sign} = '+';
+ }
+ else
+ {
+ $x->{_n} = $MBI->_sub($x->{_n}, $x->{_d}); # -5/2 => -3/2
+ }
+ }
+ else
+ {
+ $x->{_n} = $MBI->_add($x->{_n},$x->{_d}); # 5/2 => 7/2
+ }
+ $x->bnorm()->round(@r);
}
##############################################################################
sub is_int
{
# return true if arg (BRAT or num_str) is an integer
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
- $x->{_d}->is_one(); # 1e-1 => no integer
+ $MBI->_is_one($x->{_d}); # x/y && y != 1 => no integer
0;
}
sub is_zero
{
# return true if arg (BRAT or num_str) is zero
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
- return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
+ return 1 if $x->{sign} eq '+' && $MBI->_is_zero($x->{_n});
0;
}
sub is_one
{
# return true if arg (BRAT or num_str) is +1 or -1 if signis given
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
- my $sign = shift || ''; $sign = '+' if $sign ne '-';
+ my $sign = $_[2] || ''; $sign = '+' if $sign ne '-';
return 1
- if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
+ if ($x->{sign} eq $sign && $MBI->_is_one($x->{_n}) && $MBI->_is_one($x->{_d}));
0;
}
sub is_odd
{
# return true if arg (BFLOAT or num_str) is odd or false if even
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
- ($x->{_d}->is_one() && $x->{_n}->is_odd()); # x/2 is not, but 3/1
+ ($MBI->_is_one($x->{_d}) && $MBI->_is_odd($x->{_n})); # x/2 is not, but 3/1
0;
}
sub is_even
{
# return true if arg (BINT or num_str) is even or false if odd
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't
- return 1 if ($x->{_d}->is_one() # x/3 is never
- && $x->{_n}->is_even()); # but 4/1 is
+ return 1 if ($MBI->_is_one($x->{_d}) # x/3 is never
+ && $MBI->_is_even($x->{_n})); # but 4/1 is
0;
}
-BEGIN
- {
- *objectify = \&Math::BigInt::objectify;
- }
-
##############################################################################
# parts() and friends
sub numerator
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
+
+ # NaN, inf, -inf
+ return Math::BigInt->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
+
+ my $n = Math::BigInt->new($MBI->_str($x->{_n})); $n->{sign} = $x->{sign};
$n;
}
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- $x->{_d}->copy();
+ # NaN
+ return Math::BigInt->new($x->{sign}) if $x->{sign} eq 'NaN';
+ # inf, -inf
+ return Math::BigInt->bone() if $x->{sign} !~ /^[+-]$/;
+
+ Math::BigInt->new($MBI->_str($x->{_d}));
}
sub parts
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- my $n = $x->{_n}->copy();
+ my $c = 'Math::BigInt';
+
+ return ($c->bnan(),$c->bnan()) if $x->{sign} eq 'NaN';
+ return ($c->binf(),$c->binf()) if $x->{sign} eq '+inf';
+ return ($c->binf('-'),$c->binf()) if $x->{sign} eq '-inf';
+
+ my $n = $c->new( $MBI->_str($x->{_n}));
$n->{sign} = $x->{sign};
- return ($x->{_n}->copy(),$x->{_d}->copy());
+ my $d = $c->new( $MBI->_str($x->{_d}));
+ ($n,$d);
}
sub length
{
- return 0;
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $nan unless $x->is_int();
+ $MBI->_len($x->{_n}); # length(-123/1) => length(123)
}
sub digit
{
- return 0;
+ my ($self,$x,$n) = ref($_[0]) ? (undef,$_[0],$_[1]) : objectify(1,@_);
+
+ return $nan unless $x->is_int();
+ $MBI->_digit($x->{_n},$n || 0); # digit(-123/1,2) => digit(123,2)
}
##############################################################################
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return $x unless $x->{sign} =~ /^[+-]$/;
- return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
+ return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf
+ $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0
- $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1
- $x->{_d}->bone();
- $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
+ $x->{_n} = $MBI->_div($x->{_n},$x->{_d}); # 22/7 => 3/1 w/ truncate
+ $x->{_d} = $MBI->_one(); # d => 1
+ $x->{_n} = $MBI->_inc($x->{_n})
+ if $x->{sign} eq '+'; # +22/7 => 4/1
+ $x->{sign} = '+' if $MBI->_is_zero($x->{_n}); # -0 => 0
$x;
}
{
my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
- return $x unless $x->{sign} =~ /^[+-]$/;
- return $x if $x->{_d}->is_one(); # 22/1 => 22, 0/1 => 0
+ return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf
+ $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0
- $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1
- $x->{_d}->bone();
- $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
+ $x->{_n} = $MBI->_div($x->{_n},$x->{_d}); # 22/7 => 3/1 w/ truncate
+ $x->{_d} = $MBI->_one(); # d => 1
+ $x->{_n} = $MBI->_inc($x->{_n})
+ if $x->{sign} eq '-'; # -22/7 => -4/1
$x;
}
sub bfac
{
- return Math::BigRat->bnan();
+ my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ # if $x is not an integer
+ if (($x->{sign} ne '+') || (!$MBI->_is_one($x->{_d})))
+ {
+ return $x->bnan();
+ }
+
+ $x->{_n} = $MBI->_fac($x->{_n});
+ # since _d is 1, we don't need to reduce/norm the result
+ $x->round(@r);
}
sub bpow
{
- my ($self,$x,$y,@r) = objectify(2,@_);
+ # power ($x ** $y)
+
+ # 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->{sign} =~ /^[+-]inf$/; # -inf/+inf ** x
return $x->bnan() if $x->{sign} eq $nan || $y->{sign} eq $nan;
return $x->bone(@r) if $y->is_zero();
return $x->round(@r) if $x->is_one() || $y->is_one();
- if ($x->{sign} eq '-' && $x->{_n}->is_one() && $x->{_d}->is_one())
+
+ if ($x->{sign} eq '-' && $MBI->_is_one($x->{_n}) && $MBI->_is_one($x->{_d}))
{
# if $x == -1 and odd/even y => +1/-1
return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
}
# 1 ** -y => 1 / (1 ** |y|)
# so do test for negative $y after above's clause
- # return $x->bnan() if $y->{sign} eq '-';
+
return $x->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0)
- my $pow2 = $self->__one();
- my $y1 = Math::BigInt->new($y->{_n}/$y->{_d})->babs();
- my $two = Math::BigInt->new(2);
- while (!$y1->is_one())
+ # shortcut y/1 (and/or x/1)
+ if ($MBI->_is_one($y->{_d}))
+ {
+ # shortcut for x/1 and y/1
+ if ($MBI->_is_one($x->{_d}))
+ {
+ $x->{_n} = $MBI->_pow($x->{_n},$y->{_n}); # x/1 ** y/1 => (x ** y)/1
+ if ($y->{sign} eq '-')
+ {
+ # 0.2 ** -3 => 1/(0.2 ** 3)
+ ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
+ }
+ # correct sign; + ** + => +
+ if ($x->{sign} eq '-')
+ {
+ # - * - => +, - * - * - => -
+ $x->{sign} = '+' if $MBI->_is_even($y->{_n});
+ }
+ return $x->round(@r);
+ }
+ # x/z ** y/1
+ $x->{_n} = $MBI->_pow($x->{_n},$y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y
+ $x->{_d} = $MBI->_pow($x->{_d},$y->{_n});
+ if ($y->{sign} eq '-')
+ {
+ # 0.2 ** -3 => 1/(0.2 ** 3)
+ ($x->{_n},$x->{_d}) = ($x->{_d},$x->{_n}); # swap
+ }
+ # correct sign; + ** + => +
+ if ($x->{sign} eq '-')
+ {
+ # - * - => +, - * - * - => -
+ $x->{sign} = '+' if $MBI->_is_even($y->{_n});
+ }
+ return $x->round(@r);
+ }
+
+ # regular calculation (this is wrong for d/e ** f/g)
+ my $pow2 = $self->bone();
+ my $y1 = $MBI->_div ( $MBI->_copy($y->{_n}), $y->{_d});
+ my $two = $MBI->_two();
+
+ while (!$MBI->_is_one($y1))
{
- print "at $y1 (= $x)\n";
- $pow2->bmul($x) if $y1->is_odd();
- $y1->bdiv($two);
+ $pow2->bmul($x) if $MBI->_is_odd($y1);
+ $MBI->_div($y1, $two);
$x->bmul($x);
}
$x->bmul($pow2) unless $pow2->is_one();
# n ** -x => 1/n ** x
($x->{_d},$x->{_n}) = ($x->{_n},$x->{_d}) if $y->{sign} eq '-';
- $x;
- #$x->round(@r);
+ $x->bnorm()->round(@r);
}
sub blog
{
- return Math::BigRat->bnan();
- }
+ # set up parameters
+ my ($self,$x,$y,@r) = (ref($_[0]),@_);
-sub bsqrt
- {
- my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,@r) = objectify(2,$class,@_);
+ }
- return $x->bnan() if $x->{sign} ne '+'; # inf, NaN, -1 etc
- $x->{_d}->bsqrt($a,$p,$r);
- $x->{_n}->bsqrt($a,$p,$r);
- $x->bnorm();
- }
+ # blog(1,Y) => 0
+ return $x->bzero() if $x->is_one() && $y->{sign} eq '+';
-sub blsft
- {
- my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
-
- $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
- $x;
+ # $x <= 0 => NaN
+ return $x->bnan() if $x->is_zero() || $x->{sign} ne '+' || $y->{sign} ne '+';
+
+ if ($x->is_int() && $y->is_int())
+ {
+ return $self->new($x->as_number()->blog($y->as_number(),@r));
+ }
+
+ # do it with floats
+ $x->_new_from_float( $x->_as_float()->blog(Math::BigFloat->new("$y"),@r) );
}
-sub brsft
+sub bexp
{
- my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
+ # set up parameters
+ my ($self,$x,$y,$a,$p,$r) = (ref($_[0]),@_);
- $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
- $x;
- }
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,$a,$p,$r) = objectify(2,$class,@_);
+ }
-##############################################################################
-# round
+ return $x->binf() if $x->{sign} eq '+inf';
+ return $x->bzero() if $x->{sign} eq '-inf';
-sub round
- {
- $_[0];
- }
+ # we need to limit the accuracy to protect against overflow
+ my $fallback = 0;
+ my ($scale,@params);
+ ($x,@params) = $x->_find_round_parameters($a,$p,$r);
-sub bround
- {
- $_[0];
- }
+ # also takes care of the "error in _find_round_parameters?" case
+ return $x if $x->{sign} eq 'NaN';
-sub bfround
- {
- $_[0];
- }
+ # no rounding at all, so must use fallback
+ if (scalar @params == 0)
+ {
+ # simulate old behaviour
+ $params[0] = $self->div_scale(); # and round to it as accuracy
+ $params[1] = undef; # P = undef
+ $scale = $params[0]+4; # at least four more for proper round
+ $params[2] = $r; # round mode by caller or undef
+ $fallback = 1; # to clear a/p afterwards
+ }
+ else
+ {
+ # the 4 below is empirical, and there might be cases where it's not enough...
+ $scale = abs($params[0] || $params[1]) + 4; # take whatever is defined
+ }
-##############################################################################
-# comparing
+ return $x->bone(@params) if $x->is_zero();
-sub bcmp
- {
- my ($self,$x,$y) = objectify(2,@_);
+ # See the comments in Math::BigFloat on how this algorithm works.
+ # Basically we calculate A and B (where B is faculty(N)) so that A/B = e
- if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
+ my $x_org = $x->copy();
+ if ($scale <= 75)
+ {
+ # set $x directly from a cached string form
+ $x->{_n} = $MBI->_new("90933395208605785401971970164779391644753259799242");
+ $x->{_d} = $MBI->_new("33452526613163807108170062053440751665152000000000");
+ $x->{sign} = '+';
+ }
+ else
+ {
+ # compute A and B so that e = A / B.
+
+ # After some terms we end up with this, so we use it as a starting point:
+ my $A = $MBI->_new("90933395208605785401971970164779391644753259799242");
+ my $F = $MBI->_new(42); my $step = 42;
+
+ # Compute how many steps we need to take to get $A and $B sufficiently big
+ my $steps = Math::BigFloat::_len_to_steps($scale - 4);
+# print STDERR "# Doing $steps steps for ", $scale-4, " digits\n";
+ while ($step++ <= $steps)
+ {
+ # calculate $a * $f + 1
+ $A = $MBI->_mul($A, $F);
+ $A = $MBI->_inc($A);
+ # increment f
+ $F = $MBI->_inc($F);
+ }
+ # compute $B as factorial of $steps (this is faster than doing it manually)
+ my $B = $MBI->_fac($MBI->_new($steps));
+
+# print "A ", $MBI->_str($A), "\nB ", $MBI->_str($B), "\n";
+
+ $x->{_n} = $A;
+ $x->{_d} = $B;
+ $x->{sign} = '+';
+ }
+
+ # $x contains now an estimate of e, with some surplus digits, so we can round
+ if (!$x_org->is_one())
+ {
+ # raise $x to the wanted power and round it in one step:
+ $x->bpow($x_org, @params);
+ }
+ else
+ {
+ # else just round the already computed result
+ delete $x->{_a}; delete $x->{_p};
+ # shortcut to not run through _find_round_parameters again
+ if (defined $params[0])
+ {
+ $x->bround($params[0],$params[2]); # then round accordingly
+ }
+ else
+ {
+ $x->bfround($params[1],$params[2]); # then round accordingly
+ }
+ }
+ if ($fallback)
+ {
+ # clear a/p after round, since user did not request it
+ delete $x->{_a}; delete $x->{_p};
+ }
+
+ $x;
+ }
+
+sub bnok
+ {
+ # 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,$class,@_);
+ }
+
+ # do it with floats
+ $x->_new_from_float( $x->_as_float()->bnok(Math::BigFloat->new("$y"),@r) );
+ }
+
+sub _float_from_part
+ {
+ my $x = shift;
+
+ my $f = Math::BigFloat->bzero();
+ $f->{_m} = $MBI->_copy($x);
+ $f->{_e} = $MBI->_zero();
+
+ $f;
+ }
+
+sub _as_float
+ {
+ my $x = shift;
+
+ local $Math::BigFloat::upgrade = undef;
+ local $Math::BigFloat::accuracy = undef;
+ local $Math::BigFloat::precision = undef;
+ # 22/7 => 3.142857143..
+
+ my $a = $x->accuracy() || 0;
+ if ($a != 0 || !$MBI->_is_one($x->{_d}))
+ {
+ # n/d
+ return Math::BigFloat->new($x->{sign} . $MBI->_str($x->{_n}))->bdiv( $MBI->_str($x->{_d}), $x->accuracy());
+ }
+ # just n
+ Math::BigFloat->new($x->{sign} . $MBI->_str($x->{_n}));
+ }
+
+sub broot
+ {
+ # 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,@_);
+ }
+
+ if ($x->is_int() && $y->is_int())
+ {
+ return $self->new($x->as_number()->broot($y->as_number(),@r));
+ }
+
+ # do it with floats
+ $x->_new_from_float( $x->_as_float()->broot($y,@r) );
+ }
+
+sub bmodpow
+ {
+ # set up parameters
+ my ($self,$x,$y,$m,@r) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y,$m,@r) = objectify(3,@_);
+ }
+
+ # $x or $y or $m are NaN or +-inf => NaN
+ return $x->bnan()
+ if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/ ||
+ $m->{sign} !~ /^[+-]$/;
+
+ if ($x->is_int() && $y->is_int() && $m->is_int())
+ {
+ return $self->new($x->as_number()->bmodpow($y->as_number(),$m,@r));
+ }
+
+ warn ("bmodpow() not fully implemented");
+ $x->bnan();
+ }
+
+sub bmodinv
+ {
+ # 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,@_);
+ }
+
+ # $x or $y are NaN or +-inf => NaN
+ return $x->bnan()
+ if $x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/;
+
+ if ($x->is_int() && $y->is_int())
+ {
+ return $self->new($x->as_number()->bmodinv($y->as_number(),@r));
+ }
+
+ warn ("bmodinv() not fully implemented");
+ $x->bnan();
+ }
+
+sub bsqrt
+ {
+ my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
+
+ return $x->bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0
+ return $x if $x->{sign} eq '+inf'; # sqrt(inf) == inf
+ return $x->round(@r) if $x->is_zero() || $x->is_one();
+
+ local $Math::BigFloat::upgrade = undef;
+ local $Math::BigFloat::downgrade = undef;
+ local $Math::BigFloat::precision = undef;
+ local $Math::BigFloat::accuracy = undef;
+ local $Math::BigInt::upgrade = undef;
+ local $Math::BigInt::precision = undef;
+ local $Math::BigInt::accuracy = undef;
+
+ $x->{_n} = _float_from_part( $x->{_n} )->bsqrt();
+ $x->{_d} = _float_from_part( $x->{_d} )->bsqrt();
+
+ # XXX TODO: we probably can optimze this:
+
+ # if sqrt(D) was not integer
+ if ($x->{_d}->{_es} ne '+')
+ {
+ $x->{_n}->blsft($x->{_d}->exponent()->babs(),10); # 7.1/4.51 => 7.1/45.1
+ $x->{_d} = $MBI->_copy( $x->{_d}->{_m} ); # 7.1/45.1 => 71/45.1
+ }
+ # if sqrt(N) was not integer
+ if ($x->{_n}->{_es} ne '+')
+ {
+ $x->{_d}->blsft($x->{_n}->exponent()->babs(),10); # 71/45.1 => 710/45.1
+ $x->{_n} = $MBI->_copy( $x->{_n}->{_m} ); # 710/45.1 => 710/451
+ }
+
+ # convert parts to $MBI again
+ $x->{_n} = $MBI->_lsft( $MBI->_copy( $x->{_n}->{_m} ), $x->{_n}->{_e}, 10)
+ if ref($x->{_n}) ne $MBI && ref($x->{_n}) ne 'ARRAY';
+ $x->{_d} = $MBI->_lsft( $MBI->_copy( $x->{_d}->{_m} ), $x->{_d}->{_e}, 10)
+ if ref($x->{_d}) ne $MBI && ref($x->{_d}) ne 'ARRAY';
+
+ $x->bnorm()->round(@r);
+ }
+
+sub blsft
+ {
+ my ($self,$x,$y,$b,@r) = objectify(3,@_);
+
+ $b = 2 unless defined $b;
+ $b = $self->new($b) unless ref ($b);
+ $x->bmul( $b->copy()->bpow($y), @r);
+ $x;
+ }
+
+sub brsft
+ {
+ my ($self,$x,$y,$b,@r) = objectify(3,@_);
+
+ $b = 2 unless defined $b;
+ $b = $self->new($b) unless ref ($b);
+ $x->bdiv( $b->copy()->bpow($y), @r);
+ $x;
+ }
+
+##############################################################################
+# round
+
+sub round
+ {
+ $_[0];
+ }
+
+sub bround
+ {
+ $_[0];
+ }
+
+sub bfround
+ {
+ $_[0];
+ }
+
+##############################################################################
+# comparing
+
+sub bcmp
+ {
+ # compare two signed numbers
+
+ # set up parameters
+ my ($self,$x,$y) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y) = objectify(2,@_);
+ }
+
+ if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
{
# handle +-inf and NaN
return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
return -1 if $x->{sign} eq '-' && $y->{sign} eq '+'; # does also -x <=> 0
# shortcut
- my $xz = $x->{_n}->is_zero();
- my $yz = $y->{_n}->is_zero();
+ my $xz = $MBI->_is_zero($x->{_n});
+ my $yz = $MBI->_is_zero($y->{_n});
return 0 if $xz && $yz; # 0 <=> 0
return -1 if $xz && $y->{sign} eq '+'; # 0 <=> +y
return 1 if $yz && $x->{sign} eq '+'; # +x <=> 0
- my $t = $x->{_n} * $y->{_d}; $t->{sign} = $x->{sign};
- my $u = $y->{_n} * $x->{_d}; $u->{sign} = $y->{sign};
- $t->bcmp($u);
+ my $t = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d});
+ my $u = $MBI->_mul( $MBI->_copy($y->{_n}), $x->{_d});
+
+ my $cmp = $MBI->_acmp($t,$u); # signs are equal
+ $cmp = -$cmp if $x->{sign} eq '-'; # both are '-' => reverse
+ $cmp;
}
sub bacmp
{
- my ($self,$x,$y) = objectify(2,@_);
+ # compare two numbers (as unsigned)
+
+ # set up parameters
+ my ($self,$x,$y) = (ref($_[0]),@_);
+ # objectify is costly, so avoid it
+ if ((!ref($_[0])) || (ref($_[0]) ne ref($_[1])))
+ {
+ ($self,$x,$y) = objectify(2,$class,@_);
+ }
if (($x->{sign} !~ /^[+-]$/) || ($y->{sign} !~ /^[+-]$/))
{
# handle +-inf and NaN
return undef if (($x->{sign} eq $nan) || ($y->{sign} eq $nan));
return 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
- return +1; # inf is always bigger
+ return 1 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/;
+ return -1;
}
- my $t = $x->{_n} * $y->{_d};
- my $u = $y->{_n} * $x->{_d};
- $t->bacmp($u);
+ my $t = $MBI->_mul( $MBI->_copy($x->{_n}), $y->{_d});
+ my $u = $MBI->_mul( $MBI->_copy($y->{_n}), $x->{_d});
+ $MBI->_acmp($t,$u); # ignore signs
}
##############################################################################
# output conversation
+sub numify
+ {
+ # convert 17/8 => float (aka 2.125)
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $x->bstr() if $x->{sign} !~ /^[+-]$/; # inf, NaN, etc
+
+ # N/1 => N
+ my $neg = ''; $neg = '-' if $x->{sign} eq '-';
+ return $neg . $MBI->_num($x->{_n}) if $MBI->_is_one($x->{_d});
+
+ $x->_as_float()->numify() + 0.0;
+ }
+
sub as_number
{
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ # NaN, inf etc
+ return Math::BigInt->new($x->{sign}) if $x->{sign} !~ /^[+-]$/;
+
+ my $u = Math::BigInt->bzero();
+ $u->{sign} = $x->{sign};
+ $u->{value} = $MBI->_div( $MBI->_copy($x->{_n}), $x->{_d}); # 22/7 => 3
+ $u;
+ }
+
+sub as_bin
+ {
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $x unless $x->is_int();
- return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
- my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
- $t->{sign} = $x->{sign};
- $t;
+ my $s = $x->{sign}; $s = '' if $s eq '+';
+ $s . $MBI->_as_bin($x->{_n});
}
-#sub import
-# {
-# my $self = shift;
-# Math::BigInt->import(@_);
-# $self->SUPER::import(@_); # need it for subclasses
-# #$self->export_to_level(1,$self,@_); # need this ?
-# }
+sub as_hex
+ {
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $x unless $x->is_int();
+
+ my $s = $x->{sign}; $s = '' if $s eq '+';
+ $s . $MBI->_as_hex($x->{_n});
+ }
+
+sub as_oct
+ {
+ my ($self,$x) = ref($_[0]) ? (undef,$_[0]) : objectify(1,@_);
+
+ return $x unless $x->is_int();
+
+ my $s = $x->{sign}; $s = '' if $s eq '+';
+ $s . $MBI->_as_oct($x->{_n});
+ }
+
+##############################################################################
+
+sub from_hex
+ {
+ my $class = shift;
+
+ $class->new(@_);
+ }
+
+sub from_bin
+ {
+ my $class = shift;
+
+ $class->new(@_);
+ }
+
+sub from_oct
+ {
+ my $class = shift;
+
+ my @parts;
+ for my $c (@_)
+ {
+ push @parts, Math::BigInt->from_oct($c);
+ }
+ $class->new ( @parts );
+ }
+
+##############################################################################
+# import
+
+sub import
+ {
+ my $self = shift;
+ my $l = scalar @_;
+ my $lib = ''; my @a;
+ my $try = 'try';
+
+ for ( my $i = 0; $i < $l ; $i++)
+ {
+ if ( $_[$i] eq ':constant' )
+ {
+ # this rest causes overlord er load to step in
+ overload::constant float => sub { $self->new(shift); };
+ }
+# elsif ($_[$i] eq 'upgrade')
+# {
+# # this causes upgrading
+# $upgrade = $_[$i+1]; # or undef to disable
+# $i++;
+# }
+ elsif ($_[$i] eq 'downgrade')
+ {
+ # this causes downgrading
+ $downgrade = $_[$i+1]; # or undef to disable
+ $i++;
+ }
+ elsif ($_[$i] =~ /^(lib|try|only)\z/)
+ {
+ $lib = $_[$i+1] || ''; # default Calc
+ $try = $1; # lib, try or only
+ $i++;
+ }
+ elsif ($_[$i] eq 'with')
+ {
+ # this argument is no longer used
+ #$MBI = $_[$i+1] || 'Math::BigInt::Calc'; # default Math::BigInt::Calc
+ $i++;
+ }
+ else
+ {
+ push @a, $_[$i];
+ }
+ }
+ require Math::BigInt;
+
+ # let use Math::BigInt lib => 'GMP'; use Math::BigRat; still have GMP
+ if ($lib ne '')
+ {
+ my @c = split /\s*,\s*/, $lib;
+ foreach (@c)
+ {
+ $_ =~ tr/a-zA-Z0-9://cd; # limit to sane characters
+ }
+ $lib = join(",", @c);
+ }
+ my @import = ('objectify');
+ push @import, $try => $lib if $lib ne '';
+
+ # MBI already loaded, so feed it our lib arguments
+ Math::BigInt->import( @import );
+
+ $MBI = Math::BigFloat->config()->{lib};
+
+ # register us with MBI to get notified of future lib changes
+ Math::BigInt::_register_callback( $self, sub { $MBI = $_[0]; } );
+
+ # any non :constant stuff is handled by our parent, Exporter (loaded
+ # by Math::BigFloat, even if @_ is empty, to give it a chance
+ $self->SUPER::import(@a); # for subclasses
+ $self->export_to_level(1,$self,@a); # need this, too
+ }
1;
=head1 NAME
-Math::BigRat - arbitrarily big rationales
+Math::BigRat - Arbitrary big rational numbers
=head1 SYNOPSIS
- use Math::BigRat;
+ use Math::BigRat;
+
+ my $x = Math::BigRat->new('3/7'); $x += '5/9';
+
+ print $x->bstr(),"\n";
+ print $x ** 2,"\n";
- $x = Math::BigRat->new('3/7');
+ my $y = Math::BigRat->new('inf');
+ print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n";
- print $x->bstr(),"\n";
+ my $z = Math::BigRat->new(144); $z->bsqrt();
=head1 DESCRIPTION
-This is just a placeholder until the real thing is up and running. Watch this
-space...
+Math::BigRat complements Math::BigInt and Math::BigFloat by providing support
+for arbitrary big rational numbers.
=head2 MATH LIBRARY
-Math with the numbers is done (by default) by a module called
-Math::BigInt::Calc. This is equivalent to saying:
+You can change the underlying module that does the low-level
+math operations by using:
- use Math::BigRat lib => 'Calc';
+ use Math::BigRat try => 'GMP';
-You can change this by using:
-
- use Math::BigRat lib => 'BitVect';
+Note: This needs Math::BigInt::GMP installed.
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 Math::BigRat try => 'Foo,Math::BigInt::Bar';
+
+If you want to get warned when the fallback occurs, replace "try" with
+"lib":
+
use Math::BigRat lib => 'Foo,Math::BigInt::Bar';
-Calc.pm uses as internal format an array of elements of some decimal base
-(usually 1e7, but this might be differen for some systems) with the least
-significant digit first, while BitVect.pm uses a bit vector of base 2, most
-significant bit first. Other modules might use even different means of
-representing the numbers. See the respective module documentation for further
-details.
+If you want the code to die instead, replace "try" with
+"only":
+
+ use Math::BigRat only => 'Foo,Math::BigInt::Bar';
=head1 METHODS
-=head2 new
+Any methods not listed here are derived from Math::BigFloat (or
+Math::BigInt), so make sure you check these two modules for further
+information.
+
+=head2 new()
$x = Math::BigRat->new('1/3');
Create a new Math::BigRat object. Input can come in various forms:
+ $x = Math::BigRat->new(123); # scalars
+ $x = Math::BigRat->new('inf'); # infinity
+ $x = Math::BigRat->new('123.3'); # float
$x = Math::BigRat->new('1/3'); # simple string
$x = Math::BigRat->new('1 / 3'); # spaced
$x = Math::BigRat->new('1 / 0.1'); # w/ floats
$x = Math::BigRat->new(Math::BigInt->new(3)); # BigInt
$x = Math::BigRat->new(Math::BigFloat->new('3.1')); # BigFloat
+ $x = Math::BigRat->new(Math::BigInt::Lite->new('2')); # BigLite
-=head2 numerator
+ # You can also give D and N as different objects:
+ $x = Math::BigRat->new(
+ Math::BigInt->new(-123),
+ Math::BigInt->new(7),
+ ); # => -123/7
+
+=head2 numerator()
$n = $x->numerator();
Returns a copy of the numerator (the part above the line) as signed BigInt.
-=head2 denominator
+=head2 denominator()
$d = $x->denominator();
Returns a copy of the denominator (the part under the line) as positive BigInt.
-=head2 parts
+=head2 parts()
($n,$d) = $x->parts();
Return a list consisting of (signed) numerator and (unsigned) denominator as
BigInts.
+=head2 numify()
+
+ my $y = $x->numify();
+
+Returns the object as a scalar. This will lose some data if the object
+cannot be represented by a normal Perl scalar (integer or float), so
+use as_int() instead.
+
+This routine is automatically used whenever a scalar is required:
+
+ my $x = Math::BigRat->new('3/1');
+ @array = (1,2,3);
+ $y = $array[$x]; # set $y to 3
+
+=head2 as_int()/as_number()
+
+ $x = Math::BigRat->new('13/7');
+ print $x->as_int(),"\n"; # '1'
+
+Returns a copy of the object as BigInt, truncated to an integer.
+
+C<as_number()> is an alias for C<as_int()>.
+
+=head2 as_hex()
+
+ $x = Math::BigRat->new('13');
+ print $x->as_hex(),"\n"; # '0xd'
+
+Returns the BigRat as hexadecimal string. Works only for integers.
+
+=head2 as_bin()
+
+ $x = Math::BigRat->new('13');
+ print $x->as_bin(),"\n"; # '0x1101'
+
+Returns the BigRat as binary string. Works only for integers.
+
+=head2 as_oct()
+
+ $x = Math::BigRat->new('13');
+ print $x->as_oct(),"\n"; # '015'
+
+Returns the BigRat as octal string. Works only for integers.
+
+=head2 from_hex()/from_bin()/from_oct()
+
+ my $h = Math::BigRat->from_hex('0x10');
+ my $b = Math::BigRat->from_bin('0b10000000');
+ my $o = Math::BigRat->from_oct('020');
+
+Create a BigRat from an hexadecimal, binary or octal number
+in string form.
+
+=head2 length()
+
+ $len = $x->length();
+
+Return the length of $x in digitis for integer values.
+
+=head2 digit()
+
+ print Math::BigRat->new('123/1')->digit(1); # 1
+ print Math::BigRat->new('123/1')->digit(-1); # 3
+
+Return the N'ths digit from X when X is an integer value.
+
+=head2 bnorm()
+
+ $x->bnorm();
+
+Reduce the number to the shortest form. This routine is called
+automatically whenever it is needed.
+
+=head2 bfac()
+
+ $x->bfac();
+
+Calculates the factorial of $x. For instance:
+
+ print Math::BigRat->new('3/1')->bfac(),"\n"; # 1*2*3
+ print Math::BigRat->new('5/1')->bfac(),"\n"; # 1*2*3*4*5
+
+Works currently only for integers.
+
+=head2 bround()/round()/bfround()
+
+Are not yet implemented.
+
+=head2 bmod()
+
+ use Math::BigRat;
+ my $x = Math::BigRat->new('7/4');
+ my $y = Math::BigRat->new('4/3');
+ print $x->bmod($y);
+
+Set $x to the remainder of the division of $x by $y.
+
+=head2 bneg()
+
+ $x->bneg();
+
+Used to negate the object in-place.
+
+=head2 is_one()
+
+ print "$x is 1\n" if $x->is_one();
+
+Return true if $x is exactly one, otherwise false.
+
+=head2 is_zero()
+
+ print "$x is 0\n" if $x->is_zero();
+
+Return true if $x is exactly zero, otherwise false.
+
+=head2 is_pos()/is_positive()
+
+ print "$x is >= 0\n" if $x->is_positive();
+
+Return true if $x is positive (greater than or equal to zero), otherwise
+false. Please note that '+inf' is also positive, while 'NaN' and '-inf' aren't.
+
+C<is_positive()> is an alias for C<is_pos()>.
+
+=head2 is_neg()/is_negative()
+
+ print "$x is < 0\n" if $x->is_negative();
+
+Return true if $x is negative (smaller than zero), otherwise false. Please
+note that '-inf' is also negative, while 'NaN' and '+inf' aren't.
+
+C<is_negative()> is an alias for C<is_neg()>.
+
+=head2 is_int()
+
+ print "$x is an integer\n" if $x->is_int();
+
+Return true if $x has a denominator of 1 (e.g. no fraction parts), otherwise
+false. Please note that '-inf', 'inf' and 'NaN' aren't integer.
+
+=head2 is_odd()
+
+ print "$x is odd\n" if $x->is_odd();
+
+Return true if $x is odd, otherwise false.
+
+=head2 is_even()
+
+ print "$x is even\n" if $x->is_even();
+
+Return true if $x is even, otherwise false.
+
+=head2 bceil()
+
+ $x->bceil();
+
+Set $x to the next bigger integer value (e.g. truncate the number to integer
+and then increment it by one).
+
+=head2 bfloor()
+
+ $x->bfloor();
+
+Truncate $x to an integer value.
+
+=head2 bsqrt()
+
+ $x->bsqrt();
+
+Calculate the square root of $x.
+
+=head2 broot()
+
+ $x->broot($n);
+
+Calculate the N'th root of $x.
+
+=head2 badd()/bmul()/bsub()/bdiv()/bdec()/binc()
+
+Please see the documentation in L<Math::BigInt>.
+
+=head2 copy()
+
+ my $z = $x->copy();
+
+Makes a deep copy of the object.
+
+Please see the documentation in L<Math::BigInt> for further details.
+
+=head2 bstr()/bsstr()
+
+ my $x = Math::BigInt->new('8/4');
+ print $x->bstr(),"\n"; # prints 1/2
+ print $x->bsstr(),"\n"; # prints 1/2
+
+Return a string representating this object.
+
+=head2 bacmp()/bcmp()
+
+Used to compare numbers.
+
+Please see the documentation in L<Math::BigInt> for further details.
+
+=head2 blsft()/brsft()
+
+Used to shift numbers left/right.
+
+Please see the documentation in L<Math::BigInt> for further details.
+
+=head2 bpow()
+
+ $x->bpow($y);
+
+Compute $x ** $y.
+
+Please see the documentation in L<Math::BigInt> for further details.
+
+=head2 bexp()
+
+ $x->bexp($accuracy); # calculate e ** X
+
+Calculates two integers A and B so that A/B is equal to C<e ** $x>, where C<e> is
+Euler's number.
+
+This method was added in v0.20 of Math::BigRat (May 2007).
+
+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 v0.20 of Math::BigRat (May 2007).
+
+=head2 config()
+
+ use Data::Dumper;
+
+ print Dumper ( Math::BigRat->config() );
+ print Math::BigRat->config()->{lib},"\n";
+
+Returns a hash containing the configuration, e.g. the version number, lib
+loaded etc. The following hash keys are currently filled in with the
+appropriate information.
+
+ key RO/RW Description
+ Example
+ ============================================================
+ lib RO Name of the Math library
+ Math::BigInt::Calc
+ lib_version RO Version of 'lib'
+ 0.30
+ class RO The class of config you just called
+ Math::BigRat
+ version RO version number of the class you used
+ 0.10
+ upgrade RW To which class numbers are upgraded
+ undef
+ downgrade RW To which class numbers are downgraded
+ undef
+ precision RW Global precision
+ undef
+ accuracy RW Global accuracy
+ undef
+ round_mode RW Global round mode
+ even
+ div_scale RW Fallback accuracy for div
+ 40
+ trap_nan RW Trap creation of NaN (undef = no)
+ undef
+ trap_inf RW Trap creation of +inf/-inf (undef = no)
+ undef
+
+By passing a reference to a hash you may set the configuration values. This
+works only for values that a marked with a C<RW> above, anything else is
+read-only.
+
=head1 BUGS
-None know yet. Please see also L<Math::BigInt>.
+Some things are not yet implemented, or only implemented half-way:
+
+=over 2
+
+=item inf handling (partial)
+
+=item NaN handling (partial)
+
+=item rounding (not implemented except for bceil/bfloor)
+
+=item $x ** $y where $y is not an integer
+
+=item bmod(), blog(), bmodinv() and bmodpow() (partial)
+
+=back
=head1 LICENSE
L<Math::BigFloat> and L<Math::Big> as well as L<Math::BigInt::BitVect>,
L<Math::BigInt::Pari> and L<Math::BigInt::GMP>.
-The package at
-L<http://search.cpan.org/search?mode=module&query=Math%3A%3ABigRat> may
-contain more documentation and examples as well as testcases.
+See L<http://search.cpan.org/search?dist=bignum> for a way to use
+Math::BigRat.
+
+The package at L<http://search.cpan.org/search?dist=Math%3A%3ABigRat>
+may contain more documentation and examples as well as testcases.
=head1 AUTHORS
-(C) by Tels L<http://bloodgate.com/> 2001-2002.
+(C) by Tels L<http://bloodgate.com/> 2001 - 2007.
=cut