Change 33115 by [EMAIL PROTECTED] on 2008/01/29 23:30:27

        Integrate:
        [ 32786]
        Upgrade to Math-Complex-1.38
        
        [ 32908]
        Upgrade to Math-Complex-1.42
        
        [ 32914]
        1e4 isn't large enough with 16 byte long doubles (at least on x86_64).
        However, 1e5 does take us "to infinity and beyond"
        (Plus use cmp_ok, for better diagnostics)
        
        [ 32929]
        Upgrade to Math-Complex-1.43
        
        [ 32970]
        Upgrade to Math-Complex-1.44
        
        [ 32989]
        Upgrade to Math-Complex-1.47

Affected files ...

... //depot/maint-5.10/perl/lib/Math/Complex.pm#2 integrate
... //depot/maint-5.10/perl/lib/Math/Complex.t#2 integrate
... //depot/maint-5.10/perl/lib/Math/Trig.pm#2 integrate
... //depot/maint-5.10/perl/lib/Math/Trig.t#2 integrate

Differences ...

==== //depot/maint-5.10/perl/lib/Math/Complex.pm#2 (text) ====
Index: perl/lib/Math/Complex.pm
--- perl/lib/Math/Complex.pm#1~32694~   2007-12-22 01:23:09.000000000 -0800
+++ perl/lib/Math/Complex.pm    2008-01-29 15:30:27.000000000 -0800
@@ -9,26 +9,38 @@
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $Inf);
 
-$VERSION = 1.37;
+$VERSION = 1.47;
 
 BEGIN {
-    unless ($^O eq 'unicosmk') {
+    # For 64-bit doubles, anyway.
+    my $IEEE_DBL_MAX = eval "1.7976931348623157e+308";
+    if ($^O eq 'unicosmk') {
+       $Inf = $IEEE_DBL_MAX;
+    } else {
         local $!;
        # 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
+       for my $t (
+           'exp(999)',
+           '9**9**9',
+           '1e999',
+           'inf',
+           'Inf',
+           'INF',
+           'infinity',
+           'Infinity',
+           'INFINITY',
+           ) {
+           local $SIG{FPE} = { };
+           local $^W = 0;
+           my $i = eval "$t+1.0";
+           if ($i =~ /inf/i && $i > 1e+99) {
+               $Inf = $i;
+               last;
+           }
        }
+       $Inf = $IEEE_DBL_MAX unless defined $Inf;  # Oh well, close enough.
+       die "Could not get Infinity" unless $Inf > 1e99;
     }
-    $Inf = "Inf" if !defined $Inf || !($Inf > 0); # Desperation.
 }
 
 use strict;
@@ -65,7 +77,7 @@
             ),
           @trig);
 
-my @pi = qw(pi pi2 pi4 pip2 pip4);
+my @pi = qw(pi pi2 pi4 pip2 pip4 Inf);
 
 @EXPORT_OK = @pi;
 
@@ -109,8 +121,6 @@
 #      c_dirty         cartesian form not up-to-date
 #      p_dirty         polar form not up-to-date
 #      display         display format (package's global when not set)
-#      bn_cartesian
-#       bnc_dirty
 #
 
 # Die on bad *make() arguments.
@@ -858,7 +868,7 @@
        my $ey = CORE::exp($y);
        my $sx = CORE::sin($x);
        my $cx = CORE::cos($x);
-       my $ey_1 = $ey ? 1 / $ey : $Inf;
+       my $ey_1 = $ey ? 1 / $ey : Inf();
        return (ref $z)->make($cx * ($ey + $ey_1)/2,
                              $sx * ($ey_1 - $ey)/2);
 }
@@ -875,7 +885,7 @@
        my $ey = CORE::exp($y);
        my $sx = CORE::sin($x);
        my $cx = CORE::cos($x);
-       my $ey_1 = $ey ? 1 / $ey : $Inf;
+       my $ey_1 = $ey ? 1 / $ey : Inf();
        return (ref $z)->make($sx * ($ey + $ey_1)/2,
                              $cx * ($ey - $ey_1)/2);
 }
@@ -1069,11 +1079,11 @@
        my $ex;
        unless (ref $z) {
            $ex = CORE::exp($z);
-           return $ex ? ($ex + 1/$ex)/2 : $Inf;
+           return $ex ? ($ex + 1/$ex)/2 : Inf();
        }
        my ($x, $y) = @{$z->_cartesian};
        $ex = CORE::exp($x);
-       my $ex_1 = $ex ? 1 / $ex : $Inf;
+       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);
 }
@@ -1089,13 +1099,13 @@
        unless (ref $z) {
            return 0 if $z == 0;
            $ex = CORE::exp($z);
-           return $ex ? ($ex - 1/$ex)/2 : "-$Inf";
+           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 = $ex ? 1 / $ex : $Inf;
+       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);
 }
@@ -1109,7 +1119,10 @@
        my ($z) = @_;
        my $cz = cosh($z);
        _divbyzero "tanh($z)", "cosh($z)" if ($cz == 0);
-       return sinh($z) / $cz;
+       my $sz = sinh($z);
+       return  1 if $cz ==  $sz;
+       return -1 if $cz == -$sz;
+       return $sz / $cz;
 }
 
 #
@@ -1152,7 +1165,10 @@
        my ($z) = @_;
        my $sz = sinh($z);
        _divbyzero "coth($z)", "sinh($z)" if $sz == 0;
-       return cosh($z) / $sz;
+       my $cz = cosh($z);
+       return  1 if $cz ==  $sz;
+       return -1 if $cz == -$sz;
+       return $cz / $sz;
 }
 
 #
@@ -1488,6 +1504,10 @@
        return "[$r,$theta]";
 }
 
+sub Inf {
+    return $Inf;
+}
+
 1;
 __END__
 
@@ -1755,11 +1775,11 @@
 You can return the I<k>th root directly by C<root(z, n, k)>,
 indexing starting from I<zero> and ending at I<n - 1>.
 
-The I<spaceship> comparison operator, E<lt>=E<gt>, is also defined. In
-order to ensure its restriction to real numbers is conform to what you
-would expect, the comparison is run on the real part of the complex
-number first, and imaginary parts are compared only when the real
-parts match.
+The I<spaceship> numeric comparison operator, E<lt>=E<gt>, is also
+defined. In order to ensure its restriction to real numbers is conform
+to what you would expect, the comparison is run on the real part of
+the complex number first, and imaginary parts are compared only when
+the real parts match.
 
 =head1 CREATION
 
@@ -1907,6 +1927,8 @@
        $j->arg(2);                     # (the last two aka rho, theta)
                                        # can be used also as mutators.
 
+=head1 CONSTANTS
+
 =head2 PI
 
 The constant C<pi> and some handy multiples of it (pi2, pi4,
@@ -1916,6 +1938,32 @@
     use Math::Complex ':pi'; 
     $third_of_circle = pi2 / 3;
 
+=head2 Inf
+
+The floating point infinity can be exported as a subroutine Inf():
+
+    use Math::Complex qw(Inf sinh);
+    my $AlsoInf = Inf() + 42;
+    my $AnotherInf = sinh(1e42);
+    print "$AlsoInf is $AnotherInf\n" if $AlsoInf == $AnotherInf;
+
+Note that the stringified form of infinity varies between platforms:
+it can be for example any of
+
+   inf
+   infinity
+   INF
+   1.#INF
+
+or it can be something else. 
+
+Also note that in some platforms trying to use the infinity in
+arithmetic operations may result in Perl crashing because using
+an infinity causes SIGFPE or its moral equivalent to be sent.
+The way to ignore this is
+
+  local $SIG{FPE} = sub { };
+
 =head1 ERRORS DUE TO DIVISION BY ZERO OR LOGARITHM OF ZERO
 
 The division (/) and the following functions
@@ -1980,12 +2028,21 @@
 The bug may be in UNICOS math libs, in UNICOS C compiler, in Math::Complex.
 Whatever it is, it does not manifest itself anywhere else where Perl runs.
 
+=head1 SEE ALSO
+
+L<Math::Trig>
+
 =head1 AUTHORS
 
 Daniel S. Lewart <F<lewart!at!uiuc.edu>>
 Jarkko Hietaniemi <F<jhi!at!iki.fi>>
 Raphael Manfredi <F<Raphael_Manfredi!at!pobox.com>>
 
+=head1 LICENSE
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself. 
+
 =cut
 
 1;

==== //depot/maint-5.10/perl/lib/Math/Complex.t#2 (xtext) ====
Index: perl/lib/Math/Complex.t
--- perl/lib/Math/Complex.t#1~32694~    2007-12-22 01:23:09.000000000 -0800
+++ perl/lib/Math/Complex.t     2008-01-29 15:30:27.000000000 -0800
@@ -13,7 +13,7 @@
     }
 }
 
-use Math::Complex;
+use Math::Complex 1.47;
 
 use vars qw($VERSION);
 

==== //depot/maint-5.10/perl/lib/Math/Trig.pm#2 (text) ====
Index: perl/lib/Math/Trig.pm
--- perl/lib/Math/Trig.pm#1~32694~      2007-12-22 01:23:09.000000000 -0800
+++ perl/lib/Math/Trig.pm       2008-01-29 15:30:27.000000000 -0800
@@ -10,21 +10,23 @@
 use 5.005;
 use strict;
 
-use Math::Complex 1.36;
+use Math::Complex 1.47;
 use Math::Complex qw(:trig :pi);
 
 use vars qw($VERSION $PACKAGE @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
 @ISA = qw(Exporter);
 
-$VERSION = 1.04;
+$VERSION = 1.12;
 
 my @angcnv = qw(rad2deg rad2grad
                deg2rad deg2grad
                grad2rad grad2deg);
 
+my @areal = qw(asin_real acos_real);
+
 @EXPORT = (@{$Math::Complex::EXPORT_TAGS{'trig'}},
-          @angcnv);
+          @angcnv, @areal);
 
 my @rdlcnv = qw(cartesian_to_cylindrical
                cartesian_to_spherical
@@ -44,7 +46,7 @@
 
 my @pi = qw(pi pi2 pi4 pip2 pip4);
 
[EMAIL PROTECTED] = (@rdlcnv, @greatcircle, @pi);
[EMAIL PROTECTED] = (@rdlcnv, @greatcircle, @pi, 'Inf');
 
 # See e.g. the following pages:
 # http://www.movable-type.co.uk/scripts/LatLong.html
@@ -92,6 +94,22 @@
 
 sub grad2rad ($;$) { my $d = _GR * $_[0]; $_[1] ? $d : rad2rad($d) }
 
+#
+# acos and asin functions which always return a real number
+#
+
+sub acos_real {
+    return 0  if $_[0] >=  1;
+    return pi if $_[0] <= -1;
+    return acos($_[0]);
+}
+
+sub asin_real {
+    return  &pip2 if $_[0] >=  1;
+    return -&pip2 if $_[0] <= -1;
+    return asin($_[0]);
+}
+
 sub cartesian_to_spherical {
     my ( $x, $y, $z ) = @_;
 
@@ -99,7 +117,7 @@
 
     return ( $rho,
              atan2( $y, $x ),
-             $rho ? acos( $z / $rho ) : 0 );
+             $rho ? acos_real( $z / $rho ) : 0 );
 }
 
 sub spherical_to_cartesian {
@@ -141,8 +159,8 @@
     my $lat1 = pip2 - $phi1;
 
     return $rho *
-        acos(cos( $lat0 ) * cos( $lat1 ) * cos( $theta0 - $theta1 ) +
-             sin( $lat0 ) * sin( $lat1 ) );
+       acos_real( cos( $lat0 ) * cos( $lat1 ) * cos( $theta0 - $theta1 ) +
+                  sin( $lat0 ) * sin( $lat1 ) );
 }
 
 sub great_circle_direction {
@@ -154,9 +172,9 @@
     my $lat1 = pip2 - $phi1;
 
     my $direction =
-       acos((sin($lat1) - sin($lat0) * cos($distance)) /
-            (cos($lat0) * sin($distance)));
-
+       acos_real((sin($lat1) - sin($lat0) * cos($distance)) /
+                 (cos($lat0) * sin($distance)));
+  
     $direction = pi2 - $direction
        if sin($theta1 - $theta0) < 0;
 
@@ -189,7 +207,7 @@
     my $z = $A * sin($lat0)                + $B * sin($lat1);
 
     my $theta = atan2($y, $x);
-    my $phi   = acos($z);
+    my $phi   = acos_real($z);
     
     return ($theta, $phi);
 }
@@ -203,7 +221,8 @@
 
     my $lat0 = pip2 - $phi0;
 
-    my $phi1   = asin(sin($lat0)*cos($dst)+cos($lat0)*sin($dst)*cos($dir0));
+    my $phi1   = asin_real(sin($lat0)*cos($dst) + 
+                          cos($lat0)*sin($dst)*cos($dir0));
     my $theta1 = $theta0 + atan2(sin($dir0)*sin($dst)*cos($lat0),
                                 cos($dst)-sin($lat0)*sin($phi1));
 
@@ -538,7 +557,7 @@
 defaults to radians.
 
 If you think geographically the I<theta> are longitudes: zero at the
-Greenwhich meridian, eastward positive, westward negative--and the
+Greenwhich meridian, eastward positive, westward negative -- and the
 I<phi> are latitudes: zero at the North Pole, northward positive,
 southward negative.  B<NOTE>: this formula thinks in mathematics, not
 geographically: the I<phi> zero is at the North Pole, not at the
@@ -617,9 +636,11 @@
 
 Notice that the resulting directions might be somewhat surprising if
 you are looking at a flat worldmap: in such map projections the great
-circles quite often do not look like the shortest routes-- but for
+circles quite often do not look like the shortest routes --  but for
 example the shortest possible routes from Europe or North America to
-Asia do often cross the polar regions.
+Asia do often cross the polar regions.  (The common Mercator projection
+does B<not> show great circles as straight lines: straight lines in the
+Mercator projection are lines of constant bearing.)
 
 =head1 EXAMPLES
 
@@ -647,7 +668,7 @@
 
     my @M = great_circle_midpoint(@L, @T);
 
-or about 89.16N 68.93E, practically at the North Pole.
+or about 68.93N 89.16E, in the frozen wastes of Siberia.
 
 =head2 CAVEAT FOR GREAT CIRCLE FORMULAS
 
@@ -655,6 +676,51 @@
 (slightly aspherical) form of the Earth.  The errors are at worst
 about 0.55%, but generally below 0.3%.
 
+=head2 Real-valued asin and acos
+
+For small inputs asin() and acos() may return complex numbers even
+when real numbers would be enough and correct, this happens because of
+floating-point inaccuracies.  You can see these inaccuracies for
+example by trying theses:
+
+  print cos(1e-6)**2+sin(1e-6)**2 - 1,"\n";
+  printf "%.20f", cos(1e-6)**2+sin(1e-6)**2,"\n";
+
+which will print something like this
+
+  -1.11022302462516e-16
+  0.99999999999999988898
+
+even though the expected results are of course exactly zero and one.
+The formulas used to compute asin() and acos() are quite sensitive to
+this, and therefore they might accidentally slip into the complex
+plane even when they should not.  To counter this there are two
+interfaces that are guaranteed to return a real-valued output.
+
+=over 4
+
+=item asin_real
+
+    use Math::Trig qw(asin_real);
+
+    $real_angle = asin_real($input_sin);
+
+Return a real-valued arcus sine if the input is between [-1, 1],
+B<inclusive> the endpoints.  For inputs greater than one, pi/2
+is returned.  For inputs less than minus one, -pi/2 is returned.
+
+=item acos_real
+
+    use Math::Trig qw(acos_real);
+
+    $real_angle = acos_real($input_cos);
+
+Return a real-valued arcus cosine if the input is between [-1, 1],
+B<inclusive> the endpoints.  For inputs greater than one, zero
+is returned.  For inputs less than minus one, pi is returned.
+
+=back
+
 =head1 BUGS
 
 Saying C<use Math::Trig;> exports many mathematical routines in the
@@ -669,11 +735,18 @@
 
 Do not attempt navigation using these formulas.
 
+L<Math::Complex>
+
 =head1 AUTHORS
 
 Jarkko Hietaniemi <F<jhi!at!iki.fi>> and 
 Raphael Manfredi <F<Raphael_Manfredi!at!pobox.com>>.
 
+=head1 LICENSE
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself. 
+
 =cut
 
 # eof

==== //depot/maint-5.10/perl/lib/Math/Trig.t#2 (xtext) ====
Index: perl/lib/Math/Trig.t
--- perl/lib/Math/Trig.t#1~32694~       2007-12-22 01:23:09.000000000 -0800
+++ perl/lib/Math/Trig.t        2008-01-29 15:30:27.000000000 -0800
@@ -26,9 +26,10 @@
     }
 }
 
-plan(tests => 69);
+plan(tests => 153);
 
-use Math::Trig 1.03;
+use Math::Trig 1.12;
+use Math::Trig 1.12 qw(:pi Inf);
 
 my $pip2 = pi / 2;
 
@@ -49,6 +50,42 @@
     $_[1] ? ($d < $e) : abs($_[0]) < $e;
 }
 
+print "# Sanity checks\n";
+
+ok(near(sin(1), 0.841470984807897));
+ok(near(cos(1), 0.54030230586814));
+ok(near(tan(1), 1.5574077246549));
+
+ok(near(sec(1), 1.85081571768093));
+ok(near(csc(1), 1.18839510577812));
+ok(near(cot(1), 0.642092615934331));
+
+ok(near(asin(1), 1.5707963267949));
+ok(near(acos(1), 0));
+ok(near(atan(1), 0.785398163397448));
+
+ok(near(asec(1), 0));
+ok(near(acsc(1), 1.5707963267949));
+ok(near(acot(1), 0.785398163397448));
+
+ok(near(sinh(1), 1.1752011936438));
+ok(near(cosh(1), 1.54308063481524));
+ok(near(tanh(1), 0.761594155955765));
+
+ok(near(sech(1), 0.648054273663885));
+ok(near(csch(1), 0.850918128239322));
+ok(near(coth(1), 1.31303528549933));
+
+ok(near(asinh(1), 0.881373587019543));
+ok(near(acosh(1), 0));
+ok(near(atanh(0.9), 1.47221948958322)); # atanh(1.0) would be an error.
+
+ok(near(asech(0.9), 0.467145308103262));
+ok(near(acsch(2), 0.481211825059603));
+ok(near(acoth(2), 0.549306144334055));
+
+print "# Basics\n";
+
 $x = 0.9;
 ok(near(tan($x), sin($x) / cos($x)));
 
@@ -145,8 +182,8 @@
     ok(near(great_circle_distance(0, 0, pi, pi), pi));
 
     # London to Tokyo.
-    my @L = (deg2rad(-0.5), deg2rad(90 - 51.3));
-    my @T = (deg2rad(139.8),deg2rad(90 - 35.7));
+    my @L = (deg2rad(-0.5),  deg2rad(90 - 51.3));
+    my @T = (deg2rad(139.8), deg2rad(90 - 35.7));
 
     my $km = great_circle_distance(@L, @T, 6378);
 
@@ -270,4 +307,86 @@
     ok(near($dst1, $dst2));
 }
 
+print "# Infinity\n";
+
+my $BigDouble = 1e40;
+
+local $SIG{FPE} = { };  # E.g. netbsd-alpha core dumps on Inf arith
+
+ok(Inf() > $BigDouble);  # This passes in netbsd-alpha.
+ok(Inf() + $BigDouble > $BigDouble); # This coredumps.
+ok(Inf() + $BigDouble == Inf());
+ok(Inf() - $BigDouble > $BigDouble);
+ok(Inf() - $BigDouble == Inf());
+ok(Inf() * $BigDouble > $BigDouble);
+ok(Inf() * $BigDouble == Inf());
+ok(Inf() / $BigDouble > $BigDouble);
+ok(Inf() / $BigDouble == Inf());
+
+ok(-Inf() < -$BigDouble);
+ok(-Inf() + $BigDouble < $BigDouble);
+ok(-Inf() + $BigDouble == -Inf());
+ok(-Inf() - $BigDouble < -$BigDouble);
+ok(-Inf() - $BigDouble == -Inf());
+ok(-Inf() * $BigDouble < -$BigDouble);
+ok(-Inf() * $BigDouble == -Inf());
+ok(-Inf() / $BigDouble < -$BigDouble);
+ok(-Inf() / $BigDouble == -Inf());
+
+print "# sinh/sech/cosh/csch/tanh/coth unto infinity\n";
+
+ok(near(sinh(100), 1.3441e+43, 1e-3));
+ok(near(sech(100), 7.4402e-44, 1e-3));
+ok(near(cosh(100), 1.3441e+43, 1e-3));
+ok(near(csch(100), 7.4402e-44, 1e-3));
+ok(near(tanh(100), 1));
+ok(near(coth(100), 1));
+
+ok(near(sinh(-100), -1.3441e+43, 1e-3));
+ok(near(sech(-100),  7.4402e-44, 1e-3));
+ok(near(cosh(-100),  1.3441e+43, 1e-3));
+ok(near(csch(-100), -7.4402e-44, 1e-3));
+ok(near(tanh(-100), -1));
+ok(near(coth(-100), -1));
+
+cmp_ok(sinh(1e5), '==', Inf());
+cmp_ok(sech(1e5), '==', 0);
+cmp_ok(cosh(1e5), '==', Inf());
+cmp_ok(csch(1e5), '==', 0);
+cmp_ok(tanh(1e5), '==', 1);
+cmp_ok(coth(1e5), '==', 1);
+
+cmp_ok(sinh(-1e5), '==', -Inf());
+cmp_ok(sech(-1e5), '==', 0);
+cmp_ok(cosh(-1e5), '==', Inf());
+cmp_ok(csch(-1e5), '==', 0);
+cmp_ok(tanh(-1e5), '==', -1);
+cmp_ok(coth(-1e5), '==', -1);
+
+print "# great_circle_distance with small angles\n";
+
+for my $e (qw(1e-2 1e-3 1e-4 1e-5)) {
+    # Can't assume == 0 because of floating point fuzz,
+    # but let's hope for at least < $e.
+    cmp_ok(great_circle_distance(0, $e, 0, $e), '<', $e);
+}
+
+print "# asin_real, acos_real\n";
+
+is(acos_real(-2.0), pi);
+is(acos_real(-1.0), pi);
+is(acos_real(-0.5), acos(-0.5));
+is(acos_real( 0.0), acos( 0.0));
+is(acos_real( 0.5), acos( 0.5));
+is(acos_real( 1.0), 0);
+is(acos_real( 2.0), 0);
+
+is(asin_real(-2.0), -&pip2);
+is(asin_real(-1.0), -&pip2);
+is(asin_real(-0.5), asin(-0.5));
+is(asin_real( 0.0), asin( 0.0));
+is(asin_real( 0.5), asin( 0.5));
+is(asin_real( 1.0),  pip2);
+is(asin_real( 2.0),  pip2);
+
 # eof
End of Patch.

Reply via email to