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 -- useful routines with big numbers (Math::BigInt/Math::BigFloat)
3              
4             package Math::Big;
5              
6             require 5.006002; # anything lower is simple untested
7              
8 3     3   125408 use strict;
  3         32  
  3         87  
9 3     3   14 use warnings;
  3         6  
  3         93  
10              
11 3     3   2234 use Math::BigInt '1.97';
  3         58088  
  3         16  
12 3     3   49067 use Math::BigFloat;
  3         52028  
  3         17  
13 3     3   723 use Exporter;
  3         5  
  3         10768  
14              
15             our $VERSION = '1.16';
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 3824 my $end = shift;
37 16 50       35 return unless defined $end;
38 16 50       38 $end = $end->numify() if ref($end) =~ /^Math::Big/;
39 16 0       33 if ($end < 2) { return !wantarray ? Math::BigInt->bzero() : (); }
  0 50       0  
40 16 0       32 if ($end < 3) { return !wantarray ? $one->copy : ($two->copy); }
  0 50       0  
41 16 100       27 if ($end < 5) { return !wantarray ? $two->copy : ($two->copy, $three->copy); }
  3 100       15  
42              
43 13 100       32 $end-- unless ($end & 1);
44 13         22 my $s_end = $end >> 1;
45 13         32 my $whole = int( ($end>>1) / 15);
46             # Be conservative. This would result in terabytes of array output.
47 13 50       30 die "Cannot return $end primes!" if $whole > 1_145_324_612; # ~32 GB string
48 13         39 my $sieve = "100010010010110" . "011010010010110" x $whole;
49 13         28 substr($sieve, $s_end+1) = ''; # Clip to the right number of entries
50 13         31 my ($n, $limit) = ( 7, int(sqrt($end)) );
51 13         29 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         23 my @primes = (2, 3, 5);
61 12         17 $n = 7-2;
62 12         44 foreach my $s (split("0", substr($sieve, 3), -1)) {
63 44         70 $n += 2 + 2 * length($s);
64 44 100       82 push @primes, $n if $n <= $end;
65             }
66 12         30 return map { Math::BigInt->new($_) } @primes;
  69         2574  
67             }
68              
69             sub fibonacci
70             {
71 22     22 1 20173 my $x = shift;
72 22 50 33     86 $x = Math::BigInt -> new($x)
73             unless ref($x) && $x -> isa("Math::BigInt");
74 22         1273 $x -> bfib();
75             }
76              
77             sub base
78             {
79 7     7 1 3947 my ($number,$base) = @_;
80              
81 7 50       28 $number = Math::BigInt->new($number) unless ref $number;
82 7 50       371 $base = Math::BigInt->new($base) unless ref $base;
83              
84 7 50       308 return if $number < $base;
85 7         214 my $n = Math::BigInt->new(0);
86 7         661 my $trial = $base;
87             # 9 = 2**3 + 1
88 7         14 while ($trial < $number)
89             {
90 20         840 $trial *= $base; $n++;
  20         1030  
91             }
92 7         359 $trial /= $base; $a = $number - $trial;
  7         850  
93 7         739 ($n,$a);
94             }
95              
96             sub to_base
97             {
98 10     10 1 9564 my $x = shift;
99 10 50 33     45 $x = Math::BigInt->new($x)
100             unless ref($x) && $x -> isa("Math::BigInt");
101 10         772 $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 9724 my ($n) = @_;
109              
110 11 50       44 $n = Math::BigInt->new($n) unless ref $n;
111              
112 11 50 33     581 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         141 my $lib = Math::BigInt->config()->{lib};
118 11         387 $n = $n->{value};
119 11         17 my $three_ = $three->{value};
120 11         13 my $two_ = $two->{value};
121              
122 11 100       20 if (wantarray)
123             {
124 4         6 my @seq;
125 4         10 while (! $lib->_is_one($n))
126             {
127             # push @seq, Math::BigInt->new( $lib->_str($n) );
128 47         467 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       269 if ($lib->_is_odd($n))
132             {
133 19         60 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  19         137  
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         100 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         198 $n = $lib->_div($n, $two_);
144             }
145 4         42 push @seq, Math::BigInt->bone();
146 4         193 return @seq;
147             }
148              
149 7         8 my $i = 1;
150 7         17 while (! $lib->_is_one($n))
151             {
152 47         399 $i++;
153             # was: ($n->is_odd()) ? ($n = $n * 3 + 1) : ($n = $n / 2);
154 47 100       62 if ($lib->_is_odd($n))
155             {
156 18         55 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  18         121  
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         82 $i++; # one more (we know that $n cannot be 1)
162             }
163 47         111 $n = $lib->_div($n, $two_);
164             }
165 7         75 Math::BigInt->new($i);
166             }
167              
168             sub factorial
169             {
170             # calculate n! - use Math::BigInt bfac() for speed
171 7     7 1 4450 my ($n) = shift;
172              
173 7 50       14 if (ref($n))
174             {
175 0         0 $n->copy()->bfac();
176             }
177             else
178             {
179 7         20 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:BigFloat objects, which, if divided,
187             # give 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 25279 my $n = shift;
194              
195 61 50       143 return if $n < 0;
196 61         85 my @table_1 = ( 1,1, -1,2 ); # 0, 1
197 61         189 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         77 my ($a,$b);
213 61 100       145 if ($n < 2)
    100          
    50          
214             {
215 2         8 $a = Math::BigFloat->new($table_1[$n*2]);
216 2         129 $b = Math::BigFloat->new($table_1[$n*2+1]);
217             }
218             # n is odd:
219             elsif (($n & 1) == 1)
220             {
221 20         58 $a = Math::BigFloat->bzero();
222 20         865 $b = Math::BigFloat->bone();
223             }
224             elsif ($n <= 40)
225             {
226 39         40 $n -= 2;
227 39         94 $a = Math::BigFloat->new($table[$n]);
228 39         2699 $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       5080 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 4893 my $x = $_[0];
245 4 50 33     24 $x = Math::BigFloat->new($x) if !ref($x) || (!$x->isa('Math::BigFloat'));
246              
247 4         334 $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 6969 my $x = shift; $x = 0 if !defined $x;
  8         22  
258 8   50     25 my $d = abs(shift || 42); $d = abs($d)+1;
  8         12  
259              
260 8 50       37 $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         1020 my $sin = $x->copy(); my $last = 0;
  8         184  
269 8         9 my $sign = 1; # start with -=
270 8         25 my $x2 = $x * $x; # X ^ 2, difference between terms
271 8         838 my $over = $x2 * $x; # X ^ 3
272 8         779 my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  8         447  
273 8         408 while ($sin->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
274             {
275 46         9298 $last = $sin->copy();
276 46 100       1131 if ($sign == 0)
277             {
278 22         34 $sin += $over->copy()->bdiv($below,$d);
279             }
280             else
281             {
282 24         40 $sin -= $over->copy()->bdiv($below,$d);
283             }
284 46         51211 $sign = 1-$sign; # alternate
285 46         91 $over *= $x2; # $x*$x
286 46         3934 $below *= $factorial; $factorial++; # n*(n+1)
  46         3564  
287 46         3305 $below *= $factorial; $factorial++;
  46         4399  
288             }
289 8         1524 $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 7213 my $x = shift; $x = 0 if !defined $x;
  8         40  
300 8   50     27 my $d = abs(shift || 42); $d = abs($d)+1;
  8         11  
301              
302 8 50       35 $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         1030 my $cos = Math::BigFloat->bone(); my $last = 0;
  8         415  
311 8         19 my $over = $x * $x; # X ^ 2
312 8         889 my $x2 = $over->copy(); # X ^ 2; difference between terms
313 8         174 my $sign = 1; # start with -=
314 8         15 my $below = Math::BigFloat->new(2); my $factorial = Math::BigFloat->new(3);
  8         460  
315 8         411 while ($cos->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
316             {
317 52         10217 $last = $cos->copy();
318 52 100       1223 if ($sign == 0)
319             {
320 24         35 $cos += $over->copy()->bdiv($below,$d);
321             }
322             else
323             {
324 28         47 $cos -= $over->copy()->bdiv($below,$d);
325             }
326 52         53497 $sign = 1-$sign; # alternate
327 52         101 $over *= $x2; # $x*$x
328 52         4396 $below *= $factorial; $factorial++; # n*(n+1)
  52         4646  
329 52         3687 $below *= $factorial; $factorial++;
  52         4251  
330             }
331 8         1400 $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 3037 my $x = shift; $x = 0 if !defined $x;
  3         14  
342 3   50     8 my $d = abs(shift || 42); $d = abs($d)+1;
  3         4  
343              
344 3 50       14 $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         264 my $tan = $x->copy(); my $last = 0;
  3         66  
362 3         7 my $x2 = $x*$x;
363 3         312 my $over = $x2*$x;
364 3         285 my $below = Math::BigFloat->new(24); # (1*2*3*4) (2n)!
365 3         168 my $factorial = Math::BigFloat->new(5); # for next (2n)!
366 3         185 my $two_n = Math::BigFloat->new(16); # 2^2n
367 3         156 my $two_factor = Math::BigFloat->new(4); # 2^2(n+1) = $two_n * $two_factor
368 3         149 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         2298 $last = $tan->copy();
372 19         483 ($b1,$b2) = bernoulli($b);
373 19         68 $tan += $over->copy()->bmul($two_n)->bmul($two_n - $fone)->bmul($b1->babs())->bdiv($below,$d)->bdiv($b2,$d);
374 19         41752 $over *= $x2; # x^3, x^5 etc
375 19         1526 $below *= $factorial; $factorial++; # n*(n+1)
  19         1851  
376 19         1411 $below *= $factorial; $factorial++;
  19         1587  
377 19         1391 $two_n *= $two_factor; # 2^2(n+1) = 2^2n * 4
378 19         1498 $b += 2; # next bernoulli index
379 19 100       61 last if $b > 40; # safeguard
380             }
381 3         329 $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 1156 my $x = shift; $x = 0 if !defined $x;
  2         6  
392 2   50     7 my $d = abs(shift || 42); $d = abs($d)+1;
  2         4  
393              
394 2 50       9 $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         189 my $sinh = $x->copy(); my $last = 0;
  2         52  
403 2         6 my $x2 = $x*$x;
404 2         222 my $over = $x2 * $x; my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  2         514  
  2         120  
405 2         106 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         341 $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 5685 my $x = shift; $x = 0 if !defined $x;
  8         21  
456 8   50     21 my $d = abs(shift || 42); $d = abs($d)+1;
  8         13  
457              
458 8 100       30 $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         606 my $arctan = $x->copy(); my $last = 0;
  8         182  
468 8         23 my $x2 = $x*$x;
469 8         1768 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  8         1747  
  8         512  
470 8         449 my $sign = 1;
471 8         18 while ($arctan->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
472             {
473 85         22235 $last = $arctan->copy();
474 85 100       2018 if ($sign == 0)
475             {
476 42         64 $arctan += $over->copy()->bdiv($below,$d);
477             }
478             else
479             {
480 43         60 $arctan -= $over->copy()->bdiv($below,$d);
481             }
482 85         98713 $sign = 1-$sign; # alternate
483 85         168 $over *= $x2; # $x*$x
484 85         16646 $below += $add;
485             }
486 8         1787 $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 1700 my $x = shift; $x = 0 if !defined $x;
  2         7  
497 2   50     6 my $d = abs(shift || 42); $d = abs($d)+1;
  2         4  
498              
499 2 50       11 $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         220 my $arctanh = $x->copy(); my $last = 0;
  2         46  
509 2         5 my $x2 = $x*$x;
510 2         195 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  2         178  
  2         117  
511 2         104 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         322 $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 2355 my $x = shift; $x = 0 if !defined $x;
  4         17  
529 4   100     13 my $d = abs(shift || 42); $d = abs($d)+1;
  4         6  
530              
531 4 50       16 $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         480 my $arcsin = $x->copy(); my $last = 0;
  4         89  
541 4         9 my $x2 = $x*$x;
542 4         437 my $over = $x2*$x; my $below = Math::BigFloat->new(6);
  4         369  
543 4         227 my $fac1 = Math::BigFloat->new(1);
544 4         202 my $fac2 = Math::BigFloat->new(2);
545 4         197 my $two = Math::BigFloat->new(2);
546 4         198 while ($arcsin->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
547             {
548 30         10513 $last = $arcsin->copy();
549 30         711 $arcsin += $over->copy()->bmul($fac1)->bdiv($below->copy->bmul($fac2),$d);
550 30         41983 $over *= $x2; # $x*$x
551 30         2851 $below += $one;
552 30         7738 $fac1 += $two;
553 30         3541 $fac2 += $two;
554             }
555 4         1087 $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 1771 my $x = shift; $x = 0 if !defined $x;
  2         6  
566 2   50     7 my $d = abs(shift || 42); $d = abs($d)+1;
  2         3  
567              
568 2 50       9 $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         213 my $arcsinh = $x->copy(); my $last = 0;
  2         44  
578 2         6 my $x2 = $x*$x; my $sign = 0;
  2         192  
579 2         4 my $over = $x2*$x; my $below = 6;
  2         175  
580 2         7 my $fac1 = Math::BigInt->new(1);
581 2         123 my $fac2 = Math::BigInt->new(2);
582 2         91 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         51 $arcsinh->round($d-1);
601             }
602              
603             sub log
604             {
605 5     5 1 3301 my ($x,$base,$d) = @_;
606              
607 5         6 my $y;
608 5 50 33     18 if (!ref($x) || !$x->isa('Math::BigFloat'))
609             {
610 5         13 $y = Math::BigFloat->new($x);
611             }
612             else
613             {
614 0         0 $y = $x->copy();
615             }
616 5         609 $y->blog($base,$d);
617 5         4020 $y;
618             }
619              
620             sub pi
621             {
622             # calculate PI (as suggested by Robert Creager)
623 2   50 2 1 1605 my $digits = abs(shift || 1024);
624              
625 2         5 my $d = $digits+5;
626              
627 2         7 my $pi = $sixteen * arctan( scalar $fone->copy()->bdiv($five,$d), $d )
628             - $four * arctan( scalar $fone->copy()->bdiv($twothreenine,$d), $d);
629 2         2215 $pi->bround($digits+1); # +1 for the "3."
630             }
631              
632             1;
633              
634             __END__