File Coverage

blib/lib/ConstantCalculus/CircleConstant.pm
Criterion Covered Total %
statement 37 226 16.3
branch 4 56 7.1
condition 3 3 100.0
subroutine 9 28 32.1
pod 15 21 71.4
total 68 334 20.3


line stmt bran cond sub pod time code
1             package ConstantCalculus::CircleConstant;
2              
3             # Set the VERSION.
4             our $VERSION = "0.03";
5              
6             # Load the Perl pragmas.
7 1     1   75236 use 5.008009;
  1         4  
8 1     1   11 use strict;
  1         2  
  1         20  
9 1     1   11 use warnings;
  1         2  
  1         50  
10              
11             # Load the Perl pragma Exporter.
12 1     1   11 use vars qw(@ISA @EXPORT @EXPORT_OK);
  1         12  
  1         75  
13 1     1   10 use Exporter 'import';
  1         2  
  1         132  
14              
15             # Base class of this 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 subroutines 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         42  
45              
46             # Load the Perl module.
47 1     1   654 use bignum;
  1         5686  
  1         11  
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 the exit condition.
112 0 0       0 $run = ($y0 == $y ? 0 : 1);
113             # Save 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 local variables.
220 0         0 my $sum = 0;
221 0         0 my $l = 0;
222 0         0 my $r = 0;
223 0         0 my $d = 0;
224 0         0 my $k = 0;
225 0         0 my $r0 = 0;
226 0         0 my $run = 1;
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             # Reset the loop counter.
237 0         0 $k = $n + 1;
238             # Calculate the right sum.
239 0         0 while ($run == 1) {
240             # Calculate the value of the denominator.
241 0         0 $d = 8*$k+$j;
242             # Calculate the right partial sum.
243 0         0 $r = $r0 + (16**($n-$k)) / $d;
244             # Check the exit condition of the loop.
245 0 0       0 $run = ($r0 == $r ? 0 : 1);
246             # Save the old partial sum.
247 0         0 $r0 = $r;
248             # Increment the loop counter $k.
249 0         0 $k += 1;
250             };
251             # Calculate the total sum.
252 0         0 $sum = $l + $r;
253             # Return the total sum.
254 0         0 return $sum;
255             };
256              
257             #------------------------------------------------------------------------------#
258             # Subroutine bbp_digit() #
259             #------------------------------------------------------------------------------#
260             sub bbp_digit {
261             # Assign the subroutine arguments to the local variables.
262 0     0 1 0 my ($n, $p) = @_;
263             # Set the precision for bignum.
264 0         0 bignum -> precision(-$p);
265             # Decrement the value with the digit of interest by 1.
266 0         0 $n -= 1;
267             # Calculate the fraction with the hex numbers from the nth digit upwards.
268 0         0 my $dec = (4*S(1, $n, $p) - 2*S(4, $n, $p) -
269             S(5, $n, $p) - S(6, $n, $p)) % 1;
270             # Get the decimal representation of the digit.
271 0         0 $dec = ($dec * 16) - ($dec * 16) % 1;
272             # Get the hexadecimal representation of the digit.
273 0         0 my $hex = $HX[$dec];
274             # Return the hex number.
275 0         0 return $hex;
276             };
277              
278             #------------------------------------------------------------------------------#
279             # Subroutine bbp_digits() #
280             #------------------------------------------------------------------------------#
281             sub bbp_digits {
282             # Assign the subroutine arguments to the local variables.
283 0     0 1 0 my ($n, $digits, $p) = @_;
284             # Set the precision for bignum.
285 0         0 bignum -> precision(-$p);
286             # Initialise the local variables.
287 0         0 my $newdec = undef;
288 0         0 my $hex_digit = '';
289 0         0 my $hex_digits = '';
290             # Decrement the value with the digit of interest by 1.
291 0         0 $n -= 1;
292             # Calculate the fraction with the hex numbers from the nth digit upwards.
293 0         0 my $dec = (4*S(1, $n, $p) - 2*S(4, $n, $p) -
294             S(5, $n, $p) - S(6, $n, $p)) % 1;
295             # Calculate the hexadecimal number.
296 0         0 for (my $j = 0; $j < length($dec); $j++) {
297 0         0 $newdec = $dec * 16;
298 0         0 $dec = $newdec - ($newdec % 1);
299 0         0 $hex_digit = $HX[$dec];
300 0         0 $hex_digits = $hex_digits . $hex_digit;
301 0         0 $dec = $newdec - $dec;
302             };
303             # Return the hexadecimal number.
304 0         0 return substr($hex_digits, 0, $digits);
305             };
306              
307             #------------------------------------------------------------------------------#
308             # Subroutine bbp_algorith() #
309             #------------------------------------------------------------------------------#
310             sub bbp_algorithm {
311             # Assign the subroutine arguments to the local variables.
312 0     0 1 0 my ($places, $p) = @_;
313             # Set the precision for bignum.
314 0         0 bignum -> precision(-$p);
315             # Initialise the local variable $pi_hex.
316 0         0 my $pi_hex = "3.";
317             # Declare the local variable $nth_digit.
318 0         0 my $digits = undef;
319             # Determine the hexadecimal places in a loop.
320 0         0 for (my $i = 1; $i <= $places; $i++) {
321 0         0 $digits = bbp_digit($i, $p);
322 0         0 $pi_hex = $pi_hex . $digits;
323             };
324             # Return Pi in hexadecimal representation.
325 0         0 return $pi_hex;
326             };
327              
328             # ---------------------------------------------------------------------------- #
329             # Subroutine borwein25_algorithm() #
330             # ---------------------------------------------------------------------------- #
331             sub borwein25_algorithm {
332             # Assign the hash with the subroutine arguments to the local hash.
333 0     0 1 0 my (%params) = @_;
334             # Initialise the local variables.
335 0         0 my $type0 = "pi";
336 0         0 my $type1 = "tau";
337             # Make a reference from the hash.
338 0         0 my $args = \%params;
339             # Initialise the locale variables.
340 0         0 my $places = $args->{Places};
341 0         0 my $n = $args->{Terms};
342 0         0 my $p = $args->{Precision};
343 0         0 my $trim = $args->{Trim};
344 0         0 my $type = $args->{Type};
345             # Check if the the local variables are defined.
346 0 0       0 $places = (defined $places ? $places : 0);
347 0 0       0 $n = (defined $n ? $n : 0);
348 0 0       0 $p = (defined $p ? $p : $places);
349 0 0       0 $trim = (defined $trim ? $trim : $places);
350 0 0       0 $type = (defined $type ? $type : $type0);
351             # Set the precision.
352 0         0 bignum -> precision(-$p);
353             # Initialise the return variable.
354 0         0 my $circle_constant = 0;
355             # Set the local constants.
356 0         0 my $sqrt61 = sqrtroot(61);
357 0         0 my $A = (212175710912 * $sqrt61) + 1657145277365;
358 0         0 my $B = (13773980892672 * $sqrt61) + 107578229802750;
359 0         0 my $C = (5280 * (236674 + 30303 * $sqrt61))**3;
360 0         0 my $factor = sqrtroot($C) / 12;
361             # Set the initial values.
362 0         0 my $a_k = 0;
363 0         0 my $a_sum = 0;
364 0         0 my $numerator = 0;
365 0         0 my $denominator = 0;
366             # Set the sign to 1.
367 0         0 my $sign = 1;
368             # Run index is of type int.
369 0         0 for (my $k = 0; $k <= $n; $k++) {
370             # Calculate numerator and denominator.
371 0         0 $numerator = factorial(6*$k)*($A +$k*$B);
372 0         0 $denominator = (factorial($k)**3)*factorial(3*$k)*($C**($k));
373             # Calculate the quotient.
374 0         0 $a_k = $sign*($numerator / $denominator);
375             # Add quotient to total sum.
376 0         0 $a_sum += $a_k;
377             # Change the sign.
378 0         0 $sign *= -1;
379             };
380             # Calculate the value of the circle_constant.
381 0         0 $circle_constant = $factor / $a_sum;
382             # Determine the value for Pi or for Tau.
383 0 0       0 if ($type eq $type0) {
    0          
384 0         0 $circle_constant = 1.0 * $circle_constant;
385             } elsif ($type eq $type1) {
386 0         0 $circle_constant = 2.0 * $circle_constant;
387             };
388             # Cut the decimal number to the needed places.
389 0         0 $circle_constant = truncate_places($circle_constant, $trim);
390             # Return the value of the circle constant.
391 0         0 return "$circle_constant";
392             };
393              
394             # ---------------------------------------------------------------------------- #
395             # Subroutine chudnovsky_algorithm() #
396             # ---------------------------------------------------------------------------- #
397             sub chudnovsky_algorithm {
398             # Assign the hash with the subroutine arguments to the local hash.
399 0     0 1 0 my (%params) = @_;
400             # Initialise the local variables.
401 0         0 my $type0 = "pi";
402 0         0 my $type1 = "tau";
403             # Make a reference from the hash.
404 0         0 my $args = \%params;
405             # Initialise the locale variables.
406 0         0 my $places = $args->{Places};
407 0         0 my $n = $args->{Terms};
408 0         0 my $p = $args->{Precision};
409 0         0 my $trim = $args->{Trim};
410 0         0 my $type = $args->{Type};
411             # Check if the the local variables are defined.
412 0 0       0 $places = (defined $places ? $places : 0);
413 0 0       0 $n = (defined $n ? $n : 0);
414 0 0       0 $p = (defined $p ? $p : $places);
415 0 0       0 $trim = (defined $trim ? $trim : $places);
416 0 0       0 $type = (defined $type ? $type : $type0);
417             # Set the precision.
418 0         0 bignum -> precision(-$p);
419             # Initialise the return variable.
420 0         0 my $circle_constant = 0;
421             # Set the local integer constants.
422 0         0 my $c0 = 545140134;
423 0         0 my $c1 = 13591409;
424 0         0 my $c2 = 640320;
425 0         0 my $c3 = 4270934400;
426 0         0 my $c4 = 10005;
427             # Set the initial values.
428 0         0 my $a_k = 0;
429 0         0 my $a_sum = 0;
430 0         0 my $numerator = 0;
431 0         0 my $denominator = 0;
432             # Set the sign to 1.
433 0         0 my $sign = 1;
434             # Run index is of type int.
435 0         0 for (my $k = 0; $k <= $n; $k++) {
436             # Calculate numerator and denominator.
437 0         0 $numerator = factorial(6*$k)*($c0*$k+$c1);
438 0         0 $denominator = factorial(3*$k)*(factorial($k)**3)*($c2**(3*$k));
439             # Calculate the quotient.
440 0         0 $a_k = $sign*($numerator / $denominator);
441             # Add quotient to total sum.
442 0         0 $a_sum += $a_k;
443             # Change the sign.
444 0         0 $sign *= -1;
445             };
446             # Calculate the value of the circle_constant.
447 0         0 $circle_constant = $c3 / (sqrt($c4)*$a_sum);
448             # Determine Pi or Tau.
449 0 0       0 if ($type eq $type0) {
    0          
450 0         0 $circle_constant = 1.0 * $circle_constant;
451             } elsif ($type eq $type1) {
452 0         0 $circle_constant = 2.0 * $circle_constant;
453             };
454             # Cut the decimal number to the needed places.
455 0         0 $circle_constant = truncate_places($circle_constant, $trim);
456             # Return the value of the circle constant.
457 0         0 return "$circle_constant";
458             };
459              
460             # ---------------------------------------------------------------------------- #
461             # Subroutine chudnovsky_caller() #
462             # ---------------------------------------------------------------------------- #
463             sub chudnovsky_caller {
464             # Assign the subroutine arguments to the local variables.
465 0     0 0 0 my $places = $_[0];
466 0         0 my $method = $_[1];
467             # Check if the places are valid. If not exit the script.
468 0         0 check_places($places);
469             # Calculate the required number of terms.
470 0         0 my $terms = estimate_terms($places, 14);
471             # Set the precision for the calculation.
472 0         0 my $precision = $places + 15;
473             # Get the value of the circle constant from the calculation.
474 0         0 my $circle_constant = chudnovsky_algorithm(
475             Places => $places,
476             Terms => $terms,
477             Precision => $precision,
478             Trim => $places,
479             Type => $method
480             );
481             # Return the value of the circle constant Pi or Tau.
482 0         0 return "$circle_constant";
483             };
484              
485             # ---------------------------------------------------------------------------- #
486             # Subroutine borwein25_caller() #
487             # ---------------------------------------------------------------------------- #
488             sub borwein25_caller {
489             # Assign the subroutine arguments to the local variables.
490 0     0 0 0 my $places = $_[0];
491 0         0 my $method = $_[1];
492             # Check if the places are valid. If not exit the script.
493 0         0 check_places($places);
494             # Calculate the required number of terms.
495 0         0 my $terms = estimate_terms($places, 25);
496             # Set the precision for the calculation.
497 0         0 my $precision = $places + 26;
498             # Get the value of the circle constant from the calculation.
499 0         0 my $circle_constant = borwein25_algorithm(
500             Places => $places,
501             Terms => $terms,
502             Precision => $precision,
503             Trim => $places,
504             Type => $method
505             );
506             # Return the value of the circle constant Pi or Tau.
507 0         0 return "$circle_constant";
508             };
509              
510             # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
511             # Call the caller subroutines #
512             # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
513 0     0 1 0 sub pi_chudnovsky { return chudnovsky_caller($_[0], "pi") };
514 0     0 1 0 sub tau_chudnovsky { return chudnovsky_caller($_[0], "tau") };
515 0     0 1 0 sub pi_borwein25 { return borwein25_caller($_[0], "pi") };
516 0     0 1 0 sub tau_borwein25 { return borwein25_caller($_[0], "tau") };
517              
518             # ============================================================================ #
519             # Subroutine read_pi() #
520             # ============================================================================ #
521             sub read_pi {
522             # Assign the subroutine argument to the local variable.
523 2     2 0 4 my $type = $_[0];
524             # Initialise the local variables.
525 2         3 my $line = "";
526 2         4 my $pi = "";
527             # Assemble the begin and end pattern.
528 2         5 my $pattern0 = "# Begin Pi ${type}";
529 2         4 my $pattern1 = "# End Pi ${type}";
530             # Read the data from the DATA section.
531 2         23 while () {
532             # Read data only between the two given pattern.
533 1118 100       2986 if (/$pattern0/../$pattern1/) {
534 46 100 100     222 next if /$pattern0/ || /$pattern1/;
535 42         72 $line = $_;
536 42         334 $line =~ s/^\s+|\s+$//g;
537 42         126 $pi = $pi . $line;
538             };
539             };
540             # Return Pi.
541 2         16 return $pi;
542             };
543              
544             # ============================================================================ #
545             # Subroutine read_data() #
546             # #
547             # Description: #
548             # The subroutine reads Pi in decimal and hexadecimal representation from the #
549             # DATA section. #
550             # ============================================================================ #
551             sub read_data {
552             # Save the position of DATA.
553 1     1 0 4 my $data_start = tell DATA;
554             # Read decimal Pi with 1000 places.
555 1         4 my $pi_dec = uc(read_pi("dec"));
556             # Reposition the filehandle right past to __DATA__
557 1         6 seek DATA, $data_start, 0;
558             # Read hexadecimal Pi with 1000 places.
559 1         46 my $pi_hex = uc(read_pi("hex"));
560             # Return PI.
561 1         5 return $pi_dec, $pi_hex;
562             };
563              
564             # Read data from DATA.
565             ($PI_DEC, $PI_HEX) = read_data();
566              
567             1;
568              
569             __DATA__