diff options
author | Norbert Preining <norbert@preining.info> | 2024-03-15 03:06:35 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-03-15 03:06:35 +0000 |
commit | 12679ab7d3c2a210f4123163671b532b8b55d5f9 (patch) | |
tree | 0060d13467186ad977f4e73488ee20dd6c0017ab /systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm | |
parent | 62170822e034fdd3f81de7274835d0d3b0467100 (diff) |
CTAN sync 202403150306
Diffstat (limited to 'systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm')
-rw-r--r-- | systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm | 902 |
1 files changed, 644 insertions, 258 deletions
diff --git a/systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm b/systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm index e3d172836c..0d0f246795 100644 --- a/systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm +++ b/systems/texlive/tlnet/tlpkg/tlperl/lib/Math/BigRat.pm @@ -16,11 +16,12 @@ use 5.006; use strict; use warnings; -use Carp qw< carp croak >; +use Carp qw< carp croak >; +use Scalar::Util qw< blessed >; -use Math::BigFloat 1.999718; +use Math::BigFloat (); -our $VERSION = '0.2614'; +our $VERSION = '0.2624'; our @ISA = qw(Math::BigFloat); @@ -199,12 +200,6 @@ use overload 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 - # Math::BigFloat has a different $LIB variable than we, because - # Math::BigFloat 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_number = \&as_int; *is_pos = \&is_positive; *is_neg = \&is_negative; @@ -305,20 +300,32 @@ sub new { unless (defined $d) { #return $n -> copy($n) if $n -> isa('Math::BigRat'); - return $class -> copy($n) if $n -> isa('Math::BigRat'); - return $class -> bnan() if $n -> is_nan(); - return $class -> binf($n -> sign()) if $n -> is_inf(); + if ($n -> isa('Math::BigRat')) { + return $downgrade -> new($n) + if defined($downgrade) && $n -> is_int(); + return $class -> copy($n); + } + + if ($n -> is_nan()) { + return $class -> bnan(); + } + + if ($n -> is_inf()) { + return $class -> binf($n -> sign()); + } if ($n -> isa('Math::BigInt')) { - $self -> {_n} = $LIB -> _new($n -> copy() -> babs() -> bstr()); + $self -> {_n} = $LIB -> _new($n -> copy() -> babs(undef, undef) + -> bstr()); $self -> {_d} = $LIB -> _one(); $self -> {sign} = $n -> sign(); + return $downgrade -> new($n) if defined $downgrade; return $self; } if ($n -> isa('Math::BigFloat')) { - my $m = $n -> mantissa() -> babs(); - my $e = $n -> exponent(); + my $m = $n -> mantissa(undef, undef) -> babs(undef, undef); + my $e = $n -> exponent(undef, undef); $self -> {_n} = $LIB -> _new($m -> bstr()); $self -> {_d} = $LIB -> _one(); @@ -329,7 +336,8 @@ sub new { $self -> {_d} = $LIB -> _lsft($self -> {_d}, $LIB -> _new(-$e -> bstr()), 10); - my $gcd = $LIB -> _gcd($LIB -> _copy($self -> {_n}), $self -> {_d}); + my $gcd = $LIB -> _gcd($LIB -> _copy($self -> {_n}), + $self -> {_d}); if (!$LIB -> _is_one($gcd)) { $self -> {_n} = $LIB -> _div($self->{_n}, $gcd); $self -> {_d} = $LIB -> _div($self->{_d}, $gcd); @@ -337,6 +345,8 @@ sub new { } $self -> {sign} = $n -> sign(); + return $downgrade -> new($n, undef, undef) + if defined($downgrade) && $n -> is_int(); return $self; } @@ -353,19 +363,30 @@ sub new { # At this point both $n and $d are objects. - return $class -> bnan() if $n -> is_nan() || $d -> is_nan(); + if ($n -> is_nan() || $d -> is_nan()) { + return $class -> bnan(); + } # At this point neither $n nor $d is a NaN. if ($n -> is_zero()) { - return $class -> bnan() if $d -> is_zero(); # 0/0 = NaN + if ($d -> is_zero()) { # 0/0 = NaN + return $class -> bnan(); + } return $class -> bzero(); } - return $class -> binf($d -> sign()) if $d -> is_zero(); + if ($d -> is_zero()) { + return $class -> binf($d -> sign()); + } # At this point, neither $n nor $d is a NaN or a zero. + # Copy them now before manipulating them. + + $n = $n -> copy(); + $d = $d -> copy(); + if ($d < 0) { # make sure denominator is positive $n -> bneg(); $d -> bneg(); @@ -411,6 +432,8 @@ sub new { $self -> {_d} = $LIB -> _mul($LIB -> _div($LIB -> _copy($q), $gcd_sq), $LIB -> _div($LIB -> _copy($r), $gcd_pr)); + return $downgrade -> new($n->bstr()) + if defined($downgrade) && $self -> is_int(); return $self; # no need for $self -> bnorm() here } @@ -516,6 +539,8 @@ sub new { } } + return $downgrade -> new($self -> bstr()) + if defined($downgrade) && $self -> is_int(); return $self; } @@ -553,6 +578,8 @@ sub bnan { croak ("Tried to set a variable to NaN in $class->bnan()"); } + return $downgrade -> bnan() if defined $downgrade; + $self -> {sign} = $nan; $self -> {_n} = $LIB -> _zero(); $self -> {_d} = $LIB -> _one(); @@ -577,6 +604,8 @@ sub binf { croak ("Tried to set a variable to +-inf in $class->binf()"); } + return $downgrade -> binf($sign) if defined $downgrade; + $self -> {sign} = $sign; $self -> {_n} = $LIB -> _zero(); $self -> {_d} = $LIB -> _one(); @@ -592,11 +621,12 @@ sub bone { my $selfref = ref $self; my $class = $selfref || $self; - $self = bless {}, $class unless $selfref; - my $sign = shift(); $sign = '+' unless defined($sign) && $sign eq '-'; + return $downgrade -> bone($sign) if defined $downgrade; + + $self = bless {}, $class unless $selfref; $self -> {sign} = $sign; $self -> {_n} = $LIB -> _one(); $self -> {_d} = $LIB -> _one(); @@ -612,8 +642,9 @@ sub bzero { my $selfref = ref $self; my $class = $selfref || $self; - $self = bless {}, $class unless $selfref; + return $downgrade -> bzero() if defined $downgrade; + $self = bless {}, $class unless $selfref; $self -> {sign} = '+'; $self -> {_n} = $LIB -> _zero(); $self -> {_d} = $LIB -> _one(); @@ -644,36 +675,86 @@ sub config { $cfg; } -############################################################################## +############################################################################### +# String conversion methods +############################################################################### sub bstr { - my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_); + 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->{sign} !~ /^[+-]$/) { # inf, NaN etc - my $s = $x->{sign}; - $s =~ s/^\+//; # +inf => inf - return $s; + if ($x->{sign} ne '+' && $x->{sign} ne '-') { + return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN + return 'inf'; # +inf } + # Upgrade? + + return $upgrade -> bstr($x, @r) + if defined($upgrade) && !$x -> isa($class); + + # Finite number + my $s = ''; $s = $x->{sign} if $x->{sign} ne '+'; # '+3/2' => '3/2' - return $s . $LIB->_str($x->{_n}) if $LIB->_is_one($x->{_d}); - $s . $LIB->_str($x->{_n}) . '/' . $LIB->_str($x->{_d}); + 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) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_); + my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_); + + carp "Rounding is not supported for ", (caller(0))[3], "()" if @r; - if ($x->{sign} !~ /^[+-]$/) { # inf, NaN etc - my $s = $x->{sign}; - $s =~ s/^\+//; # +inf => inf - return $s; + # Inf and NaN + + if ($x->{sign} ne '+' && $x->{sign} ne '-') { + return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN + return 'inf'; # +inf } - my $s = ''; - $s = $x->{sign} if $x->{sign} ne '+'; # +3 vs 3 - $s . $LIB->_str($x->{_n}) . '/' . $LIB->_str($x->{_d}); + # Upgrade? + + return $upgrade -> bsstr($x, @r) + if defined($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 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->{sign} ne '+' && $x->{sign} ne '-') { + return $x->{sign} unless $x->{sign} eq '+inf'; # -inf, NaN + return 'inf'; # +inf + } + + # Upgrade? + + return $upgrade -> bfstr($x, @r) + if defined($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 bnorm { @@ -689,16 +770,24 @@ sub bnorm { } # no normalize for NaN, inf etc. - return $x if $x->{sign} !~ /^[+-]$/; + if ($x->{sign} !~ /^[+-]$/) { + return $downgrade -> new($x) if defined $downgrade; + return $x; + } # normalize zeros to 0/1 if ($LIB->_is_zero($x->{_n})) { + return $downgrade -> bzero() if defined($downgrade); $x->{sign} = '+'; # never leave a -0 $x->{_d} = $LIB->_one() unless $LIB->_is_one($x->{_d}); return $x; } - return $x if $LIB->_is_one($x->{_d}); # no need to reduce + # n/1 + if ($LIB->_is_one($x->{_d})) { + return $downgrade -> new($x) if defined($downgrade); + return $x; # no need to reduce + } # Compute the GCD. my $gcd = $LIB->_gcd($LIB->_copy($x->{_n}), $x->{_d}); @@ -723,56 +812,10 @@ sub 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; -} - -############################################################################## -# special values - -sub _bnan { - # used by parent class bnan() to initialize number to NaN - my $self = shift; - - if ($_trap_nan) { - 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} = $LIB->_zero() unless defined $self->{_d}; - $self->{_n} = $LIB->_zero() unless defined $self->{_n}; - croak ("Tried to set $self to NaN in $class\::_bnan()"); - } - $self->{_n} = $LIB->_zero(); - $self->{_d} = $LIB->_zero(); -} - -sub _binf { - # used by parent class bone() to initialize number to +inf/-inf - my $self = shift; - - if ($_trap_inf) { - 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} = $LIB->_zero() unless defined $self->{_d}; - $self->{_n} = $LIB->_zero() unless defined $self->{_n}; - croak ("Tried to set $self to inf in $class\::_binf()"); - } - $self->{_n} = $LIB->_zero(); - $self->{_d} = $LIB->_zero(); -} - -sub _bone { - # used by parent class bone() to initialize number to +1/-1 - my $self = shift; - $self->{_n} = $LIB->_one(); - $self->{_d} = $LIB->_one(); -} -sub _bzero { - # used by parent class bzero() to initialize number to 0 - my $self = shift; - $self->{_n} = $LIB->_zero(); - $self->{_d} = $LIB->_one(); + return $downgrade -> new($x) + if defined($downgrade) && $LIB -> _is_one($x->{_d}); + $x; } ############################################################################## @@ -788,12 +831,21 @@ sub badd { ($class, $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} !~ /^[+-]$/); + 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 # - + - = --------- = -- @@ -813,7 +865,7 @@ sub badd { my $m = $LIB->_mul($LIB->_copy($y->{_n}), $x->{_d}); # 5 * 3 + 7 * 4 - ($x->{_n}, $x->{sign}) = _e_add($x->{_n}, $m, $x->{sign}, $y->{sign}); + ($x->{_n}, $x->{sign}) = $LIB -> _sadd($x->{_n}, $x->{sign}, $m, $y->{sign}); # 4 * 3 $x->{_d} = $LIB->_mul($x->{_d}, $y->{_d}); @@ -834,12 +886,12 @@ sub bsub { # flip sign of $x, call badd(), then flip sign of result $x->{sign} =~ tr/+-/-+/ - unless $x->{sign} eq '+' && $LIB->_is_zero($x->{_n}); # not -0 - $x->badd($y, @r); # does norm and round + unless $x->{sign} eq '+' && $x -> is_zero(); # not -0 + $x = $x->badd($y, @r); # does norm and round $x->{sign} =~ tr/+-/-+/ - unless $x->{sign} eq '+' && $LIB->_is_zero($x->{_n}); # not -0 + unless $x->{sign} eq '+' && $x -> is_zero(); # not -0 - $x; + $x->bnorm(); } sub bmul { @@ -866,10 +918,13 @@ sub bmul { } # x == 0 # also: or y == 1 or y == -1 - return wantarray ? ($x, $class->bzero()) : $x if $x -> is_zero(); + if ($x -> is_zero()) { + $x = $downgrade -> bzero($x) if defined $downgrade; + return wantarray ? ($x, $class->bzero()) : $x; + } if ($y -> is_zero()) { - $x -> bzero(); + $x = defined($downgrade) ? $downgrade -> bzero($x) : $x -> bzero(); return wantarray ? ($x, $class->bzero()) : $x; } @@ -893,7 +948,7 @@ sub bmul { # compute new sign $x->{sign} = $x->{sign} eq $y->{sign} ? '+' : '-'; - $x->round(@r); + $x->bnorm()->round(@r); } sub bdiv { @@ -916,7 +971,15 @@ sub bdiv { # method. if ($x -> is_nan() || $y -> is_nan()) { - return $wantarray ? ($x -> bnan(), $class -> bnan()) : $x -> bnan(); + if ($wantarray) { + return $downgrade -> bnan(), $downgrade -> bnan() + if defined($downgrade); + return $x -> bnan(), $class -> bnan(); + } else { + return $downgrade -> bnan() + if defined($downgrade); + return $x -> bnan(); + } } # Divide by zero and modulo zero. This is handled the same way as in @@ -933,6 +996,11 @@ sub bdiv { } else { $quo = $x -> binf($x -> {sign}); } + + $quo = $downgrade -> new($quo) + if defined($downgrade) && $quo -> is_int(); + $rem = $downgrade -> new($rem) + if $wantarray && defined($downgrade) && $rem -> is_int(); return $wantarray ? ($quo, $rem) : $quo; } @@ -949,6 +1017,11 @@ sub bdiv { my $sign = $x -> bcmp(0) == $y -> bcmp(0) ? '+' : '-'; $quo = $x -> binf($sign); } + + $quo = $downgrade -> new($quo) + if defined($downgrade) && $quo -> is_int(); + $rem = $downgrade -> new($rem) + if $wantarray && defined($downgrade) && $rem -> is_int(); return $wantarray ? ($quo, $rem) : $quo; } @@ -966,12 +1039,18 @@ sub bdiv { $rem = $class -> binf($y -> {sign}); $quo = $x -> bone('-'); } + $quo = $downgrade -> new($quo) + if defined($downgrade) && $quo -> is_int(); + $rem = $downgrade -> new($rem) + if defined($downgrade) && $rem -> is_int(); return ($quo, $rem); } else { if ($y -> is_inf()) { if ($x -> is_nan() || $x -> is_inf()) { + return $downgrade -> bnan() if defined $downgrade; return $x -> bnan(); } else { + return $downgrade -> bzero() if defined $downgrade; return $x -> bzero(); } } @@ -982,7 +1061,11 @@ sub bdiv { # the denominator (divisor) is non-zero. # x == 0? - return wantarray ? ($x, $class->bzero()) : $x if $x->is_zero(); + if ($x->is_zero()) { + return $wantarray ? ($downgrade -> bzero(), $downgrade -> bzero()) + : $downgrade -> bzero() if defined $downgrade; + return $wantarray ? ($x, $class->bzero()) : $x; + } # XXX TODO: list context, upgrade # According to Knuth, this can be optimized by doing gcd twice (for d and n) @@ -1001,13 +1084,14 @@ sub bdiv { $x -> bnorm(); if (wantarray) { my $rem = $x -> copy(); - $x -> bfloor(); - $x -> round(@r); - $rem -> bsub($x -> copy()) -> bmul($y); + $x = $x -> bfloor(); + $x = $x -> round(@r); + $rem = $rem -> bsub($x -> copy()) -> bmul($y); + $x = $downgrade -> new($x) if defined($downgrade) && $x -> is_int(); + $rem = $downgrade -> new($rem) if defined($downgrade) && $rem -> is_int(); return $x, $rem; } else { - $x -> round(@r); - return $x; + return $x -> round(@r); } } @@ -1033,6 +1117,7 @@ sub bmod { # Modulo zero. This is handled the same way as in Math::BigInt -> bmod(). if ($y -> is_zero()) { + return $downgrade -> bzero() if defined $downgrade; return $x; } @@ -1048,8 +1133,10 @@ sub bmod { if ($y -> is_inf()) { if ($x -> is_zero() || $x -> bcmp(0) == $y -> bcmp(0)) { + return $downgrade -> new($x) if defined($downgrade) && $x -> is_int(); return $x; } else { + return $downgrade -> binf($y -> sign()) if defined($downgrade); return $x -> binf($y -> sign()); } } @@ -1057,7 +1144,10 @@ sub bmod { # At this point, both the numerator and denominator are finite numbers, and # the denominator (divisor) is non-zero. - return $x if $x->is_zero(); # 0 / 7 = 0, mod 0 + if ($x->is_zero()) { # 0 / 7 = 0, mod 0 + return $downgrade -> bzero() if defined $downgrade; + return $x; + } # Compute $x - $y * floor($x/$y). This can probably be optimized by working # on a lower level. @@ -1073,7 +1163,10 @@ sub bdec { # decrement value (subtract 1) my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_); - return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf + if ($x->{sign} !~ /^[+-]$/) { # NaN, inf, -inf + return $downgrade -> new($x) if defined $downgrade; + return $x; + } if ($x->{sign} eq '-') { $x->{_n} = $LIB->_add($x->{_n}, $x->{_d}); # -5/2 => -7/2 @@ -1094,7 +1187,10 @@ sub binc { # increment value (add 1) my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_); - return $x if $x->{sign} !~ /^[+-]$/; # NaN, inf, -inf + if ($x->{sign} !~ /^[+-]$/) { # NaN, inf, -inf + return $downgrade -> new($x) if defined $downgrade; + return $x; + } if ($x->{sign} eq '-') { if ($LIB->_acmp($x->{_n}, $x->{_d}) < 0) { @@ -1110,6 +1206,20 @@ sub binc { $x->bnorm()->round(@r); } +sub binv { + my $x = shift; + my @r = @_; + + return $x if $x->modify('binv'); + + return $x if $x -> is_nan(); + return $x -> bzero() if $x -> is_inf(); + return $x -> binf("+") if $x -> is_zero(); + + ($x -> {_n}, $x -> {_d}) = ($x -> {_d}, $x -> {_n}); + $x -> round(@r); +} + ############################################################################## # is_foo methods (the rest is inherited) @@ -1201,6 +1311,53 @@ sub parts { ($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 length { my ($class, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_); @@ -1221,37 +1378,52 @@ sub digit { sub bceil { my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_); - return $x if ($x->{sign} !~ /^[+-]$/ || # not for NaN, inf - $LIB->_is_one($x->{_d})); # 22/1 => 22, 0/1 => 0 + if ($x->{sign} !~ /^[+-]$/ || # NaN or inf or + $LIB->_is_one($x->{_d})) # integer + { + return $downgrade -> new($x) if defined $downgrade; + 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 + return $downgrade -> new($x) if defined $downgrade; $x; } sub bfloor { my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_); - return $x if ($x->{sign} !~ /^[+-]$/ || # not for NaN, inf - $LIB->_is_one($x->{_d})); # 22/1 => 22, 0/1 => 0 + if ($x->{sign} !~ /^[+-]$/ || # NaN or inf or + $LIB->_is_one($x->{_d})) # integer + { + return $downgrade -> new($x) if defined $downgrade; + 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 + return $downgrade -> new($x) if defined $downgrade; $x; } sub bint { - my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_); + my ($class, $x) = ref($_[0]) ? (ref($_[0]), $_[0]) : objectify(1, @_); - return $x if ($x->{sign} !~ /^[+-]$/ || # +/-inf or NaN - $LIB -> _is_one($x->{_d})); # already an integer + if ($x->{sign} !~ /^[+-]$/ || # NaN or inf or + $LIB->_is_one($x->{_d})) # integer + { + return $downgrade -> new($x) if defined $downgrade; + 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}); + return $downgrade -> new($x) if defined $downgrade; return $x; } @@ -1279,6 +1451,8 @@ sub bpow { ($class, $x, $y, @r) = objectify(2, @_); } + return $x if $x->modify('bpow'); + # $x and/or $y is a NaN return $x->bnan() if $x->is_nan() || $y->is_nan(); @@ -1304,21 +1478,28 @@ sub bpow { return $x->binf("+"); } - if ($x->is_zero()) { - return $x->binf() if $y->is_negative(); - return $x->bone("+") if $y->is_zero(); + if ($x -> is_zero()) { + return $x -> bone() if $y -> is_zero(); + return $x -> binf() if $y -> is_negative(); return $x; - } elsif ($x->is_one()) { - return $x->round(@r) if $y->is_odd(); # x is -1, y is odd => -1 - return $x->babs()->round(@r); # x is -1, y is even => 1 - } elsif ($y->is_zero()) { - return $x->bone(@r); # x^0 and x != 0 => 1 - } elsif ($y->is_one()) { - return $x->round(@r); # x^1 => x } - # we don't support complex numbers, so return NaN - return $x->bnan() if $x->is_negative() && !$y->is_int(); + # We don't support complex numbers, so upgrade or return NaN. + + if ($x -> is_negative() && !$y -> is_int()) { + return $upgrade -> bpow($upgrade -> new($x), $y, @r) + if defined $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(); @@ -1348,11 +1529,11 @@ sub blog { # $x->blog(undef) signals that the base is Euler's number. if (!ref($_[0]) && $_[0] =~ /^[A-Za-z]|::/) { - # E.g., Math::BigFloat->blog(256, 2) + # E.g., Math::BigRat->blog(256, 2) ($class, $x, $base, @r) = defined $_[2] ? objectify(2, @_) : objectify(1, @_); } else { - # E.g., Math::BigFloat::blog(256, 2) or $x->blog(2) + # E.g., Math::BigRat::blog(256, 2) or $x->blog(2) ($class, $x, $base, @r) = defined $_[1] ? objectify(2, @_) : objectify(1, @_); } @@ -1393,20 +1574,50 @@ sub blog { return $x -> binf($sign); } + # Now take care of the cases where $x and/or $base is 1/N. + # + # log(1/N) / log(B) = -log(N)/log(B) + # log(1/N) / log(1/B) = log(N)/log(B) + # log(N) / log(1/B) = -log(N)/log(B) + + my $neg = 0; + if ($x -> numerator() -> is_one()) { + $x -> binv(); + $neg = !$neg; + } + if (defined(blessed($base)) && $base -> isa($class)) { + if ($base -> numerator() -> is_one()) { + $base = $base -> copy() -> binv(); + $neg = !$neg; + } + } + + # disable upgrading and downgrading + + require Math::BigFloat; + my $upg = Math::BigFloat -> upgrade(); + my $dng = Math::BigFloat -> downgrade(); + Math::BigFloat -> upgrade(undef); + Math::BigFloat -> downgrade(undef); + # At this point we are done handling all exception cases and trivial cases. $base = Math::BigFloat -> new($base) if defined $base; + my $xnum = Math::BigFloat -> new($LIB -> _str($x->{_n})); + my $xden = Math::BigFloat -> new($LIB -> _str($x->{_d})); + my $xstr = $xnum -> bdiv($xden) -> blog($base, @r) -> bsstr(); - my $xn = Math::BigFloat -> new($LIB -> _str($x->{_n})); - my $xd = Math::BigFloat -> new($LIB -> _str($x->{_d})); + # reset upgrading and downgrading - my $xtmp = Math::BigRat -> new($xn -> bdiv($xd) -> blog($base, @r) -> bsstr()); + Math::BigFloat -> upgrade($upg); + Math::BigFloat -> downgrade($dng); - $x -> {sign} = $xtmp -> {sign}; - $x -> {_n} = $xtmp -> {_n}; - $x -> {_d} = $xtmp -> {_d}; + my $xobj = Math::BigRat -> new($xstr); + $x -> {sign} = $xobj -> {sign}; + $x -> {_n} = $xobj -> {_n}; + $x -> {_d} = $xobj -> {_d}; - return $x; + return $neg ? $x -> bneg() : $x; } sub bexp { @@ -1513,13 +1724,18 @@ sub bnok { ($class, $x, $y, @r) = objectify(2, @_); } - my $xint = Math::BigInt -> new($x -> bint() -> bsstr()); - my $yint = Math::BigInt -> new($y -> bint() -> bsstr()); + return $x->bnan() if $x->is_nan() || $y->is_nan(); + return $x->bnan() if (($x->is_finite() && !$x->is_int()) || + ($y->is_finite() && !$y->is_int())); + + my $xint = Math::BigInt -> new($x -> bstr()); + my $yint = Math::BigInt -> new($y -> bstr()); $xint -> bnok($yint); + my $xrat = Math::BigRat -> new($xint); - $x -> {sign} = $xint -> {sign}; - $x -> {_n} = $xint -> {_n}; - $x -> {_d} = $xint -> {_d}; + $x -> {sign} = $xrat -> {sign}; + $x -> {_n} = $xrat -> {_n}; + $x -> {_d} = $xrat -> {_d}; return $x; } @@ -1570,7 +1786,7 @@ sub bmodpow { my $yint = Math::BigInt -> new($y -> copy() -> bint()); my $mint = Math::BigInt -> new($m -> copy() -> bint()); - $xint -> bmodpow($y, $m, @r); + $xint -> bmodpow($yint, $mint, @r); my $xtmp = Math::BigRat -> new($xint -> bsstr()); $x -> {sign} = $xtmp -> {sign}; @@ -1592,7 +1808,7 @@ sub bmodinv { my $xint = Math::BigInt -> new($x -> copy() -> bint()); my $yint = Math::BigInt -> new($y -> copy() -> bint()); - $xint -> bmodinv($y, @r); + $xint -> bmodinv($yint, @r); my $xtmp = Math::BigRat -> new($xint -> bsstr()); $x -> {sign} = $xtmp -> {sign}; @@ -1651,7 +1867,7 @@ sub bsqrt { } sub blsft { - my ($class, $x, $y, $b, @r) = objectify(2, @_); + my ($class, $x, $y, $b) = objectify(2, @_); $b = 2 if !defined $b; $b = $class -> new($b) unless ref($b) && $b -> isa($class); @@ -1665,7 +1881,7 @@ sub blsft { } sub brsft { - my ($class, $x, $y, $b, @r) = objectify(2, @_); + my ($class, $x, $y, $b) = objectify(2, @_); $b = 2 if !defined $b; $b = $class -> new($b) unless ref($b) && $b -> isa($class); @@ -1776,15 +1992,24 @@ sub bnot { # round sub round { - $_[0]; + my $x = shift; + return $downgrade -> new($x) if defined($downgrade) && + ($x -> is_int() || $x -> is_inf() || $x -> is_nan()); + $x; } sub bround { - $_[0]; + my $x = shift; + return $downgrade -> new($x) if defined($downgrade) && + ($x -> is_int() || $x -> is_inf() || $x -> is_nan()); + $x; } sub bfround { - $_[0]; + my $x = shift; + return $downgrade -> new($x) if defined($downgrade) && + ($x -> is_int() || $x -> is_inf() || $x -> is_nan()); + $x; } ############################################################################## @@ -1803,9 +2028,9 @@ sub bcmp { if ($x->{sign} !~ /^[+-]$/ || $y->{sign} !~ /^[+-]$/) { # $x is NaN and/or $y is NaN - return undef if $x->{sign} eq $nan || $y->{sign} eq $nan; + return if $x->{sign} eq $nan || $y->{sign} eq $nan; # $x and $y are both either +inf or -inf - return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; + return 0 if $x->{sign} eq $y->{sign} && $x->{sign} =~ /^[+-]inf$/; # $x = +inf and $y < +inf return +1 if $x->{sign} eq '+inf'; # $x = -inf and $y > -inf @@ -1850,9 +2075,9 @@ sub bacmp { 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 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; } @@ -1864,7 +2089,7 @@ sub bacmp { sub beq { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'beq() is an instance method, not a class method' unless $selfref; croak 'Wrong number of arguments for beq()' unless @_ == 1; @@ -1876,7 +2101,7 @@ sub beq { sub bne { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'bne() is an instance method, not a class method' unless $selfref; croak 'Wrong number of arguments for bne()' unless @_ == 1; @@ -1888,7 +2113,7 @@ sub bne { sub blt { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'blt() is an instance method, not a class method' unless $selfref; croak 'Wrong number of arguments for blt()' unless @_ == 1; @@ -1900,7 +2125,7 @@ sub blt { sub ble { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'ble() is an instance method, not a class method' unless $selfref; croak 'Wrong number of arguments for ble()' unless @_ == 1; @@ -1912,7 +2137,7 @@ sub ble { sub bgt { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'bgt() is an instance method, not a class method' unless $selfref; croak 'Wrong number of arguments for bgt()' unless @_ == 1; @@ -1924,7 +2149,7 @@ sub bgt { sub bge { my $self = shift; my $selfref = ref $self; - my $class = $selfref || $self; + #my $class = $selfref || $self; croak 'bge() is an instance method, not a class method' unless $selfref; @@ -1943,7 +2168,17 @@ sub numify { # Non-finite number. - return $x->bstr() if $x->{sign} !~ /^[+-]$/; + if ($x -> is_nan()) { + require Math::Complex; + my $inf = $Math::Complex::Inf; + return $inf - $inf; + } + + if ($x -> is_inf()) { + require Math::Complex; + my $inf = $Math::Complex::Inf; + return $x -> is_negative() ? -$inf : $inf; + } # Finite number. @@ -1956,34 +2191,70 @@ sub numify { } sub as_int { - my ($self, $x) = ref($_[0]) ? (undef, $_[0]) : objectify(1, @_); + my ($class, $x) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_); - # NaN, inf etc - return Math::BigInt->new($x->{sign}) if $x->{sign} !~ /^[+-]$/; + return $x -> copy() if $x -> isa("Math::BigInt"); - my $u = Math::BigInt->bzero(); - $u->{value} = $LIB->_div($LIB->_copy($x->{_n}), $x->{_d}); # 22/7 => 3 - $u->bneg if $x->{sign} eq '-'; # no negative zero - $u; + # disable upgrading and downgrading + + require Math::BigInt; + my $upg = Math::BigInt -> upgrade(); + my $dng = Math::BigInt -> downgrade(); + Math::BigInt -> upgrade(undef); + Math::BigInt -> downgrade(undef); + + my $y; + if ($x -> is_inf()) { + $y = Math::BigInt -> binf($x->sign()); + } elsif ($x -> is_nan()) { + $y = Math::BigInt -> bnan(); + } else { + my $int = $LIB -> _div($LIB -> _copy($x->{_n}), $x->{_d}); # 22/7 => 3 + $y = Math::BigInt -> new($LIB -> _str($int)); + $y = $y -> bneg() if $x -> is_neg(); + } + + # reset upgrading and downgrading + + Math::BigInt -> upgrade($upg); + Math::BigInt -> downgrade($dng); + + return $y; } sub as_float { - # return N/D as Math::BigFloat + my ($class, $x, @r) = ref($_[0]) ? (ref($_[0]), @_) : objectify(1, @_); - # set up parameters - my ($class, $x, @r) = (ref($_[0]), @_); - # objectify is costly, so avoid it - ($class, $x, @r) = objectify(1, @_) unless ref $_[0]; + return $x -> copy() if $x -> isa("Math::BigFloat"); - # NaN, inf etc - return Math::BigFloat->new($x->{sign}) if $x->{sign} !~ /^[+-]$/; + # disable upgrading and downgrading - my $xd = Math::BigFloat -> new($LIB -> _str($x->{_d})); - my $xflt = Math::BigFloat -> new($LIB -> _str($x->{_n})); - $xflt -> {sign} = $x -> {sign}; - $xflt -> bdiv($xd, @r); + require Math::BigFloat; + my $upg = Math::BigFloat -> upgrade(); + my $dng = Math::BigFloat -> downgrade(); + Math::BigFloat -> upgrade(undef); + Math::BigFloat -> downgrade(undef); + + my $y; + if ($x -> is_inf()) { + $y = Math::BigFloat -> binf($x->sign()); + } elsif ($x -> is_nan()) { + $y = Math::BigFloat -> bnan(); + } else { + $y = Math::BigFloat -> new($LIB -> _str($x->{_n})); + $y -> {sign} = $x -> {sign}; + unless ($LIB -> _is_one($x->{_d})) { + my $xd = Math::BigFloat -> new($LIB -> _str($x->{_d})); + $y -> bdiv($xd, @r); + } + } + + # reset upgrading and downgrading - return $xflt; + Math::BigFloat -> upgrade($upg); + Math::BigFloat -> downgrade($dng); + + return $y; } sub as_bin { @@ -2019,18 +2290,27 @@ sub as_oct { sub from_hex { my $class = shift; - $class->new(@_); + # The relationship should probably go the otherway, i.e, that new() calls + # from_hex(). Fixme! + my ($x, @r) = @_; + $x =~ s|^\s*(?:0?[Xx]_*)?|0x|; + $class->new($x, @r); } sub from_bin { my $class = shift; - $class->new(@_); + # The relationship should probably go the otherway, i.e, that new() calls + # from_bin(). Fixme! + my ($x, @r) = @_; + $x =~ s|^\s*(?:0?[Bb]_*)?|0b|; + $class->new($x, @r); } sub from_oct { my $class = shift; + # Why is this different from from_hex() and from_bin()? Fixme! my @parts; for my $c (@_) { push @parts, Math::BigInt->from_oct($c); @@ -2043,57 +2323,101 @@ sub from_oct { sub import { my $class = 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 { $class->new(shift); }; + my @a; # unrecognized arguments + my $lib_param = ''; + my $lib_value = ''; + + while (@_) { + my $param = shift; + + # Enable overloading of constants. + + if ($param eq ':constant') { + overload::constant + + integer => sub { + $class -> new(shift); + }, + + float => sub { + $class -> new(shift); + }, + + binary => sub { + # E.g., a literal 0377 shall result in an object whose value + # is decimal 255, but new("0377") returns decimal 377. + return $class -> from_oct($_[0]) if $_[0] =~ /^0_*[0-7]/; + $class -> new(shift); + }; + next; } - # 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 - #$LIB = $_[$i+1] || 'Math::BigInt::Calc'; # default Math::BigInt::Calc - $i++; - } else { - push @a, $_[$i]; + + # Upgrading. + + if ($param eq 'upgrade') { + $class -> upgrade(shift); + next; } - } - 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 + # Downgrading. + + if ($param eq 'downgrade') { + $class -> downgrade(shift); + next; } - $lib = join(",", @c); + + # Accuracy. + + if ($param eq 'accuracy') { + $class -> accuracy(shift); + next; + } + + # Precision. + + if ($param eq 'precision') { + $class -> precision(shift); + next; + } + + # Rounding mode. + + if ($param eq 'round_mode') { + $class -> round_mode(shift); + next; + } + + # Backend library. + + if ($param =~ /^(lib|try|only)\z/) { + # alternative library + $lib_param = $param; # "lib", "try", or "only" + $lib_value = shift; + next; + } + + if ($param eq 'with') { + # alternative class for our private parts() + # XXX: no longer supported + # $LIB = shift() || 'Calc'; + # carp "'with' is no longer supported, use 'lib', 'try', or 'only'"; + shift; + next; + } + + # Unrecognized parameter. + + push @a, $param; } - my @import = ('objectify'); - push @import, $try => $lib if $lib ne ''; - # LIB already loaded, so feed it our lib arguments - Math::BigInt->import(@import); + require Math::BigInt; - $LIB = Math::BigFloat->config("lib"); + my @import = ('objectify'); + push @import, $lib_param, $lib_value if $lib_param ne ''; + Math::BigInt -> import(@import); - # register us with LIB to get notified of future lib changes - Math::BigInt::_register_callback($class, sub { $LIB = $_[0]; }); + # find out which one was actually loaded + $LIB = Math::BigInt -> config("lib"); # any non :constant stuff is handled by Exporter (loaded by parent class) # even if @_ is empty, to give it a chance @@ -2109,7 +2433,7 @@ __END__ =head1 NAME -Math::BigRat - Arbitrary big rational numbers +Math::BigRat - arbitrary size rational number math package =head1 SYNOPSIS @@ -2201,6 +2525,16 @@ Returns a copy of the denominator (the part under the line) as positive BigInt. Return a list consisting of (signed) numerator and (unsigned) denominator as BigInts. +=item dparts() + +Returns the integer part and the fraction part. + +=item fparts() + +Returns the smallest possible numerator and denominator so that the numerator +divided by the denominator gives back the original value. For finite numbers, +both values are integers. Mnemonic: fraction. + =item numify() my $y = $x->numify(); @@ -2513,7 +2847,13 @@ Subtracts $y from $x and returns the result. In scalar context, divides $x by $y and returns the result. In list context, does floored division (F-division), returning an integer $q and a remainder $r so that $x = $q * $y + $r. The remainer (modulo) is equal to what is returned -by C<$x->bmod($y)>. +by C<< $x->bmod($y) >>. + +=item binv() + + $x->binv(); + +Inverse of $x. =item bdec() @@ -2695,6 +3035,70 @@ supported. =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 Please report any bugs or feature requests to @@ -2714,43 +3118,25 @@ You can also look for information at: =over 4 -=item * RT: CPAN's request tracker - -L<https://rt.cpan.org/Public/Dist/Display.html?Name=Math-BigRat> - -=item * AnnoCPAN: Annotated CPAN documentation +=item * GitHub -L<http://annocpan.org/dist/Math-BigRat> +L<https://github.com/pjacklam/p5-Math-BigRat> -=item * CPAN Ratings +=item * RT: CPAN's request tracker -L<http://cpanratings.perl.org/dist/Math-BigRat> +L<https://rt.cpan.org/Dist/Display.html?Name=Math-BigRat> -=item * Search CPAN +=item * MetaCPAN -L<http://search.cpan.org/dist/Math-BigRat/> +L<https://metacpan.org/release/Math-BigRat> =item * CPAN Testers Matrix L<http://matrix.cpantesters.org/?dist=Math-BigRat> -=item * The Bignum mailing list - -=over 4 - -=item * Post to mailing list - -C<bignum at lists.scsys.co.uk> - -=item * View mailing list - -L<http://lists.scsys.co.uk/pipermail/bignum/> - -=item * Subscribe/Unsubscribe - -L<http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/bignum> +=item * CPAN Ratings -=back +L<https://cpanratings.perl.org/dist/Math-BigRat> =back @@ -2774,7 +3160,7 @@ Tels L<http://bloodgate.com/> 2001-2009. =item * -Maintained by Peter John Acklam <pjacklam@online.no> 2011- +Maintained by Peter John Acklam <pjacklam@gmail.com> 2011- =back |