Mention of possible use of a scalar variable on the rhs of an arrow "->"
[p5sagit/p5-mst-13.2.git] / lib / Math / Trig.pm
CommitLineData
5aabfad6 1#
2# Trigonometric functions, mostly inherited from Math::Complex.
d54bf66f 3# -- Jarkko Hietaniemi, since April 1997
5cd24f17 4# -- Raphael Manfredi, September 1996 (indirectly: because of Math::Complex)
5aabfad6 5#
6
7require Exporter;
8package Math::Trig;
9
3b825e41 10use 5.006;
5aabfad6 11use strict;
12
bf5f1b4c 13use Math::Complex 1.35;
5aabfad6 14use Math::Complex qw(:trig);
15
17f410f9 16our($VERSION, $PACKAGE, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
5aabfad6 17
18@ISA = qw(Exporter);
19
bf5f1b4c 20$VERSION = 1.03;
5aabfad6 21
ace5de91 22my @angcnv = qw(rad2deg rad2grad
d139edd6 23 deg2rad deg2grad
24 grad2rad grad2deg);
5aabfad6 25
26@EXPORT = (@{$Math::Complex::EXPORT_TAGS{'trig'}},
27 @angcnv);
28
d54bf66f 29my @rdlcnv = qw(cartesian_to_cylindrical
30 cartesian_to_spherical
31 cylindrical_to_cartesian
32 cylindrical_to_spherical
33 spherical_to_cartesian
34 spherical_to_cylindrical);
35
bf5f1b4c 36my @greatcircle = qw(
37 great_circle_distance
38 great_circle_direction
39 great_circle_bearing
40 great_circle_waypoint
41 great_circle_midpoint
42 great_circle_destination
43 );
d54bf66f 44
bf5f1b4c 45my @pi = qw(pi2 pip2 pip4);
46
47@EXPORT_OK = (@rdlcnv, @greatcircle, @pi);
48
49# See e.g. the following pages:
50# http://www.movable-type.co.uk/scripts/LatLong.html
51# http://williams.best.vwh.net/avform.htm
52
53%EXPORT_TAGS = ('radial' => [ @rdlcnv ],
54 'great_circle' => [ @greatcircle ],
55 'pi' => [ @pi ]);
d54bf66f 56
9db5a202 57sub pi2 () { 2 * pi }
58sub pip2 () { pi / 2 }
bf5f1b4c 59sub pip4 () { pi / 4 }
9db5a202 60
61sub DR () { pi2/360 }
62sub RD () { 360/pi2 }
63sub DG () { 400/360 }
64sub GD () { 360/400 }
65sub RG () { 400/pi2 }
66sub GR () { pi2/400 }
5aabfad6 67
68#
69# Truncating remainder.
70#
71
72sub remt ($$) {
73 # Oh yes, POSIX::fmod() would be faster. Possibly. If it is available.
74 $_[0] - $_[1] * int($_[0] / $_[1]);
75}
76
77#
78# Angle conversions.
79#
80
9db5a202 81sub rad2rad($) { remt($_[0], pi2) }
82
83sub deg2deg($) { remt($_[0], 360) }
84
85sub grad2grad($) { remt($_[0], 400) }
5aabfad6 86
9db5a202 87sub rad2deg ($;$) { my $d = RD * $_[0]; $_[1] ? $d : deg2deg($d) }
5aabfad6 88
9db5a202 89sub deg2rad ($;$) { my $d = DR * $_[0]; $_[1] ? $d : rad2rad($d) }
5aabfad6 90
9db5a202 91sub grad2deg ($;$) { my $d = GD * $_[0]; $_[1] ? $d : deg2deg($d) }
5aabfad6 92
9db5a202 93sub deg2grad ($;$) { my $d = DG * $_[0]; $_[1] ? $d : grad2grad($d) }
5aabfad6 94
9db5a202 95sub rad2grad ($;$) { my $d = RG * $_[0]; $_[1] ? $d : grad2grad($d) }
96
97sub grad2rad ($;$) { my $d = GR * $_[0]; $_[1] ? $d : rad2rad($d) }
5aabfad6 98
d54bf66f 99sub cartesian_to_spherical {
100 my ( $x, $y, $z ) = @_;
101
102 my $rho = sqrt( $x * $x + $y * $y + $z * $z );
103
104 return ( $rho,
105 atan2( $y, $x ),
106 $rho ? acos( $z / $rho ) : 0 );
107}
108
109sub spherical_to_cartesian {
110 my ( $rho, $theta, $phi ) = @_;
111
112 return ( $rho * cos( $theta ) * sin( $phi ),
113 $rho * sin( $theta ) * sin( $phi ),
114 $rho * cos( $phi ) );
115}
116
117sub spherical_to_cylindrical {
118 my ( $x, $y, $z ) = spherical_to_cartesian( @_ );
119
120 return ( sqrt( $x * $x + $y * $y ), $_[1], $z );
121}
122
123sub cartesian_to_cylindrical {
124 my ( $x, $y, $z ) = @_;
125
126 return ( sqrt( $x * $x + $y * $y ), atan2( $y, $x ), $z );
127}
128
129sub cylindrical_to_cartesian {
130 my ( $rho, $theta, $z ) = @_;
131
132 return ( $rho * cos( $theta ), $rho * sin( $theta ), $z );
133}
134
135sub cylindrical_to_spherical {
136 return ( cartesian_to_spherical( cylindrical_to_cartesian( @_ ) ) );
137}
138
139sub great_circle_distance {
140 my ( $theta0, $phi0, $theta1, $phi1, $rho ) = @_;
141
142 $rho = 1 unless defined $rho; # Default to the unit sphere.
143
144 my $lat0 = pip2 - $phi0;
145 my $lat1 = pip2 - $phi1;
146
147 return $rho *
148 acos(cos( $lat0 ) * cos( $lat1 ) * cos( $theta0 - $theta1 ) +
149 sin( $lat0 ) * sin( $lat1 ) );
150}
151
7e5f197a 152sub great_circle_direction {
153 my ( $theta0, $phi0, $theta1, $phi1 ) = @_;
154
d139edd6 155 my $distance = &great_circle_distance;
156
7e5f197a 157 my $lat0 = pip2 - $phi0;
158 my $lat1 = pip2 - $phi1;
159
160 my $direction =
d139edd6 161 acos((sin($lat1) - sin($lat0) * cos($distance)) /
162 (cos($lat0) * sin($distance)));
163
164 $direction = pi2 - $direction
165 if sin($theta1 - $theta0) < 0;
7e5f197a 166
167 return rad2rad($direction);
168}
169
bf5f1b4c 170*great_circle_bearing = \&great_circle_direction;
171
172sub great_circle_waypoint {
173 my ( $theta0, $phi0, $theta1, $phi1, $point ) = @_;
174
175 $point = 0.5 unless defined $point;
176
177 my $d = great_circle_distance( $theta0, $phi0, $theta1, $phi1 );
178
179 return undef if $d == pi;
180
181 my $sd = sin($d);
182
183 return ($theta0, $phi0) if $sd == 0;
184
185 my $A = sin((1 - $point) * $d) / $sd;
186 my $B = sin( $point * $d) / $sd;
187
188 my $lat0 = pip2 - $phi0;
189 my $lat1 = pip2 - $phi1;
190
191 my $x = $A * cos($lat0) * cos($theta0) + $B * cos($lat1) * cos($theta1);
192 my $y = $A * cos($lat0) * sin($theta0) + $B * cos($lat1) * sin($theta1);
193 my $z = $A * sin($lat0) + $B * sin($lat1);
194
195 my $theta = atan2($y, $x);
196 my $phi = atan2($z, sqrt($x*$x + $y*$y));
197
198 return ($theta, $phi);
199}
200
201sub great_circle_midpoint {
202 great_circle_waypoint(@_[0..3], 0.5);
203}
204
205sub great_circle_destination {
206 my ( $theta0, $phi0, $dir0, $dst ) = @_;
207
208 my $lat0 = pip2 - $phi0;
209
210 my $phi1 = asin(sin($lat0)*cos($dst)+cos($lat0)*sin($dst)*cos($dir0));
211 my $theta1 = $theta0 + atan2(sin($dir0)*sin($dst)*cos($lat0),
212 cos($dst)-sin($lat0)*sin($phi1));
213
214 my $dir1 = great_circle_bearing($theta1, $phi1, $theta0, $phi0) + pi;
215
216 $dir1 -= pi2 if $dir1 > pi2;
217
218 return ($theta1, $phi1, $dir1);
219}
220
ea0630ea 2211;
222
223__END__
d54bf66f 224=pod
225
5aabfad6 226=head1 NAME
227
228Math::Trig - trigonometric functions
229
230=head1 SYNOPSIS
231
232 use Math::Trig;
3cb6de81 233
5aabfad6 234 $x = tan(0.9);
235 $y = acos(3.7);
236 $z = asin(2.4);
3cb6de81 237
5aabfad6 238 $halfpi = pi/2;
239
ace5de91 240 $rad = deg2rad(120);
5aabfad6 241
bf5f1b4c 242 # Import constants pi2, pip2, pip4 (2*pi, pi/2, pi/4).
243 use Math::Trig ':pi';
244
245 # Import the conversions between cartesian/spherical/cylindrical.
246 use Math::Trig ':radial';
247
248 # Import the great circle formulas.
249 use Math::Trig ':great_circle';
250
5aabfad6 251=head1 DESCRIPTION
252
253C<Math::Trig> defines many trigonometric functions not defined by the
4ae80833 254core Perl which defines only the C<sin()> and C<cos()>. The constant
5aabfad6 255B<pi> is also defined as are a few convenience functions for angle
bf5f1b4c 256conversions, and I<great circle formulas> for spherical movement.
5aabfad6 257
258=head1 TRIGONOMETRIC FUNCTIONS
259
260The tangent
261
d54bf66f 262=over 4
263
264=item B<tan>
265
266=back
5aabfad6 267
268The cofunctions of the sine, cosine, and tangent (cosec/csc and cotan/cot
269are aliases)
270
d54bf66f 271B<csc>, B<cosec>, B<sec>, B<sec>, B<cot>, B<cotan>
5aabfad6 272
273The arcus (also known as the inverse) functions of the sine, cosine,
274and tangent
275
d54bf66f 276B<asin>, B<acos>, B<atan>
5aabfad6 277
278The principal value of the arc tangent of y/x
279
d54bf66f 280B<atan2>(y, x)
5aabfad6 281
282The arcus cofunctions of the sine, cosine, and tangent (acosec/acsc
283and acotan/acot are aliases)
284
d54bf66f 285B<acsc>, B<acosec>, B<asec>, B<acot>, B<acotan>
5aabfad6 286
287The hyperbolic sine, cosine, and tangent
288
d54bf66f 289B<sinh>, B<cosh>, B<tanh>
5aabfad6 290
291The cofunctions of the hyperbolic sine, cosine, and tangent (cosech/csch
292and cotanh/coth are aliases)
293
d54bf66f 294B<csch>, B<cosech>, B<sech>, B<coth>, B<cotanh>
5aabfad6 295
296The arcus (also known as the inverse) functions of the hyperbolic
297sine, cosine, and tangent
298
d54bf66f 299B<asinh>, B<acosh>, B<atanh>
5aabfad6 300
301The arcus cofunctions of the hyperbolic sine, cosine, and tangent
302(acsch/acosech and acoth/acotanh are aliases)
303
d54bf66f 304B<acsch>, B<acosech>, B<asech>, B<acoth>, B<acotanh>
5aabfad6 305
306The trigonometric constant B<pi> is also defined.
307
d54bf66f 308$pi2 = 2 * B<pi>;
5aabfad6 309
5cd24f17 310=head2 ERRORS DUE TO DIVISION BY ZERO
311
312The following functions
313
d54bf66f 314 acoth
5cd24f17 315 acsc
5cd24f17 316 acsch
d54bf66f 317 asec
318 asech
319 atanh
320 cot
321 coth
322 csc
323 csch
324 sec
325 sech
326 tan
327 tanh
5cd24f17 328
329cannot be computed for all arguments because that would mean dividing
8c03c583 330by zero or taking logarithm of zero. These situations cause fatal
331runtime errors looking like this
5cd24f17 332
333 cot(0): Division by zero.
334 (Because in the definition of cot(0), the divisor sin(0) is 0)
335 Died at ...
336
8c03c583 337or
338
339 atanh(-1): Logarithm of zero.
340 Died at...
341
342For the C<csc>, C<cot>, C<asec>, C<acsc>, C<acot>, C<csch>, C<coth>,
343C<asech>, C<acsch>, the argument cannot be C<0> (zero). For the
344C<atanh>, C<acoth>, the argument cannot be C<1> (one). For the
345C<atanh>, C<acoth>, the argument cannot be C<-1> (minus one). For the
346C<tan>, C<sec>, C<tanh>, C<sech>, the argument cannot be I<pi/2 + k *
bf5f1b4c 347pi>, where I<k> is any integer. atan2(0, 0) is undefined.
5cd24f17 348
349=head2 SIMPLE (REAL) ARGUMENTS, COMPLEX RESULTS
5aabfad6 350
351Please note that some of the trigonometric functions can break out
352from the B<real axis> into the B<complex plane>. For example
353C<asin(2)> has no definition for plain real numbers but it has
354definition for complex numbers.
355
356In Perl terms this means that supplying the usual Perl numbers (also
357known as scalars, please see L<perldata>) as input for the
358trigonometric functions might produce as output results that no more
359are simple real numbers: instead they are complex numbers.
360
361The C<Math::Trig> handles this by using the C<Math::Complex> package
362which knows how to handle complex numbers, please see L<Math::Complex>
363for more information. In practice you need not to worry about getting
364complex numbers as results because the C<Math::Complex> takes care of
365details like for example how to display complex numbers. For example:
366
367 print asin(2), "\n";
3cb6de81 368
5aabfad6 369should produce something like this (take or leave few last decimals):
370
371 1.5707963267949-1.31695789692482i
372
5cd24f17 373That is, a complex number with the real part of approximately C<1.571>
374and the imaginary part of approximately C<-1.317>.
5aabfad6 375
d54bf66f 376=head1 PLANE ANGLE CONVERSIONS
5aabfad6 377
378(Plane, 2-dimensional) angles may be converted with the following functions.
379
ace5de91 380 $radians = deg2rad($degrees);
381 $radians = grad2rad($gradians);
3cb6de81 382
ace5de91 383 $degrees = rad2deg($radians);
384 $degrees = grad2deg($gradians);
3cb6de81 385
ace5de91 386 $gradians = deg2grad($degrees);
387 $gradians = rad2grad($radians);
5aabfad6 388
5cd24f17 389The full circle is 2 I<pi> radians or I<360> degrees or I<400> gradians.
9db5a202 390The result is by default wrapped to be inside the [0, {2pi,360,400}[ circle.
391If you don't want this, supply a true second argument:
392
393 $zillions_of_radians = deg2rad($zillions_of_degrees, 1);
394 $negative_degrees = rad2deg($negative_radians, 1);
395
396You can also do the wrapping explicitly by rad2rad(), deg2deg(), and
397grad2grad().
5aabfad6 398
d54bf66f 399=head1 RADIAL COORDINATE CONVERSIONS
400
401B<Radial coordinate systems> are the B<spherical> and the B<cylindrical>
402systems, explained shortly in more detail.
403
404You can import radial coordinate conversion functions by using the
405C<:radial> tag:
406
407 use Math::Trig ':radial';
408
409 ($rho, $theta, $z) = cartesian_to_cylindrical($x, $y, $z);
410 ($rho, $theta, $phi) = cartesian_to_spherical($x, $y, $z);
411 ($x, $y, $z) = cylindrical_to_cartesian($rho, $theta, $z);
412 ($rho_s, $theta, $phi) = cylindrical_to_spherical($rho_c, $theta, $z);
413 ($x, $y, $z) = spherical_to_cartesian($rho, $theta, $phi);
414 ($rho_c, $theta, $z) = spherical_to_cylindrical($rho_s, $theta, $phi);
415
416B<All angles are in radians>.
417
418=head2 COORDINATE SYSTEMS
419
bf5f1b4c 420B<Cartesian> coordinates are the usual rectangular I<(x, y, z)>-coordinates.
d54bf66f 421
422Spherical coordinates, I<(rho, theta, pi)>, are three-dimensional
423coordinates which define a point in three-dimensional space. They are
424based on a sphere surface. The radius of the sphere is B<rho>, also
425known as the I<radial> coordinate. The angle in the I<xy>-plane
426(around the I<z>-axis) is B<theta>, also known as the I<azimuthal>
427coordinate. The angle from the I<z>-axis is B<phi>, also known as the
428I<polar> coordinate. The `North Pole' is therefore I<0, 0, rho>, and
429the `Bay of Guinea' (think of the missing big chunk of Africa) I<0,
4b0d1da8 430pi/2, rho>. In geographical terms I<phi> is latitude (northward
431positive, southward negative) and I<theta> is longitude (eastward
432positive, westward negative).
d54bf66f 433
4b0d1da8 434B<BEWARE>: some texts define I<theta> and I<phi> the other way round,
d54bf66f 435some texts define the I<phi> to start from the horizontal plane, some
436texts use I<r> in place of I<rho>.
437
438Cylindrical coordinates, I<(rho, theta, z)>, are three-dimensional
439coordinates which define a point in three-dimensional space. They are
440based on a cylinder surface. The radius of the cylinder is B<rho>,
441also known as the I<radial> coordinate. The angle in the I<xy>-plane
442(around the I<z>-axis) is B<theta>, also known as the I<azimuthal>
443coordinate. The third coordinate is the I<z>, pointing up from the
444B<theta>-plane.
445
446=head2 3-D ANGLE CONVERSIONS
447
448Conversions to and from spherical and cylindrical coordinates are
449available. Please notice that the conversions are not necessarily
450reversible because of the equalities like I<pi> angles being equal to
451I<-pi> angles.
452
453=over 4
454
455=item cartesian_to_cylindrical
456
457 ($rho, $theta, $z) = cartesian_to_cylindrical($x, $y, $z);
458
459=item cartesian_to_spherical
460
461 ($rho, $theta, $phi) = cartesian_to_spherical($x, $y, $z);
462
463=item cylindrical_to_cartesian
464
465 ($x, $y, $z) = cylindrical_to_cartesian($rho, $theta, $z);
466
467=item cylindrical_to_spherical
468
469 ($rho_s, $theta, $phi) = cylindrical_to_spherical($rho_c, $theta, $z);
470
471Notice that when C<$z> is not 0 C<$rho_s> is not equal to C<$rho_c>.
472
473=item spherical_to_cartesian
474
475 ($x, $y, $z) = spherical_to_cartesian($rho, $theta, $phi);
476
477=item spherical_to_cylindrical
478
479 ($rho_c, $theta, $z) = spherical_to_cylindrical($rho_s, $theta, $phi);
480
481Notice that when C<$z> is not 0 C<$rho_c> is not equal to C<$rho_s>.
482
483=back
484
7e5f197a 485=head1 GREAT CIRCLE DISTANCES AND DIRECTIONS
d54bf66f 486
487You can compute spherical distances, called B<great circle distances>,
7e5f197a 488by importing the great_circle_distance() function:
d54bf66f 489
7e5f197a 490 use Math::Trig 'great_circle_distance';
d54bf66f 491
4b0d1da8 492 $distance = great_circle_distance($theta0, $phi0, $theta1, $phi1, [, $rho]);
d54bf66f 493
494The I<great circle distance> is the shortest distance between two
495points on a sphere. The distance is in C<$rho> units. The C<$rho> is
496optional, it defaults to 1 (the unit sphere), therefore the distance
497defaults to radians.
498
4b0d1da8 499If you think geographically the I<theta> are longitudes: zero at the
500Greenwhich meridian, eastward positive, westward negative--and the
2d06e7d7 501I<phi> are latitudes: zero at the North Pole, northward positive,
4b0d1da8 502southward negative. B<NOTE>: this formula thinks in mathematics, not
2d06e7d7 503geographically: the I<phi> zero is at the North Pole, not at the
504Equator on the west coast of Africa (Bay of Guinea). You need to
505subtract your geographical coordinates from I<pi/2> (also known as 90
506degrees).
4b0d1da8 507
508 $distance = great_circle_distance($lon0, pi/2 - $lat0,
509 $lon1, pi/2 - $lat1, $rho);
510
bf5f1b4c 511The direction you must follow the great circle (also known as I<bearing>)
512can be computed by the great_circle_direction() function:
7e5f197a 513
514 use Math::Trig 'great_circle_direction';
515
516 $direction = great_circle_direction($theta0, $phi0, $theta1, $phi1);
517
bf5f1b4c 518(Alias 'great_circle_bearing' is also available.)
7e5f197a 519The result is in radians, zero indicating straight north, pi or -pi
520straight south, pi/2 straight west, and -pi/2 straight east.
521
bf5f1b4c 522You can inversely compute the destination if you know the
523starting point, direction, and distance:
524
525 use Math::Trig 'great_circle_destination';
526
527 # thetad and phid are the destination coordinates,
528 # dird is the final direction at the destination.
529
530 ($thetad, $phid, $dird) =
531 great_circle_destination($theta, $phi, $direction, $distance);
532
533or the midpoint if you know the end points:
534
535 use Math::Trig 'great_circle_midpoint';
536
537 ($thetam, $phim) =
538 great_circle_midpoint($theta0, $phi0, $theta1, $phi1);
539
540The great_circle_midpoint() is just a special case of
541
542 use Math::Trig 'great_circle_waypoint';
543
544 ($thetai, $phii) =
545 great_circle_waypoint($theta0, $phi0, $theta1, $phi1, $way);
546
547Where the $way is a value from zero ($theta0, $phi0) to one ($theta1,
548$phi1). Note that antipodal points (where their distance is I<pi>
549radians) do not have waypoints between them (they would have an an
550"equator" between them), and therefore C<undef> is returned for
551antipodal points. If the points are the same and the distance
552therefore zero and all waypoints therefore identical, the first point
553(either point) is returned.
554
555The thetas, phis, direction, and distance in the above are all in radians.
556
557You can import all the great circle formulas by
558
559 use Math::Trig ':great_circle';
560
7e5f197a 561Notice that the resulting directions might be somewhat surprising if
562you are looking at a flat worldmap: in such map projections the great
563circles quite often do not look like the shortest routes-- but for
564example the shortest possible routes from Europe or North America to
565Asia do often cross the polar regions.
566
51301382 567=head1 EXAMPLES
d54bf66f 568
7e5f197a 569To calculate the distance between London (51.3N 0.5W) and Tokyo
570(35.7N 139.8E) in kilometers:
d54bf66f 571
572 use Math::Trig qw(great_circle_distance deg2rad);
573
574 # Notice the 90 - latitude: phi zero is at the North Pole.
bf5f1b4c 575 sub NESW { deg2rad($_[0]), deg2rad(90 - $_[1]) }
576 my @L = NESW( -0.5, 51.3);
577 my @T = NESW(139.8, 35.7);
578 my $km = great_circle_distance(@L, @T, 6378); # About 9600 km.
d54bf66f 579
bf5f1b4c 580The direction you would have to go from London to Tokyo (in radians,
581straight north being zero, straight east being pi/2).
7e5f197a 582
583 use Math::Trig qw(great_circle_direction);
584
bf5f1b4c 585 my $rad = great_circle_direction(@L, @T); # About 0.547 or 0.174 pi.
7e5f197a 586
bf5f1b4c 587The midpoint between London and Tokyo being
7e5f197a 588
bf5f1b4c 589 use Math::Trig qw(great_circle_midpoint);
590
591 my @M = great_circle_midpoint(@L, @T);
592
593or about 68.11N 24.74E, in the Finnish Lapland.
41bd693c 594
bf5f1b4c 595=head2 CAVEAT FOR GREAT CIRCLE FORMULAS
41bd693c 596
bf5f1b4c 597The answers may be off by few percentages because of the irregular
598(slightly aspherical) form of the Earth. The errors are at worst
599about 0.55%, but generally below 0.3%.
d54bf66f 600
5cd24f17 601=head1 BUGS
5aabfad6 602
5cd24f17 603Saying C<use Math::Trig;> exports many mathematical routines in the
604caller environment and even overrides some (C<sin>, C<cos>). This is
605construed as a feature by the Authors, actually... ;-)
5aabfad6 606
5cd24f17 607The code is not optimized for speed, especially because we use
608C<Math::Complex> and thus go quite near complex numbers while doing
609the computations even when the arguments are not. This, however,
610cannot be completely avoided if we want things like C<asin(2)> to give
611an answer instead of giving a fatal runtime error.
5aabfad6 612
bf5f1b4c 613Do not attempt navigation using these formulas.
614
5cd24f17 615=head1 AUTHORS
5aabfad6 616
ace5de91 617Jarkko Hietaniemi <F<jhi@iki.fi>> and
6e238990 618Raphael Manfredi <F<Raphael_Manfredi@pobox.com>>.
5aabfad6 619
620=cut
621
622# eof