File Coverage

blib/lib/ConstantCalculus/CircleConstant.pm
Criterion Covered Total %
statement 37 225 16.4
branch 4 56 7.1
condition 3 3 100.0
subroutine 9 28 32.1
pod 15 21 71.4
total 68 333 20.4


line stmt bran cond sub pod time code
1             package ConstantCalculus::CircleConstant;
2              
3             # Set the VERSION.
4             our $VERSION = "0.02";
5              
6             # Load the Perl pragmas.
7 1     1   75228 use 5.008009;
  1         16  
8 1     1   5 use strict;
  1         3  
  1         20  
9 1     1   5 use warnings;
  1         1  
  1         53  
10              
11             # Load the Perl pragma Exporter.
12 1     1   7 use vars qw(@ISA @EXPORT @EXPORT_OK);
  1         12  
  1         90  
13 1     1   8 use Exporter 'import';
  1         2  
  1         107  
14              
15             # Base class of this (tron_addr) module.
16             our @ISA = qw(Exporter);
17              
18             # Exporting the implemented subroutine.
19             our @EXPORT = qw(
20             chudnovsky_algorithm
21             borwein25_algorithm
22             pi_borwein25
23             tau_borwein25
24             pi_chudnovsky
25             tau_chudnovsky
26             bbp_algorithm
27             bbp_digits
28             bbp_digit
29             S
30             $PI_DEC
31             $PI_HEX
32             );
33              
34             # Exporting the multiply and divide routine on demand basis.
35             @EXPORT_OK = qw(
36             factorial
37             sqrtroot
38             modexp
39             estimate_terms
40             truncate_places
41             );
42              
43             # Disable a warning.
44 1     1   7 no warnings 'recursion';
  1         2  
  1         34  
45              
46             # Load the Perl module.
47 1     1   760 use bignum;
  1         5868  
  1         6  
48              
49             # Set the precision for bignum.
50             bignum -> precision(-14);
51              
52             # Initialise pre-defined values of Pi.
53             our $PI_DEC = undef;
54             our $PI_HEX = undef;
55              
56             # Initialise the array with the hexadecimal numbers.
57             our @HX = ("0", "1", "2", "3", "4", "5", "6", "7",
58             "8", "9", "A", "B", "C", "D", "E", "F");
59              
60             # ---------------------------------------------------------------------------- #
61             # Subroutine modexp() #
62             # #
63             # Description: #
64             # The subroutine returns the result of a modular exponentiation. A modular #
65             # exponentiation is an exponentiation applied over a modulus. The result of #
66             # the modular exponentiation is the remainder when an integer b (the base) #
67             # is exponentiated by e (the exponent) and divided by a positive integer m #
68             # (modulus). #
69             # #
70             # @params $b Value of the base #
71             # $e Value of the exponent #
72             # $m Value of the modulus #
73             # @returns $y Result of the modular exponentiation #
74             # ---------------------------------------------------------------------------- #
75             sub modexp {
76             # Assign the subroutine arguments to the local variables.
77 0     0 1 0 my($b, $e, $m) = @_;
78             # Initialise the return variable.
79 0         0 my $y = 1;
80             # Perfom the modular exponentiation.
81 0         0 do {
82 0 0       0 ($y *= $b) %= $m if $e % 2;
83 0         0 ($b *= $b) %= $m;
84             } while ($e = ($e/2) - (($e/2) % 1));
85             # Return the result of the modular exponentiation.
86 0         0 return $y;
87             };
88              
89             # ---------------------------------------------------------------------------- #
90             # Subroutine sqrtroot() #
91             # #
92             # Description: #
93             # The subroutine calculates the square root of given number. #
94             # #
95             # @params $x Number for which the square root is sought #
96             # @returns $y Square root of the given number $x #
97             # ---------------------------------------------------------------------------- #
98             sub sqrtroot {
99             # Assign the subroutine argument to the local variable.
100 0     0 1 0 my $x = $_[0];
101             # Set the start values for the iteration.
102 0         0 my $y = 1;
103 0         0 my $y0 = 0;
104             # Initialise the loop variables.
105 0         0 my $i = 0;
106 0         0 my $run = 1;
107             # Run an infinite loop until the exit conditions is reached.
108 0         0 while ($run == 1) {
109             # Calculate the approximation.
110 0         0 $y -= ($y * $y - $x) / (2 * $y);
111             # Check termination criterion.
112 0 0       0 $run = ($y0 == $y ? 0 : 1);
113             # Store the approximation.
114 0         0 $y0 = $y;
115             # Increment the running variable.
116 0         0 $i++;
117             };
118             # Return the square root.
119 0         0 return $y;
120             };
121              
122             # ---------------------------------------------------------------------------- #
123             # Subroutine factorial() #
124             # #
125             # Description: #
126             # The subroutine calculates the factorial of given number. #
127             # #
128             # @params $n Number for which the factorial is sought #
129             # @returns $fact Factorial of the given number $n #
130             # ---------------------------------------------------------------------------- #
131             sub factorial {
132             # Assign the subroutine argument to the local variable.
133 0     0 1 0 my $n = $_[0];
134             # Calculate the factorial of a number by recursion.
135 0 0       0 my $fact = ($n == 0 ? 1 : factorial($n - 1) * $n);
136             # Return the value of the factorial.
137 0         0 return $fact;
138             };
139              
140             # ---------------------------------------------------------------------------- #
141             # Subroutine truncate_places() #
142             # ---------------------------------------------------------------------------- #
143             sub truncate_places {
144             # Assign the subroutine arguments to the local variables.
145 0     0 1 0 my $decimal = $_[0];
146 0         0 my $places = $_[1];
147             # Calculate the multiplication factor.
148 0         0 my $factor = 10**$places;
149             # Truncate the places using the multiplication factor.
150 0         0 $decimal = int($decimal*$factor) / ($factor);
151             # Truncate the decimal using the places plus two chars.
152 0         0 $decimal = substr($decimal, 0, $places + 2);
153             # Return the truncated decimal number.
154 0         0 return $decimal;
155             };
156              
157             # ---------------------------------------------------------------------------- #
158             # Subroutine estimate_terms() #
159             # ---------------------------------------------------------------------------- #
160             sub estimate_terms {
161             # Assign the subroutine arguments to the local variables.
162 0 0   0 1 0 my $places = (defined $_[0] ? $_[0] : 0);
163 0 0       0 my $new_places = (defined $_[1] ? $_[1] : 0);
164             # Declare the return variable.
165 0         0 my $number_terms = undef;
166             # Determine the estimated number of terms.
167 0 0       0 if ($places <= 1) {
    0          
168 0         0 $number_terms = $places;
169             } elsif ($places <= $new_places) {
170 0         0 $number_terms = 1;
171             } else {
172 0         0 $number_terms = int($places / $new_places) + 1;
173             };
174             # Return the estimated number of terms.
175 0         0 return $number_terms;
176             };
177              
178             #------------------------------------------------------------------------------#
179             # Subroutine is_unsigned_int() #
180             #------------------------------------------------------------------------------#
181             sub is_unsigned_int {
182             # Assign the subroutine argument to the local variable.
183 0 0   0 0 0 my $arg = (defined $_[0] ? $_[0] : '');
184             # Set the Perl conform regex pattern.
185 0         0 my $re = qr/^(([1-9][0-9]*)|0)$/;
186             # Check the argument with the regex pattern.
187 0 0       0 my $is_unsigned_int = (($arg =~ $re) ? 1 : 0);
188             # Return 0 (false) or 1 (true).
189 0         0 return $is_unsigned_int;
190             };
191              
192             # ---------------------------------------------------------------------------- #
193             # Subroutine check_places() #
194             # ---------------------------------------------------------------------------- #
195             sub check_places {
196             # Assign the subroutine argument to the local variable.
197 0 0   0 0 0 my $places = (defined $_[0] ? $_[0] : 0);
198             # Set the string for the farewell message.
199 0         0 my $msg = "The number of places is not valid. Terminating processing. Bye!";
200             # Check if the given places are a valid number.
201 0 0       0 if (is_unsigned_int($places) != 1) {
202             # Print a message into the terminal window.
203 0         0 print $msg . "\n";
204             # Exit the script.
205 0         0 exit;
206             };
207             # Return 1 (true).
208 0         0 return 1;
209             };
210              
211             # ---------------------------------------------------------------------------- #
212             # Subroutine S() #
213             # ---------------------------------------------------------------------------- #
214             sub S {
215             # Assign the subroutine arguments to local variables.
216 0     0 1 0 my ($j, $n, $p) = @_;
217             # Set the precision for bignum.
218 0         0 bignum -> precision(-$p);
219             # Initialise the variables for the left sum as well as the right sum.
220 0         0 my ($l, $r) = (0, 0);
221             # Initialise the variables for the total sum.
222 0         0 my $sum = 0;
223             # Initialise the variable for the denominator.
224 0         0 my $d = 0;
225             # Initialise the loop counter.
226 0         0 my $k = 0;
227             # Calculate the left sum.
228 0         0 while ($k <= $n) {
229             # Calculate the value of the denominator.
230 0         0 $d = 8*$k+$j;
231             # Calculate the left partial sum.
232 0         0 $l = ($l + modexp(16, $n-$k, $d) / $d) % 1;
233             # Increment the loop counter $k.
234 0         0 $k += 1;
235             };
236             # Initialise the variables for the right sum calculation.
237 0         0 my $r0 = 0;
238             # Reinitialise the loop counter.
239 0         0 $k = $n + 1;
240             # Initialise the run variable.
241 0         0 my $run = 1;
242             # Calculate the right sum.
243 0         0 while ($run == 1) {
244             # Calculate the value of the denominator.
245 0         0 $d = 8*$k+$j;
246             # Calculate the right partial sum.
247 0         0 $r = $r0 + (16**($n-$k)) / $d;
248             # Check the exit condition of the loop.
249 0 0       0 $run = ($r0 == $r ? 0 : 1);
250             # Save the old partial sum.
251 0         0 $r0 = $r;
252             # Increment the loop counter $k.
253 0         0 $k += 1;
254             };
255             # Calculate the total sum.
256 0         0 $sum = $l + $r;
257             # Return the total sum.
258 0         0 return $sum;
259             };
260              
261             #------------------------------------------------------------------------------#
262             # Subroutine bbp_digit() #
263             #------------------------------------------------------------------------------#
264             sub bbp_digit {
265             # Assign the subroutine arguments to the local variables.
266 0     0 1 0 my ($n, $p) = @_;
267             # Set the precision for bignum.
268 0         0 bignum -> precision(-$p);
269             # Decrement the value with the digit of interest by 1.
270 0         0 $n -= 1;
271             # Calculate the fraction with the hex numbers from the nth digit upwards.
272 0         0 my $dec = (4*S(1, $n, $p) - 2*S(4, $n, $p) -
273             S(5, $n, $p) - S(6, $n, $p)) % 1;
274             # Get the decimal representation of the digit..
275 0         0 $dec = ($dec * 16) - ($dec * 16) % 1;
276             # Get the hexadecimal representation of the digit.
277 0         0 my $hex = $HX[$dec];
278             # Return the hex number.
279 0         0 return $hex;
280             };
281              
282             #------------------------------------------------------------------------------#
283             # Subroutine bbp_digits() #
284             #------------------------------------------------------------------------------#
285             sub bbp_digits {
286             # Assign the subroutine arguments to the local variables.
287 0     0 1 0 my ($n, $digits, $p) = @_;
288             # Set the precision for bignum.
289 0         0 bignum -> precision(-$p);
290             # Initialise the local variables.
291 0         0 my $newdec = undef;
292 0         0 my $hex_digit = '';
293 0         0 my $hex_digits = '';
294             # Decrement the value with the digit of interest by 1.
295 0         0 $n -= 1;
296             # Calculate the fraction with the hex numbers from the nth digit upwards.
297 0         0 my $dec = (4*S(1, $n, $p) - 2*S(4, $n, $p) -
298             S(5, $n, $p) - S(6, $n, $p)) % 1;
299             # Calculate the hexadecimal number.
300 0         0 for (my $j = 0; $j < length($dec); $j++) {
301 0         0 $newdec = $dec * 16;
302 0         0 $dec = $newdec - ($newdec % 1);
303 0         0 $hex_digit = $HX[$dec];
304 0         0 $hex_digits = $hex_digits . $hex_digit;
305 0         0 $dec = $newdec - $dec;
306             };
307             # Return the hexadecimal number.
308 0         0 return substr($hex_digits, 0, $digits);
309             };
310              
311             #------------------------------------------------------------------------------#
312             # Subroutine bbp_algorith() #
313             #------------------------------------------------------------------------------#
314             sub bbp_algorithm {
315             # Assign the subroutine arguments to the local variables.
316 0     0 1 0 my ($places, $p) = @_;
317             # Set the precision for bignum.
318 0         0 bignum -> precision(-$p);
319             # Initialise the local variable $pi_hex.
320 0         0 my $pi_hex = "3.";
321             # Declare the local variable $nth_digit.
322 0         0 my $digits = undef;
323             # Determine the hexadecimal places in a loop.
324 0         0 for (my $i = 1; $i <= $places; $i++) {
325 0         0 $digits = bbp_digit($i, $p);
326 0         0 $pi_hex = $pi_hex . $digits;
327             };
328             # Return Pi in hexadecimal representation.
329 0         0 return $pi_hex;
330             };
331              
332             # ---------------------------------------------------------------------------- #
333             # Subroutine borwein25_algorithm() #
334             # ---------------------------------------------------------------------------- #
335             sub borwein25_algorithm {
336             # Assign the hash with the subroutine arguments to the local hash.
337 0     0 1 0 my (%params) = @_;
338             # Initialise the local variables.
339 0         0 my $type0 = "pi";
340 0         0 my $type1 = "tau";
341             # Make a reference from the hash.
342 0         0 my $args = \%params;
343             # Initialise the locale variables.
344 0         0 my $places = $args->{Places};
345 0         0 my $n = $args->{Terms};
346 0         0 my $p = $args->{Precision};
347 0         0 my $trim = $args->{Trim};
348 0         0 my $type = $args->{Type};
349             # Check if the the local variables are defined.
350 0 0       0 $places = (defined $places ? $places : 0);
351 0 0       0 $n = (defined $n ? $n : 0);
352 0 0       0 $p = (defined $p ? $p : $places);
353 0 0       0 $trim = (defined $trim ? $trim : $places);
354 0 0       0 $type = (defined $type ? $type : $type0);
355             # Set the precision.
356 0         0 bignum -> precision(-$p);
357             # Initialise the return variable.
358 0         0 my $circle_constant = 0;
359             # Set the local constants.
360 0         0 my $sqrt61 = sqrtroot(61);
361 0         0 my $A = (212175710912 * $sqrt61) + 1657145277365;
362 0         0 my $B = (13773980892672 * $sqrt61) + 107578229802750;
363 0         0 my $C = (5280 * (236674 + 30303 * $sqrt61))**3;
364 0         0 my $factor = sqrtroot($C) / 12;
365             # Set the initial values.
366 0         0 my $a_k = 0;
367 0         0 my $a_sum = 0;
368 0         0 my $numerator = 0;
369 0         0 my $denominator = 0;
370             # Set the sign to 1.
371 0         0 my $sign = 1;
372             # Run index is of type int.
373 0         0 for (my $k = 0; $k <= $n; $k++) {
374             # Calculate numerator and denominator.
375 0         0 $numerator = factorial(6*$k)*($A +$k*$B);
376 0         0 $denominator = (factorial($k)**3)*factorial(3*$k)*($C**($k));
377             # Calculate the quotient.
378 0         0 $a_k = $sign*($numerator / $denominator);
379             # Add quotient to total sum.
380 0         0 $a_sum += $a_k;
381             # Change the sign.
382 0         0 $sign *= -1;
383             };
384             # Calculate the value of the circle_constant.
385 0         0 $circle_constant = $factor / $a_sum;
386             # Determine the value for Pi or for Tau.
387 0 0       0 if ($type eq $type0) {
    0          
388 0         0 $circle_constant = 1.0 * $circle_constant;
389             } elsif ($type eq $type1) {
390 0         0 $circle_constant = 2.0 * $circle_constant;
391             };
392             # Cut the decimal number to the needed places.
393 0         0 $circle_constant = truncate_places($circle_constant, $trim);
394             # Return the value of the circle constant.
395 0         0 return "$circle_constant";
396             };
397              
398             # ---------------------------------------------------------------------------- #
399             # Subroutine chudnovsky_algorithm() #
400             # ---------------------------------------------------------------------------- #
401             sub chudnovsky_algorithm {
402             # Assign the hash with the subroutine arguments to the local hash.
403 0     0 1 0 my (%params) = @_;
404             # Initialise the local variables.
405 0         0 my $type0 = "pi";
406 0         0 my $type1 = "tau";
407             # Make a reference from the hash.
408 0         0 my $args = \%params;
409             # Initialise the locale variables.
410 0         0 my $places = $args->{Places};
411 0         0 my $n = $args->{Terms};
412 0         0 my $p = $args->{Precision};
413 0         0 my $trim = $args->{Trim};
414 0         0 my $type = $args->{Type};
415             # Check if the the local variables are defined.
416 0 0       0 $places = (defined $places ? $places : 0);
417 0 0       0 $n = (defined $n ? $n : 0);
418 0 0       0 $p = (defined $p ? $p : $places);
419 0 0       0 $trim = (defined $trim ? $trim : $places);
420 0 0       0 $type = (defined $type ? $type : $type0);
421             # Set the precision.
422 0         0 bignum -> precision(-$p);
423             # Initialise the return variable.
424 0         0 my $circle_constant = 0;
425             # Set the local integer constants.
426 0         0 my $c0 = 545140134;
427 0         0 my $c1 = 13591409;
428 0         0 my $c2 = 640320;
429 0         0 my $c3 = 4270934400;
430 0         0 my $c4 = 10005;
431             # Set the initial values.
432 0         0 my $a_k = 0;
433 0         0 my $a_sum = 0;
434 0         0 my $numerator = 0;
435 0         0 my $denominator = 0;
436             # Set the sign to 1.
437 0         0 my $sign = 1;
438             # Run index is of type int.
439 0         0 for (my $k = 0; $k <= $n; $k++) {
440             # Calculate numerator and denominator.
441 0         0 $numerator = factorial(6*$k)*($c0*$k+$c1);
442 0         0 $denominator = factorial(3*$k)*(factorial($k)**3)*($c2**(3*$k));
443             # Calculate the quotient.
444 0         0 $a_k = $sign*($numerator / $denominator);
445             # Add quotient to total sum.
446 0         0 $a_sum += $a_k;
447             # Change the sign.
448 0         0 $sign *= -1;
449             };
450             # Calculate the value of the circle_constant.
451 0         0 $circle_constant = $c3 / (sqrt($c4)*$a_sum);
452             # Determine Pi or Tau.
453 0 0       0 if ($type eq $type0) {
    0          
454 0         0 $circle_constant = 1.0 * $circle_constant;
455             } elsif ($type eq $type1) {
456 0         0 $circle_constant = 2.0 * $circle_constant;
457             };
458             # Cut the decimal number to the needed places.
459 0         0 $circle_constant = truncate_places($circle_constant, $trim);
460             # Return the value of the circle constant.
461 0         0 return "$circle_constant";
462             };
463              
464             # ---------------------------------------------------------------------------- #
465             # Subroutine chudnovsky_caller() #
466             # ---------------------------------------------------------------------------- #
467             sub chudnovsky_caller {
468             # Assign the subroutine arguments to the local variables.
469 0     0 0 0 my $places = $_[0];
470 0         0 my $method = $_[1];
471             # Check if the places are valid. If not exit the script.
472 0         0 check_places($places);
473             # Calculate the required number of terms.
474 0         0 my $terms = estimate_terms($places, 14);
475             # Set the precision for the calculation.
476 0         0 my $precision = $places + 15;
477             # Get the value of the circle constant from the calculation.
478 0         0 my $circle_constant = chudnovsky_algorithm(
479             Places => $places,
480             Terms => $terms,
481             Precision => $precision,
482             Trim => $places,
483             Type => $method
484             );
485             # Return the value of the circle constant Pi or Tau.
486 0         0 return "$circle_constant";
487             };
488              
489             # ---------------------------------------------------------------------------- #
490             # Subroutine borwein1989_caller() #
491             # ---------------------------------------------------------------------------- #
492             sub borwein25_caller {
493             # Assign the subroutine arguments to the local variables.
494 0     0 0 0 my $places = $_[0];
495 0         0 my $method = $_[1];
496             # Check if the places are valid. If not exit the script.
497 0         0 check_places($places);
498             # Calculate the required number of terms.
499 0         0 my $terms = estimate_terms($places, 25);
500             # Set the precision for the calculation.
501 0         0 my $precision = $places + 26;
502             # Get the value of the circle constant from the calculation.
503 0         0 my $circle_constant = borwein25_algorithm(
504             Places => $places,
505             Terms => $terms,
506             Precision => $precision,
507             Trim => $places,
508             Type => $method
509             );
510             # Return the value of the circle constant Pi or Tau.
511 0         0 return "$circle_constant";
512             };
513              
514             # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
515             # Call the caller subroutines #
516             # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
517 0     0 1 0 sub pi_chudnovsky { return chudnovsky_caller($_[0], "pi") };
518 0     0 1 0 sub tau_chudnovsky { return chudnovsky_caller($_[0], "tau") };
519 0     0 1 0 sub pi_borwein25 { return borwein25_caller($_[0], "pi") };
520 0     0 1 0 sub tau_borwein25 { return borwein25_caller($_[0], "tau") };
521              
522             # ============================================================================ #
523             # Subroutine read_pi() #
524             # ============================================================================ #
525             sub read_pi {
526             # Assign the subroutine argument to the local variable.
527 2     2 0 5 my $type = $_[0];
528             # Initialise the local variables.
529 2         4 my $line = "";
530 2         5 my $pi = "";
531             # Assemble the begin and end pattern.
532 2         4 my $pattern0 = "# Begin Pi ${type}";
533 2         4 my $pattern1 = "# End Pi ${type}";
534             # Read the data from the DATA section.
535 2         21 while () {
536             # Read data only between the two given pattern.
537 1058 100       2782 if (/$pattern0/../$pattern1/) {
538 46 100 100     232 next if /$pattern0/ || /$pattern1/;
539 42         71 $line = $_;
540 42         331 $line =~ s/^\s+|\s+$//g;
541 42         128 $pi = $pi . $line;
542             };
543             };
544             # Return Pi.
545 2         16 return $pi;
546             };
547              
548             # ============================================================================ #
549             # Subroutine read_data() #
550             # #
551             # Description: #
552             # The subroutine reads Pi in decimal and hexadecimal representation from the #
553             # DATA section. #
554             # ============================================================================ #
555             sub read_data {
556             # Save the position of DATA.
557 1     1 0 3 my $data_start = tell DATA;
558             # Read decimal Pi with 1000 places.
559 1         2 my $pi_dec = uc(read_pi("dec"));
560             # Reposition the filehandle right past to __DATA__
561 1         8 seek DATA, $data_start, 0;
562             # Read hexadecimal Pi with 1000 places.
563 1         51 my $pi_hex = uc(read_pi("hex"));
564             # Return PI.
565 1         5 return $pi_dec, $pi_hex;
566             };
567              
568             # Read data from DATA.
569             ($PI_DEC, $PI_HEX) = read_data();
570              
571             1;
572              
573             __DATA__