File Coverage

blib/lib/Math/Big.pm
Criterion Covered Total %
statement 272 320 85.0
branch 63 108 58.3
condition 15 35 42.8
subroutine 23 24 95.8
pod 19 19 100.0
total 392 506 77.4


line stmt bran cond sub pod time code
1             #############################################################################
2             # Math/Big.pm -- usefull routines with Big numbers (BigInt/BigFloat)
3              
4             package Math::Big;
5              
6             require 5.006002; # anything lower is simple untested
7              
8 3     3   142749 use strict;
  3         31  
  3         98  
9 3     3   17 use warnings;
  3         5  
  3         105  
10              
11 3     3   2610 use Math::BigInt '1.97';
  3         62729  
  3         14  
12 3     3   55346 use Math::BigFloat;
  3         62163  
  3         17  
13 3     3   854 use Exporter;
  3         7  
  3         9842  
14              
15             our $VERSION = '1.15';
16             our @ISA = qw( Exporter );
17             our @EXPORT_OK = qw( primes fibonacci base to_base hailstone factorial
18             euler bernoulli pi log
19             tan cos sin cosh sinh arctan arctanh arcsin arcsinh
20             );
21              
22             # some often used constants:
23             my $four = Math::BigFloat->new(4);
24             my $sixteen = Math::BigFloat->new(16);
25             my $fone = Math::BigFloat->bone(); # pi
26             my $one = Math::BigInt->bone(); # hailstone, sin, cos etc
27             my $two = Math::BigInt->new(2); # hailstone, sin, cos etc
28             my $three = Math::BigInt->new(3); # hailstone
29              
30             my $five = Math::BigFloat->new(5); # for pi
31             my $twothreenine = Math::BigFloat->new(239); # for pi
32              
33             # In scalar context this returns the prime count (# of primes <= N).
34             # In array context it returns a list of primes from 2 to N.
35             sub primes {
36 16     16 1 5049 my $end = shift;
37 16 50       45 return unless defined $end;
38 16 50       42 $end = $end->numify() if ref($end) =~ /^Math::Big/;
39 16 0       41 if ($end < 2) { return !wantarray ? Math::BigInt->bzero() : (); }
  0 50       0  
40 16 0       37 if ($end < 3) { return !wantarray ? $one->copy : ($two->copy); }
  0 50       0  
41 16 100       30 if ($end < 5) { return !wantarray ? $two->copy : ($two->copy, $three->copy); }
  3 100       19  
42              
43 13 100       38 $end-- unless ($end & 1);
44 13         25 my $s_end = $end >> 1;
45 13         38 my $whole = int( ($end>>1) / 15);
46             # Be conservative. This would result in terabytes of array output.
47 13 50       31 die "Cannot return $end primes!" if $whole > 1_145_324_612; # ~32 GB string
48 13         37 my $sieve = "100010010010110" . "011010010010110" x $whole;
49 13         27 substr($sieve, $s_end+1) = ''; # Clip to the right number of entries
50 13         37 my ($n, $limit) = ( 7, int(sqrt($end)) );
51 13         32 while ( $n <= $limit ) {
52 0         0 for (my $s = ($n*$n) >> 1; $s <= $s_end; $s += $n) {
53 0         0 substr($sieve, $s, 1) = '1';
54             }
55 0         0 do { $n += 2 } while substr($sieve, $n>>1, 1);
  0         0  
56             }
57              
58 13 100       31 return Math::BigInt->new(1 + $sieve =~ tr/0//) if !wantarray;
59              
60 12         26 my @primes = (2, 3, 5);
61 12         20 $n = 7-2;
62 12         48 foreach my $s (split("0", substr($sieve, 3), -1)) {
63 44         64 $n += 2 + 2 * length($s);
64 44 100       88 push @primes, $n if $n <= $end;
65             }
66 12         27 return map { Math::BigInt->new($_) } @primes;
  69         2052  
67             }
68              
69             sub fibonacci
70             {
71 22     22 1 25226 my $x = shift;
72 22 50 33     107 $x = Math::BigInt -> new($x)
73             unless ref($x) && $x -> isa("Math::BigInt");
74 22         1073 $x -> bfib();
75             }
76              
77             sub base
78             {
79 7     7 1 4819 my ($number,$base) = @_;
80              
81 7 50       37 $number = Math::BigInt->new($number) unless ref $number;
82 7 50       314 $base = Math::BigInt->new($base) unless ref $base;
83              
84 7 50       249 return if $number < $base;
85 7         260 my $n = Math::BigInt->new(0);
86 7         751 my $trial = $base;
87             # 9 = 2**3 + 1
88 7         17 while ($trial < $number)
89             {
90 20         1057 $trial *= $base; $n++;
  20         1350  
91             }
92 7         472 $trial /= $base; $a = $number - $trial;
  7         1060  
93 7         921 ($n,$a);
94             }
95              
96             sub to_base
97             {
98 10     10 1 10232 my $x = shift;
99 10 50 33     54 $x = Math::BigInt->new($x)
100             unless ref($x) && $x -> isa("Math::BigInt");
101 10         589 $x -> to_base(@_);
102             }
103              
104             sub hailstone
105             {
106             # return in list context the hailstone sequence, in scalar context the
107             # number of steps to reach 1
108 11     11 1 12195 my ($n) = @_;
109              
110 11 50       54 $n = Math::BigInt->new($n) unless ref $n;
111              
112 11 50 33     485 return if $n->is_nan() || $n->is_negative();
113              
114             # Use the Math::BigInt lib directly for more speed, since all numbers
115             # involved are positive integers.
116              
117 11         175 my $lib = Math::BigInt->config()->{lib};
118 11         499 $n = $n->{value};
119 11         24 my $three_ = $three->{value};
120 11         16 my $two_ = $two->{value};
121              
122 11 100       23 if (wantarray)
123             {
124 4         6 my @seq;
125 4         11 while (! $lib->_is_one($n))
126             {
127             # push @seq, Math::BigInt->new( $lib->_str($n) );
128 47         563 push @seq, bless { value => $lib->_copy($n), sign => '+' }, "Math::BigInt";
129              
130             # was: ($n->is_odd()) ? ($n = $n * 3 + 1) : ($n = $n / 2);
131 47 100       331 if ($lib->_is_odd($n))
132             {
133 19         78 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  19         165  
134              
135             # We now know that $n is at least 10 ( (3 * 3) + 1 ) because $n > 1
136             # before we entered, and since $n was odd, it must have been at least
137             # 3. So the next step is $n /= 2:
138 19         179 push @seq, bless { value => $lib->_copy($n), sign => '+' }, "Math::BigInt";
139             # this is better, but slower:
140             #push @seq, Math::BigInt->new( $lib->_str($n) );
141             # next step is $n /= 2 as usual (we save the else {} block, too)
142             }
143 47         239 $n = $lib->_div($n, $two_);
144             }
145 4         52 push @seq, Math::BigInt->bone();
146 4         275 return @seq;
147             }
148              
149 7         9 my $i = 1;
150 7         21 while (! $lib->_is_one($n))
151             {
152 47         497 $i++;
153             # was: ($n->is_odd()) ? ($n = $n * 3 + 1) : ($n = $n / 2);
154 47 100       74 if ($lib->_is_odd($n))
155             {
156 18         68 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  18         152  
157              
158             # We now know that $n is at least 10 ( (3 * 3) + 1 ) because $n > 1
159             # before we entered, and since $n was odd, it must have been at least 3.
160             # So the next step is $n /= 2 as usual (we save the else {} block, too).
161 18         105 $i++; # one more (we know that $n cannot be 1)
162             }
163 47         142 $n = $lib->_div($n, $two_);
164             }
165 7         90 Math::BigInt->new($i);
166             }
167              
168             sub factorial
169             {
170             # calculate n! - use Math::BigInt bfac() for speed
171 7     7 1 5426 my ($n) = shift;
172              
173 7 50       18 if (ref($n))
174             {
175 0         0 $n->copy()->bfac();
176             }
177             else
178             {
179 7         23 Math::BigInt->new($n)->bfac();
180             }
181             }
182              
183             sub bernoulli
184             {
185             # returns the nth Bernoulli number. In scalar context as Math::BigFloat
186             # fraction, in list context as two Math:BigFloats, which, if divided, give
187             # the same result. The series runs this:
188             # 1/6, 1/30, 1/42, 1/30, 5/66, 691/2730, etc
189              
190             # Since I do not have yet a way to compute this, I have a table of the
191             # first 40. So bernoulli(41) will fail for now.
192              
193 61     61 1 29942 my $n = shift;
194              
195 61 50       490 return if $n < 0;
196 61         118 my @table_1 = ( 1,1, -1,2 ); # 0, 1
197 61         217 my @table = (
198             1,6, -1,30, 1,42, -1,30, 5,66, -691,2730, # 2, 4,
199             7,6, -3617,510, 43867,798,
200             -174611,330,
201             854513,138,
202             '-236364091',2730,
203             '8553103',6,
204             '-23749461029',870,
205             '8615841276005',14322,
206             '-7709321041217',510,
207             '2577687858367',6,
208             '-26315271553053477373',1919190,
209             '2929993913841559',6,
210             '-261082718496449122051',13530, # 40
211             );
212 61         79 my ($a,$b);
213 61 100       170 if ($n < 2)
    100          
    50          
214             {
215 2         11 $a = Math::BigFloat->new($table_1[$n*2]);
216 2         231 $b = Math::BigFloat->new($table_1[$n*2+1]);
217             }
218             # n is odd:
219             elsif (($n & 1) == 1)
220             {
221 20         63 $a = Math::BigFloat->bzero();
222 20         1102 $b = Math::BigFloat->bone();
223             }
224             elsif ($n <= 40)
225             {
226 39         51 $n -= 2;
227 39         108 $a = Math::BigFloat->new($table[$n]);
228 39         3713 $b = Math::BigFloat->new($table[$n+1]);
229             }
230             else
231             {
232 0 0       0 die 'Bernoulli numbers over 40 not yet implemented.' if $n > 40;
233             }
234 61 50       7414 wantarray ? ($a,$b): $a/$b;
235             }
236              
237             sub euler
238             {
239             # Calculate Euler's number.
240             # first argument is x, so that result is e ** x
241             # Second argument is accuracy (number of significant digits), it
242             # stops when at least so much plus one digits are 'stable' and then
243             # rounds it. Default is 42.
244 4     4 1 6307 my $x = $_[0];
245 4 50 33     42 $x = Math::BigFloat->new($x) if !ref($x) || (!$x->isa('Math::BigFloat'));
246              
247 4         565 $x->bexp($_[1]);
248             }
249              
250             sub sin
251             {
252             # calculate sinus
253             # first argument is x, so that result is sin(x)
254             # Second argument is accuracy (number of significant digits), it
255             # stops when at least so much plus one digits are 'stable' and then
256             # rounds it. Default is 42.
257 8 50   8 1 8046 my $x = shift; $x = 0 if !defined $x;
  8         25  
258 8   50     25 my $d = abs(shift || 42); $d = abs($d)+1;
  8         11  
259              
260 8 50       38 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
261              
262             # taylor: x^3 x^5 x^7 x^9
263             # sin = x - --- + --- - --- + --- ...
264             # 3! 5! 7! 9!
265              
266             # difference for each term is thus x^2 and 1,2
267              
268 8         1283 my $sin = $x->copy(); my $last = 0;
  8         221  
269 8         11 my $sign = 1; # start with -=
270 8         23 my $x2 = $x * $x; # X ^ 2, difference between terms
271 8         1059 my $over = $x2 * $x; # X ^ 3
272 8         972 my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  8         815  
273 8         758 while ($sin->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
274             {
275 46         11188 $last = $sin->copy();
276 46 100       1371 if ($sign == 0)
277             {
278 22         45 $sin += $over->copy()->bdiv($below,$d);
279             }
280             else
281             {
282 24         47 $sin -= $over->copy()->bdiv($below,$d);
283             }
284 46         62693 $sign = 1-$sign; # alternate
285 46         118 $over *= $x2; # $x*$x
286 46         4867 $below *= $factorial; $factorial++; # n*(n+1)
  46         4544  
287 46         4106 $below *= $factorial; $factorial++;
  46         5551  
288             }
289 8         1875 $sin->bround($d-1);
290             }
291              
292             sub cos
293             {
294             # calculate cosinus
295             # first argument is x, so that result is cos(x)
296             # Second argument is accuracy (number of significant digits), it
297             # stops when at least so much plus one digits are 'stable' and then
298             # rounds it. Default is 42.
299 8 50   8 1 8283 my $x = shift; $x = 0 if !defined $x;
  8         46  
300 8   50     25 my $d = abs(shift || 42); $d = abs($d)+1;
  8         11  
301              
302 8 50       36 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
303              
304             # taylor: x^2 x^4 x^6 x^8
305             # cos = 1 - --- + --- - --- + --- ...
306             # 2! 4! 6! 8!
307              
308             # difference for each term is thus x^2 and 1,2
309              
310 8         1304 my $cos = Math::BigFloat->bone(); my $last = 0;
  8         458  
311 8         23 my $over = $x * $x; # X ^ 2
312 8         1077 my $x2 = $over->copy(); # X ^ 2; difference between terms
313 8         223 my $sign = 1; # start with -=
314 8         22 my $below = Math::BigFloat->new(2); my $factorial = Math::BigFloat->new(3);
  8         821  
315 8         757 while ($cos->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
316             {
317 52         13204 $last = $cos->copy();
318 52 100       1508 if ($sign == 0)
319             {
320 24         44 $cos += $over->copy()->bdiv($below,$d);
321             }
322             else
323             {
324 28         55 $cos -= $over->copy()->bdiv($below,$d);
325             }
326 52         66288 $sign = 1-$sign; # alternate
327 52         127 $over *= $x2; # $x*$x
328 52         5757 $below *= $factorial; $factorial++; # n*(n+1)
  52         5971  
329 52         4635 $below *= $factorial; $factorial++;
  52         5117  
330             }
331 8         1734 $cos->round($d-1);
332             }
333              
334             sub tan
335             {
336             # calculate tangens
337             # first argument is x, so that result is tan(x)
338             # Second argument is accuracy (number of significant digits), it
339             # stops when at least so much plus one digits are 'stable' and then
340             # rounds it. Default is 42.
341 3 50   3 1 3948 my $x = shift; $x = 0 if !defined $x;
  3         9  
342 3   50     11 my $d = abs(shift || 42); $d = abs($d)+1;
  3         6  
343              
344 3 50       15 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
345              
346             # taylor: 1 2 3 4 5
347              
348             # x^3 x^5 x^7 x^9
349             # tan = x + 1 * ----- + 2 * ----- + 17 * ----- + 62 * ----- ...
350             # 3 15 315 2835
351             #
352             # 2^2n * ( 2^2n - 1) * Bn * x^(2n-1) 256*255 * 1 * x^7 17
353             # ---------------------------------- : n=4: ----------------- = --- * x^7
354             # (2n)! 40320 * 30 315
355             #
356             # 8! = 40320, B4 (Bernoully number 4) = 1/30
357              
358             # for each term we need: 2^2n, but if we have 2^2(n-1) we use n = (n-1)*2
359             # 2 copy, 7 bmul, 2 bdiv, 3 badd, 1 bernoulli
360              
361 3         371 my $tan = $x->copy(); my $last = 0;
  3         84  
362 3         12 my $x2 = $x*$x;
363 3         394 my $over = $x2*$x;
364 3         337 my $below = Math::BigFloat->new(24); # (1*2*3*4) (2n)!
365 3         228 my $factorial = Math::BigFloat->new(5); # for next (2n)!
366 3         304 my $two_n = Math::BigFloat->new(16); # 2^2n
367 3         196 my $two_factor = Math::BigFloat->new(4); # 2^2(n+1) = $two_n * $two_factor
368 3         286 my ($b,$b1,$b2); $b = 4;
  3         4  
369 3         9 while ($tan->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
370             {
371 19         3050 $last = $tan->copy();
372 19         578 ($b1,$b2) = bernoulli($b);
373 19         88 $tan += $over->copy()->bmul($two_n)->bmul($two_n - $fone)->bmul($b1->babs())->bdiv($below,$d)->bdiv($b2,$d);
374 19         51327 $over *= $x2; # x^3, x^5 etc
375 19         1919 $below *= $factorial; $factorial++; # n*(n+1)
  19         2774  
376 19         1764 $below *= $factorial; $factorial++;
  19         2192  
377 19         1861 $two_n *= $two_factor; # 2^2(n+1) = 2^2n * 4
378 19         1891 $b += 2; # next bernoulli index
379 19 100       71 last if $b > 40; # safeguard
380             }
381 3         431 $tan->round($d-1);
382             }
383              
384             sub sinh
385             {
386             # calculate sinus hyperbolicus
387             # first argument is x, so that result is sinh(x)
388             # Second argument is accuracy (number of significant digits), it
389             # stops when at least so much plus one digits are 'stable' and then
390             # rounds it. Default is 42.
391 2 50   2 1 1470 my $x = shift; $x = 0 if !defined $x;
  2         7  
392 2   50     8 my $d = abs(shift || 42); $d = abs($d)+1;
  2         3  
393              
394 2 50       11 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
395              
396             # taylor: x^3 x^5 x^7
397             # sinh = x + --- + --- + --- ...
398             # 3! 5! 7!
399              
400             # difference for each term is thus x^2 and 1,2
401              
402 2         236 my $sinh = $x->copy(); my $last = 0;
  2         83  
403 2         9 my $x2 = $x*$x;
404 2         273 my $over = $x2 * $x; my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  2         224  
  2         204  
405 2         197 while ($sinh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on accuracy
406             {
407 0         0 $last = $sinh->copy();
408 0         0 $sinh += $over->copy()->bdiv($below,$d);
409 0         0 $over *= $x2; # $x*$x
410 0         0 $below *= $factorial; $factorial++; # n*(n+1)
  0         0  
411 0         0 $below *= $factorial; $factorial++;
  0         0  
412             }
413 2         417 $sinh->bround($d-1);
414             }
415              
416             sub cosh
417             {
418             # calculate cosinus hyperbolicus
419             # first argument is x, so that result is cosh(x)
420             # Second argument is accuracy (number of significant digits), it
421             # stops when at least so much plus one digits are 'stable' and then
422             # rounds it. Default is 42.
423 0 0   0 1 0 my $x = shift; $x = 0 if !defined $x;
  0         0  
424 0   0     0 my $d = abs(shift || 42); $d = abs($d)+1;
  0         0  
425              
426 0 0       0 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
427              
428             # taylor: x^2 x^4 x^6
429             # cosh = x + --- + --- + --- ...
430             # 2! 4! 6!
431              
432             # difference for each term is thus x^2 and 1,2
433              
434 0         0 my $cosh = Math::BigFloat->bone(); my $last = 0;
  0         0  
435 0         0 my $x2 = $x*$x;
436 0         0 my $over = $x2; my $below = Math::BigFloat->new(); my $factorial = Math::BigFloat->new(3);
  0         0  
  0         0  
437 0         0 while ($cosh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on accuracy
438             {
439 0         0 $last = $cosh->copy();
440 0         0 $cosh += $over->copy()->bdiv($below,$d);
441 0         0 $over *= $x2; # $x*$x
442 0         0 $below *= $factorial; $factorial++; # n*(n+1)
  0         0  
443 0         0 $below *= $factorial; $factorial++;
  0         0  
444             }
445 0         0 $cosh->bround($d-1);
446             }
447              
448             sub arctan
449             {
450             # calculate arcus tangens
451             # first argument is x, so that result is arctan(x)
452             # Second argument is accuracy (number of significant digits), it
453             # stops when at least so much plus one digits are 'stable' and then
454             # rounds it. Default is 42.
455 8 50   8 1 6961 my $x = shift; $x = 0 if !defined $x;
  8         28  
456 8   50     24 my $d = abs(shift || 42); $d = abs($d)+1;
  8         15  
457              
458 8 100       32 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
459              
460             # taylor: x^3 x^5 x^7 x^9
461             # arctan = x - --- + --- - --- + --- ...
462             # 3 5 7 9
463              
464             # difference for each term is thus x^2 and 2:
465             # 2 copy, 1 bmul, 1 badd, 1 bdiv
466              
467 8         699 my $arctan = $x->copy(); my $last = 0;
  8         228  
468 8         25 my $x2 = $x*$x;
469 8         2147 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  8         2065  
  8         866  
470 8         745 my $sign = 1;
471 8         22 while ($arctan->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
472             {
473 85         28169 $last = $arctan->copy();
474 85 100       2537 if ($sign == 0)
475             {
476 42         81 $arctan += $over->copy()->bdiv($below,$d);
477             }
478             else
479             {
480 43         89 $arctan -= $over->copy()->bdiv($below,$d);
481             }
482 85         123116 $sign = 1-$sign; # alternate
483 85         214 $over *= $x2; # $x*$x
484 85         20572 $below += $add;
485             }
486 8         2356 $arctan->bround($d-1);
487             }
488              
489             sub arctanh
490             {
491             # calculate arcus tangens hyperbolicus
492             # first argument is x, so that result is arctanh(x)
493             # Second argument is accuracy (number of significant digits), it
494             # stops when at least so much plus one digits are 'stable' and then
495             # rounds it. Default is 42.
496 2 50   2 1 1889 my $x = shift; $x = 0 if !defined $x;
  2         7  
497 2   50     9 my $d = abs(shift || 42); $d = abs($d)+1;
  2         4  
498              
499 2 50       10 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
500              
501             # taylor: x^3 x^5 x^7 x^9
502             # arctanh = x + --- + --- + --- + --- + ...
503             # 3 5 7 9
504              
505             # difference for each term is thus x^2 and 2:
506             # 2 copy, 1 bmul, 1 badd, 1 bdiv
507              
508 2         242 my $arctanh = $x->copy(); my $last = 0;
  2         55  
509 2         7 my $x2 = $x*$x;
510 2         281 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  2         233  
  2         206  
511 2         212 while ($arctanh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
512             {
513 0         0 $last = $arctanh->copy();
514 0         0 $arctanh += $over->copy()->bdiv($below,$d);
515 0         0 $over *= $x2; # $x*$x
516 0         0 $below += $add;
517             }
518 2         384 $arctanh->bround($d-1);
519             }
520              
521             sub arcsin
522             {
523             # calculate arcus sinus
524             # first argument is x, so that result is arcsin(x)
525             # Second argument is accuracy (number of significant digits), it
526             # stops when at least so much plus one digits are 'stable' and then
527             # rounds it. Default is 42.
528 4 50   4 1 2973 my $x = shift; $x = 0 if !defined $x;
  4         23  
529 4   100     17 my $d = abs(shift || 42); $d = abs($d)+1;
  4         7  
530              
531 4 50       19 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
532              
533             # taylor: 1 * x^3 1 * 3 * x^5 1 * 3 * 5 * x^7
534             # arcsin = x + ------- + ----------- + --------------- + ...
535             # 2 * 3 2 * 4 * 5 2 * 4 * 6 * 7
536              
537             # difference for each term is thus x^2 and two muls (fac1, fac2):
538             # 3 copy, 3 bmul, 1 bdiv, 3 badd
539              
540 4         559 my $arcsin = $x->copy(); my $last = 0;
  4         109  
541 4         11 my $x2 = $x*$x;
542 4         493 my $over = $x2*$x; my $below = Math::BigFloat->new(6);
  4         462  
543 4         398 my $fac1 = Math::BigFloat->new(1);
544 4         370 my $fac2 = Math::BigFloat->new(2);
545 4         362 my $two = Math::BigFloat->new(2);
546 4         365 while ($arcsin->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
547             {
548 30         11444 $last = $arcsin->copy();
549 30         917 $arcsin += $over->copy()->bmul($fac1)->bdiv($below->copy->bmul($fac2),$d);
550 30         51844 $over *= $x2; # $x*$x
551 30         3548 $below += $one;
552 30         9631 $fac1 += $two;
553 30         4475 $fac2 += $two;
554             }
555 4         1006 $arcsin->bround($d-1);
556             }
557              
558             sub arcsinh
559             {
560             # calculate arcus sinus hyperbolicus
561             # first argument is x, so that result is arcsinh(x)
562             # Second argument is accuracy (number of significant digits), it
563             # stops when at least so much plus one digits are 'stable' and then
564             # rounds it. Default is 42.
565 2 50   2 1 2136 my $x = shift; $x = 0 if !defined $x;
  2         8  
566 2   50     9 my $d = abs(shift || 42); $d = abs($d)+1;
  2         3  
567              
568 2 50       12 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
569              
570             # taylor: 1 * x^3 1 * 3 * x^5 1 * 3 * 5 * x^7
571             # arcsin = x - ------- + ----------- - --------------- + ...
572             # 2 * 3 2 * 4 * 5 2 * 4 * 6 * 7
573              
574             # difference for each term is thus x^2 and two muls (fac1, fac2):
575             # 3 copy, 3 bmul, 1 bdiv, 3 badd
576              
577 2         260 my $arcsinh = $x->copy(); my $last = 0;
  2         56  
578 2         8 my $x2 = $x*$x; my $sign = 0;
  2         240  
579 2         5 my $over = $x2*$x; my $below = 6;
  2         221  
580 2         7 my $fac1 = Math::BigInt->new(1);
581 2         96 my $fac2 = Math::BigInt->new(2);
582 2         73 while ($arcsinh ne $last) # no $x-$last > $diff because bdiv() limit on A
583             {
584 0         0 $last = $arcsinh->copy();
585 0 0       0 if ($sign == 0)
586             {
587 0         0 $arcsinh += $over->copy()->bmul(
588             $fac1)->bdiv($below->copy->bmul($fac2),$d);
589             }
590             else
591             {
592 0         0 $arcsinh -= $over->copy()->bmul(
593             $fac1)->bdiv($below->copy->bmul($fac2),$d);
594             }
595 0         0 $over *= $x2; # $x*$x
596 0         0 $below += $one;
597 0         0 $fac1 += $two;
598 0         0 $fac2 += $two;
599             }
600 2         64 $arcsinh->round($d-1);
601             }
602              
603             sub log
604             {
605 5     5 1 4237 my ($x,$base,$d) = @_;
606              
607 5         7 my $y;
608 5 50 33     21 if (!ref($x) || !$x->isa('Math::BigFloat'))
609             {
610 5         20 $y = Math::BigFloat->new($x);
611             }
612             else
613             {
614 0         0 $y = $x->copy();
615             }
616 5         979 $y->blog($base,$d);
617 5         4863 $y;
618             }
619              
620             sub pi
621             {
622             # calculate PI (as suggested by Robert Creager)
623 2   50 2 1 1870 my $digits = abs(shift || 1024);
624              
625 2         5 my $d = $digits+5;
626              
627 2         8 my $pi = $sixteen * arctan( scalar $fone->copy()->bdiv($five,$d), $d )
628             - $four * arctan( scalar $fone->copy()->bdiv($twothreenine,$d), $d);
629 2         2679 $pi->bround($digits+1); # +1 for the "3."
630             }
631              
632             1;
633              
634             __END__