home *** CD-ROM | disk | FTP | other *** search
-
- #
- # "Tax the rat farms."
- #
-
- # The following hash values are used:
- # sign : +,-,NaN,+inf,-inf
- # _d : denominator
- # _n : numeraotr (value = _n/_d)
- # _a : accuracy
- # _p : precision
- # _f : flags, used by MBR to flag parts of a rational as untouchable
-
- package Math::BigRat;
-
- require 5.005_03;
- use strict;
-
- use Exporter;
- use Math::BigFloat;
- use vars qw($VERSION @ISA $PACKAGE @EXPORT_OK $upgrade $downgrade
- $accuracy $precision $round_mode $div_scale);
-
- @ISA = qw(Exporter Math::BigFloat);
- @EXPORT_OK = qw();
-
- $VERSION = '0.07';
-
- use overload; # inherit from Math::BigFloat
-
- ##############################################################################
- # global constants, flags and accessory
-
- use constant MB_NEVER_ROUND => 0x0001;
-
- $accuracy = $precision = undef;
- $round_mode = 'even';
- $div_scale = 40;
- $upgrade = undef;
- $downgrade = undef;
-
- my $nan = 'NaN';
- my $class = 'Math::BigRat';
- my $MBI = 'Math::BigInt';
-
- sub isa
- {
- return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
- UNIVERSAL::isa(@_);
- }
-
- sub _new_from_float
- {
- # turn a single float input into a rational (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';
-
- #print "f $f caller", join(' ',caller()),"\n";
- $self->{_n} = $f->{_m}->copy(); # mantissa
- $self->{_d} = $MBI->bone();
- $self->{sign} = $f->{sign}; $self->{_n}->{sign} = '+';
- if ($f->{_e}->{sign} eq '-')
- {
- # something like Math::BigRat->new('0.1');
- $self->{_d}->blsft($f->{_e}->copy()->babs(),10); # 1 / 1 => 1/10
- }
- else
- {
- # something like Math::BigRat->new('10');
- # 1 / 1 => 10/1
- $self->{_n}->blsft($f->{_e},10) unless $f->{_e}->is_zero();
- }
- $self;
- }
-
- sub new
- {
- # create a Math::BigRat
- my $class = shift;
-
- my ($n,$d) = shift;
-
- my $self = { }; bless $self,$class;
-
- # input like (BigInt,BigInt) or (BigFloat,BigFloat) not handled yet
-
- if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat')))
- {
- if ($n->isa('Math::BigFloat'))
- {
- return $self->_new_from_float($n)->bnorm();
- }
- if ($n->isa('Math::BigInt'))
- {
- $self->{_n} = $n->copy(); # "mantissa" = $n
- $self->{_d} = $MBI->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- return $self->bnorm();
- }
- if ($n->isa('Math::BigInt::Lite'))
- {
- $self->{_n} = $MBI->new($$n); # "mantissa" = $n
- $self->{_d} = $MBI->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- return $self->bnorm();
- }
- }
- return $n->copy() if ref $n;
-
- if (!defined $n)
- {
- $self->{_n} = $MBI->bzero(); # undef => 0
- $self->{_d} = $MBI->bone();
- $self->{sign} = '+';
- return $self->bnorm();
- }
- # 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
- ($n,$d) = split (/\//,$n);
- # try as BigFloats first
- if (($n =~ /[\.eE]/) || ($d =~ /[\.eE]/))
- {
- # one of them looks like a float
- $self->_new_from_float(Math::BigFloat->new($n));
- # now correct $self->{_n} due to $n
- my $f = Math::BigFloat->new($d);
- if ($f->{_e}->{sign} eq '-')
- {
- # 10 / 0.1 => 100/1
- $self->{_n}->blsft($f->{_e}->copy()->babs(),10);
- }
- else
- {
- $self->{_d}->blsft($f->{_e},10); # 1 / 1 => 10/1
- }
- }
- else
- {
- $self->{_n} = $MBI->new($n);
- $self->{_d} = $MBI->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
- }
- return $self->bnorm();
- }
-
- # simple string input
- if (($n =~ /[\.eE]/))
- {
- # work around bug in BigFloat that makes 1.1.2 valid
- return $self->bnan() if $n =~ /\..*\./;
- # looks like a float
- $self->_new_from_float(Math::BigFloat->new($n));
- }
- else
- {
- $self->{_n} = $MBI->new($n);
- $self->{_d} = $MBI->bone();
- $self->{sign} = $self->{_n}->{sign}; $self->{_n}->{sign} = '+';
- return $self->bnan() if $self->{sign} eq 'NaN';
- return $self->binf($self->{sign}) if $self->{sign} =~ /^[+-]inf$/;
- }
- $self->bnorm();
- }
-
- ###############################################################################
-
- sub bstr
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
- {
- my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
- return $s;
- }
-
- my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
-
- return $s.$x->{_n}->bstr() if $x->{_d}->is_one();
- return $s.$x->{_n}->bstr() . '/' . $x->{_d}->bstr();
- }
-
- sub bsstr
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- if ($x->{sign} !~ /^[+-]$/) # inf, NaN etc
- {
- my $s = $x->{sign}; $s =~ s/^\+//; # +inf => inf
- return $s;
- }
-
- my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3
- return $x->{_n}->bstr() . '/' . $x->{_d}->bstr();
- }
-
- 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,@_);
-
- # both parts must be BigInt's
- die ("n is not $MBI but (".ref($x->{_n}).')')
- if ref($x->{_n}) ne $MBI;
- die ("d is not $MBI but (".ref($x->{_d}).')')
- if ref($x->{_d}) ne $MBI;
-
- # 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()))
- {
- $x->{sign} = '+'; # never -0
- $x->{_d} = $MBI->bone() unless $x->{_d}->is_one();
- return $x;
- }
-
- return $x if $x->{_d}->is_one(); # no need to reduce
-
- # reduce other numbers
- # disable upgrade in BigInt, otherwise deep recursion
- local $Math::BigInt::upgrade = undef;
- my $gcd = $x->{_n}->bgcd($x->{_d});
-
- if (!$gcd->is_one())
- {
- $x->{_n}->bdiv($gcd);
- $x->{_d}->bdiv($gcd);
- }
- $x;
- }
-
- ##############################################################################
- # special values
-
- sub _bnan
- {
- # used by parent class bone() to initialize number to 1
- my $self = shift;
- $self->{_n} = $MBI->bzero();
- $self->{_d} = $MBI->bzero();
- }
-
- sub _binf
- {
- # used by parent class bone() to initialize number to 1
- my $self = shift;
- $self->{_n} = $MBI->bzero();
- $self->{_d} = $MBI->bzero();
- }
-
- sub _bone
- {
- # used by parent class bone() to initialize number to 1
- my $self = shift;
- $self->{_n} = $MBI->bone();
- $self->{_d} = $MBI->bone();
- }
-
- sub _bzero
- {
- # used by parent class bone() to initialize number to 1
- my $self = shift;
- $self->{_n} = $MBI->bzero();
- $self->{_d} = $MBI->bone();
- }
-
- ##############################################################################
- # mul/add/div etc
-
- sub badd
- {
- # add two rationals
- my ($self,$x,$y,$a,$p,$r) = objectify(2,@_);
-
- $x = $self->new($x) unless $x->isa($self);
- $y = $self->new($y) unless $y->isa($self);
-
- return $x->bnan() if ($x->{sign} eq 'NaN' || $y->{sign} eq 'NaN');
-
- # 1 1 gcd(3,4) = 1 1*3 + 1*4 7
- # - + - = --------- = --
- # 4 3 4*3 12
-
- my $gcd = $x->{_d}->bgcd($y->{_d});
-
- 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);
-
- $x->{_d}->bmul($y->{_d});
-
- # calculate new sign
- $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
-
- $x->bnorm()->round($a,$p,$r);
- }
-
- sub bsub
- {
- # subtract two rationals
- 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});
-
- 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}->bsub($m);
-
- $x->{_d}->bmul($y->{_d});
-
- # calculate new sign
- $x->{sign} = $x->{_n}->{sign}; $x->{_n}->{sign} = '+';
-
- $x->bnorm()->round($a,$p,$r);
- }
-
- sub bmul
- {
- # multiply two rationals
- 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');
-
- # inf handling
- 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('-');
- }
-
- # 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)
-
- # 1 1 2 1
- # - * - = - = -
- # 4 3 12 6
- $x->{_n}->bmul($y->{_n});
- $x->{_d}->bmul($y->{_d});
-
- # compute new sign
- $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
-
- $x->bnorm()->round($a,$p,$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);
-
- 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
-
- # 1 1 1 3
- # - / - == - * -
- # 4 3 4 1
- $x->{_n}->bmul($y->{_d});
- $x->{_d}->bmul($y->{_n});
-
- # compute new sign
- $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
-
- $x->bnorm()->round($a,$p,$r);
- $x;
- }
-
- ##############################################################################
- # 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}->badd($x->{_d}); # -5/2 => -7/2
- }
- else
- {
- if ($x->{_n}->bacmp($x->{_d}) < 0)
- {
- # 1/3 -- => -2/3
- $x->{_n} = $x->{_d} - $x->{_n};
- $x->{sign} = '-';
- }
- else
- {
- $x->{_n}->bsub($x->{_d}); # 5/2 => 3/2
- }
- }
- $x->bnorm()->round(@r);
-
- #$x->bsub($self->bone())->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 ($x->{_n}->bacmp($x->{_d}) < 0)
- {
- # -1/3 ++ => 2/3 (overflow at 0)
- $x->{_n} = $x->{_d} - $x->{_n};
- $x->{sign} = '+';
- }
- else
- {
- $x->{_n}->bsub($x->{_d}); # -5/2 => -3/2
- }
- }
- else
- {
- $x->{_n}->badd($x->{_d}); # 5/2 => 7/2
- }
- $x->bnorm()->round(@r);
-
- #$x->badd($self->bone())->round(@r);
- }
-
- ##############################################################################
- # is_foo methods (the rest is inherited)
-
- sub is_int
- {
- # return true if arg (BRAT or num_str) is an integer
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't
- $x->{_d}->is_one(); # 1e-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,@_);
-
- return 1 if $x->{sign} eq '+' && $x->{_n}->is_zero();
- 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 $sign = shift || ''; $sign = '+' if $sign ne '-';
- return 1
- if ($x->{sign} eq $sign && $x->{_n}->is_one() && $x->{_d}->is_one());
- 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,@_);
-
- return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't
- ($x->{_d}->is_one() && $x->{_n}->is_odd()); # 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,@_);
-
- 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
- 0;
- }
-
- BEGIN
- {
- *objectify = \&Math::BigInt::objectify;
- }
-
- ##############################################################################
- # parts() and friends
-
- sub numerator
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
-
- my $n = $x->{_n}->copy(); $n->{sign} = $x->{sign};
- $n;
- }
-
- sub denominator
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- return $MBI->new($x->{sign}) if ($x->{sign} !~ /^[+-]$/);
- $x->{_d}->copy();
- }
-
- sub parts
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- return ($self->bnan(),$self->bnan()) if $x->{sign} eq 'NaN';
- return ($self->binf(),$self->binf()) if $x->{sign} eq '+inf';
- return ($self->binf('-'),$self->binf()) if $x->{sign} eq '-inf';
-
- my $n = $x->{_n}->copy();
- $n->{sign} = $x->{sign};
- return ($n,$x->{_d}->copy());
- }
-
- sub length
- {
- return 0;
- }
-
- sub digit
- {
- return 0;
- }
-
- ##############################################################################
- # special calc routines
-
- sub bceil
- {
- 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
-
- $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
- $x->{_d}->bone();
- $x->{_n}->binc() if $x->{sign} eq '+'; # +22/7 => 4/1
- $x->{sign} = '+' if $x->{_n}->is_zero(); # -0 => 0
- $x;
- }
-
- sub bfloor
- {
- 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
-
- $x->{_n}->bdiv($x->{_d}); # 22/7 => 3/1 w/ truncate
- $x->{_d}->bone();
- $x->{_n}->binc() if $x->{sign} eq '-'; # -22/7 => -4/1
- $x;
- }
-
- sub bfac
- {
- my ($self,$x,@r) = ref($_[0]) ? (ref($_[0]),@_) : objectify(1,@_);
-
- if (($x->{sign} eq '+') && ($x->{_d}->is_one()))
- {
- $x->{_n}->bfac();
- return $x->round(@r);
- }
- $x->bnan();
- }
-
- sub bpow
- {
- my ($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 == -1 and odd/even y => +1/-1
- return $y->is_odd() ? $x->round(@r) : $x->babs()->round(@r);
- # my Casio FX-5500L has a bug here: -1 ** 2 is -1, but -1 * -1 is 1;
- }
- # 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)
-
- # shortcut y/1 (and/or x/1)
- if ($y->{_d}->is_one())
- {
- # shortcut for x/1 and y/1
- if ($x->{_d}->is_one())
- {
- $x->{_n}->bpow($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 $y->{_n}->is_even();
- }
- return $x->round(@r);
- }
- # x/z ** y/1
- $x->{_n}->bpow($y->{_n}); # 5/2 ** y/1 => 5 ** y / 2 ** y
- $x->{_d}->bpow($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 $y->{_n}->is_even();
- }
- return $x->round(@r);
- }
-
- # regular calculation (this is wrong for d/e ** f/g)
- my $pow2 = $self->__one();
- my $y1 = $MBI->new($y->{_n}/$y->{_d})->babs();
- my $two = $MBI->new(2);
- while (!$y1->is_one())
- {
- $pow2->bmul($x) if $y1->is_odd();
- $y1->bdiv($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);
- }
-
- sub blog
- {
- return Math::BigRat->bnan();
- }
-
- sub bsqrt
- {
- my ($self,$x,$a,$p,$r) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- 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();
- }
-
- sub blsft
- {
- my ($self,$x,$y,$b,$a,$p,$r) = objectify(3,@_);
-
- $x->bmul( $b->copy()->bpow($y), $a,$p,$r);
- $x;
- }
-
- sub brsft
- {
- my ($self,$x,$y,$b,$a,$p,$r) = objectify(2,@_);
-
- $x->bdiv( $b->copy()->bpow($y), $a,$p,$r);
- $x;
- }
-
- ##############################################################################
- # round
-
- sub round
- {
- $_[0];
- }
-
- sub bround
- {
- $_[0];
- }
-
- sub bfround
- {
- $_[0];
- }
-
- ##############################################################################
- # comparing
-
- sub bcmp
- {
- my ($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 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/;
- return +1 if $x->{sign} eq '+inf';
- return -1 if $x->{sign} eq '-inf';
- return -1 if $y->{sign} eq '+inf';
- return +1;
- }
- # check sign for speed first
- return 1 if $x->{sign} eq '+' && $y->{sign} eq '-'; # does also 0 <=> -y
- 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();
- 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);
- }
-
- sub bacmp
- {
- my ($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 0 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} =~ /^[+-]inf$/;
- return +1; # inf is always bigger
- }
-
- my $t = $x->{_n} * $y->{_d};
- my $u = $y->{_n} * $x->{_d};
- $t->bacmp($u);
- }
-
- ##############################################################################
- # output conversation
-
- sub as_number
- {
- my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_);
-
- return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf etc
- my $t = $x->{_n}->copy()->bdiv($x->{_d}); # 22/7 => 3
- $t->{sign} = $x->{sign};
- $t;
- }
-
- sub import
- {
- my $self = shift;
- my $l = scalar @_;
- my $lib = ''; my @a;
- for ( my $i = 0; $i < $l ; $i++)
- {
- # print "at $_[$i] (",$_[$i+1]||'undef',")\n";
- if ( $_[$i] eq ':constant' )
- {
- # this rest causes overlord er load to step in
- # print "overload @_\n";
- 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] eq 'lib')
- {
- $lib = $_[$i+1] || ''; # default Calc
- $i++;
- }
- elsif ($_[$i] eq 'with')
- {
- $MBI = $_[$i+1] || 'Math::BigInt'; # default Math::BigInt
- $i++;
- }
- else
- {
- push @a, $_[$i];
- }
- }
- # let use Math::BigInt lib => 'GMP'; use Math::BigFloat; still work
- my $mbilib = eval { Math::BigInt->config()->{lib} };
- if ((defined $mbilib) && ($MBI eq 'Math::BigInt'))
- {
- # MBI already loaded
- $MBI->import('lib',"$lib,$mbilib", 'objectify');
- }
- else
- {
- # MBI not loaded, or not with "Math::BigInt"
- $lib .= ",$mbilib" if defined $mbilib;
-
- if ($] < 5.006)
- {
- # Perl < 5.6.0 dies with "out of memory!" when eval() and ':constant' is
- # used in the same script, or eval inside import().
- my @parts = split /::/, $MBI; # Math::BigInt => Math BigInt
- my $file = pop @parts; $file .= '.pm'; # BigInt => BigInt.pm
- $file = File::Spec->catfile (@parts, $file);
- eval { require $file; $MBI->import( lib => '$lib', 'objectify' ); }
- }
- else
- {
- my $rc = "use $MBI lib => '$lib', 'objectify';";
- eval $rc;
- }
- }
- die ("Couldn't load $MBI: $! $@") if $@;
-
- # any non :constant stuff is handled by our parent, Exporter
- # 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;
-
- __END__
-
- =head1 NAME
-
- Math::BigRat - arbitrarily big rationals
-
- =head1 SYNOPSIS
-
- use Math::BigRat;
-
- $x = Math::BigRat->new('3/7');
-
- print $x->bstr(),"\n";
-
- =head1 DESCRIPTION
-
- This is just a placeholder until the real thing is up and running. Watch this
- space...
-
- =head2 MATH LIBRARY
-
- Math with the numbers is done (by default) by a module called
- Math::BigInt::Calc. This is equivalent to saying:
-
- use Math::BigRat lib => 'Calc';
-
- You can change this by using:
-
- use Math::BigRat lib => 'BitVect';
-
- The following would first try to find Math::BigInt::Foo, then
- Math::BigInt::Bar, and when this also fails, revert to Math::BigInt::Calc:
-
- use 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.
-
- =head1 METHODS
-
- Any method not listed here is dervied 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('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()
-
- $n = $x->numerator();
-
- Returns a copy of the numerator (the part above the line) as signed BigInt.
-
- =head2 denominator()
-
- $d = $x->denominator();
-
- Returns a copy of the denominator (the part under the line) as positive BigInt.
-
- =head2 parts()
-
- ($n,$d) = $x->parts();
-
- Return a list consisting of (signed) numerator and (unsigned) denominator as
- BigInts.
-
- =head2 as_number()
-
- Returns a copy of the object as BigInt by truncating it to integer.
-
- =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
-
- Only works for integers for now.
-
- =head2 blog()
-
- Is not yet implemented.
-
- =head2 bround()/round()/bfround()
-
- Are not yet implemented.
-
-
- =head1 BUGS
-
- Some things are not yet implemented, or only implemented half-way.
-
- =head1 LICENSE
-
- This program is free software; you may redistribute it and/or modify it under
- the same terms as Perl itself.
-
- =head1 SEE ALSO
-
- 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.
-
- =head1 AUTHORS
-
- (C) by Tels L<http://bloodgate.com/> 2001-2002.
-
- =cut
-