diff options
author | markm <markm@FreeBSD.org> | 2002-03-16 20:14:30 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2002-03-16 20:14:30 +0000 |
commit | 3eac21f49bc763a6c0044b4afbc0c7ece760144f (patch) | |
tree | 4cf1274fa3ca68f7ecf6a3051e0c2243e378afc5 /contrib/perl5/lib/Math | |
parent | 259bd53c06712c4ffb0ab7e06898c19ebf221b21 (diff) | |
download | FreeBSD-src-3eac21f49bc763a6c0044b4afbc0c7ece760144f.zip FreeBSD-src-3eac21f49bc763a6c0044b4afbc0c7ece760144f.tar.gz |
Vendor import Perl 5.6.1
Diffstat (limited to 'contrib/perl5/lib/Math')
-rw-r--r-- | contrib/perl5/lib/Math/BigFloat.pm | 84 | ||||
-rw-r--r-- | contrib/perl5/lib/Math/BigInt.pm | 27 | ||||
-rw-r--r-- | contrib/perl5/lib/Math/Complex.pm | 429 | ||||
-rw-r--r-- | contrib/perl5/lib/Math/Trig.pm | 43 |
4 files changed, 361 insertions, 222 deletions
diff --git a/contrib/perl5/lib/Math/BigFloat.pm b/contrib/perl5/lib/Math/BigFloat.pm index d8d643c..1eefac2 100644 --- a/contrib/perl5/lib/Math/BigFloat.pm +++ b/contrib/perl5/lib/Math/BigFloat.pm @@ -4,6 +4,7 @@ use Math::BigInt; use Exporter; # just for use to be happy @ISA = (Exporter); +$VERSION = '0.02'; use overload '+' => sub {new Math::BigFloat &fadd}, @@ -12,9 +13,12 @@ use overload '<=>' => sub {$_[2]? fcmp($_[1],${$_[0]}) : fcmp(${$_[0]},$_[1])}, 'cmp' => sub {$_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])}, '*' => sub {new Math::BigFloat &fmul}, -'/' => sub {new Math::BigFloat +'/' => sub {new Math::BigFloat $_[2]? scalar fdiv($_[1],${$_[0]}) : scalar fdiv(${$_[0]},$_[1])}, +'%' => sub {new Math::BigFloat + $_[2]? scalar fmod($_[1],${$_[0]}) : + scalar fmod(${$_[0]},$_[1])}, 'neg' => sub {new Math::BigFloat &fneg}, 'abs' => sub {new Math::BigFloat &fabs}, @@ -43,12 +47,15 @@ sub stringify { my $e = $1; my $ln = length($n); - if ($e > 0) { - $n .= "0" x $e . '.'; - } elsif (abs($e) < $ln) { - substr($n, $ln + $e, 0) = '.'; - } else { - $n = '.' . ("0" x (abs($e) - $ln)) . $n; + if ( defined $e ) + { + if ($e > 0) { + $n .= "0" x $e . '.'; + } elsif (abs($e) < $ln) { + substr($n, $ln + $e, 0) = '.'; + } else { + $n = '.' . ("0" x (abs($e) - $ln)) . $n; + } } $n = "-$n" if $minus; @@ -85,6 +92,7 @@ sub fnorm { #(string) return fnum_str # normalize number -- for internal use sub norm { #(mantissa, exponent) return fnum_str local($_, $exp) = @_; + $exp = 0 unless defined $exp; if ($_ eq 'NaN') { 'NaN'; } else { @@ -140,7 +148,7 @@ sub fadd { #(fnum_str, fnum_str) return fnum_str # subtraction sub fsub { #(fnum_str, fnum_str) return fnum_str - fadd($_[$[],fneg($_[$[+1])); + fadd($_[$[],fneg($_[$[+1])); } # division @@ -164,6 +172,27 @@ sub fdiv #(fnum_str, fnum_str[,scale]) return fnum_str } } +# modular division +# args are dividend, divisor +sub fmod #(fnum_str, fnum_str) return fnum_str +{ + local($x,$y) = (fnorm($_[$[]),fnorm($_[$[+1])); + if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') { + 'NaN'; + } else { + local($xm,$xe) = split('E',$x); + local($ym,$ye) = split('E',$y); + if ( $xe < $ye ) + { + $ym .= ('0' x ($ye-$xe)); + } + else + { + $xm .= ('0' x ($xe-$ye)); + } + &norm(Math::BigInt::bmod($xm,$ym)); + } +} # round int $q based on fraction $r/$base using $rnd_mode sub round { #(int_str, int_str, int_str) return int_str local($q,$r,$base) = @_; @@ -174,12 +203,14 @@ sub round { #(int_str, int_str, int_str) return int_str } else { local($cmp) = Math::BigInt::bcmp(Math::BigInt::bmul($r,'+2'),$base); if ( $cmp < 0 || - ($cmp == 0 && - ( $rnd_mode eq 'zero' || + ($cmp == 0 && ( + ($rnd_mode eq 'zero' ) || ($rnd_mode eq '-inf' && (substr($q,$[,1) eq '+')) || ($rnd_mode eq '+inf' && (substr($q,$[,1) eq '-')) || - ($rnd_mode eq 'even' && $q =~ /[24680]$/) || - ($rnd_mode eq 'odd' && $q =~ /[13579]$/) )) ) { + ($rnd_mode eq 'even' && $q =~ /[24680]$/ ) || + ($rnd_mode eq 'odd' && $q =~ /[13579]$/ ) ) + ) + ) { $q; # round down } else { Math::BigInt::badd($q, ((substr($q,$[,1) eq '-') ? '-1' : '+1')); @@ -199,7 +230,7 @@ sub fround { #(fnum_str, scale) return fnum_str $x; } else { &norm(&round(substr($xm,$[,$scale+1), - "+0".substr($xm,$[+$scale+1,1),"+10"), + "+0".substr($xm,$[+$scale+1),"+1"."0" x length(substr($xm,$[+$scale+1))), $xe+length($xm)-$scale-1); } } @@ -223,15 +254,17 @@ sub ffround { #(fnum_str, scale) return fnum_str # normalized "-0" to &round when rounding -0.006 (for # example), purely so &round won't lose the sign. &norm(&round(substr($xm,$[,1).'0', - "+0".substr($xm,$[+1,1),"+10"), $scale); + "+0".substr($xm,$[+1), + "+1"."0" x length(substr($xm,$[+1))), $scale); } else { &norm(&round(substr($xm,$[,$xe), - "+0".substr($xm,$[+$xe,1),"+10"), $scale); + "+0".substr($xm,$[+$xe), + "+1"."0" x length(substr($xm,$[+$xe))), $scale); } } } } - + # compare 2 values returns one of undef, <0, =0, >0 # returns undef if either or both input value are not numbers sub fcmp #(fnum_str, fnum_str) return cond_code @@ -244,9 +277,17 @@ sub fcmp #(fnum_str, fnum_str) return cond_code if ($xm eq '+0' || $ym eq '+0') { return $xm <=> $ym; } - ord($y) <=> ord($x) - || ($xe <=> $ye) * (substr($x,$[,1).'1') - || Math::BigInt::cmp($xm,$ym); + if ( $xe < $ye ) # adjust the exponents to be equal + { + $ym .= '0' x ($ye - $xe); + $ye = $xe; + } + elsif ( $ye < $xe ) # same here + { + $xm .= '0' x ($xe - $ye); + $xe = $ye; + } + return Math::BigInt::cmp($xm,$ym); } } @@ -286,6 +327,7 @@ Math::BigFloat - Arbitrary length float math package $f->fsub(NSTR) return NSTR subtraction $f->fmul(NSTR) return NSTR multiplication $f->fdiv(NSTR[,SCALE]) returns NSTR division to SCALE places + $f->fmod(NSTR) returns NSTR modular remainder $f->fneg() return NSTR negation $f->fabs() return NSTR absolute value $f->fcmp(NSTR) return CODE compare undef,<0,=0,>0 @@ -313,7 +355,7 @@ have embedded whitespace. An input parameter was "Not a Number" or divide by zero or sqrt of negative number. -=item Division is computed to +=item Division is computed to C<max($Math::BigFloat::div_scale,length(dividend)+length(divisor))> digits by default. @@ -352,5 +394,5 @@ as follows: =head1 AUTHOR Mark Biggar - +Patches by John Peacock Apr 2001 =cut diff --git a/contrib/perl5/lib/Math/BigInt.pm b/contrib/perl5/lib/Math/BigInt.pm index a43969c..066577d 100644 --- a/contrib/perl5/lib/Math/BigInt.pm +++ b/contrib/perl5/lib/Math/BigInt.pm @@ -1,4 +1,5 @@ package Math::BigInt; +$VERSION='0.01'; use overload '+' => sub {new Math::BigInt &badd}, @@ -51,6 +52,11 @@ sub import { $zero = 0; +# overcome a floating point problem on certain osnames (posix-bc, os390) +BEGIN { + my $x = 100000.0; + my $use_mult = int($x*1e-5)*1e5 == $x ? 1 : 0; +} # normalize string form of number. Strip leading zeros. Strip any # white space and add a sign, if missing. @@ -227,8 +233,14 @@ sub mul { #(*int_num_array, *int_num_array) return int_num_array ($car, $cty) = (0, $[); for $y (@y) { $prod = $x * $y + ($prod[$cty] || 0) + $car; + if ($use_mult) { $prod[$cty++] = $prod - ($car = int($prod * 1e-5)) * 1e5; + } + else { + $prod[$cty++] = + $prod - ($car = int($prod / 1e5)) * 1e5; + } } $prod[$cty] += $car if $car; $x = shift @prod; @@ -253,12 +265,22 @@ sub bdiv { #(dividend: num_str, divisor: num_str) return num_str if (($dd = int(1e5/($y[$#y]+1))) != 1) { for $x (@x) { $x = $x * $dd + $car; + if ($use_mult) { $x -= ($car = int($x * 1e-5)) * 1e5; + } + else { + $x -= ($car = int($x / 1e5)) * 1e5; + } } push(@x, $car); $car = 0; for $y (@y) { $y = $y * $dd + $car; + if ($use_mult) { $y -= ($car = int($y * 1e-5)) * 1e5; + } + else { + $y -= ($car = int($y / 1e5)) * 1e5; + } } } else { @@ -275,7 +297,12 @@ sub bdiv { #(dividend: num_str, divisor: num_str) return num_str ($car, $bar) = (0,0); for ($y = $[, $x = $#x-$#y+$[-1; $y <= $#y; ++$y,++$x) { $prd = $q * $y[$y] + $car; + if ($use_mult) { $prd -= ($car = int($prd * 1e-5)) * 1e5; + } + else { + $prd -= ($car = int($prd / 1e5)) * 1e5; + } $x[$x] += 1e5 if ($bar = (($x[$x] -= $prd + $bar) < 0)); } if ($x[$#x] < $car + $bar) { diff --git a/contrib/perl5/lib/Math/Complex.pm b/contrib/perl5/lib/Math/Complex.pm index 1a47f4a..9812513 100644 --- a/contrib/perl5/lib/Math/Complex.pm +++ b/contrib/perl5/lib/Math/Complex.pm @@ -5,17 +5,39 @@ # -- Daniel S. Lewart Since Sep 1997 # -require Exporter; package Math::Complex; -use 5.005_64; -use strict; +our($VERSION, @ISA, @EXPORT, %EXPORT_TAGS, $Inf); + +$VERSION = 1.31; + +BEGIN { + unless ($^O eq 'unicosmk') { + my $e = $!; + # We do want an arithmetic overflow, Inf INF inf Infinity:. + undef $Inf unless eval <<'EOE' and $Inf =~ /^inf(?:inity)?$/i; + local $SIG{FPE} = sub {die}; + my $t = CORE::exp 30; + $Inf = CORE::exp $t; +EOE + if (!defined $Inf) { # Try a different method + undef $Inf unless eval <<'EOE' and $Inf =~ /^inf(?:inity)?$/i; + local $SIG{FPE} = sub {die}; + my $t = 1; + $Inf = $t + "1e99999999999999999999999999999999"; +EOE + } + $! = $e; # Clear ERANGE. + } + $Inf = "Inf" if !defined $Inf || !($Inf > 0); # Desperation. +} -our($VERSION, @ISA, @EXPORT, %EXPORT_TAGS); +use strict; -my ( $i, $ip2, %logn ); +my $i; +my %LOGN; -$VERSION = sprintf("%s", q$Id: Complex.pm,v 1.26 1998/11/01 00:00:00 dsl Exp $ =~ /(\d+\.\d+)/); +require Exporter; @ISA = qw(Exporter); @@ -49,6 +71,7 @@ use overload '*' => \&multiply, '/' => \÷, '**' => \&power, + '==' => \&numeq, '<=>' => \&spaceship, 'neg' => \&negate, '~' => \&conjugate, @@ -66,7 +89,6 @@ use overload # Package "privates" # -my $package = 'Math::Complex'; # Package name my %DISPLAY_FORMAT = ('style' => 'cartesian', 'polar_pretty_print' => 1); my $eps = 1e-14; # Epsilon @@ -228,6 +250,13 @@ sub i () { } # +# ip2 +# +# Half of i. +# +sub ip2 () { i / 2 } + +# # Attribute access/set routines # @@ -262,7 +291,8 @@ sub update_polar { my ($x, $y) = @{$self->{'cartesian'}}; $self->{p_dirty} = 0; return $self->{'polar'} = [0, 0] if $x == 0 && $y == 0; - return $self->{'polar'} = [CORE::sqrt($x*$x + $y*$y), CORE::atan2($y, $x)]; + return $self->{'polar'} = [CORE::sqrt($x*$x + $y*$y), + CORE::atan2($y, $x)]; } # @@ -342,7 +372,7 @@ sub _divbyzero { if (defined $_[1]) { $mess .= "(Because in the definition of $_[0], the divisor "; - $mess .= "$_[1] " unless ($_[1] eq '0'); + $mess .= "$_[1] " unless ("$_[1]" eq '0'); $mess .= "is 0)\n"; } @@ -416,8 +446,8 @@ sub power { return 1 if $z2 == 0 || $z1 == 1; return 0 if $z1 == 0 && Re($z2) > 0; } - my $w = $inverted ? CORE::exp($z1 * CORE::log($z2)) - : CORE::exp($z2 * CORE::log($z1)); + my $w = $inverted ? &exp($z1 * &log($z2)) + : &exp($z2 * &log($z1)); # If both arguments cartesian, return cartesian, else polar. return $z1->{c_dirty} == 0 && (not ref $z2 or $z2->{c_dirty} == 0) ? @@ -440,6 +470,19 @@ sub spaceship { } # +# (numeq) +# +# Computes z1 == z2. +# +# (Required in addition to spaceship() because of NaNs.) +sub numeq { + my ($z1, $z2, $inverted) = @_; + my ($re1, $im1) = ref $z1 ? @{$z1->cartesian} : ($z1, 0); + my ($re2, $im2) = ref $z2 ? @{$z2->cartesian} : ($z2, 0); + return $re1 == $re2 && $im1 == $im2 ? 1 : 0; +} + +# # (negate) # # Computes -z. @@ -477,7 +520,13 @@ sub conjugate { # sub abs { my ($z, $rho) = @_; - return $z unless ref $z; + unless (ref $z) { + if (@_ == 2) { + $_[0] = $_[1]; + } else { + return CORE::abs($z); + } + } if (defined $rho) { $z->{'polar'} = [ $rho, ${$z->polar}[1] ]; $z->{p_dirty} = 0; @@ -533,7 +582,8 @@ sub arg { sub sqrt { my ($z) = @_; my ($re, $im) = ref $z ? @{$z->cartesian} : ($z, 0); - return $re < 0 ? cplx(0, CORE::sqrt(-$re)) : CORE::sqrt($re) if $im == 0; + return $re < 0 ? cplx(0, CORE::sqrt(-$re)) : CORE::sqrt($re) + if $im == 0; my ($r, $t) = @{$z->polar}; return (ref $z)->emake(CORE::sqrt($r), $t/2); } @@ -547,9 +597,12 @@ sub sqrt { # sub cbrt { my ($z) = @_; - return $z < 0 ? -CORE::exp(CORE::log(-$z)/3) : ($z > 0 ? CORE::exp(CORE::log($z)/3): 0) + return $z < 0 ? + -CORE::exp(CORE::log(-$z)/3) : + ($z > 0 ? CORE::exp(CORE::log($z)/3): 0) unless ref $z; my ($r, $t) = @{$z->polar}; + return 0 if $r == 0; return (ref $z)->emake(CORE::exp(CORE::log($r)/3), $t/3); } @@ -559,7 +612,7 @@ sub cbrt { # Die on bad root. # sub _rootbad { - my $mess = "Root $_[0] not defined, root must be positive integer.\n"; + my $mess = "Root $_[0] illegal, root rank must be positive integer.\n"; my @up = caller(1); @@ -581,7 +634,8 @@ sub _rootbad { sub root { my ($z, $n) = @_; _rootbad($n) if ($n < 1 or int($n) != $n); - my ($r, $t) = ref $z ? @{$z->polar} : (CORE::abs($z), $z >= 0 ? 0 : pi); + my ($r, $t) = ref $z ? + @{$z->polar} : (CORE::abs($z), $z >= 0 ? 0 : pi); my @root; my $k; my $theta_inc = pit2 / $n; @@ -620,7 +674,7 @@ sub Re { # sub Im { my ($z, $Im) = @_; - return $z unless ref $z; + return 0 unless ref $z; if (defined $Im) { $z->{'cartesian'} = [ ${$z->cartesian}[0], $Im ]; $z->{c_dirty} = 0; @@ -723,9 +777,9 @@ sub log10 { sub logn { my ($z, $n) = @_; $z = cplx($z, 0) unless ref $z; - my $logn = $logn{$n}; - $logn = $logn{$n} = CORE::log($n) unless defined $logn; # Cache log(n) - return CORE::log($z) / $logn; + my $logn = $LOGN{$n}; + $logn = $LOGN{$n} = CORE::log($n) unless defined $logn; # Cache log(n) + return &log($z) / $logn; } # @@ -735,11 +789,14 @@ sub logn { # sub cos { my ($z) = @_; + return CORE::cos($z) unless ref $z; my ($x, $y) = @{$z->cartesian}; my $ey = CORE::exp($y); - my $ey_1 = 1 / $ey; - return (ref $z)->make(CORE::cos($x) * ($ey + $ey_1)/2, - CORE::sin($x) * ($ey_1 - $ey)/2); + my $sx = CORE::sin($x); + my $cx = CORE::cos($x); + my $ey_1 = $ey ? 1 / $ey : $Inf; + return (ref $z)->make($cx * ($ey + $ey_1)/2, + $sx * ($ey_1 - $ey)/2); } # @@ -749,11 +806,14 @@ sub cos { # sub sin { my ($z) = @_; + return CORE::sin($z) unless ref $z; my ($x, $y) = @{$z->cartesian}; my $ey = CORE::exp($y); - my $ey_1 = 1 / $ey; - return (ref $z)->make(CORE::sin($x) * ($ey + $ey_1)/2, - CORE::cos($x) * ($ey - $ey_1)/2); + my $sx = CORE::sin($x); + my $cx = CORE::cos($x); + my $ey_1 = $ey ? 1 / $ey : $Inf; + return (ref $z)->make($sx * ($ey + $ey_1)/2, + $cx * ($ey - $ey_1)/2); } # @@ -763,9 +823,9 @@ sub sin { # sub tan { my ($z) = @_; - my $cz = CORE::cos($z); - _divbyzero "tan($z)", "cos($z)" if (CORE::abs($cz) < $eps); - return CORE::sin($z) / $cz; + my $cz = &cos($z); + _divbyzero "tan($z)", "cos($z)" if $cz == 0; + return &sin($z) / $cz; } # @@ -775,7 +835,7 @@ sub tan { # sub sec { my ($z) = @_; - my $cz = CORE::cos($z); + my $cz = &cos($z); _divbyzero "sec($z)", "cos($z)" if ($cz == 0); return 1 / $cz; } @@ -787,7 +847,7 @@ sub sec { # sub csc { my ($z) = @_; - my $sz = CORE::sin($z); + my $sz = &sin($z); _divbyzero "csc($z)", "sin($z)" if ($sz == 0); return 1 / $sz; } @@ -806,9 +866,9 @@ sub cosec { Math::Complex::csc(@_) } # sub cot { my ($z) = @_; - my $sz = CORE::sin($z); + my $sz = &sin($z); _divbyzero "cot($z)", "sin($z)" if ($sz == 0); - return CORE::cos($z) / $sz; + return &cos($z) / $sz; } # @@ -825,8 +885,11 @@ sub cotan { Math::Complex::cot(@_) } # sub acos { my $z = $_[0]; - return CORE::atan2(CORE::sqrt(1-$z*$z), $z) if (! ref $z) && CORE::abs($z) <= 1; - my ($x, $y) = ref $z ? @{$z->cartesian} : ($z, 0); + return CORE::atan2(CORE::sqrt(1-$z*$z), $z) + if (! ref $z) && CORE::abs($z) <= 1; + $z = cplx($z, 0) unless ref $z; + my ($x, $y) = @{$z->cartesian}; + return 0 if $x == 1 && $y == 0; my $t1 = CORE::sqrt(($x+1)*($x+1) + $y*$y); my $t2 = CORE::sqrt(($x-1)*($x-1) + $y*$y); my $alpha = ($t1 + $t2)/2; @@ -837,7 +900,7 @@ sub acos { my $u = CORE::atan2(CORE::sqrt(1-$beta*$beta), $beta); my $v = CORE::log($alpha + CORE::sqrt($alpha*$alpha-1)); $v = -$v if $y > 0 || ($y == 0 && $x < -1); - return __PACKAGE__->make($u, $v); + return (ref $z)->make($u, $v); } # @@ -847,8 +910,11 @@ sub acos { # sub asin { my $z = $_[0]; - return CORE::atan2($z, CORE::sqrt(1-$z*$z)) if (! ref $z) && CORE::abs($z) <= 1; - my ($x, $y) = ref $z ? @{$z->cartesian} : ($z, 0); + return CORE::atan2($z, CORE::sqrt(1-$z*$z)) + if (! ref $z) && CORE::abs($z) <= 1; + $z = cplx($z, 0) unless ref $z; + my ($x, $y) = @{$z->cartesian}; + return 0 if $x == 0 && $y == 0; my $t1 = CORE::sqrt(($x+1)*($x+1) + $y*$y); my $t2 = CORE::sqrt(($x-1)*($x-1) + $y*$y); my $alpha = ($t1 + $t2)/2; @@ -859,7 +925,7 @@ sub asin { my $u = CORE::atan2($beta, CORE::sqrt(1-$beta*$beta)); my $v = -CORE::log($alpha + CORE::sqrt($alpha*$alpha-1)); $v = -$v if $y > 0 || ($y == 0 && $x < -1); - return __PACKAGE__->make($u, $v); + return (ref $z)->make($u, $v); } # @@ -870,11 +936,12 @@ sub asin { sub atan { my ($z) = @_; return CORE::atan2($z, 1) unless ref $z; + my ($x, $y) = ref $z ? @{$z->cartesian} : ($z, 0); + return 0 if $x == 0 && $y == 0; _divbyzero "atan(i)" if ( $z == i); - _divbyzero "atan(-i)" if (-$z == i); - my $log = CORE::log((i + $z) / (i - $z)); - $ip2 = 0.5 * i unless defined $ip2; - return $ip2 * $log; + _logofzero "atan(-i)" if (-$z == i); # -i is a bad file test... + my $log = &log((i + $z) / (i - $z)); + return ip2 * $log; } # @@ -913,10 +980,11 @@ sub acosec { Math::Complex::acsc(@_) } # sub acot { my ($z) = @_; - _divbyzero "acot(0)" if (CORE::abs($z) < $eps); - return ($z >= 0) ? CORE::atan2(1, $z) : CORE::atan2(-1, -$z) unless ref $z; - _divbyzero "acot(i)" if (CORE::abs($z - i) < $eps); - _logofzero "acot(-i)" if (CORE::abs($z + i) < $eps); + _divbyzero "acot(0)" if $z == 0; + return ($z >= 0) ? CORE::atan2(1, $z) : CORE::atan2(-1, -$z) + unless ref $z; + _divbyzero "acot(i)" if ($z - i == 0); + _logofzero "acot(-i)" if ($z + i == 0); return atan(1 / $z); } @@ -937,11 +1005,11 @@ sub cosh { my $ex; unless (ref $z) { $ex = CORE::exp($z); - return ($ex + 1/$ex)/2; + return $ex ? ($ex + 1/$ex)/2 : $Inf; } my ($x, $y) = @{$z->cartesian}; $ex = CORE::exp($x); - my $ex_1 = 1 / $ex; + my $ex_1 = $ex ? 1 / $ex : $Inf; return (ref $z)->make(CORE::cos($y) * ($ex + $ex_1)/2, CORE::sin($y) * ($ex - $ex_1)/2); } @@ -955,12 +1023,15 @@ sub sinh { my ($z) = @_; my $ex; unless (ref $z) { + return 0 if $z == 0; $ex = CORE::exp($z); - return ($ex - 1/$ex)/2; + return $ex ? ($ex - 1/$ex)/2 : "-$Inf"; } my ($x, $y) = @{$z->cartesian}; + my $cy = CORE::cos($y); + my $sy = CORE::sin($y); $ex = CORE::exp($x); - my $ex_1 = 1 / $ex; + my $ex_1 = $ex ? 1 / $ex : $Inf; return (ref $z)->make(CORE::cos($y) * ($ex - $ex_1)/2, CORE::sin($y) * ($ex + $ex_1)/2); } @@ -1016,7 +1087,7 @@ sub cosech { Math::Complex::csch(@_) } sub coth { my ($z) = @_; my $sz = sinh($z); - _divbyzero "coth($z)", "sinh($z)" if ($sz == 0); + _divbyzero "coth($z)", "sinh($z)" if $sz == 0; return cosh($z) / $sz; } @@ -1035,25 +1106,44 @@ sub cotanh { Math::Complex::coth(@_) } sub acosh { my ($z) = @_; unless (ref $z) { - return CORE::log($z + CORE::sqrt($z*$z-1)) if $z >= 1; $z = cplx($z, 0); } my ($re, $im) = @{$z->cartesian}; if ($im == 0) { - return cplx(CORE::log($re + CORE::sqrt($re*$re - 1)), 0) if $re >= 1; - return cplx(0, CORE::atan2(CORE::sqrt(1-$re*$re), $re)) if CORE::abs($re) <= 1; + return CORE::log($re + CORE::sqrt($re*$re - 1)) + if $re >= 1; + return cplx(0, CORE::atan2(CORE::sqrt(1 - $re*$re), $re)) + if CORE::abs($re) < 1; } - return CORE::log($z + CORE::sqrt($z*$z - 1)); + my $t = &sqrt($z * $z - 1) + $z; + # Try Taylor if looking bad (this usually means that + # $z was large negative, therefore the sqrt is really + # close to abs(z), summing that with z...) + $t = 1/(2 * $z) - 1/(8 * $z**3) + 1/(16 * $z**5) - 5/(128 * $z**7) + if $t == 0; + my $u = &log($t); + $u->Im(-$u->Im) if $re < 0 && $im == 0; + return $re < 0 ? -$u : $u; } # # asinh # -# Computes the arc hyperbolic sine asinh(z) = log(z + sqrt(z*z-1)) +# Computes the arc hyperbolic sine asinh(z) = log(z + sqrt(z*z+1)) # sub asinh { my ($z) = @_; - return CORE::log($z + CORE::sqrt($z*$z + 1)); + unless (ref $z) { + my $t = $z + CORE::sqrt($z*$z + 1); + return CORE::log($t) if $t; + } + my $t = &sqrt($z * $z + 1) + $z; + # Try Taylor if looking bad (this usually means that + # $z was large negative, therefore the sqrt is really + # close to abs(z), summing that with z...) + $t = 1/(2 * $z) - 1/(8 * $z**3) + 1/(16 * $z**5) - 5/(128 * $z**7) + if $t == 0; + return &log($t); } # @@ -1067,9 +1157,9 @@ sub atanh { return CORE::log((1 + $z)/(1 - $z))/2 if CORE::abs($z) < 1; $z = cplx($z, 0); } - _divbyzero 'atanh(1)', "1 - $z" if ($z == 1); - _logofzero 'atanh(-1)' if ($z == -1); - return 0.5 * CORE::log((1 + $z) / (1 - $z)); + _divbyzero 'atanh(1)', "1 - $z" if (1 - $z == 0); + _logofzero 'atanh(-1)' if (1 + $z == 0); + return 0.5 * &log((1 + $z) / (1 - $z)); } # @@ -1079,7 +1169,7 @@ sub atanh { # sub asech { my ($z) = @_; - _divbyzero 'asech(0)', $z if ($z == 0); + _divbyzero 'asech(0)', "$z" if ($z == 0); return acosh(1 / $z); } @@ -1108,14 +1198,14 @@ sub acosech { Math::Complex::acsch(@_) } # sub acoth { my ($z) = @_; - _divbyzero 'acoth(0)' if (CORE::abs($z) < $eps); + _divbyzero 'acoth(0)' if ($z == 0); unless (ref $z) { return CORE::log(($z + 1)/($z - 1))/2 if CORE::abs($z) > 1; $z = cplx($z, 0); } - _divbyzero 'acoth(1)', "$z - 1" if (CORE::abs($z - 1) < $eps); - _logofzero 'acoth(-1)', "1 / $z" if (CORE::abs($z + 1) < $eps); - return CORE::log((1 + $z) / ($z - 1)) / 2; + _divbyzero 'acoth(1)', "$z - 1" if ($z - 1 == 0); + _logofzero 'acoth(-1)', "1 + $z" if (1 + $z == 0); + return &log((1 + $z) / ($z - 1)) / 2; } # @@ -1141,8 +1231,8 @@ sub atan2 { ($re2, $im2) = ref $z2 ? @{$z2->cartesian} : ($z2, 0); } if ($im2 == 0) { - return cplx(CORE::atan2($re1, $re2), 0) if $im1 == 0; - return cplx(($im1<=>0) * pip2, 0) if $re2 == 0; + return CORE::atan2($re1, $re2) if $im1 == 0; + return ($im1<=>0) * pip2 if $re2 == 0; } my $w = atan($z1/$z2); my ($u, $v) = ref $w ? @{$w->cartesian} : ($w, 0); @@ -1173,23 +1263,15 @@ sub display_format { my %obj = %{$self->{display_format}}; @display_format{keys %obj} = values %obj; } - if (@_ == 1) { - $display_format{style} = shift; - } else { - my %new = @_; - @display_format{keys %new} = values %new; - } - } else { # Called as a class method - if (@_ = 1) { - $display_format{style} = $self; - } else { - my %new = @_; - @display_format{keys %new} = values %new; - } - undef $self; + } + if (@_ == 1) { + $display_format{style} = shift; + } else { + my %new = @_; + @display_format{keys %new} = values %new; } - if (defined $self) { + if (ref $self) { # Called as an object method $self->{display_format} = { %display_format }; return wantarray ? @@ -1197,6 +1279,7 @@ sub display_format { $self->{display_format}->{style}; } + # Called as a class method %DISPLAY_FORMAT = %display_format; return wantarray ? @@ -1235,67 +1318,58 @@ sub stringify_cartesian { my ($x, $y) = @{$z->cartesian}; my ($re, $im); - $x = int($x + ($x < 0 ? -1 : 1) * $eps) - if int(CORE::abs($x)) != int(CORE::abs($x) + $eps); - $y = int($y + ($y < 0 ? -1 : 1) * $eps) - if int(CORE::abs($y)) != int(CORE::abs($y) + $eps); - - $re = "$x" if CORE::abs($x) >= $eps; - my %format = $z->display_format; my $format = $format{format}; - if ($y == 1) { $im = 'i' } - elsif ($y == -1) { $im = '-i' } - elsif (CORE::abs($y) >= $eps) { - $im = (defined $format ? sprintf($format, $y) : $y) . "i"; + if ($x) { + if ($x =~ /^NaN[QS]?$/i) { + $re = $x; + } else { + if ($x =~ /^-?$Inf$/oi) { + $re = $x; + } else { + $re = defined $format ? sprintf($format, $x) : $x; + } + } + } else { + undef $re; } - my $str = ''; - $str = defined $format ? sprintf($format, $re) : $re - if defined $re; + if ($y) { + if ($y =~ /^(NaN[QS]?)$/i) { + $im = $y; + } else { + if ($y =~ /^-?$Inf$/oi) { + $im = $y; + } else { + $im = + defined $format ? + sprintf($format, $y) : + ($y == 1 ? "" : ($y == -1 ? "-" : $y)); + } + } + $im .= "i"; + } else { + undef $im; + } + + my $str = $re; + if (defined $im) { if ($y < 0) { $str .= $im; - } elsif ($y > 0) { + } elsif ($y > 0 || $im =~ /^NaN[QS]?i$/i) { $str .= "+" if defined $re; $str .= $im; } + } elsif (!defined $re) { + $str = "0"; } return $str; } -# Helper for stringify_polar, a Greatest Common Divisor with a memory. - -sub _gcd { - my ($a, $b) = @_; - - use integer; - - # Loops forever if given negative inputs. - - if ($b and $a > $b) { return gcd($a % $b, $b) } - elsif ($a and $b > $a) { return gcd($b % $a, $a) } - else { return $a ? $a : $b } -} - -my %gcd; - -sub gcd { - my ($a, $b) = @_; - - my $id = "$a $b"; - - unless (exists $gcd{$id}) { - $gcd{$id} = _gcd($a, $b); - $gcd{"$b $a"} = $gcd{$id}; - } - - return $gcd{$id}; -} - # # ->stringify_polar # @@ -1306,74 +1380,52 @@ sub stringify_polar { my ($r, $t) = @{$z->polar}; my $theta; - return '[0,0]' if $r <= $eps; - my %format = $z->display_format; + my $format = $format{format}; - my $nt = $t / pit2; - $nt = ($nt - int($nt)) * pit2; - $nt += pit2 if $nt < 0; # Range [0, 2pi] - - if (CORE::abs($nt) <= $eps) { $theta = 0 } - elsif (CORE::abs(pi-$nt) <= $eps) { $theta = 'pi' } - - if (defined $theta) { - $r = int($r + ($r < 0 ? -1 : 1) * $eps) - if int(CORE::abs($r)) != int(CORE::abs($r) + $eps); - $theta = int($theta + ($theta < 0 ? -1 : 1) * $eps) - if ($theta ne 'pi' and - int(CORE::abs($theta)) != int(CORE::abs($theta) + $eps)); - return "\[$r,$theta\]"; + if ($t =~ /^NaN[QS]?$/i || $t =~ /^-?$Inf$/oi) { + $theta = $t; + } elsif ($t == pi) { + $theta = "pi"; + } elsif ($r == 0 || $t == 0) { + $theta = defined $format ? sprintf($format, $t) : $t; } + return "[$r,$theta]" if defined $theta; + # - # Okay, number is not a real. Try to identify pi/n and friends... + # Try to identify pi/n and friends. # - $nt -= pit2 if $nt > pi; - - if ($format{polar_pretty_print} && CORE::abs($nt) >= deg1) { - my ($n, $k, $kpi); - - for ($k = 1, $kpi = pi; $k < 10; $k++, $kpi += pi) { - $n = int($kpi / $nt + ($nt > 0 ? 1 : -1) * 0.5); - if (CORE::abs($kpi/$n - $nt) <= $eps) { - $n = CORE::abs($n); - my $gcd = gcd($k, $n); - if ($gcd > 1) { - $k /= $gcd; - $n /= $gcd; - } - next if $n > 360; - $theta = ($nt < 0 ? '-':''). - ($k == 1 ? 'pi':"${k}pi"); - $theta .= '/'.$n if $n > 1; + $t -= int(CORE::abs($t) / pit2) * pit2; + + if ($format{polar_pretty_print} && $t) { + my ($a, $b); + for $a (2..9) { + $b = $t * $a / pi; + if ($b =~ /^-?\d+$/) { + $b = $b < 0 ? "-" : "" if CORE::abs($b) == 1; + $theta = "${b}pi/$a"; last; } } } - $theta = $nt unless defined $theta; - - $r = int($r + ($r < 0 ? -1 : 1) * $eps) - if int(CORE::abs($r)) != int(CORE::abs($r) + $eps); - $theta = int($theta + ($theta < 0 ? -1 : 1) * $eps) - if ($theta !~ m(^-?\d*pi/\d+$) and - int(CORE::abs($theta)) != int(CORE::abs($theta) + $eps)); - - my $format = $format{format}; if (defined $format) { $r = sprintf($format, $r); - $theta = sprintf($format, $theta); + $theta = sprintf($format, $theta) unless defined $theta; + } else { + $theta = $t unless defined $theta; } - return "\[$r,$theta\]"; + return "[$r,$theta]"; } 1; __END__ =pod + =head1 NAME Math::Complex - complex numbers and associated mathematical functions @@ -1695,7 +1747,7 @@ For instance: print "j = $j\n"; # Prints "j = -0.5+0.866025403784439i" The polar style attempts to emphasize arguments like I<k*pi/n> -(where I<n> is a positive integer and I<k> an integer within [-9,+9]), +(where I<n> is a positive integer and I<k> an integer within [-9, +9]), this is called I<polar pretty-printing>. =head2 CHANGED IN PERL 5.6 @@ -1705,29 +1757,33 @@ C<display_format> object method can now be called using a parameter hash instead of just a one parameter. The old display format style, which can have values C<"cartesian"> or -C<"polar">, can be changed using the C<"style"> parameter. (The one -parameter calling convention also still works.) +C<"polar">, can be changed using the C<"style"> parameter. + + $j->display_format(style => "polar"); + +The one parameter calling convention also still works. + + $j->display_format("polar"); There are two new display parameters. -The first one is C<"format">, which is a sprintf()-style format -string to be used for both parts of the complex number(s). The -default is C<undef>, which corresponds usually (this is somewhat -system-dependent) to C<"%.15g">. You can revert to the default by -setting the format string to C<undef>. +The first one is C<"format">, which is a sprintf()-style format string +to be used for both numeric parts of the complex number(s). The is +somewhat system-dependent but most often it corresponds to C<"%.15g">. +You can revert to the default by setting the C<format> to C<undef>. # the $j from the above example $j->display_format('format' => '%.5f'); print "j = $j\n"; # Prints "j = -0.50000+0.86603i" - $j->display_format('format' => '%.6f'); + $j->display_format('format' => undef); print "j = $j\n"; # Prints "j = -0.5+0.86603i" Notice that this affects also the return values of the C<display_format> methods: in list context the whole parameter hash -will be returned, as opposed to only the style parameter value. If -you want to know the whole truth for a complex number, you must call -both the class method and the object method: +will be returned, as opposed to only the style parameter value. +This is a potential incompatibility with earlier versions if you +have been calling the C<display_format> method in list context. The second new display parameter is C<"polar_pretty_print">, which can be set to true or false, the default being true. See the previous @@ -1791,8 +1847,7 @@ is any integer. Note that because we are operating on approximations of real numbers, these errors can happen when merely `too close' to the singularities -listed above. For example C<tan(2*atan2(1,1)+1e-15)> will die of -division by zero. +listed above. =head1 ERRORS DUE TO INDIGESTIBLE ARGUMENTS diff --git a/contrib/perl5/lib/Math/Trig.pm b/contrib/perl5/lib/Math/Trig.pm index 492706c..b28f150 100644 --- a/contrib/perl5/lib/Math/Trig.pm +++ b/contrib/perl5/lib/Math/Trig.pm @@ -36,14 +36,15 @@ my @rdlcnv = qw(cartesian_to_cylindrical %EXPORT_TAGS = ('radial' => [ @rdlcnv ]); -sub pi2 () { 2 * pi } # use constant generates warning -sub pip2 () { pi / 2 } # use constant generates warning -use constant DR => pi2/360; -use constant RD => 360/pi2; -use constant DG => 400/360; -use constant GD => 360/400; -use constant RG => 400/pi2; -use constant GR => pi2/400; +sub pi2 () { 2 * pi } +sub pip2 () { pi / 2 } + +sub DR () { pi2/360 } +sub RD () { 360/pi2 } +sub DG () { 400/360 } +sub GD () { 360/400 } +sub RG () { 400/pi2 } +sub GR () { pi2/400 } # # Truncating remainder. @@ -58,17 +59,23 @@ sub remt ($$) { # Angle conversions. # -sub rad2deg ($) { remt(RD * $_[0], 360) } +sub rad2rad($) { remt($_[0], pi2) } + +sub deg2deg($) { remt($_[0], 360) } + +sub grad2grad($) { remt($_[0], 400) } -sub deg2rad ($) { remt(DR * $_[0], pi2) } +sub rad2deg ($;$) { my $d = RD * $_[0]; $_[1] ? $d : deg2deg($d) } -sub grad2deg ($) { remt(GD * $_[0], 360) } +sub deg2rad ($;$) { my $d = DR * $_[0]; $_[1] ? $d : rad2rad($d) } -sub deg2grad ($) { remt(DG * $_[0], 400) } +sub grad2deg ($;$) { my $d = GD * $_[0]; $_[1] ? $d : deg2deg($d) } -sub rad2grad ($) { remt(RG * $_[0], 400) } +sub deg2grad ($;$) { my $d = DG * $_[0]; $_[1] ? $d : grad2grad($d) } -sub grad2rad ($) { remt(GR * $_[0], pi2) } +sub rad2grad ($;$) { my $d = RG * $_[0]; $_[1] ? $d : grad2grad($d) } + +sub grad2rad ($;$) { my $d = GR * $_[0]; $_[1] ? $d : rad2rad($d) } sub cartesian_to_spherical { my ( $x, $y, $z ) = @_; @@ -280,6 +287,14 @@ and the imaginary part of approximately C<-1.317>. $gradians = rad2grad($radians); The full circle is 2 I<pi> radians or I<360> degrees or I<400> gradians. +The result is by default wrapped to be inside the [0, {2pi,360,400}[ circle. +If you don't want this, supply a true second argument: + + $zillions_of_radians = deg2rad($zillions_of_degrees, 1); + $negative_degrees = rad2deg($negative_radians, 1); + +You can also do the wrapping explicitly by rad2rad(), deg2deg(), and +grad2grad(). =head1 RADIAL COORDINATE CONVERSIONS |