diff options
Diffstat (limited to 'cpan/Math-BigRat/lib/Math/BigRat.pm')
-rw-r--r-- | cpan/Math-BigRat/lib/Math/BigRat.pm | 2020 |
1 files changed, 0 insertions, 2020 deletions
diff --git a/cpan/Math-BigRat/lib/Math/BigRat.pm b/cpan/Math-BigRat/lib/Math/BigRat.pm deleted file mode 100644 index 030a87ecdb..0000000000 --- a/cpan/Math-BigRat/lib/Math/BigRat.pm +++ /dev/null @@ -1,2020 +0,0 @@ - -# -# "Tax the rat farms." - Lord Vetinari -# - -# The following hash values are used: -# sign : +,-,NaN,+inf,-inf -# _d : denominator -# _n : numeraotr (value = _n/_d) -# _a : accuracy -# _p : precision -# You should not look at the innards of a BigRat - use the methods for this. - -package Math::BigRat; - -# anythig older is untested, and unlikely to work -use 5.006; -use strict; - -use Math::BigFloat; -use vars qw($VERSION @ISA $upgrade $downgrade - $accuracy $precision $round_mode $div_scale $_trap_nan $_trap_inf); - -@ISA = qw(Math::BigFloat); - -$VERSION = '0.26'; -$VERSION = eval $VERSION; - -use overload; # inherit overload from Math::BigFloat - -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; - } - -############################################################################## -# Global constants and flags. Access these only via the accessor methods! - -$accuracy = $precision = undef; -$round_mode = 'even'; -$div_scale = 40; -$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'; - -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 number (like '0.1') - my ($self,$f) = @_; - - return $self->bnan() if $f->is_nan(); - return $self->binf($f->{sign}) if $f->{sign} =~ /^[+-]inf$/; - - $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'); - # 1 / 1 => 1/10 - $MBI->_lsft ( $self->{_d}, $f->{_e} ,10); - } - else - { - # something like Math::BigRat->new('10'); - # 1 / 1 => 10/1 - $MBI->_lsft ( $self->{_n}, $f->{_e} ,10) unless - $MBI->_is_zero($f->{_e}); - } - $self; - } - -sub new - { - # create a Math::BigRat - my $class = shift; - - my ($n,$d) = @_; - - my $self = { }; bless $self,$class; - - # input like (BigInt) or (BigFloat): - if ((!defined $d) && (ref $n) && (!$n->isa('Math::BigRat'))) - { - if ($n->isa('Math::BigFloat')) - { - $self->_new_from_float($n); - } - if ($n->isa('Math::BigInt')) - { - # 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')) - { - # 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; # already a BigRat - - if (!defined $n) - { - $self->{_n} = $MBI->_zero(); # undef => 0 - $self->{_d} = $MBI->_one(); - $self->{sign} = '+'; - return $self; - } - - # string input with / delimiter - if ($n =~ /\s*\/\s*/) - { - 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 - 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,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()) - { - # > 0: mul n with it - $MBI->_lsft( $self->{_n}, $MBI->_new( $diff_e), 10); - } - } - else - { - # 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]/) && $n !~ /^0x/) - { - # 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 - { - # 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]) ? (undef,$_[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/2' => '3/2' - - 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]) ? (undef,$_[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 - $s . $MBI->_str($x->{_n}) . '/' . $MBI->_str($x->{_d}); - } - -sub bnorm - { - # 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()"); - } - - # no normalize for NaN, inf etc. - return $x if $x->{sign} !~ /^[+-]$/; - - # normalize zeros to 0/1 - if ($MBI->_is_zero($x->{_n})) - { - $x->{sign} = '+'; # never leave a -0 - $x->{_d} = $MBI->_one() unless $MBI->_is_one($x->{_d}); - return $x; - } - - return $x if $MBI->_is_one($x->{_d}); # no need to reduce - - # reduce other numbers - my $gcd = $MBI->_copy($x->{_n}); - $gcd = $MBI->_gcd($gcd,$x->{_d}); - - if (!$MBI->_is_one($gcd)) - { - $x->{_n} = $MBI->_div($x->{_n},$gcd); - $x->{_d} = $MBI->_div($x->{_d},$gcd); - } - $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; - } - -############################################################################## -# special values - -sub _bnan - { - # used by parent class bnan() to initialize number to NaN - my $self = shift; - - 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 +inf/-inf - my $self = shift; - - 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/-1 - my $self = shift; - $self->{_n} = $MBI->_one(); - $self->{_d} = $MBI->_one(); - } - -sub _bzero - { - # used by parent class bzero() to initialize number to 0 - my $self = shift; - $self->{_n} = $MBI->_zero(); - $self->{_d} = $MBI->_one(); - } - -############################################################################## -# mul/add/div etc - -sub badd - { - # 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,@_); - } - - # +inf + +inf => +inf, -inf + -inf => -inf - return $x->binf(substr($x->{sign},0,1)) - if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; - - # +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 - - # 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 - - # 5 * 3 - $x->{_n} = $MBI->_mul( $x->{_n}, $y->{_d}); - - # 7 * 4 - my $m = $MBI->_mul( $MBI->_copy( $y->{_n} ), $x->{_d} ); - - # 5 * 3 + 7 * 4 - ($x->{_n}, $x->{sign}) = _e_add( $x->{_n}, $m, $x->{sign}, $y->{sign}); - - # 4 * 3 - $x->{_d} = $MBI->_mul( $x->{_d}, $y->{_d}); - - # normalize result, and possible round - $x->bnorm()->round(@r); - } - -sub bsub - { - # subtract 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,@_); - } - - # 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 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'); - - # 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(); - - # 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 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(@r); - } - -sub bdiv - { - # (dividend: BRAT or num_str, divisor: BRAT or num_str) return - # (BRAT,BRAT) (quo,rem) or BRAT (only rem) - - # 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(); - - # 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} = $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(@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); - } - -############################################################################## -# 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]) ? (undef,$_[0]) : objectify(1,@_); - - return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN and +-inf aren't - $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]) ? (undef,$_[0]) : objectify(1,@_); - - 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]) ? (undef,$_[0]) : objectify(1,@_); - - my $sign = $_[2] || ''; $sign = '+' if $sign ne '-'; - return 1 - 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]) ? (undef,$_[0]) : objectify(1,@_); - - return 1 if ($x->{sign} =~ /^[+-]$/) && # NaN & +-inf aren't - ($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]) ? (undef,$_[0]) : objectify(1,@_); - - return 0 if $x->{sign} !~ /^[+-]$/; # NaN & +-inf aren't - return 1 if ($MBI->_is_one($x->{_d}) # x/3 is never - && $MBI->_is_even($x->{_n})); # but 4/1 is - 0; - } - -############################################################################## -# parts() and friends - -sub numerator - { - my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - - # 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; - } - -sub denominator - { - my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - - # 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 $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}; - my $d = $c->new( $MBI->_str($x->{_d})); - ($n,$d); - } - -sub length - { - 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 - { - 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) - } - -############################################################################## -# special calc routines - -sub bceil - { - my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - - return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf - $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0 - - $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; - } - -sub bfloor - { - my ($self,$x) = ref($_[0]) ? (ref($_[0]),$_[0]) : objectify(1,@_); - - return $x if $x->{sign} !~ /^[+-]$/ || # not for NaN, inf - $MBI->_is_one($x->{_d}); # 22/1 => 22, 0/1 => 0 - - $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 - { - 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 - { - # 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 '-' && $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); - # 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->round(@r) if $x->is_zero(); # 0**y => 0 (if not y <= 0) - - # shortcut if y == 1/N (is then sqrt() respective broot()) - if ($MBI->_is_one($y->{_n})) - { - return $x->bsqrt(@r) if $MBI->_is_two($y->{_d}); # 1/2 => sqrt - return $x->broot($MBI->_str($y->{_d}),@r); # 1/N => root(N) - } - - # 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); - } - -# print STDERR "# $x $y\n"; - - # otherwise: - - # n/d n ______________ - # a/b = -\/ (a/b) ** d - - # (a/b) ** n == (a ** n) / (b ** n) - $MBI->_pow($x->{_n}, $y->{_n} ); - $MBI->_pow($x->{_d}, $y->{_n} ); - - return $x->broot($MBI->_str($y->{_d}),@r); # n/d => root(n) - } - -sub blog - { - # 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,@_); - } - - # blog(1,Y) => 0 - return $x->bzero() if $x->is_one() && $y->{sign} eq '+'; - - # $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 bexp - { - # 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,@_); - } - - return $x->binf(@r) if $x->{sign} eq '+inf'; - return $x->bzero(@r) if $x->{sign} eq '-inf'; - - # we need to limit the accuracy to protect against overflow - my $fallback = 0; - my ($scale,@params); - ($x,@params) = $x->_find_round_parameters(@r); - - # also takes care of the "error in _find_round_parameters?" case - return $x if $x->{sign} eq 'NaN'; - - # 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[2]; # 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 - } - - return $x->bone(@params) if $x->is_zero(); - - # 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 - - 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 scalar 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->_as_float(),@r) )->bnorm()->bround(@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 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 = $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 = $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 - { - # 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 if $x->{sign} =~ /^[+-]inf$/ && $y->{sign} !~ /^[+-]inf$/; - return -1; - } - - 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]) ? (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_float - { - # return N/D as Math::BigFloat - - # set up parameters - my ($self,$x,@r) = (ref($_[0]),@_); - # objectify is costly, so avoid it - ($self,$x,@r) = objectify(1,$class,@_) unless ref $_[0]; - - # NaN, inf etc - return Math::BigFloat->new($x->{sign}) if $x->{sign} !~ /^[+-]$/; - - my $u = Math::BigFloat->bzero(); - $u->{sign} = $x->{sign}; - # n - $u->{_m} = $MBI->_copy($x->{_n}); - $u->{_e} = $MBI->_zero(); - $u->bdiv( $MBI->_str($x->{_d}), @r); - # return $u - $u; - } - -sub as_bin - { - 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_bin($x->{_n}); - } - -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; - -__END__ - -=head1 NAME - -Math::BigRat - Arbitrary big rational numbers - -=head1 SYNOPSIS - - use Math::BigRat; - - my $x = Math::BigRat->new('3/7'); $x += '5/9'; - - print $x->bstr(),"\n"; - print $x ** 2,"\n"; - - my $y = Math::BigRat->new('inf'); - print "$y ", ($y->is_inf ? 'is' : 'is not') , " infinity\n"; - - my $z = Math::BigRat->new(144); $z->bsqrt(); - -=head1 DESCRIPTION - -Math::BigRat complements Math::BigInt and Math::BigFloat by providing support -for arbitrary big rational numbers. - -=head2 MATH LIBRARY - -You can change the underlying module that does the low-level -math operations by using: - - use Math::BigRat try => 'GMP'; - -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'; - -If you want the code to die instead, replace "try" with -"only": - - use Math::BigRat only => 'Foo,Math::BigInt::Bar'; - -=head1 METHODS - -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 - - # 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() - - $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 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 L<as_int()> or L<as_float()> instead. - -This routine is automatically used whenever a scalar is required: - - my $x = Math::BigRat->new('3/1'); - @array = (0,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_float() - - $x = Math::BigRat->new('13/7'); - print $x->as_float(),"\n"; # '1' - - $x = Math::BigRat->new('2/3'); - print $x->as_float(5),"\n"; # '0.66667' - -Returns a copy of the object as BigFloat, preserving the -accuracy as wanted, or the default of 40 digits. - -This method was added in v0.22 of Math::BigRat (April 2008). - -=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. - -=head2 objectify() - -This is an internal routine that turns scalars into objects. - -=head1 BUGS - -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 - -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>. - -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 - 2009. - -Currently maintained by Jonathan "Duke" Leto <jonathan@leto.net> L<http://leto.net> - -=cut |