view release on metacpan or search on metacpan
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
#
# "Tax the rat farms." - Lord Vetinari
#
# The following hash values are used:
# sign : "+", "-", "+inf", "-inf", or "NaN"
# _d : denominator
# _n : numerator (value = _n/_d)
# accuracy : accuracy
# precision : precision
# You should not look at the innards of a BigRat - use the methods for this.
package Math::BigRat;
use 5.006;
use strict;
use warnings;
use Carp qw< carp croak >;
use Scalar::Util qw< blessed >;
use Math::BigFloat qw<>;
our $VERSION = '2.005002';
$VERSION =~ tr/_//d;
require Exporter;
our @ISA = qw< Math::BigFloat >;
use overload
# overload key: with_assign
'+' => sub { $_[0] -> copy() -> badd($_[1]); },
'-' => sub { my $c = $_[0] -> copy;
$_[2] ? $c -> bneg() -> badd( $_[1])
: $c -> bsub($_[1]); },
'*' => sub { $_[0] -> copy() -> bmul($_[1]); },
'/' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> bfdiv($_[0])
: $_[0] -> copy() -> bfdiv($_[1]); },
'%' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> bfmod($_[0])
: $_[0] -> copy() -> bfmod($_[1]); },
'**' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> bpow($_[0])
: $_[0] -> copy() -> bpow($_[1]); },
'<<' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> bblsft($_[0])
: $_[0] -> copy() -> bblsft($_[1]); },
'>>' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> bbrsft($_[0])
: $_[0] -> copy() -> bbrsft($_[1]); },
# overload key: assign
'+=' => sub { $_[0] -> badd($_[1]); },
'-=' => sub { $_[0] -> bsub($_[1]); },
'*=' => sub { $_[0] -> bmul($_[1]); },
'/=' => sub { scalar $_[0] -> bfdiv($_[1]); },
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
# '~.' => sub { },
# overload key: mutators
'++' => sub { $_[0] -> binc() },
'--' => sub { $_[0] -> bdec() },
# overload key: func
'atan2' => sub { $_[2] ? ref($_[0]) -> new($_[1]) -> batan2($_[0])
: $_[0] -> copy() -> batan2($_[1]); },
'cos' => sub { $_[0] -> copy() -> bcos(); },
'sin' => sub { $_[0] -> copy() -> bsin(); },
'exp' => sub { $_[0] -> copy() -> bexp($_[1]); },
'abs' => sub { $_[0] -> copy() -> babs(); },
'log' => sub { $_[0] -> copy() -> blog(); },
'sqrt' => sub { $_[0] -> copy() -> bsqrt(); },
'int' => sub { $_[0] -> copy() -> bint(); },
# overload key: conversion
'bool' => sub { $_[0] -> is_zero() ? '' : 1; },
'""' => sub { $_[0] -> bstr(); },
'0+' => sub { $_[0] -> numify(); },
'=' => sub { $_[0]->copy(); },
;
BEGIN {
*objectify = \&Math::BigInt::objectify;
*AUTOLOAD = \&Math::BigFloat::AUTOLOAD; # can't inherit AUTOLOAD
*as_number = \&as_int;
*is_pos = \&is_positive;
*is_neg = \&is_negative;
}
##############################################################################
# Global constants and flags. Access these only via the accessor methods!
our $accuracy = undef;
our $precision = undef;
our $round_mode = 'even';
our $div_scale = 40;
our $upgrade = undef;
our $downgrade = undef;
our $_trap_nan = 0; # croak on NaNs?
our $_trap_inf = 0; # croak on Infs?
my $nan = 'NaN'; # constant for easier life
my $LIB = Math::BigInt -> config('lib'); # math backend library
# Has import() been called yet? This variable is needed to make "require" work.
my $IMPORT = 0;
# Compare the following function with @ISA above. This inheritance mess needs a
# clean up. When doing so, also consider the BEGIN block and the AUTOLOAD code.
# Fixme!
sub isa {
return 0 if $_[1] =~ /^Math::Big(Int|Float)/; # we aren't
UNIVERSAL::isa(@_);
}
##############################################################################
sub new {
# Create a new Math::BigFloat object from a string or another Math::BigInt,
# Math::BigFloat, or Math::BigRat object. See hash keys documented at top.
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
# Make "require" work.
$class -> import() if $IMPORT == 0;
if (@_ > 2) {
carp("Superfluous arguments to new() ignored.");
}
# Calling new() with no input arguments has been discouraged for more than
# 10 years, but people apparently still use it, so we still support it.
# Also, if any of the arguments is undefined, return zero.
if (@_ == 0 ||
@_ == 1 && !defined($_[0]) ||
@_ == 2 && (!defined($_[0]) || !defined($_[1])))
{
#carp("Use of uninitialized value in new()");
return $class -> bzero();
}
my @args = @_;
# Initialize a new object.
$self = bless {}, $class;
# Special cases for speed and to avoid infinite recursion. The methods
# Math::BigInt->as_rat() and Math::BigFloat->as_rat() call
# Math::BigRat->as_rat() (i.e., this method) with a scalar (non-ref)
# integer argument.
if (@args == 1 && !ref($args[0])) {
# "3", "+3", "-3", "+001_2_3e+4"
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
# Get the sign.
my $sign = '+'; # default is to return positive infinity
if (defined($_[0]) && $_[0] =~ /^\s*([+-])(inf|$)/i) {
$sign = $1;
shift;
}
# Get the rounding parameters, if any.
my @r = @_;
# Downgrade?
my $dng = $class -> downgrade();
if ($dng && $dng ne $class) {
return $self -> _dng() -> binf($sign, @r) if $selfref;
return $dng -> binf($sign, @r);
}
# If called as a class method, initialize a new object.
$self = bless {}, $class unless $selfref;
$self -> {sign} = $sign . 'inf';
$self -> {_n} = $LIB -> _zero();
$self -> {_d} = $LIB -> _one();
# If rounding parameters are given as arguments, use them. If no rounding
# parameters are given, and if called as a class method initialize the new
# instance with the class variables.
#return $self -> round(@r); # this should work, but doesnt; fixme!
if (@r) {
if (@r >= 2 && defined($r[0]) && defined($r[1])) {
carp "can't specify both accuracy and precision";
return $self -> bnan();
}
$self->{accuracy} = $r[0];
$self->{precision} = $r[1];
} else {
unless($selfref) {
$self->{accuracy} = $class -> accuracy();
$self->{precision} = $class -> precision();
}
}
return $self;
}
sub bnan {
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
{
no strict 'refs';
if (${"${class}::_trap_nan"}) {
croak("Tried to create NaN in $class->bnan()");
}
}
# Make "require" work.
$class -> import() if $IMPORT == 0;
# Don't modify constant (read-only) objects.
return $self if $selfref && $self -> modify('bnan');
my $dng = $class -> downgrade();
if ($dng && $dng ne $class) {
return $self -> _dng() -> bnan(@_) if $selfref;
return $dng -> bnan(@_);
}
# Get the rounding parameters, if any.
my @r = @_;
# If called as a class method, initialize a new object.
$self = bless {}, $class unless $selfref;
$self -> {sign} = $nan;
$self -> {_n} = $LIB -> _zero();
$self -> {_d} = $LIB -> _one();
# If rounding parameters are given as arguments, use them. If no rounding
# parameters are given, and if called as a class method initialize the new
# instance with the class variables.
#return $self -> round(@r); # this should work, but doesnt; fixme!
if (@r) {
if (@r >= 2 && defined($r[0]) && defined($r[1])) {
carp "can't specify both accuracy and precision";
return $self -> bnan();
}
$self->{accuracy} = $r[0];
$self->{precision} = $r[1];
} else {
unless($selfref) {
$self->{accuracy} = $class -> accuracy();
$self->{precision} = $class -> precision();
}
}
return $self;
}
sub bpi {
my $self = shift;
my $selfref = ref $self;
my $class = $selfref || $self;
my @r = @_; # rounding paramters
# Make "require" work.
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
sub is_even {
# return true if arg (BINT or num_str) is even or false if odd
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
return 0 unless $x -> is_finite();
return 1 if $LIB->_is_one($x->{_d}) && $LIB->_is_even($x->{_n});
return 0;
}
sub is_int {
# return true if arg (BRAT or num_str) is an integer
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
return 1 if $x -> is_finite() && $LIB->_is_one($x->{_d});
return 0;
}
##############################################################################
sub config {
my $self = shift;
my $class = ref($self) || $self || __PACKAGE__;
# Getter/accessor.
if (@_ == 1 && ref($_[0]) ne 'HASH') {
my $param = shift;
return $class if $param eq 'class';
return $LIB if $param eq 'with';
return $self -> SUPER::config($param);
}
# Setter.
my $cfg = $self -> SUPER::config(@_);
# We need only to override the ones that are different from our parent.
unless (ref($self)) {
$cfg->{class} = $class;
$cfg->{with} = $LIB;
}
$cfg;
}
##############################################################################
# comparing
sub bcmp {
# compare two signed numbers
# set up parameters
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
if (!$x -> is_finite() || !$y -> is_finite()) {
# $x is NaN and/or $y is NaN
return if $x -> is_nan() || $y -> is_nan();
# $x and $y are both either +inf or -inf
return 0 if $x->{sign} eq $y->{sign} && $x -> is_inf();
# $x = +inf and $y < +inf
return +1 if $x -> is_inf("+");
# $x = -inf and $y > -inf
return -1 if $x -> is_inf("-");
# $x < +inf and $y = +inf
return -1 if $y -> is_inf("+");
# $x > -inf and $y = -inf
return +1;
}
# $x >= 0 and $y < 0
return 1 if $x->{sign} eq '+' && $y->{sign} eq '-';
# $x < 0 and $y >= 0
return -1 if $x->{sign} eq '-' && $y->{sign} eq '+';
# At this point, we know that $x and $y have the same sign.
# shortcut
my $xz = $LIB->_is_zero($x->{_n});
my $yz = $LIB->_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 = $LIB->_mul($LIB->_copy($x->{_n}), $y->{_d});
my $u = $LIB->_mul($LIB->_copy($y->{_n}), $x->{_d});
my $cmp = $LIB->_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 ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
# handle +-inf and NaN
if (!$x -> is_finite() || !$y -> is_finite()) {
return if ($x -> is_nan() || $y -> is_nan());
return 0 if $x -> is_inf() && $y -> is_inf();
return 1 if $x -> is_inf() && !$y -> is_inf();
return -1;
}
my $t = $LIB->_mul($LIB->_copy($x->{_n}), $y->{_d});
my $u = $LIB->_mul($LIB->_copy($y->{_n}), $x->{_d});
$LIB->_acmp($t, $u); # ignore signs
}
##############################################################################
# sign manipulation
sub bneg {
# (BRAT or num_str) return BRAT
# negate number or make a negated number from string
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bneg');
# for +0 do not negate (to have always normalized +0). Does nothing for 'NaN'
$x->{sign} =~ tr/+-/-+/
unless ($x->{sign} eq '+' && $LIB->_is_zero($x->{_n}));
$x -> round(@r);
$x -> _dng() if $x -> is_int() || $x -> is_inf() || $x -> is_nan();
return $x;
}
sub bnorm {
# reduce the number to the shortest form
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
# Both parts must be objects of whatever we are using today.
if (my $c = $LIB->_check($x->{_n})) {
croak("n did not pass the self-check ($c) in bnorm()");
}
if (my $c = $LIB->_check($x->{_d})) {
croak("d did not pass the self-check ($c) in bnorm()");
}
# no normalize for NaN, inf etc.
if (!$x -> is_finite()) {
$x -> _dng();
return $x;
}
# normalize zeros to 0/1
if ($LIB->_is_zero($x->{_n})) {
$x->{sign} = '+'; # never leave a -0
$x->{_d} = $LIB->_one() unless $LIB->_is_one($x->{_d});
$x -> _dng();
return $x;
}
# n/1
if ($LIB->_is_one($x->{_d})) {
$x -> _dng();
return $x; # no need to reduce
}
# Compute the GCD.
my $gcd = $LIB->_gcd($LIB->_copy($x->{_n}), $x->{_d});
if (!$LIB->_is_one($gcd)) {
$x->{_n} = $LIB->_div($x->{_n}, $gcd);
$x->{_d} = $LIB->_div($x->{_d}, $gcd);
}
$x;
}
sub binc {
# increment value (add 1)
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('binc');
if (!$x -> is_finite()) { # NaN, inf, -inf
$x -> round(@r);
$x -> _dng();
return $x;
}
if ($x->{sign} eq '-') {
if ($LIB->_acmp($x->{_n}, $x->{_d}) < 0) {
# -1/3 ++ => 2/3 (overflow at 0)
$x->{_n} = $LIB->_sub($LIB->_copy($x->{_d}), $x->{_n});
$x->{sign} = '+';
} else {
$x->{_n} = $LIB->_sub($x->{_n}, $x->{_d}); # -5/2 => -3/2
}
} else {
$x->{_n} = $LIB->_add($x->{_n}, $x->{_d}); # 5/2 => 7/2
}
$x -> bnorm(); # is this necessary? check! XXX
$x -> round(@r);
$x -> _dng() if $x -> is_int() || $x -> is_inf() || $x -> is_nan();
return $x;
}
sub bdec {
# decrement value (subtract 1)
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bdec');
if (!$x -> is_finite()) { # NaN, inf, -inf
$x -> round(@r);
$x -> _dng();
return $x;
}
if ($x->{sign} eq '-') {
$x->{_n} = $LIB->_add($x->{_n}, $x->{_d}); # -5/2 => -7/2
} else {
if ($LIB->_acmp($x->{_n}, $x->{_d}) < 0) # n < d?
{
# 1/3 -- => -2/3
$x->{_n} = $LIB->_sub($LIB->_copy($x->{_d}), $x->{_n});
$x->{sign} = '-';
} else {
$x->{_n} = $LIB->_sub($x->{_n}, $x->{_d}); # 5/2 => 3/2
}
}
$x -> bnorm(); # is this necessary? check! XXX
$x -> round(@r);
$x -> _dng() if $x -> is_int() || $x -> is_inf() || $x -> is_nan();
return $x;
}
##############################################################################
# mul/add/div etc
sub badd {
# add two rational numbers
# set up parameters
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('badd');
unless ($x -> is_finite() && $y -> is_finite()) {
if ($x -> is_nan() || $y -> is_nan()) {
return $x -> bnan(@r);
} elsif ($x -> is_inf("+")) {
return $x -> bnan(@r) if $y -> is_inf("-");
return $x -> binf("+", @r);
} elsif ($x -> is_inf("-")) {
return $x -> bnan(@r) if $y -> is_inf("+");
return $x -> binf("-", @r);
} elsif ($y -> is_inf("+")) {
return $x -> binf("+", @r);
} elsif ($y -> is_inf("-")) {
return $x -> binf("-", @r);
}
}
# 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:
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
# +inf * -/-inf => -inf, -inf * +/+inf => -inf
return $x -> binf(@r) if $x -> is_positive() && $y -> is_positive();
return $x -> binf(@r) if $x -> is_negative() && $y -> is_negative();
return $x -> binf('-', @r);
}
return $x -> _upg() -> bmul($y, @r) if $class -> upgrade();
if ($x -> is_zero() || $y -> is_zero()) {
return $x -> bzero(@r);
}
# According to Knuth, this can be optimized by doing gcd twice (for d
# and n) and reducing in one step.
#
# p s p * s (p / gcd(p, r)) * (s / gcd(s, q))
# - * - = ----- = ---------------------------------
# q r q * r (q / gcd(s, q)) * (r / gcd(p, r))
my $gcd_pr = $LIB -> _gcd($LIB -> _copy($x->{_n}), $y->{_d});
my $gcd_sq = $LIB -> _gcd($LIB -> _copy($y->{_n}), $x->{_d});
$x->{_n} = $LIB -> _mul(scalar $LIB -> _div($x->{_n}, $gcd_pr),
scalar $LIB -> _div($LIB -> _copy($y->{_n}),
$gcd_sq));
$x->{_d} = $LIB -> _mul(scalar $LIB -> _div($x->{_d}, $gcd_sq),
scalar $LIB -> _div($LIB -> _copy($y->{_d}),
$gcd_pr));
# compute new sign
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
$x -> bnorm(); # this is probably redundant; check XXX
$x -> round(@r);
$x -> _dng() if $x -> is_int();
return $x;
}
*bdiv = \&bfdiv;
*bmod = \&bfmod;
sub bfdiv {
# (dividend: BRAT or num_str, divisor: BRAT or num_str) return
# (BRAT, BRAT) (quo, rem) or BRAT (only rem)
# Set up parameters.
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
###########################################################################
# Code for all classes that share the common interface.
###########################################################################
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bfdiv');
my $wantarray = wantarray; # call only once
# At least one argument is NaN. This is handled the same way as in
# Math::BigInt -> bfdiv().
if ($x -> is_nan() || $y -> is_nan()) {
return $wantarray ? ($x -> bnan(@r), $class -> bnan(@r))
: $x -> bnan(@r);
}
# Divide by zero and modulo zero. This is handled the same way as in
# Math::BigInt -> bfdiv(). See the comments in the code implementing that
# method.
if ($y -> is_zero()) {
my $rem;
if ($wantarray) {
$rem = $x -> copy() -> round(@r);
$rem -> _dng() if $rem -> is_int();
}
if ($x -> is_zero()) {
$x -> bnan(@r);
} else {
$x -> binf($x -> {sign}, @r);
}
return $wantarray ? ($x, $rem) : $x;
}
# Numerator (dividend) is +/-inf. This is handled the same way as in
# Math::BigInt -> bfdiv(). See the comment in the code for Math::BigInt ->
# bfdiv() for further details.
if ($x -> is_inf()) {
my $rem;
$rem = $class -> bnan(@r) if $wantarray;
if ($y -> is_inf()) {
$x -> bnan(@r);
} else {
my $sign = $x -> bcmp(0) == $y -> bcmp(0) ? '+' : '-';
$x -> binf($sign, @r);
}
return $wantarray ? ($x, $rem) : $x;
}
# Denominator (divisor) is +/-inf. This is handled the same way as in
# Math::BigFloat -> bfdiv(). See the comments in the code implementing that
# method.
if ($y -> is_inf()) {
my $rem;
if ($wantarray) {
if ($x -> is_zero() || $x -> bcmp(0) == $y -> bcmp(0)) {
$rem = $x -> copy() -> round(@r);
$rem -> _dng() if $rem -> is_int();
$x -> bzero(@r);
} else {
$rem = $class -> binf($y -> {sign}, @r);
$x -> bone('-', @r);
}
} else {
$x -> bzero(@r);
}
return $wantarray ? ($x, $rem) : $x;
}
# At this point, both the numerator and denominator are finite, non-zero
# numbers.
# 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().
#
# p r p * s (p / gcd(p, r)) * (s / gcd(s, q))
# - / - = ----- = ---------------------------------
# q s q * r (q / gcd(s, q)) * (r / gcd(p, r))
$x->{_n} = $LIB->_mul($x->{_n}, $y->{_d});
$x->{_d} = $LIB->_mul($x->{_d}, $y->{_n});
# compute new sign
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
$x -> bnorm();
if ($wantarray) {
my $rem = $x -> copy();
$x -> bfloor();
$x -> round(@r);
$rem -> bsub($x -> copy()) -> bmul($y);
$x -> _dng() if $x -> is_int();
$rem -> _dng() if $rem -> is_int();
return $x, $rem;
}
$x -> _dng() if $x -> is_int();
return $x;
}
sub bfmod {
# This is the remainder after floored division.
# Set up parameters.
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
###########################################################################
# Code for all classes that share the common interface.
###########################################################################
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bfmod');
# At least one argument is NaN. This is handled the same way as in
# Math::BigInt -> bfmod().
if ($x -> is_nan() || $y -> is_nan()) {
return $x -> bnan();
}
# Modulo zero. This is handled the same way as in Math::BigInt -> bfmod().
if ($y -> is_zero()) {
return $x -> round();
}
# Numerator (dividend) is +/-inf. This is handled the same way as in
# Math::BigInt -> bfmod().
if ($x -> is_inf()) {
return $x -> bnan();
}
# Denominator (divisor) is +/-inf. This is handled the same way as in
# Math::BigInt -> bfmod().
if ($y -> is_inf()) {
if ($x -> is_zero() || $x -> bcmp(0) == $y -> bcmp(0)) {
$x -> _dng() if $x -> is_int();
return $x;
} else {
return $x -> binf($y -> sign());
}
}
# At this point, both the numerator and denominator are finite numbers, and
# the denominator (divisor) is non-zero.
if ($x -> is_zero()) { # 0 / 7 = 0, mod 0
return $x -> bzero();
}
# Compute $x - $y * floor($x / $y). This can be optimized by working on the
# library thingies directly. XXX
$x -> bsub($x -> copy() -> bfdiv($y) -> bfloor() -> bmul($y));
return $x -> round(@r);
}
sub btdiv {
# (dividend: BRAT or num_str, divisor: BRAT or num_str) return
# (BRAT, BRAT) (quo, rem) or BRAT (only rem)
# Set up parameters.
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
###########################################################################
# Code for all classes that share the common interface.
###########################################################################
# Don't modify constant (read-only) objects.
return $x if $x -> modify('btdiv');
my $wantarray = wantarray; # call only once
# At least one argument is NaN. This is handled the same way as in
# Math::BigInt -> btdiv().
if ($x -> is_nan() || $y -> is_nan()) {
return $wantarray ? ($x -> bnan(@r), $class -> bnan(@r))
: $x -> bnan(@r);
}
# Divide by zero and modulo zero. This is handled the same way as in
# Math::BigInt -> btdiv(). See the comments in the code implementing that
# method.
if ($y -> is_zero()) {
my $rem;
if ($wantarray) {
$rem = $x -> copy() -> round(@r);
$rem -> _dng() if $rem -> is_int();
}
if ($x -> is_zero()) {
$x -> bnan(@r);
} else {
$x -> binf($x -> {sign}, @r);
}
return $wantarray ? ($x, $rem) : $x;
}
# Numerator (dividend) is +/-inf. This is handled the same way as in
# Math::BigInt -> btdiv(). See the comment in the code for Math::BigInt ->
# btdiv() for further details.
if ($x -> is_inf()) {
my $rem;
$rem = $class -> bnan(@r) if $wantarray;
if ($y -> is_inf()) {
$x -> bnan(@r);
} else {
my $sign = $x -> bcmp(0) == $y -> bcmp(0) ? '+' : '-';
$x -> binf($sign, @r);
}
return $wantarray ? ($x, $rem) : $x;
}
# Denominator (divisor) is +/-inf. This is handled the same way as in
# Math::BigFloat -> btdiv(). See the comments in the code implementing that
# method.
if ($y -> is_inf()) {
my $rem;
if ($wantarray) {
$rem = $x -> copy();
$rem -> _dng() if $rem -> is_int();
$x -> bzero();
return $x, $rem;
} else {
if ($y -> is_inf()) {
if ($x -> is_nan() || $x -> is_inf()) {
return $x -> bnan();
} else {
return $x -> bzero();
}
}
}
}
if ($x -> is_zero()) {
$x -> round(@r);
$x -> _dng() if $x -> is_int();
if ($wantarray) {
my $rem = $class -> bzero(@r);
return $x, $rem;
}
return $x;
}
# At this point, both the numerator and denominator are finite, non-zero
# numbers.
# 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().
#
# p r p * s (p / gcd(p, r)) * (s / gcd(s, q))
# - / - = ----- = ---------------------------------
# q s q * r (q / gcd(s, q)) * (r / gcd(p, r))
$x->{_n} = $LIB->_mul($x->{_n}, $y->{_d});
$x->{_d} = $LIB->_mul($x->{_d}, $y->{_n});
# compute new sign
$x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-';
$x -> bnorm();
if ($wantarray) {
my $rem = $x -> copy();
$x -> bint();
$x -> round(@r);
$rem -> bsub($x -> copy()) -> bmul($y);
$x -> _dng() if $x -> is_int();
$rem -> _dng() if $rem -> is_int();
return $x, $rem;
}
$x -> _dng() if $x -> is_int();
return $x;
}
sub btmod {
# This is the remainder after floored division.
# Set up parameters.
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
###########################################################################
# Code for all classes that share the common interface.
###########################################################################
# Don't modify constant (read-only) objects.
return $x if $x -> modify('btmod');
# At least one argument is NaN. This is handled the same way as in
# Math::BigInt -> btmod().
if ($x -> is_nan() || $y -> is_nan()) {
return $x -> bnan();
}
# Modulo zero. This is handled the same way as in Math::BigInt -> btmod().
if ($y -> is_zero()) {
return $x -> round();
}
# Numerator (dividend) is +/-inf. This is handled the same way as in
# Math::BigInt -> btmod().
if ($x -> is_inf()) {
return $x -> bnan();
}
# Denominator (divisor) is +/-inf. This is handled the same way as in
# Math::BigInt -> btmod().
if ($y -> is_inf()) {
$x -> _dng() if $x -> is_int();
return $x;
}
# At this point, both the numerator and denominator are finite numbers, and
# the denominator (divisor) is non-zero.
if ($x -> is_zero()) { # 0 / 7 = 0, mod 0
return $x -> bzero();
}
# Compute $x - $y * int($x / $y).
#
# p r (p * s / gcd(q, s)) mod (r * q / gcd(q, s))
# - mod - = -------------------------------------------
# q s q * s / gcd(q, s)
#
# u mod v u = p * (s / gcd(q, s))
# = ------- where v = r * (q / gcd(q, s))
# w w = q * (s / gcd(q, s))
my $p = $x -> {_n};
my $q = $x -> {_d};
my $r = $y -> {_n};
my $s = $y -> {_d};
my $gcd_qs = $LIB -> _gcd($LIB -> _copy($q), $s);
my $s_by_gcd_qs = $LIB -> _div($LIB -> _copy($s), $gcd_qs);
my $q_by_gcd_qs = $LIB -> _div($LIB -> _copy($q), $gcd_qs);
my $u = $LIB -> _mul($LIB -> _copy($p), $s_by_gcd_qs);
my $v = $LIB -> _mul($LIB -> _copy($r), $q_by_gcd_qs);
my $w = $LIB -> _mul($LIB -> _copy($q), $s_by_gcd_qs);
$x->{_n} = $LIB -> _mod($u, $v);
$x->{_d} = $w;
$x -> bnorm();
return $x -> round(@r);
}
sub binv {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('binv');
return $x -> round(@r) if $x -> is_nan();
return $x -> bzero(@r) if $x -> is_inf();
return $x -> binf("+", @r) if $x -> is_zero();
($x -> {_n}, $x -> {_d}) = ($x -> {_d}, $x -> {_n});
$x -> round(@r);
$x -> _dng() if $x -> is_int() || $x -> is_inf() || $x -> is_nan();
return $x;
}
sub bsqrt {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bsqrt');
return $x -> bnan() if $x->{sign} !~ /^[+]/; # NaN, -inf or < 0
return $x if $x -> is_inf("+"); # sqrt(inf) == inf
return $x -> round(@r) if $x -> is_zero() || $x -> is_one();
my $n = $x -> {_n};
my $d = $x -> {_d};
# Look for an exact solution. For the numerator and the denominator, take
# the square root and square it and see if we got the original value. If we
# did, for both the numerator and the denominator, we have an exact
# solution.
{
my $nsqrt = $LIB -> _sqrt($LIB -> _copy($n));
my $n2 = $LIB -> _mul($LIB -> _copy($nsqrt), $nsqrt);
if ($LIB -> _acmp($n, $n2) == 0) {
my $dsqrt = $LIB -> _sqrt($LIB -> _copy($d));
my $d2 = $LIB -> _mul($LIB -> _copy($dsqrt), $dsqrt);
if ($LIB -> _acmp($d, $d2) == 0) {
$x -> {_n} = $nsqrt;
$x -> {_d} = $dsqrt;
return $x -> round(@r);
}
}
}
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;
my $xn = Math::BigFloat -> new($LIB -> _str($n));
my $xd = Math::BigFloat -> new($LIB -> _str($d));
my $xtmp = Math::BigRat -> new($xn -> bfdiv($xd) -> bsqrt() -> bfstr());
$x -> {sign} = $xtmp -> {sign};
$x -> {_n} = $xtmp -> {_n};
$x -> {_d} = $xtmp -> {_d};
$x -> round(@r);
}
sub bpow {
# power ($x ** $y)
# Set up parameters.
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bpow');
# $x and/or $y is a NaN
return $x -> bnan() if $x -> is_nan() || $y -> is_nan();
# $x and/or $y is a +/-Inf
if ($x -> is_inf("-")) {
return $x -> bzero() if $y -> is_negative();
return $x -> bnan() if $y -> is_zero();
return $x if $y -> is_odd();
return $x -> bneg();
} elsif ($x -> is_inf("+")) {
return $x -> bzero() if $y -> is_negative();
return $x -> bnan() if $y -> is_zero();
return $x;
} elsif ($y -> is_inf("-")) {
return $x -> bnan() if $x -> is_one("-");
return $x -> binf("+") if $x > -1 && $x < 1;
return $x -> bone() if $x -> is_one("+");
return $x -> bzero();
} elsif ($y -> is_inf("+")) {
return $x -> bnan() if $x -> is_one("-");
return $x -> bzero() if $x > -1 && $x < 1;
return $x -> bone() if $x -> is_one("+");
return $x -> binf("+");
}
if ($x -> is_zero()) {
return $x -> bone() if $y -> is_zero();
return $x -> binf() if $y -> is_negative();
return $x;
}
# We don't support complex numbers, so upgrade or return NaN.
if ($x -> is_negative() && !$y -> is_int()) {
return $x -> _upg() -> bpow($y, @r) if $class -> upgrade();
return $x -> bnan();
}
if ($x -> is_one("+") || $y -> is_one()) {
return $x;
}
if ($x -> is_one("-")) {
return $x if $y -> is_odd();
return $x -> bneg();
}
# (a/b)^-(c/d) = (b/a)^(c/d)
($x->{_n}, $x->{_d}) = ($x->{_d}, $x->{_n}) if $y -> is_negative();
unless ($LIB->_is_one($y->{_n})) {
$x->{_n} = $LIB->_pow($x->{_n}, $y->{_n});
$x->{_d} = $LIB->_pow($x->{_d}, $y->{_n});
$x->{sign} = '+' if $x->{sign} eq '-' && $LIB->_is_even($y->{_n});
}
unless ($LIB->_is_one($y->{_d})) {
return $x -> bsqrt(@r) if $LIB->_is_two($y->{_d}); # 1/2 => sqrt
return $x -> broot($LIB->_str($y->{_d}), @r); # 1/N => root(N)
}
return $x -> round(@r);
}
sub broot {
# set up parameters
my ($class, $x, $y, @r) = ref($_[0]) && ref($_[0]) eq ref($_[1])
? (ref($_[0]), @_)
: objectify(2, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('broot');
# Convert $x into a Math::BigFloat.
my $xd = Math::BigFloat -> new($LIB -> _str($x->{_d}));
my $xflt = Math::BigFloat -> new($LIB -> _str($x->{_n})) -> bfdiv($xd);
$xflt -> {sign} = $x -> {sign};
# Convert $y into a Math::BigFloat.
my $yd = Math::BigFloat -> new($LIB -> _str($y->{_d}));
my $yflt = Math::BigFloat -> new($LIB -> _str($y->{_n})) -> bfdiv($yd);
$yflt -> {sign} = $y -> {sign};
# Compute the root and convert back to a Math::BigRat.
$xflt -> broot($yflt, @r);
my $xtmp = Math::BigRat -> new($xflt -> bfstr());
$x -> {sign} = $xtmp -> {sign};
$x -> {_n} = $xtmp -> {_n};
$x -> {_d} = $xtmp -> {_d};
return $x;
}
sub bmuladd {
# multiply two numbers and then add the third to the result
# (BINT or num_str, BINT or num_str, BINT or num_str) return BINT
# set up parameters
my ($class, $x, $y, $z, @r)
= ref($_[0]) && ref($_[0]) eq ref($_[1]) && ref($_[1]) eq ref($_[2])
? (ref($_[0]), @_)
: objectify(3, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bmuladd');
# At least one of x, y, and z is a NaN
return $x -> bnan(@r) if ($x -> is_nan() ||
$y -> is_nan() ||
$z -> is_nan());
# At least one of x, y, and z is an Inf
if ($x -> is_inf("-")) {
if ($y -> is_neg()) { # x = -inf, y < 0
if ($z -> is_inf("-")) {
return $x -> bnan(@r);
} else {
return $x -> binf("+", @r);
}
} elsif ($y -> is_zero()) { # x = -inf, y = 0
return $x -> bnan(@r);
} else { # x = -inf, y > 0
if ($z -> is_inf("+")) {
return $x -> bnan(@r);
} else {
return $x -> binf("-", @r);
}
}
} elsif ($x -> is_inf("+")) {
if ($y -> is_neg()) { # x = +inf, y < 0
if ($z -> is_inf("+")) {
return $x -> bnan(@r);
} else {
return $x -> binf("-", @r);
}
} elsif ($y -> is_zero()) { # x = +inf, y = 0
return $x -> bnan(@r);
} else { # x = +inf, y > 0
if ($z -> is_inf("-")) {
return $x -> bnan(@r);
} else {
return $x -> binf("+", @r);
}
}
} elsif ($x -> is_neg()) {
if ($y -> is_inf("-")) { # -inf < x < 0, y = -inf
if ($z -> is_inf("-")) {
return $x -> bnan(@r);
} else {
return $x -> binf("+", @r);
}
} elsif ($y -> is_inf("+")) { # -inf < x < 0, y = +inf
if ($z -> is_inf("+")) {
return $x -> bnan(@r);
} else {
return $x -> binf("-", @r);
}
} else { # -inf < x < 0, -inf < y < +inf
if ($z -> is_inf("-")) {
return $x -> binf("-", @r);
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
my $xtmp = $x -> as_int() -> bnot() -> as_rat();
$x -> {sign} = $xtmp -> {sign};
$x -> {_n} = $xtmp -> {_n};
$x -> {_d} = $xtmp -> {_d};
return $x -> round(@r);
}
##############################################################################
# round
sub round {
my $x = shift;
# Don't modify constant (read-only) objects.
return $x if $x -> modify('round');
$x -> _dng() if ($x -> is_int() ||
$x -> is_inf() ||
$x -> is_nan());
$x;
}
sub bround {
my $x = shift;
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bround');
$x -> _dng() if ($x -> is_int() ||
$x -> is_inf() ||
$x -> is_nan());
$x;
}
sub bfround {
my $x = shift;
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bfround');
$x -> _dng() if ($x -> is_int() ||
$x -> is_inf() ||
$x -> is_nan());
$x;
}
sub bfloor {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bfloor');
return $x -> bnan(@r) if $x -> is_nan();
if (!$x -> is_finite() || # NaN or inf or
$LIB->_is_one($x->{_d})) # integer
{
$x -> round(@r);
$x -> _dng();
return $x;
}
$x->{_n} = $LIB->_div($x->{_n}, $x->{_d}); # 22/7 => 3/1 w/ truncate
$x->{_d} = $LIB->_one(); # d => 1
$x->{_n} = $LIB->_inc($x->{_n}) if $x->{sign} eq '-'; # -22/7 => -4/1
$x -> round(@r);
$x -> _dng();
return $x;
}
sub bceil {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bceil');
return $x -> bnan(@r) if $x -> is_nan();
if (!$x -> is_finite() || # NaN or inf or
$LIB->_is_one($x->{_d})) # integer
{
$x -> round(@r);
$x -> _dng();
return $x;
}
$x->{_n} = $LIB->_div($x->{_n}, $x->{_d}); # 22/7 => 3/1 w/ truncate
$x->{_d} = $LIB->_one(); # d => 1
$x->{_n} = $LIB->_inc($x->{_n}) if $x->{sign} eq '+'; # +22/7 => 4/1
$x->{sign} = '+' if $x->{sign} eq '-' && $LIB->_is_zero($x->{_n}); # -0 => 0
$x -> round(@r);
$x -> _dng();
return $x;
}
sub bint {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Don't modify constant (read-only) objects.
return $x if $x -> modify('bint');
return $x -> bnan(@r) if $x -> is_nan();
if (!$x -> is_finite() || # NaN or inf or
$LIB->_is_one($x->{_d})) # integer
{
$x -> round(@r);
$x -> _dng();
return $x;
}
$x->{_n} = $LIB->_div($x->{_n}, $x->{_d}); # 22/7 => 3/1 w/ truncate
$x->{_d} = $LIB->_one(); # d => 1
$x->{sign} = '+' if $x->{sign} eq '-' && $LIB -> _is_zero($x->{_n});
$x -> round(@r);
$x -> _dng();
return $x;
}
sub bgcd {
# GCD -- Euclid's algorithm, variant C (Knuth Vol 3, pg 341 ff)
# Class::method(...) -> Class->method(...)
unless (@_ && (defined(blessed($_[0])) && $_[0] -> isa(__PACKAGE__) ||
($_[0] =~ /^[a-z]\w*(?:::[a-z]\w*)*$/i &&
$_[0] !~ /^(inf|nan)/i)))
{
#carp "Using ", (caller(0))[3], "() as a function is deprecated;",
# " use is as a method instead";
unshift @_, __PACKAGE__;
}
my ($class, @args) = objectify(0, @_);
# Pre-process list of operands.
for my $arg (@args) {
return $class -> bnan() unless $arg -> is_finite();
}
# Temporarily disable downgrading.
my $dng = $class -> downgrade();
$class -> downgrade(undef);
my $x = shift @args;
$x = $x -> copy(); # bgcd() and blcm() never modify any operands
while (@args) {
my $y = shift @args;
# greatest common divisor
while (! $y -> is_zero()) {
($x, $y) = ($y -> copy(), $x -> copy() -> bmod($y));
}
last if $x -> is_one();
}
$x -> babs();
# Restore downgrading.
$class -> downgrade($dng);
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
return ($c -> bnan(), $c -> bnan()) if $x -> is_nan();
return ($c -> binf(), $c -> binf()) if $x -> is_inf("+");
return ($c -> binf('-'), $c -> binf()) if $x -> is_inf("-");
my $n = $c -> new($LIB->_str($x->{_n}));
$n->{sign} = $x->{sign};
my $d = $c -> new($LIB->_str($x->{_d}));
($n, $d);
}
sub dparts {
my $x = shift;
my $class = ref $x;
croak("dparts() is an instance method") unless $class;
if ($x -> is_nan()) {
return $class -> bnan(), $class -> bnan() if wantarray;
return $class -> bnan();
}
if ($x -> is_inf()) {
return $class -> binf($x -> sign()), $class -> bzero() if wantarray;
return $class -> binf($x -> sign());
}
# 355/113 => 3 + 16/113
my ($q, $r) = $LIB -> _div($LIB -> _copy($x -> {_n}), $x -> {_d});
my $int = Math::BigRat -> new($x -> {sign} . $LIB -> _str($q));
return $int unless wantarray;
my $frc = Math::BigRat -> new($x -> {sign} . $LIB -> _str($r),
$LIB -> _str($x -> {_d}));
return $int, $frc;
}
sub fparts {
my $x = shift;
my $class = ref $x;
croak("fparts() is an instance method") unless $class;
return ($class -> bnan(),
$class -> bnan()) if $x -> is_nan();
my $numer = $x -> copy();
my $denom = $class -> bzero();
$denom -> {_n} = $numer -> {_d};
$numer -> {_d} = $LIB -> _one();
return $numer, $denom;
}
sub numerator {
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
# NaN, inf, -inf
return Math::BigInt -> new($x->{sign}) if !$x -> is_finite();
my $n = Math::BigInt -> new($LIB->_str($x->{_n}));
$n->{sign} = $x->{sign};
$n;
}
sub denominator {
my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
# NaN
return Math::BigInt -> new($x->{sign}) if $x -> is_nan();
# inf, -inf
return Math::BigInt -> bone() if !$x -> is_finite();
Math::BigInt -> new($LIB->_str($x->{_d}));
}
###############################################################################
# String conversion methods
###############################################################################
sub bstr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
# Upgrade?
return $x -> _upg() -> bstr(@r)
if $class -> upgrade() && !$x -> isa($class);
# Finite number
my $s = '';
$s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2'
my $str = $x->{sign} eq '-' ? '-' : '';
$str .= $LIB->_str($x->{_n});
$str .= '/' . $LIB->_str($x->{_d}) unless $LIB -> _is_one($x->{_d});
return $str;
}
sub bsstr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
# Upgrade?
return $x -> _upg() -> bsstr(@r)
if $class -> upgrade() && !$x -> isa($class);
# Finite number
my $str = $x->{sign} eq '-' ? '-' : '';
$str .= $LIB->_str($x->{_n});
$str .= '/' . $LIB->_str($x->{_d}) unless $LIB -> _is_one($x->{_d});
return $str;
}
sub bnstr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Inf and NaN
if ($x->{sign} ne '+' && $x->{sign} ne '-') {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
# Upgrade?
$x -> _upg() -> bnstr(@r)
if $class -> upgrade() && !$x -> isa(__PACKAGE__);
return $x -> as_float(@r) -> bnstr();
}
sub bestr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Inf and NaN
if ($x->{sign} ne '+' && $x->{sign} ne '-') {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
# Upgrade?
$x -> _upg() -> bestr(@r)
if $class -> upgrade() && !$x -> isa(__PACKAGE__);
return $x -> as_float(@r) -> bestr();
}
sub bdstr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
# Inf and NaN
if ($x->{sign} ne '+' && $x->{sign} ne '-') {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
return ($x->{sign} eq '-' ? '-' : '') . $LIB->_str($x->{_n})
if $x -> is_int();
# Upgrade?
$x -> _upg() -> bdstr(@r)
if $class -> upgrade() && !$x -> isa(__PACKAGE__);
# Integer number
return $x -> as_float(@r) -> bdstr();
}
sub bfstr {
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
# Upgrade?
return $x -> _upg() -> bfstr(@r)
if $class -> upgrade() && !$x -> isa($class);
# Finite number
my $str = $x->{sign} eq '-' ? '-' : '';
$str .= $LIB->_str($x->{_n});
$str .= '/' . $LIB->_str($x->{_d}) unless $LIB -> _is_one($x->{_d});
return $str;
}
sub to_hex {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
return $nan unless $x -> is_int();
my $str = $LIB->_to_hex($x->{_n});
return $x->{sign} eq "-" ? "-$str" : $str;
}
sub to_oct {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
return $nan unless $x -> is_int();
my $str = $LIB->_to_oct($x->{_n});
return $x->{sign} eq "-" ? "-$str" : $str;
}
sub to_bin {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
# Inf and NaN
if (!$x -> is_finite()) {
return $x->{sign} unless $x -> is_inf("+"); # -inf, NaN
return 'inf'; # +inf
}
return $nan unless $x -> is_int();
my $str = $LIB->_to_bin($x->{_n});
return $x->{sign} eq "-" ? "-$str" : $str;
}
sub to_bytes {
# return a byte string
my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
croak("to_bytes() requires a finite, non-negative integer")
if $x -> is_neg() || ! $x -> is_int();
return $x -> _upg() -> to_bytes(@r)
if $class -> upgrade() && !$x -> isa(__PACKAGE__);
croak("to_bytes() requires a newer version of the $LIB library.")
unless $LIB -> can('_to_bytes');
return $LIB->_to_bytes($x->{_n});
}
sub to_ieee754 {
my ($class, $x, $format, @r) = ref($_[0]) ? (ref($_[0]), @_)
: objectify(1, @_);
carp "Rounding is not supported for ", (caller(0))[3], "()" if @r;
return $x -> as_float() -> to_ieee754($format);
}
sub as_hex {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
return $x unless $x -> is_int();
my $s = $x->{sign}; $s = '' if $s eq '+';
$s . $LIB->_as_hex($x->{_n});
}
sub as_oct {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
return $x unless $x -> is_int();
my $s = $x->{sign}; $s = '' if $s eq '+';
$s . $LIB->_as_oct($x->{_n});
}
sub as_bin {
my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_);
return $x unless $x -> is_int();
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
__END__
=pod
=head1 NAME
Math::BigRat - arbitrary size rational number math package
=head1 SYNOPSIS
use Math::BigRat;
# Generic constructor method (always returns a new object)
$x = Math::BigRat->new($str); # defaults to 0
$x = Math::BigRat->new('256'); # from decimal
$x = Math::BigRat->new('0256'); # from decimal
$x = Math::BigRat->new('0xcafe'); # from hexadecimal
$x = Math::BigRat->new('0x1.fap+7'); # from hexadecimal
$x = Math::BigRat->new('0o377'); # from octal
$x = Math::BigRat->new('0o1.35p+6'); # from octal
$x = Math::BigRat->new('0b101'); # from binary
$x = Math::BigRat->new('0b1.101p+3'); # from binary
# Specific constructor methods (no prefix needed; when used as
# instance method, the value is assigned to the invocand)
$x = Math::BigRat->from_dec('234'); # from decimal
$x = Math::BigRat->from_hex('cafe'); # from hexadecimal
$x = Math::BigRat->from_hex('1.fap+7'); # from hexadecimal
$x = Math::BigRat->from_oct('377'); # from octal
$x = Math::BigRat->from_oct('1.35p+6'); # from octal
$x = Math::BigRat->from_bin('1101'); # from binary
$x = Math::BigRat->from_bin('1.101p+3'); # from binary
$x = Math::BigRat->from_bytes($bytes); # from byte string
$x = Math::BigRat->from_base('why', 36); # from any base
$x = Math::BigRat->from_base_num([1, 0], 2); # from any base
$x = Math::BigRat->from_ieee754($b, $fmt); # from IEEE-754 bytes
$x = Math::BigRat->bzero(); # create a +0
$x = Math::BigRat->bone(); # create a +1
$x = Math::BigRat->bone('-'); # create a -1
$x = Math::BigRat->binf(); # create a +inf
$x = Math::BigRat->binf('-'); # create a -inf
$x = Math::BigRat->bnan(); # create a Not-A-Number
$x = Math::BigRat->bpi(); # returns pi
$y = $x->copy(); # make a copy (unlike $y = $x)
$y = $x->as_int(); # return as a Math::BigInt
$y = $x->as_float(); # return as a Math::BigFloat
$y = $x->as_rat(); # return as a Math::BigRat
# Boolean methods (these don't modify the invocand)
$x->is_zero(); # true if $x is 0
$x->is_one(); # true if $x is +1
$x->is_one("+"); # true if $x is +1
$x->is_one("-"); # true if $x is -1
$x->is_inf(); # true if $x is +inf or -inf
$x->is_inf("+"); # true if $x is +inf
$x->is_inf("-"); # true if $x is -inf
$x->is_nan(); # true if $x is NaN
$x->is_finite(); # true if -inf < $x < inf
$x->is_positive(); # true if $x > 0
$x->is_pos(); # true if $x > 0
$x->is_negative(); # true if $x < 0
$x->is_neg(); # true if $x < 0
$x->is_non_positive() # true if $x <= 0
$x->is_non_negative() # true if $x >= 0
$x->is_odd(); # true if $x is odd
$x->is_even(); # true if $x is even
$x->is_int(); # true if $x is an integer
# Comparison methods (these don't modify the invocand)
$x->bcmp($y); # compare numbers (undef, < 0, == 0, > 0)
$x->bacmp($y); # compare abs values (undef, < 0, == 0, > 0)
$x->beq($y); # true if $x == $y
$x->bne($y); # true if $x != $y
$x->blt($y); # true if $x < $y
$x->ble($y); # true if $x <= $y
$x->bgt($y); # true if $x > $y
$x->bge($y); # true if $x >= $y
# Arithmetic methods (these modify the invocand)
$x->bneg(); # negation
$x->babs(); # absolute value
$x->bsgn(); # sign function (-1, 0, 1, or NaN)
$x->bdigitsum(); # sum of decimal digits
$x->binc(); # increment $x by 1
$x->bdec(); # decrement $x by 1
$x->badd($y); # addition (add $y to $x)
$x->bsub($y); # subtraction (subtract $y from $x)
$x->bmul($y); # multiplication (multiply $x by $y)
$x->bmuladd($y, $z); # $x = $x * $y + $z
$x->bdiv($y); # division (floored)
$x->bmod($y); # modulus (x % y)
$x->bmodinv($mod); # modular multiplicative inverse
$x->bmodpow($y, $mod); # modular exponentiation (($x ** $y) % $mod)
$x->btdiv($y); # division (truncated), set $x to quotient
$x->btmod($y); # modulus (truncated)
$x->binv() # inverse (1/$x)
$x->bpow($y); # power of arguments (x ** y)
$x->blog(); # logarithm of $x to base e (Euler's number)
$x->blog($base); # logarithm of $x to base $base (e.g., base 2)
$x->bexp(); # calculate e ** $x where e is Euler's number
$x->bilog2(); # log2($x) rounded down to nearest int
$x->bilog10(); # log10($x) rounded down to nearest int
$x->bclog2(); # log2($x) rounded up to nearest int
$x->bclog10(); # log10($x) rounded up to nearest int
$x->bnok($y); # combinations (binomial coefficient n over k)
$x->bperm($y); # permutations
$x->buparrow($n, $y); # Knuth's up-arrow notation
$x->bhyperop($n, $y); # n'th hyperoprator
$x->backermann($y); # the Ackermann function
$x->bsin(); # sine
$x->bcos(); # cosine
$x->batan(); # inverse tangent
$x->batan2($y); # two-argument inverse tangent
$x->bsqrt(); # calculate square root
$x->broot($y); # $y'th root of $x (e.g. $y == 3 => cubic root)
$x->bfac(); # factorial of $x (1*2*3*4*..$x)
$x->bdfac(); # double factorial of $x ($x*($x-2)*($x-4)*...)
$x->btfac(); # triple factorial of $x ($x*($x-3)*($x-6)*...)
$x->bmfac($k); # $k'th multi-factorial of $x ($x*($x-$k)*...)
$x->bfib($k); # $k'th Fibonacci number
$x->blucas($k); # $k'th Lucas number
$x->blsft($n); # left shift $n places in base 2
$x->blsft($n, $b); # left shift $n places in base $b
$x->brsft($n); # right shift $n places in base 2
$x->brsft($n, $b); # right shift $n places in base $b
# Bitwise methods (these modify the invocand)
$x->bblsft($y); # bitwise left shift
$x->bbrsft($y); # bitwise right shift
$x->band($y); # bitwise and
$x->bior($y); # bitwise inclusive or
$x->bxor($y); # bitwise exclusive or
$x->bnot(); # bitwise not (two's complement)
# Rounding methods (these modify the invocand)
$x->round($A, $P, $R); # round to accuracy or precision using
# rounding mode $R
$x->bround($n); # accuracy: preserve $n digits
$x->bfround($n); # $n > 0: round to $nth digit left of dec. point
# $n < 0: round to $nth digit right of dec. point
$x->bfloor(); # round towards minus infinity
$x->bceil(); # round towards plus infinity
$x->bint(); # round towards zero
# Other mathematical methods (these don't modify the invocand)
$x->bgcd($y); # greatest common divisor
$x->blcm($y); # least common multiple
# Object property methods (these don't modify the invocand)
$x->sign(); # the sign, either +, - or NaN
$x->digit($n); # the nth digit, counting from the right
$x->digit(-$n); # the nth digit, counting from the left
$x->digitsum(); # sum of decimal digits
$x->length(); # return number of digits in number
$x->mantissa(); # return (signed) mantissa as a Math::BigInt
$x->exponent(); # return exponent as a Math::BigInt
$x->parts(); # return (mantissa,exponent) as a Math::BigInt
$x->sparts(); # mantissa and exponent (as integers)
$x->nparts(); # mantissa and exponent (normalised)
$x->eparts(); # mantissa and exponent (engineering notation)
$x->dparts(); # integer and fraction part
$x->fparts(); # numerator and denominator
$x->numerator(); # numerator
$x->denominator(); # denominator
# Conversion methods (these don't modify the invocand)
$x->bstr(); # decimal notation (possibly zero padded)
$x->bnstr(); # string in normalized notation
$x->bestr(); # string in engineering notation
$x->bdstr(); # string in decimal notation (no padding)
$x->bfstr(); # string in fractional notation
$x->to_hex(); # as signed hexadecimal string
$x->to_bin(); # as signed binary string
$x->to_oct(); # as signed octal string
$x->to_bytes(); # as byte string
$x->to_base($b); # as string in any base
$x->to_base_num($b); # as array of integers in any base
$x->to_ieee754($fmt); # to bytes encoded according to IEEE 754-2008
$x->as_hex(); # as signed hexadecimal string with "0x" prefix
$x->as_bin(); # as signed binary string with "0b" prefix
$x->as_oct(); # as signed octal string with "0" prefix
# Other conversion methods (these don't modify the invocand)
$x->numify(); # return as scalar (might overflow or underflow)
=head1 DESCRIPTION
Math::BigRat complements L<Math::BigInt> and L<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":
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
Create a BigRat from an binary number in string form.
=item from_bytes()
$x = Math::BigRat->from_bytes("\xf3\x6b"); # $x = 62315
Interpret the input as a byte string, assuming big endian byte order. The
output is always a non-negative, finite integer.
See L<Math::BigInt/from_bytes()>.
=item from_ieee754()
# set $x to 13176795/4194304, the closest value to pi that can be
# represented in the binary32 (single) format
$x = Math::BigRat -> from_ieee754("40490fdb", "binary32");
Interpret the input as a value encoded as described in IEEE754-2008.
See L<Math::BigFloat/from_ieee754()>.
=item from_base()
See L<Math::BigInt/from_base()>.
=item bzero()
$x = Math::BigRat->bzero();
Creates a new BigRat object representing zero.
If used on an object, it will set it to zero:
$x->bzero();
=item bone()
$x = Math::BigRat->bone($sign);
Creates a new BigRat object representing one. The optional argument is
either '-' or '+', indicating whether you want one or minus one.
If used on an object, it will set it to one:
$x->bone(); # +1
$x->bone('-'); # -1
=item binf()
$x = Math::BigRat->binf($sign);
Creates a new BigRat object representing infinity. The optional argument is
either '-' or '+', indicating whether you want infinity or minus infinity.
If used on an object, it will set it to infinity:
$x->binf();
$x->binf('-');
=item bnan()
$x = Math::BigRat->bnan();
Creates a new BigRat object representing NaN (Not A Number).
If used on an object, it will set it to NaN:
$x->bnan();
=item bpi()
$x = Math::BigRat -> bpi(); # default accuracy
$x = Math::BigRat -> bpi(7); # specified accuracy
Returns a rational approximation of PI accurate to the specified accuracy or
the default accuracy if no accuracy is specified. If called as an instance
method, the value is assigned to the invocand.
$x = Math::BigRat -> bpi(1); # returns "3"
$x = Math::BigRat -> bpi(3); # returns "22/7"
$x = Math::BigRat -> bpi(7); # returns "355/113"
=item copy()
my $z = $x->copy();
Makes a deep copy of the object.
Please see the documentation in L<Math::BigInt> for further details.
=item as_int()
$y = $x -> as_int(); # $y is a Math::BigInt
Returns $x as a Math::BigInt object regardless of upgrading and downgrading. If
$x is finite, but not an integer, $x is truncated.
=item as_rat()
$y = $x -> as_rat(); # $y is a Math::BigRat
Returns $x a Math::BigRat object regardless of upgrading and downgrading. The
invocand is not modified.
=item 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 Math::BigFloat object regardless of upgrading
and downgrading, preserving the accuracy as wanted, or the default of 40
digits.
=item bround()/round()/bfround()
Are not yet implemented.
=item is_zero()
print "$x is 0\n" if $x->is_zero();
Return true if $x is exactly zero, otherwise false.
=item is_one()
print "$x is 1\n" if $x->is_one();
Return true if $x is exactly one, otherwise false.
=item is_finite()
$x->is_finite(); # true if $x is not +inf, -inf or NaN
Returns true if the invocand is a finite number, i.e., it is neither +inf,
-inf, nor NaN.
=item is_positive()
=item is_pos()
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.
L</is_positive()> is an alias for L</is_pos()>.
=item is_negative()
=item is_neg()
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.
L</is_negative()> is an alias for L</is_neg()>.
=item is_odd()
print "$x is odd\n" if $x->is_odd();
Return true if $x is odd, otherwise false.
=item is_even()
print "$x is even\n" if $x->is_even();
Return true if $x is even, otherwise false.
=item 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.
=back
=head2 Comparison methods
None of these methods modify the invocand object. Note that a C<NaN> is neither
less than, greater than, or equal to anything else, even a C<NaN>.
=over
=item bcmp()
$x->bcmp($y);
Compares $x with $y and takes the sign into account.
Returns -1, 0, 1 or undef.
=item bacmp()
$x->bacmp($y);
Compares $x with $y while ignoring their sign. Returns -1, 0, 1 or undef.
=item beq()
$x -> beq($y);
Returns true if and only if $x is equal to $y, and false otherwise.
=item bne()
$x -> bne($y);
Returns true if and only if $x is not equal to $y, and false otherwise.
=item blt()
$x -> blt($y);
Returns true if and only if $x is equal to $y, and false otherwise.
=item ble()
$x -> ble($y);
Returns true if and only if $x is less than or equal to $y, and false
otherwise.
=item bgt()
$x -> bgt($y);
Returns true if and only if $x is greater than $y, and false otherwise.
=item bge()
$x -> bge($y);
Returns true if and only if $x is greater than or equal to $y, and false
otherwise.
=item blsft()/brsft()
Used to shift numbers left/right.
Please see the documentation in L<Math::BigInt> for further details.
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
$x->binv();
Inverse of $x.
=item bsqrt()
$x->bsqrt();
Calculate the square root of $x.
=item bpow()
$x->bpow($y);
Compute $x ** $y.
Please see the documentation in L<Math::BigInt> for further details.
=item broot()
$x->broot($n);
Calculate the N'th root of $x.
=item bmodpow()
$num->bmodpow($exp,$mod); # modular exponentiation
# ($num**$exp % $mod)
Returns the value of C<$num> taken to the power C<$exp> in the modulus
C<$mod> using binary exponentiation. C<bmodpow> is far superior to
writing
$num ** $exp % $mod
because it is much faster - it reduces internal variables into
the modulus whenever possible, so it operates on smaller numbers.
C<bmodpow> also supports negative exponents.
bmodpow($num, -1, $mod)
is exactly equivalent to
bmodinv($num, $mod)
=item bmodinv()
$x->bmodinv($mod); # modular multiplicative inverse
Returns the multiplicative inverse of C<$x> modulo C<$mod>. If
$y = $x -> copy() -> bmodinv($mod)
then C<$y> is the number closest to zero, and with the same sign as C<$mod>,
satisfying
($x * $y) % $mod = 1 % $mod
If C<$x> and C<$y> are non-zero, they must be relative primes, i.e.,
C<bgcd($y, $mod)==1>. 'C<NaN>' is returned when no modular multiplicative
inverse exists.
=item blog()
$x->blog($base, $accuracy); # logarithm of x to the base $base
If C<$base> is not defined, Euler's number (e) is used:
print $x->blog(undef, 100); # log(x) to 100 digits
=item 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()>.
=item bnok()
See L<Math::BigInt/bnok()>.
=item bperm()
See L<Math::BigInt/bperm()>.
=item 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.
=item band()
$x->band($y); # bitwise and
=item bior()
$x->bior($y); # bitwise inclusive or
=item bxor()
$x->bxor($y); # bitwise exclusive or
=item bnot()
$x->bnot(); # bitwise not (two's complement)
=item bfloor()
$x->bfloor();
cpan/Math-BigInt/lib/Math/BigRat.pm view on Meta::CPAN
=item as_oct()
$x = Math::BigRat->new('13');
print $x->as_oct(), "\n"; # '015'
Returns the BigRat as octal string. Works only for integers.
=item as_bin()
$x = Math::BigRat->new('13');
print $x->as_bin(), "\n"; # '0x1101'
Returns the BigRat as binary string. Works only for integers.
=item 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
=item config()
Math::BigRat->config("trap_nan" => 1); # set
$accu = Math::BigRat->config("accuracy"); # get
Set or get configuration parameter values. Read-only parameters are marked as
RO. Read-write parameters are marked as RW. The following parameters are
supported.
Parameter RO/RW Description
Example
============================================================
lib RO Name of the math backend library
Math::BigInt::Calc
lib_version RO Version of the math backend library
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, sqrt etc.
40
trap_nan RW Trap NaNs
undef
trap_inf RW Trap +inf/-inf
undef
=back
=head1 NUMERIC LITERALS
After C<use Math::BigRat ':constant'> all numeric literals in the given scope
are converted to C<Math::BigRat> objects. This conversion happens at compile
time. Every non-integer is convert to a NaN.
For example,
perl -MMath::BigRat=:constant -le 'print 2**150'
prints the exact value of C<2**150>. Note that without conversion of constants
to objects the expression C<2**150> is calculated using Perl scalars, which
leads to an inaccurate result.
Please note that strings are not affected, so that
use Math::BigRat qw/:constant/;
$x = "1234567890123456789012345678901234567890"
+ "123456789123456789";
does give you what you expect. You need an explicit Math::BigRat->new() around
at least one of the operands. You should also quote large constants to prevent
loss of precision:
use Math::BigRat;
$x = Math::BigRat->new("1234567889123456789123456789123456789");
Without the quotes Perl first converts the large number to a floating point
constant at compile time, and then converts the result to a Math::BigRat object
at run time, which results in an inaccurate result.
=head2 Hexadecimal, octal, and binary floating point literals
Perl (and this module) accepts hexadecimal, octal, and binary floating point
literals, but use them with care with Perl versions before v5.32.0, because some
versions of Perl silently give the wrong result. Below are some examples of
different ways to write the number decimal 314.
Hexadecimal floating point literals:
0x1.3ap+8 0X1.3AP+8
0x1.3ap8 0X1.3AP8
0x13a0p-4 0X13A0P-4
Octal floating point literals (with "0" prefix):
01.164p+8 01.164P+8
01.164p8 01.164P8
011640p-4 011640P-4
Octal floating point literals (with "0o" prefix) (requires v5.34.0):
0o1.164p+8 0O1.164P+8
0o1.164p8 0O1.164P8
0o11640p-4 0O11640P-4
Binary floating point literals:
0b1.0011101p+8 0B1.0011101P+8
0b1.0011101p8 0B1.0011101P8
0b10011101000p-2 0B10011101000P-2
=head1 BUGS