File Coverage

lib/Math/Big.pm
Criterion Covered Total %
statement 359 412 87.1
branch 93 150 62.0
condition 13 29 44.8
subroutine 25 26 96.1
pod 19 19 100.0
total 509 636 80.0


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 2     2   16522 use strict;
  2         2  
  2         68  
9 2     2   8 use warnings;
  2         3  
  2         106  
10              
11 2     2   1144 use Math::BigInt '1.97';
  2         21561  
  2         12  
12 2     2   15252 use Math::BigFloat;
  2         18259  
  2         14  
13 2     2   1373 use Exporter;
  2         4  
  2         1535  
14              
15             our $VERSION = '1.14';
16             our @ISA = qw( Exporter );
17             our @EXPORT_OK = qw( primes fibonacci base hailstone factorial
18             euler bernoulli pi log
19             tan cos sin cosh sinh arctan arctanh arcsin arcsinh
20             );
21             our @F; # for fibonacci()
22              
23             # some often used constants:
24             my $four = Math::BigFloat->new(4);
25             my $sixteen = Math::BigFloat->new(16);
26             my $fone = Math::BigFloat->bone(); # pi
27             my $one = Math::BigInt->bone(); # hailstone, sin, cos etc
28             my $two = Math::BigInt->new(2); # hailstone, sin, cos etc
29             my $three = Math::BigInt->new(3); # hailstone
30              
31             my $five = Math::BigFloat->new(5); # for pi
32             my $twothreenine = Math::BigFloat->new(239); # for pi
33              
34             # In scalar context this returns the prime count (# of primes <= N).
35             # In array context it returns a list of primes from 2 to N.
36             sub primes {
37 16     16 1 3776 my $end = shift;
38 16 50       45 return unless defined $end;
39 16 50       38 $end = $end->numify() if ref($end) =~ /^Math::Big/;
40 16 0       39 if ($end < 2) { return !wantarray ? Math::BigInt->bzero() : (); }
  0 50       0  
41 16 0       33 if ($end < 3) { return !wantarray ? $one->copy : ($two->copy); }
  0 50       0  
42 16 100       33 if ($end < 5) { return !wantarray ? $two->copy : ($two->copy, $three->copy); }
  3 100       16  
43              
44 13 100       34 $end-- unless ($end & 1);
45 13         16 my $s_end = $end >> 1;
46 13         31 my $whole = int( ($end>>1) / 15);
47             # Be conservative. This would result in terabytes of array output.
48 13 50       27 die "Cannot return $end primes!" if $whole > 1_145_324_612; # ~32 GB string
49 13         31 my $sieve = "100010010010110" . "011010010010110" x $whole;
50 13         35 substr($sieve, $s_end+1) = ''; # Clip to the right number of entries
51 13         27 my ($n, $limit) = ( 7, int(sqrt($end)) );
52 13         36 while ( $n <= $limit ) {
53 0         0 for (my $s = ($n*$n) >> 1; $s <= $s_end; $s += $n) {
54 0         0 substr($sieve, $s, 1) = '1';
55             }
56 0         0 do { $n += 2 } while substr($sieve, $n>>1, 1);
  0         0  
57             }
58              
59 13 100       32 return Math::BigInt->new(1 + $sieve =~ tr/0//) if !wantarray;
60              
61 12         28 my @primes = (2, 3, 5);
62 12         28 $n = 7-2;
63 12         45 foreach my $s (split("0", substr($sieve, 3), -1)) {
64 44         45 $n += 2 + 2 * length($s);
65 44 100       86 push @primes, $n if $n <= $end;
66             }
67 12         33 return map { Math::BigInt->new($_) } @primes;
  69         1267  
68             }
69              
70             sub fibonacci
71             {
72 22 50   22 1 12698 my $n = shift; return unless defined $n;
  22         64  
73              
74 22 50       41 if (ref($n))
75             {
76 0 0       0 return if $n->sign() ne '+'; # < 0, NaN, inf
77             }
78             else
79             {
80 22 50       61 return if $n < 0;
81             }
82              
83             #####################
84             # list context
85 22 100       67 if (wantarray)
86             {
87             # make a scalar (if $n doesn't fit into a scalar, the resulting array
88             # will be too big, anyway)
89 6 50       12 $n = $n->numify() if ref($n);
90              
91 6         18 my @fib = (Math::BigInt -> bzero(),Math::BigInt -> bone(),Math::BigInt -> bone);
92 6         297 my $i = 3; # no BigInt
93 6         13 while ($i <= $n)
94             {
95 21         45 $fib[$i] = $fib[$i-1]+$fib[$i-2]; $i++;
  21         994  
96             }
97 6         55 return @fib;
98             }
99             #####################
100             # scalar context
101              
102 16         29 _fibonacci_fast($n);
103             }
104              
105             my $F;
106              
107             BEGIN
108             {
109             # Sequence of the first few fibonacci numbers:
110              
111             # 0,1,2,3,4,5,6,7, 8, 9, 10,11,12, 13, 14, 15, 16, 17, 18, 19, 20,
112 2     2   12 @F = (0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,
113             # 21,
114             10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229,
115             # 30,
116             832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352,
117             # 37, 42,
118             24157817, 39088169, 63245986, 102334155, 165580141, 267914296,
119             );
120             #433494437
121             #701408733
122             #1134903170
123             #1836311903
124             #2971215073
125             #4807526976
126             #7778742049
127             #12586269025
128 2         13 for (my $i = 0; $i < @F; $i++)
129             {
130 86         2255 $F[$i] = Math::BigInt->new($F[$i]);
131             }
132             }
133              
134             sub _fibonacci_fast
135             {
136 16 50   16   16 my $x = shift; $x = $x->numify() if ref($x);
  16         32  
137 16 100       140 return $F[$x] if $x < @F;
138              
139             # Knuth, TAOCR Vol 1, Third Edition, p. 81
140             # F(n+m) = Fm * Fn+1 + Fm-1 * Fn
141              
142             # if m is set to n+1, we get:
143             # F(n+n+1) = F(n+1) * Fn+1 + Fn * Fn
144             # F(n*2+1) = F(n+1) ^ 2 + Fn ^ 2
145              
146             # so to know Fx, we must know F((x-1)/2), which only works for odd x
147             # Fortunately:
148             # Fx+1 = F(x) + F(x-1)
149             # when x is even, then are x+1 and x-1 odd and can be calculated by the
150             # same means, and from this we get Fx.
151              
152             # starting with level 0 at Fn we fill a hash with the different n we need
153             # to calculate all Fn of the previous level. Here is an example for F1000:
154              
155             # To calculate F1000, we need F999 and F1001 (since 1000 is even)
156             # To calculate F999, we need F((999-1)/2) and F((999+1)/2), this are 499
157             # and 500. Sine for F1001 we need 500 and 501, we need F(500), F(501) and
158             # F(499) to calculate F(1001) and F(999), and from these we can get F(1000).
159             # For 500, we need 499 and 501, both are already needed.
160             # For 501, we need 250 and 251. An so on and on until all values at a level
161             # are under 17.
162             # For the deepest level we use a table-lookup. The other levels are then
163             # calulated backwards, until we arive at the top and the result is then in
164             # level 0.
165              
166             # level
167             # 0 1 2 3 and so on
168             # 1000 -> 999 -> 499 <- -> 249
169             # | |----> 500 |
170             # |--> 1001 -> 501 <- -> 250
171             # |------> 251
172              
173 2         3 my @fibo;
174 2         6 $fibo[0]->{$x} = undef; # mark our final result as needed
175             # if $x is even we need these two, too
176 2 50       7 if ($x % 1 == 0)
177             {
178 2         5 $fibo[0]->{$x-1} = undef; $fibo[0]->{$x+1} = undef;
  2         5  
179             }
180             # XXX
181             # for statistics
182 2         3 my $steps = 0; my $sum = 0; my $add = 0; my $mul = 0;
  2         3  
  2         17  
  2         2  
183 2         3 my $level = 0;
184 2         1 my $high = 1; # keep going?
185 2         3 my ($t,$t1,$f); # helper variables
186 2         5 while ($high > 0)
187             {
188 6         2 $level++; # next level
189 6         6 $high = 0; # count of results > @F
190             # print "at level $level (high=$high)\n";
191 6         6 foreach $f (keys %{$fibo[$level-1]})
  6         15  
192             {
193 19         16 $steps ++;
194 19 100       27 if (($f & 1) == 0) # odd/even?
195             {
196             # if it is even, add $f-1 and $f+1 to last level
197             # if not existing in last level, we must add
198             # ($f-1-1)/2 & ($f-1-1/2)+1 to the next level, too
199 8         7 $t = $f-1;
200 8 100       18 if (!exists $fibo[$level-1]->{$t})
201             {
202 1         3 $fibo[$level-1]->{$t} = undef; $t--; $t /= 2; # $t is odd
  1         1  
  1         2  
203 1         4 $fibo[$level]->{$t} = undef; $fibo[$level]->{$t+1} = undef;
  1         3  
204 1 50       4 $high = 1 if $t+1 >= @F; # any value not in table?
205             }
206 8         5 $t = $f+1;
207 8 100       29 if (!exists $fibo[$level-1]->{$t})
208             {
209 2         3 $fibo[$level-1]->{$t} = undef; $t--; $t /= 2; # $t is odd
  2         3  
  2         2  
210 2         4 $fibo[$level]->{$t} = undef; $fibo[$level]->{$t+1} = undef;
  2         3  
211 2 100       9 $high = 1 if $t+1 >= @F; # any value not in table?
212             }
213             # print "$f even: ",$f-1," ",$f+1," in level ",$level-1,"\n";
214             }
215             else
216             {
217             # else add ($_-1)/2and ($_-1)/2 + 1 to this level
218 11         9 $t = $f-1; $t /= 2;
  11         8  
219 11         49 $fibo[$level]->{$t} = undef; $fibo[$level]->{$t+1} = undef;
  11         17  
220 11 100       30 $high = 1 if $t+1 >= @F; # any value not in table?
221             # print "$_ odd: $t ",$t+1," in level $level (high = $high)\n";
222             }
223             }
224             }
225             # now we must fill our structure backwards with the results, combining them.
226             # numbers in the last level can be looked up:
227 2         2 foreach $f (keys %{$fibo[$level]})
  2         5  
228             {
229             # this inserts Math::BigInt objects, making the math below work:
230 7         10 $fibo[$level]->{$f} = $F[$f];
231             }
232             # use Data::Dumper; print Dumper(\@fibo);
233 2         3 my $l = $level; # for statistics
234 2         5 while ($level > 0)
235             {
236 6         110 $level--;
237             #$sum += scalar keys %{$fibo[$level]}; # for statistics
238             # first do the odd ones
239 6         8 my $fibo_level = $fibo[$level];
240 6         14 foreach $f (keys %$fibo_level)
241             {
242 22 100       2413 next if ($f & 1) == 0;
243 14         16 $t = $f-1; $t /= 2; my $t1 = $t+1;
  14         14  
  14         15  
244 14         26 $t = $fibo[$level+1]->{$t};
245 14         14 $t1 = $fibo[$level+1]->{$t1};
246             #$fibo_level->{$f} = $t*$t+$t1*$t1;
247 14         41 $fibo_level->{$f} = $t->copy->bmul($t)->badd($t1->copy()->bmul($t1));
248             #$mul += 2; $add ++; # for statistics
249             }
250             # now the even ones
251 6         1090 foreach $f (keys %{$fibo[$level]})
  6         19  
252             {
253 22 100       605 next if ($f & 1) != 0;
254 8         26 $fibo[$level]->{$f} = $fibo[$level]->{$f+1} - $fibo[$level]->{$f-1};
255             #$add ++; # for statistics
256             }
257             }
258             # print "sum $sum level $l => ",$sum/$l," steps $steps adds $add muls $mul\n";
259 2         166 $fibo[0]->{$x};
260             }
261              
262             sub base
263             {
264 7     7 1 3845 my ($number,$base) = @_;
265              
266 7 50       33 $number = Math::BigInt->new($number) unless ref $number;
267 7 50       193 $base = Math::BigInt->new($base) unless ref $base;
268              
269 7 50       137 return if $number < $base;
270 7         172 my $n = Math::BigInt->new(0);
271 7         403 my $trial = $base;
272             # 9 = 2**3 + 1
273 7         17 while ($trial < $number)
274             {
275 20         618 $trial *= $base; $n++;
  20         784  
276             }
277 7         264 $trial /= $base; $a = $number - $trial;
  7         358  
278 7         581 ($n,$a);
279             }
280              
281             sub to_base
282             {
283             # after an idea by Tilghman Lesher
284 10     10 1 7886 my ($x, $base, $alphabet) = @_;
285              
286 10 50       72 $x = Math::BigInt->new($x) unless ref $x;
287              
288 10 100       629 return '0' if $x->is_zero();
289              
290             # setup defaults:
291 9 50       152 $base = 2 unless defined $base;
292 9 50       188 my @digits = $alphabet ? split //, $alphabet : ('0' .. '9', 'A' .. 'Z');
293              
294 9 50       47 if ($base > scalar(@digits))
295             {
296 0         0 require Carp;
297 0         0 Carp::carp("Base $base higher base than number of digits (" . scalar @digits . ") in alphabet");
298             }
299              
300 9 50       34 if (!$x->is_pos())
301             {
302 0         0 require Carp;
303 0         0 Carp::carp("to_base() needs a positive number");
304             }
305              
306 9         205 my $o = $x->copy();
307 9         154 my $r;
308              
309 9         15 my $result = '';
310 9         25 while (!$o->is_zero)
311             {
312 25         672 ($o, $r) = $o->bdiv($base);
313 25         5344 $result = $digits[$r] . $result;
314             }
315              
316 9         489 $result;
317             }
318              
319             sub hailstone
320             {
321             # return in list context the hailstone sequence, in scalar context the
322             # number of steps to reach 1
323 11     11 1 7098 my ($n) = @_;
324              
325 11 50       51 $n = Math::BigInt->new($n) unless ref $n;
326              
327 11 50 33     304 return if $n->is_nan() || $n->is_negative();
328              
329             # Use the Math::BigInt lib directly for more speed, since all numbers
330             # involved are positive integers.
331              
332 11         161 my $lib = Math::BigInt->config()->{lib};
333 11         342 $n = $n->{value};
334 11         18 my $three_ = $three->{value};
335 11         15 my $two_ = $two->{value};
336              
337 11 100       23 if (wantarray)
338             {
339 4         4 my @seq;
340 4         12 while (! $lib->_is_one($n))
341             {
342             # push @seq, Math::BigInt->new( $lib->_str($n) );
343 47         356 push @seq, bless { value => $lib->_copy($n), sign => '+' }, "Math::BigInt";
344              
345             # was: ($n->is_odd()) ? ($n = $n * 3 + 1) : ($n = $n / 2);
346 47 100       189 if ($lib->_is_odd($n))
347             {
348 19         63 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  19         104  
349              
350             # We now know that $n is at least 10 ( (3 * 3) + 1 ) because $n > 1
351             # before we entered, and since $n was odd, it must have been at least
352             # 3. So the next step is $n /= 2:
353 19         83 push @seq, bless { value => $lib->_copy($n), sign => '+' }, "Math::BigInt";
354             # this is better, but slower:
355             #push @seq, Math::BigInt->new( $lib->_str($n) );
356             # next step is $n /= 2 as usual (we save the else {} block, too)
357             }
358 47         175 $n = $lib->_div($n, $two_);
359             }
360 4         37 push @seq, Math::BigInt->bone();
361 4         134 return @seq;
362             }
363              
364 7         8 my $i = 1;
365 7         18 while (! $lib->_is_one($n))
366             {
367 47         307 $i++;
368             # was: ($n->is_odd()) ? ($n = $n * 3 + 1) : ($n = $n / 2);
369 47 100       68 if ($lib->_is_odd($n))
370             {
371 18         56 $n = $lib->_mul ($n, $three_); $n = $lib->_inc ($n);
  18         112  
372              
373             # We now know that $n is at least 10 ( (3 * 3) + 1 ) because $n > 1
374             # before we entered, and since $n was odd, it must have been at least 3.
375             # So the next step is $n /= 2 as usual (we save the else {} block, too).
376 18         64 $i++; # one more (we know that $n cannot be 1)
377             }
378 47         136 $n = $lib->_div($n, $two_);
379             }
380 7         65 Math::BigInt->new($i);
381             }
382              
383             sub factorial
384             {
385             # calculate n! - use Math::BigInt bfac() for speed
386 7     7 1 5569 my ($n) = shift;
387              
388 7 50       21 if (ref($n))
389             {
390 0         0 $n->copy()->bfac();
391             }
392             else
393             {
394 7         36 Math::BigInt->new($n)->bfac();
395             }
396             }
397              
398             sub bernoulli
399             {
400             # returns the nth Bernoulli number. In scalar context as Math::BigFloat
401             # fraction, in list context as two Math:BigFloats, which, if divided, give
402             # the same result. The series runs this:
403             # 1/6, 1/30, 1/42, 1/30, 5/66, 691/2730, etc
404              
405             # Since I do not have yet a way to compute this, I have a table of the
406             # first 40. So bernoulli(41) will fail for now.
407              
408 61     61 1 53276 my $n = shift;
409              
410 61 50       228 return if $n < 0;
411 61         122 my @table_1 = ( 1,1, -1,2 ); # 0, 1
412 61         321 my @table = (
413             1,6, -1,30, 1,42, -1,30, 5,66, -691,2730, # 2, 4,
414             7,6, -3617,510, 43867,798,
415             -174611,330,
416             854513,138,
417             '-236364091',2730,
418             '8553103',6,
419             '-23749461029',870,
420             '8615841276005',14322,
421             '-7709321041217',510,
422             '2577687858367',6,
423             '-26315271553053477373',1919190,
424             '2929993913841559',6,
425             '-261082718496449122051',13530, # 40
426             );
427 61         81 my ($a,$b);
428 61 100       273 if ($n < 2)
    100          
    50          
429             {
430 2         10 $a = Math::BigFloat->new($table_1[$n*2]);
431 2         138 $b = Math::BigFloat->new($table_1[$n*2+1]);
432             }
433             # n is odd:
434             elsif (($n & 1) == 1)
435             {
436 20         98 $a = Math::BigFloat->bzero();
437 20         1379 $b = Math::BigFloat->bone();
438             }
439             elsif ($n <= 40)
440             {
441 39         51 $n -= 2;
442 39         170 $a = Math::BigFloat->new($table[$n]);
443 39         3271 $b = Math::BigFloat->new($table[$n+1]);
444             }
445             else
446             {
447 0 0       0 die 'Bernoulli numbers over 40 not yet implemented.' if $n > 40;
448             }
449 61 50       6634 wantarray ? ($a,$b): $a/$b;
450             }
451              
452             sub euler
453             {
454             # Calculate Euler's number.
455             # first argument is x, so that result is e ** x
456             # Second argument is accuracy (number of significant digits), it
457             # stops when at least so much plus one digits are 'stable' and then
458             # rounds it. Default is 42.
459 4     4 1 6318 my $x = $_[0];
460 4 50 33     44 $x = Math::BigFloat->new($x) if !ref($x) || (!$x->isa('Math::BigFloat'));
461              
462 4         594 $x->bexp($_[1]);
463             }
464              
465             sub sin
466             {
467             # calculate sinus
468             # first argument is x, so that result is sin(x)
469             # Second argument is accuracy (number of significant digits), it
470             # stops when at least so much plus one digits are 'stable' and then
471             # rounds it. Default is 42.
472 8 50   8 1 7300 my $x = shift; $x = 0 if !defined $x;
  8         30  
473 8   50     37 my $d = abs(shift || 42); $d = abs($d)+1;
  8         16  
474              
475 8 50       61 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
476              
477             # taylor: x^3 x^5 x^7 x^9
478             # sin = x - --- + --- - --- + --- ...
479             # 3! 5! 7! 9!
480              
481             # difference for each term is thus x^2 and 1,2
482              
483 8         884 my $sin = $x->copy(); my $last = 0;
  8         124  
484 8         11 my $sign = 1; # start with -=
485 8         26 my $x2 = $x * $x; # X ^ 2, difference between terms
486 8         670 my $over = $x2 * $x; # X ^ 3
487 8         606 my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  8         483  
488 8         465 while ($sin->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
489             {
490 46         6710 $last = $sin->copy();
491 46 100       649 if ($sign == 0)
492             {
493 22         45 $sin += $over->copy()->bdiv($below,$d);
494             }
495             else
496             {
497 24         49 $sin -= $over->copy()->bdiv($below,$d);
498             }
499 46         33056 $sign = 1-$sign; # alternate
500 46         97 $over *= $x2; # $x*$x
501 46         3178 $below *= $factorial; $factorial++; # n*(n+1)
  46         2871  
502 46         2496 $below *= $factorial; $factorial++;
  46         3250  
503             }
504 8         1162 $sin->bround($d-1);
505             }
506              
507             sub cos
508             {
509             # calculate cosinus
510             # first argument is x, so that result is cos(x)
511             # Second argument is accuracy (number of significant digits), it
512             # stops when at least so much plus one digits are 'stable' and then
513             # rounds it. Default is 42.
514 8 50   8 1 7434 my $x = shift; $x = 0 if !defined $x;
  8         25  
515 8   50     31 my $d = abs(shift || 42); $d = abs($d)+1;
  8         12  
516              
517 8 50       56 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
518              
519             # taylor: x^2 x^4 x^6 x^8
520             # cos = 1 - --- + --- - --- + --- ...
521             # 2! 4! 6! 8!
522              
523             # difference for each term is thus x^2 and 1,2
524              
525 8         852 my $cos = Math::BigFloat->bone(); my $last = 0;
  8         234  
526 8         26 my $over = $x * $x; # X ^ 2
527 8         665 my $x2 = $over->copy(); # X ^ 2; difference between terms
528 8         95 my $sign = 1; # start with -=
529 8         18 my $below = Math::BigFloat->new(2); my $factorial = Math::BigFloat->new(3);
  8         505  
530 8         449 while ($cos->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
531             {
532 52         7403 $last = $cos->copy();
533 52 100       765 if ($sign == 0)
534             {
535 24         48 $cos += $over->copy()->bdiv($below,$d);
536             }
537             else
538             {
539 28         51 $cos -= $over->copy()->bdiv($below,$d);
540             }
541 52         35013 $sign = 1-$sign; # alternate
542 52         112 $over *= $x2; # $x*$x
543 52         3650 $below *= $factorial; $factorial++; # n*(n+1)
  52         3489  
544 52         2772 $below *= $factorial; $factorial++;
  52         3170  
545             }
546 8         1084 $cos->round($d-1);
547             }
548              
549             sub tan
550             {
551             # calculate tangens
552             # first argument is x, so that result is tan(x)
553             # Second argument is accuracy (number of significant digits), it
554             # stops when at least so much plus one digits are 'stable' and then
555             # rounds it. Default is 42.
556 3 50   3 1 3921 my $x = shift; $x = 0 if !defined $x;
  3         12  
557 3   50     16 my $d = abs(shift || 42); $d = abs($d)+1;
  3         5  
558              
559 3 50       23 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
560              
561             # taylor: 1 2 3 4 5
562              
563             # x^3 x^5 x^7 x^9
564             # tan = x + 1 * ----- + 2 * ----- + 17 * ----- + 62 * ----- ...
565             # 3 15 315 2835
566             #
567             # 2^2n * ( 2^2n - 1) * Bn * x^(2n-1) 256*255 * 1 * x^7 17
568             # ---------------------------------- : n=4: ----------------- = --- * x^7
569             # (2n)! 40320 * 30 315
570             #
571             # 8! = 40320, B4 (Bernoully number 4) = 1/30
572              
573             # for each term we need: 2^2n, but if we have 2^2(n-1) we use n = (n-1)*2
574             # 2 copy, 7 bmul, 2 bdiv, 3 badd, 1 bernoulli
575              
576 3         316 my $tan = $x->copy(); my $last = 0;
  3         67  
577 3         71 my $x2 = $x*$x;
578 3         408 my $over = $x2*$x;
579 3         398 my $below = Math::BigFloat->new(24); # (1*2*3*4) (2n)!
580 3         209 my $factorial = Math::BigFloat->new(5); # for next (2n)!
581 3         275 my $two_n = Math::BigFloat->new(16); # 2^2n
582 3         169 my $two_factor = Math::BigFloat->new(4); # 2^2(n+1) = $two_n * $two_factor
583 3         252 my ($b,$b1,$b2); $b = 4;
  3         5  
584 3         15 while ($tan->bcmp($last) != 0) # no $x-$last > $diff because bdiv() limit on accuracy
585             {
586 19         2516 $last = $tan->copy();
587 19         391 ($b1,$b2) = bernoulli($b);
588 19         109 $tan += $over->copy()->bmul($two_n)->bmul($two_n - $fone)->bmul($b1->babs())->bdiv($below,$d)->bdiv($b2,$d);
589 19         39668 $over *= $x2; # x^3, x^5 etc
590 19         1872 $below *= $factorial; $factorial++; # n*(n+1)
  19         2074  
591 19         1606 $below *= $factorial; $factorial++;
  19         1905  
592 19         1619 $two_n *= $two_factor; # 2^2(n+1) = 2^2n * 4
593 19         1772 $b += 2; # next bernoulli index
594 19 100       104 last if $b > 40; # safeguard
595             }
596 3         400 $tan->round($d-1);
597             }
598              
599             sub sinh
600             {
601             # calculate sinus hyperbolicus
602             # first argument is x, so that result is sinh(x)
603             # Second argument is accuracy (number of significant digits), it
604             # stops when at least so much plus one digits are 'stable' and then
605             # rounds it. Default is 42.
606 2 50   2 1 1776 my $x = shift; $x = 0 if !defined $x;
  2         12  
607 2   50     9 my $d = abs(shift || 42); $d = abs($d)+1;
  2         4  
608              
609 2 50       14 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
610              
611             # taylor: x^3 x^5 x^7
612             # sinh = x + --- + --- + --- ...
613             # 3! 5! 7!
614              
615             # difference for each term is thus x^2 and 1,2
616              
617 2         190 my $sinh = $x->copy(); my $last = 0;
  2         46  
618 2         10 my $x2 = $x*$x;
619 2         263 my $over = $x2 * $x; my $below = Math::BigFloat->new(6); my $factorial = Math::BigFloat->new(4);
  2         223  
  2         168  
620 2         161 while ($sinh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on accuracy
621             {
622 0         0 $last = $sinh->copy();
623 0         0 $sinh += $over->copy()->bdiv($below,$d);
624 0         0 $over *= $x2; # $x*$x
625 0         0 $below *= $factorial; $factorial++; # n*(n+1)
  0         0  
626 0         0 $below *= $factorial; $factorial++;
  0         0  
627             }
628 2         382 $sinh->bround($d-1);
629             }
630              
631             sub cosh
632             {
633             # calculate cosinus hyperbolicus
634             # first argument is x, so that result is cosh(x)
635             # Second argument is accuracy (number of significant digits), it
636             # stops when at least so much plus one digits are 'stable' and then
637             # rounds it. Default is 42.
638 0 0   0 1 0 my $x = shift; $x = 0 if !defined $x;
  0         0  
639 0   0     0 my $d = abs(shift || 42); $d = abs($d)+1;
  0         0  
640              
641 0 0       0 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
642              
643             # taylor: x^2 x^4 x^6
644             # cosh = x + --- + --- + --- ...
645             # 2! 4! 6!
646              
647             # difference for each term is thus x^2 and 1,2
648              
649 0         0 my $cosh = Math::BigFloat->bone(); my $last = 0;
  0         0  
650 0         0 my $x2 = $x*$x;
651 0         0 my $over = $x2; my $below = Math::BigFloat->new(); my $factorial = Math::BigFloat->new(3);
  0         0  
  0         0  
652 0         0 while ($cosh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on accuracy
653             {
654 0         0 $last = $cosh->copy();
655 0         0 $cosh += $over->copy()->bdiv($below,$d);
656 0         0 $over *= $x2; # $x*$x
657 0         0 $below *= $factorial; $factorial++; # n*(n+1)
  0         0  
658 0         0 $below *= $factorial; $factorial++;
  0         0  
659             }
660 0         0 $cosh->bround($d-1);
661             }
662              
663             sub arctan
664             {
665             # calculate arcus tangens
666             # first argument is x, so that result is arctan(x)
667             # Second argument is accuracy (number of significant digits), it
668             # stops when at least so much plus one digits are 'stable' and then
669             # rounds it. Default is 42.
670 8 50   8 1 6075 my $x = shift; $x = 0 if !defined $x;
  8         34  
671 8   50     32 my $d = abs(shift || 42); $d = abs($d)+1;
  8         17  
672              
673 8 100       45 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
674              
675             # taylor: x^3 x^5 x^7 x^9
676             # arctan = x - --- + --- - --- + --- ...
677             # 3 5 7 9
678              
679             # difference for each term is thus x^2 and 2:
680             # 2 copy, 1 bmul, 1 badd, 1 bdiv
681              
682 8         604 my $arctan = $x->copy(); my $last = 0;
  8         145  
683 8         33 my $x2 = $x*$x;
684 8         1546 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  8         1635  
  8         696  
685 8         547 my $sign = 1;
686 8         27 while ($arctan->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
687             {
688 85         19505 $last = $arctan->copy();
689 85 100       1471 if ($sign == 0)
690             {
691 42         96 $arctan += $over->copy()->bdiv($below,$d);
692             }
693             else
694             {
695 43         87 $arctan -= $over->copy()->bdiv($below,$d);
696             }
697 85         73236 $sign = 1-$sign; # alternate
698 85         249 $over *= $x2; # $x*$x
699 85         13628 $below += $add;
700             }
701 8         1731 $arctan->bround($d-1);
702             }
703              
704             sub arctanh
705             {
706             # calculate arcus tangens hyperbolicus
707             # first argument is x, so that result is arctanh(x)
708             # Second argument is accuracy (number of significant digits), it
709             # stops when at least so much plus one digits are 'stable' and then
710             # rounds it. Default is 42.
711 2 50   2 1 1827 my $x = shift; $x = 0 if !defined $x;
  2         7  
712 2   50     8 my $d = abs(shift || 42); $d = abs($d)+1;
  2         4  
713              
714 2 50       12 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
715              
716             # taylor: x^3 x^5 x^7 x^9
717             # arctanh = x + --- + --- + --- + --- + ...
718             # 3 5 7 9
719              
720             # difference for each term is thus x^2 and 2:
721             # 2 copy, 1 bmul, 1 badd, 1 bdiv
722              
723 2         165 my $arctanh = $x->copy(); my $last = 0;
  2         27  
724 2         6 my $x2 = $x*$x;
725 2         161 my $over = $x2*$x; my $below = Math::BigFloat->new(3); my $add = Math::BigFloat->new(2);
  2         141  
  2         115  
726 2         102 while ($arctanh->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
727             {
728 0         0 $last = $arctanh->copy();
729 0         0 $arctanh += $over->copy()->bdiv($below,$d);
730 0         0 $over *= $x2; # $x*$x
731 0         0 $below += $add;
732             }
733 2         234 $arctanh->bround($d-1);
734             }
735              
736             sub arcsin
737             {
738             # calculate arcus sinus
739             # first argument is x, so that result is arcsin(x)
740             # Second argument is accuracy (number of significant digits), it
741             # stops when at least so much plus one digits are 'stable' and then
742             # rounds it. Default is 42.
743 4 50   4 1 3698 my $x = shift; $x = 0 if !defined $x;
  4         16  
744 4   100     24 my $d = abs(shift || 42); $d = abs($d)+1;
  4         9  
745              
746 4 50       27 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
747              
748             # taylor: 1 * x^3 1 * 3 * x^5 1 * 3 * 5 * x^7
749             # arcsin = x + ------- + ----------- + --------------- + ...
750             # 2 * 3 2 * 4 * 5 2 * 4 * 6 * 7
751              
752             # difference for each term is thus x^2 and two muls (fac1, fac2):
753             # 3 copy, 3 bmul, 1 bdiv, 3 badd
754              
755 4         546 my $arcsin = $x->copy(); my $last = 0;
  4         85  
756 4         17 my $x2 = $x*$x;
757 4         868 my $over = $x2*$x; my $below = Math::BigFloat->new(6);
  4         625  
758 4         385 my $fac1 = Math::BigFloat->new(1);
759 4         498 my $fac2 = Math::BigFloat->new(2);
760 4         383 my $two = Math::BigFloat->new(2);
761 4         343 while ($arcsin->bcmp($last)) # no $x-$last > $diff because bdiv() limit on A
762             {
763 30         11058 $last = $arcsin->copy();
764 30         777 $arcsin += $over->copy()->bmul($fac1)->bdiv($below->copy->bmul($fac2),$d);
765 30         47225 $over *= $x2; # $x*$x
766 30         3978 $below += $one;
767 30         9777 $fac1 += $two;
768 30         5020 $fac2 += $two;
769             }
770 4         939 $arcsin->bround($d-1);
771             }
772              
773             sub arcsinh
774             {
775             # calculate arcus sinus hyperbolicus
776             # first argument is x, so that result is arcsinh(x)
777             # Second argument is accuracy (number of significant digits), it
778             # stops when at least so much plus one digits are 'stable' and then
779             # rounds it. Default is 42.
780 2 50   2 1 2489 my $x = shift; $x = 0 if !defined $x;
  2         9  
781 2   50     11 my $d = abs(shift || 42); $d = abs($d)+1;
  2         5  
782              
783 2 50       28 $x = Math::BigFloat->new($x) if ref($x) ne 'Math::BigFloat';
784              
785             # taylor: 1 * x^3 1 * 3 * x^5 1 * 3 * 5 * x^7
786             # arcsin = x - ------- + ----------- - --------------- + ...
787             # 2 * 3 2 * 4 * 5 2 * 4 * 6 * 7
788              
789             # difference for each term is thus x^2 and two muls (fac1, fac2):
790             # 3 copy, 3 bmul, 1 bdiv, 3 badd
791              
792 2         231 my $arcsinh = $x->copy(); my $last = 0;
  2         41  
793 2         10 my $x2 = $x*$x; my $sign = 0;
  2         251  
794 2         8 my $over = $x2*$x; my $below = 6;
  2         260  
795 2         12 my $fac1 = Math::BigInt->new(1);
796 2         87 my $fac2 = Math::BigInt->new(2);
797 2         64 while ($arcsinh ne $last) # no $x-$last > $diff because bdiv() limit on A
798             {
799 0         0 $last = $arcsinh->copy();
800 0 0       0 if ($sign == 0)
801             {
802 0         0 $arcsinh += $over->copy()->bmul(
803             $fac1)->bdiv($below->copy->bmul($fac2),$d);
804             }
805             else
806             {
807 0         0 $arcsinh -= $over->copy()->bmul(
808             $fac1)->bdiv($below->copy->bmul($fac2),$d);
809             }
810 0         0 $over *= $x2; # $x*$x
811 0         0 $below += $one;
812 0         0 $fac1 += $two;
813 0         0 $fac2 += $two;
814             }
815 2         78 $arcsinh->round($d-1);
816             }
817              
818             sub log
819             {
820 5     5 1 3454 my ($x,$base,$d) = @_;
821              
822 5         5 my $y;
823 5 50 33     20 if (!ref($x) || !$x->isa('Math::BigFloat'))
824             {
825 5         28 $y = Math::BigFloat->new($x);
826             }
827             else
828             {
829 0         0 $y = $x->copy();
830             }
831 5         510 $y->blog($base,$d);
832 5         1852 $y;
833             }
834              
835             sub pi
836             {
837             # calculate PI (as suggested by Robert Creager)
838 2   50 2 1 1979 my $digits = abs(shift || 1024);
839              
840 2         7 my $d = $digits+5;
841              
842 2         13 my $pi = $sixteen * arctan( scalar $fone->copy()->bdiv($five,$d), $d )
843             - $four * arctan( scalar $fone->copy()->bdiv($twothreenine,$d), $d);
844 2         1633 $pi->bround($digits+1); # +1 for the "3."
845             }
846              
847             1;
848              
849             __END__