File Coverage

blib/lib/bignum.pm
Criterion Covered Total %
statement 148 188 78.7
branch 48 84 57.1
condition 13 20 65.0
subroutine 29 33 87.8
pod 15 15 100.0
total 253 340 74.4


line stmt bran cond sub pod time code
1             package bignum;
2              
3 15     15   2051574 use strict;
  15         35  
  15         2916  
4 15     15   1911 use warnings;
  15         48  
  15         2418  
5              
6 15     15   202 use Carp qw< carp croak >;
  15         44  
  15         2296  
7              
8             our $VERSION = '0.67';
9              
10 15     15   104 use Exporter;
  15         35  
  15         1729  
11             our @ISA = qw( Exporter );
12             our @EXPORT_OK = qw( PI e bpi bexp hex oct );
13             our @EXPORT = qw( inf NaN );
14              
15 15     15   9290 use overload;
  15         26589  
  15         98  
16              
17             # Defaults: When a constant is an integer, Inf or NaN, it is converted to an
18             # object of class $int_class. When a constant is a finite non-integer, it is
19             # converted to an object of class $float_class.
20              
21             my $int_class = 'Math::BigInt';
22             my $float_class = 'Math::BigFloat';
23              
24             ##############################################################################
25              
26             sub accuracy {
27 5     5 1 288056 shift;
28 5         25 $int_class -> accuracy(@_);
29 5         202 $float_class -> accuracy(@_);
30             }
31              
32             sub precision {
33 5     5 1 2206 shift;
34 5         26 $int_class -> precision(@_);
35 5         234 $float_class -> precision(@_);
36             }
37              
38             sub round_mode {
39 5     5 1 2646 shift;
40 5         38 $int_class -> round_mode(@_);
41 5         125 $float_class -> round_mode(@_);
42             }
43              
44             sub div_scale {
45 0     0 1 0 shift;
46 0         0 $int_class -> div_scale(@_);
47 0         0 $float_class -> div_scale(@_);
48             }
49              
50             sub upgrade {
51 5     5 1 61387 shift;
52 5         68 $int_class -> upgrade(@_);
53             }
54              
55             sub downgrade {
56 4     4 1 760922 shift;
57 4         65 $float_class -> downgrade(@_);
58             }
59              
60             sub in_effect {
61 0   0 0 1 0 my $level = shift || 0;
62 0         0 my $hinthash = (caller($level))[10];
63 0         0 $hinthash->{bignum};
64             }
65              
66             sub _float_constant {
67 91     91   159 my $str = shift;
68              
69             # See if we can convert the input string to a string using a normalized form
70             # consisting of the significand as a signed integer, the character "e", and
71             # the exponent as a signed integer, e.g., "+0e+0", "+314e-2", and "-1e+3".
72              
73 91         134 my $nstr;
74              
75 91 50 66     832 if (
      100        
      66        
      100        
      33        
      66        
76             # See if it is an octal number. An octal number like '0377' is also
77             # accepted by the functions parsing decimal and hexadecimal numbers, so
78             # handle octal numbers before decimal and hexadecimal numbers.
79              
80             $str =~ /^0(?:[Oo]|_*[0-7])/ and
81             $nstr = Math::BigInt -> oct_str_to_dec_flt_str($str)
82              
83             or
84              
85             # See if it is decimal number.
86              
87             $nstr = Math::BigInt -> dec_str_to_dec_flt_str($str)
88              
89             or
90              
91             # See if it is a hexadecimal number. Every hexadecimal number has a
92             # prefix, but the functions parsing numbers don't require it, so check
93             # to see if it actually is a hexadecimal number.
94              
95             $str =~ /^0[Xx]/ and
96             $nstr = Math::BigInt -> hex_str_to_dec_flt_str($str)
97              
98             or
99              
100             # See if it is a binary numbers. Every binary number has a prefix, but
101             # the functions parsing numbers don't require it, so check to see if it
102             # actually is a binary number.
103              
104             $str =~ /^0[Bb]/ and
105             $nstr = Math::BigInt -> bin_str_to_dec_flt_str($str))
106             {
107 91         24473 my $pos = index($nstr, 'e');
108 91         203 my $expo_sgn = substr($nstr, $pos + 1, 1);
109 91         162 my $sign = substr($nstr, 0, 1);
110 91         151 my $mant = substr($nstr, 1, $pos - 1);
111 91         171 my $mant_len = CORE::length($mant);
112 91         130 my $expo = substr($nstr, $pos + 2);
113              
114             # The number is a non-integer if and only if the exponent is negative.
115              
116 91 100       182 if ($expo_sgn eq '-') {
117 41         180 return $float_class -> new($str);
118              
119 0         0 my $upgrade = $int_class -> upgrade();
120 0 0       0 return $upgrade -> new($nstr) if defined $upgrade;
121              
122 0 0       0 if ($mant_len <= $expo) {
123 0         0 return $int_class -> bzero(); # underflow
124             } else {
125 0         0 $mant = substr $mant, 0, $mant_len - $expo; # truncate
126 0         0 return $int_class -> new($sign . $mant);
127             }
128             } else {
129 50         98 $mant .= "0" x $expo; # pad with zeros
130 50         169 return $int_class -> new($sign . $mant);
131             }
132             }
133              
134             # If we get here, there is a bug in the code above this point.
135              
136 0         0 warn "Internal error: unable to handle literal constant '$str'.",
137             " This is a bug, so please report this to the module author.";
138 0         0 return $int_class -> bnan();
139             }
140              
141             #############################################################################
142             # the following two routines are for "use bignum qw/hex oct/;":
143              
144 15     15   11749 use constant LEXICAL => $] > 5.009004;
  15         33  
  15         15766  
145              
146             # Internal function with the same semantics as CORE::hex(). This function is
147             # not used directly, but rather by other front-end functions.
148              
149             sub _hex_core {
150 1     1   4 my $str = shift;
151              
152             # Strip off, clean, and parse as much as we can from the beginning.
153              
154 1         2 my $x;
155 1 50       62 if ($str =~ s/ ^ ( 0? [xX] )? ( [0-9a-fA-F]* ( _ [0-9a-fA-F]+ )* ) //x) {
156 1         224 my $chrs = $2;
157 1         4 $chrs =~ tr/_//d;
158 1 50       5 $chrs = '0' unless CORE::length $chrs;
159 1         6 $x = $int_class -> from_hex($chrs);
160             } else {
161 0         0 $x = $int_class -> bzero();
162             }
163              
164             # Warn about trailing garbage.
165              
166 1 50       508 if (CORE::length($str)) {
167 0         0 require Carp;
168 0         0 Carp::carp(sprintf("Illegal hexadecimal digit '%s' ignored",
169             substr($str, 0, 1)));
170             }
171              
172 1         14 return $x;
173             }
174              
175             # Internal function with the same semantics as CORE::oct(). This function is
176             # not used directly, but rather by other front-end functions.
177              
178             sub _oct_core {
179 1     1   3 my $str = shift;
180              
181 1         5 $str =~ s/^\s*//;
182              
183             # Hexadecimal input.
184              
185 1 50       90 return _hex_core($str) if $str =~ /^0?[xX]/;
186              
187 1         3 my $x;
188              
189             # Binary input.
190              
191 1 50       5 if ($str =~ /^0?[bB]/) {
192              
193             # Strip off, clean, and parse as much as we can from the beginning.
194              
195 0 0       0 if ($str =~ s/ ^ ( 0? [bB] )? ( [01]* ( _ [01]+ )* ) //x) {
196 0         0 my $chrs = $2;
197 0         0 $chrs =~ tr/_//d;
198 0 0       0 $chrs = '0' unless CORE::length $chrs;
199 0         0 $x = $int_class -> from_bin($chrs);
200             }
201              
202             # Warn about trailing garbage.
203              
204 0 0       0 if (CORE::length($str)) {
205 0         0 require Carp;
206 0         0 Carp::carp(sprintf("Illegal binary digit '%s' ignored",
207             substr($str, 0, 1)));
208             }
209              
210 0         0 return $x;
211             }
212              
213             # Octal input. Strip off, clean, and parse as much as we can from the
214             # beginning.
215              
216 1 50       8 if ($str =~ s/ ^ ( 0? [oO] )? ( [0-7]* ( _ [0-7]+ )* ) //x) {
217 1         4 my $chrs = $2;
218 1         2 $chrs =~ tr/_//d;
219 1 50       4 $chrs = '0' unless CORE::length $chrs;
220 1         6 $x = $int_class -> from_oct($chrs);
221             }
222              
223             # Warn about trailing garbage. CORE::oct() only warns about 8 and 9, but it
224             # is more helpful to warn about all invalid digits.
225              
226 1 50       364 if (CORE::length($str)) {
227 0         0 require Carp;
228 0         0 Carp::carp(sprintf("Illegal octal digit '%s' ignored",
229             substr($str, 0, 1)));
230             }
231              
232 1         9 return $x;
233             }
234              
235             {
236             my $proto = LEXICAL ? '_' : ';$';
237 0 0   0 1 0 eval '
  0         0  
238             sub hex(' . $proto . ') {' . <<'.';
239             my $str = @_ ? $_[0] : $_;
240             _hex_core($str);
241             }
242             .
243              
244 0 0   0 1 0 eval '
  0         0  
245             sub oct(' . $proto . ') {' . <<'.';
246             my $str = @_ ? $_[0] : $_;
247             _oct_core($str);
248             }
249             .
250             }
251              
252             #############################################################################
253             # the following two routines are for Perl 5.9.4 or later and are lexical
254              
255             my ($prev_oct, $prev_hex, $overridden);
256              
257 2 50   2   257865 if (LEXICAL) { eval <<'.' }
  2 50   2   42  
  2 50       21  
  2 100       24  
    50          
    50          
    50          
    100          
258             sub _hex(_) {
259             my $hh = (caller 0)[10];
260             return $$hh{bignum} ? bignum::_hex_core($_[0])
261             : $$hh{bigrat} ? bigrat::_hex_core($_[0])
262             : $$hh{bigint} ? bigint::_hex_core($_[0])
263             : $prev_hex ? &$prev_hex($_[0])
264             : CORE::hex($_[0]);
265             }
266              
267             sub _oct(_) {
268             my $hh = (caller 0)[10];
269             return $$hh{bignum} ? bignum::_oct_core($_[0])
270             : $$hh{bigrat} ? bigrat::_oct_core($_[0])
271             : $$hh{bigint} ? bigint::_oct_core($_[0])
272             : $prev_oct ? &$prev_oct($_[0])
273             : CORE::oct($_[0]);
274             }
275             .
276              
277             sub _override {
278 46 100   46   185 return if $overridden;
279 15         52 $prev_oct = *CORE::GLOBAL::oct{CODE};
280 15         105 $prev_hex = *CORE::GLOBAL::hex{CODE};
281 15     15   130 no warnings 'redefine';
  15         31  
  15         22584  
282 15         76 *CORE::GLOBAL::oct = \&_oct;
283 15         40 *CORE::GLOBAL::hex = \&_hex;
284 15         31 $overridden = 1;
285             }
286              
287             sub unimport {
288 1     1   408 delete $^H{bignum}; # no longer in effect
289 1         5 overload::remove_constant('binary', '', 'float', '', 'integer');
290             }
291              
292             sub import {
293 46     46   1048552 my $class = shift;
294              
295 46         235 $^H{bignum} = 1; # we are in effect
296 46         222 delete $^H{bigint};
297 46         153 delete $^H{bigrat};
298              
299             # for newer Perls always override hex() and oct() with a lexical version:
300 46         76 if (LEXICAL) {
301 46         474 _override();
302             }
303              
304 46         96 my @import = (); # common options
305 46         115 my @int_import = (upgrade => $float_class); # int class only options
306 46         91 my @flt_import = (downgrade => $int_class); # float class only options
307 46         73 my @a = (); # unrecognized arguments
308 46         101 my $ver; # display version info?
309              
310 46         130 while (@_) {
311 41         85 my $param = shift;
312              
313             # Upgrading.
314              
315 41 100       115 if ($param eq 'upgrade') {
316 3         4 my $arg = shift;
317 3 100       11 $float_class = $arg if defined $arg;
318 3         6 push @int_import, 'upgrade', $arg;
319 3         9 next;
320             }
321              
322             # Downgrading.
323              
324 38 100       104 if ($param eq 'downgrade') {
325 1         1 my $arg = shift;
326 1 50       2 $int_class = $arg if defined $arg;
327 1         2 push @flt_import, 'downgrade', $arg;
328 1         3 next;
329             }
330              
331             # Accuracy.
332              
333 37 100       141 if ($param =~ /^a(ccuracy)?$/) {
334 2         6 push @import, 'accuracy', shift();
335 2         9 next;
336             }
337              
338             # Precision.
339              
340 35 100       107 if ($param =~ /^p(recision)?$/) {
341 2         5 push @import, 'precision', shift();
342 2         8 next;
343             }
344              
345             # Rounding mode.
346              
347 33 50       80 if ($param eq 'round_mode') {
348 0         0 push @import, 'round_mode', shift();
349 0         0 next;
350             }
351              
352             # Backend library.
353              
354 33 100       190 if ($param =~ /^(l|lib|try|only)$/) {
355 9 100       34 push @import, $param eq 'l' ? 'lib' : $param;
356 9 50       30 push @import, shift() if @_;
357 9         31 next;
358             }
359              
360 24 50       65 if ($param =~ /^(v|version)$/) {
361 0         0 $ver = 1;
362 0         0 next;
363             }
364              
365 24 100       75 if ($param =~ /^(PI|e|bexp|bpi|hex|oct)\z/) {
366 4         6 push @a, $param;
367 4         10 next;
368             }
369              
370 20         3436 croak("Unknown option '$param'");
371             }
372              
373 26         2696 eval "require $int_class";
374 26 50       717272 die $@ if $@;
375 26         183 $int_class -> import(@int_import, @import);
376              
377 26         446622 eval "require $float_class";
378 26 50       626970 die $@ if $@;
379 26         226 $float_class -> import(@flt_import, @import);
380              
381 26 50       4120 if ($ver) {
382 0         0 printf "%-31s v%s\n", $class, $class -> VERSION();
383 0         0 printf " lib => %-23s v%s\n",
384             $int_class -> config("lib"), $int_class -> config("lib_version");
385 0         0 printf "%-31s v%s\n", $int_class, $int_class -> VERSION();
386 0         0 exit;
387             }
388              
389 26         3011 $class -> export_to_level(1, $class, @a); # export inf, NaN, etc.
390              
391             overload::constant
392              
393             # This takes care each number written as decimal integer and within the
394             # range of what perl can represent as an integer, e.g., "314", but not
395             # "3141592653589793238462643383279502884197169399375105820974944592307".
396              
397             integer => sub {
398             #printf "Value '%s' handled by the 'integer' sub.\n", $_[0];
399 58     58   19397 my $str = shift;
400 58         301 return $int_class -> new($str);
401             },
402              
403             # This takes care of each number written with a decimal point and/or
404             # using floating point notation, e.g., "3.", "3.0", "3.14e+2" (decimal),
405             # "0b1.101p+2" (binary), "03.14p+2" and "0o3.14p+2" (octal), and
406             # "0x3.14p+2" (hexadecimal).
407              
408             float => sub {
409             #printf "# Value '%s' handled by the 'float' sub.\n", $_[0];
410 91     91   123364 _float_constant(shift);
411             },
412              
413             # Take care of each number written as an integer (no decimal point or
414             # exponent) using binary, octal, or hexadecimal notation, e.g., "0b101"
415             # (binary), "0314" and "0o314" (octal), and "0x314" (hexadecimal).
416              
417             binary => sub {
418             #printf "# Value '%s' handled by the 'binary' sub.\n", $_[0];
419 12     12   209739 my $str = shift;
420 12 100       130 return $int_class -> new($str) if $str =~ /^0[XxBb]/;
421 5         28 $int_class -> from_oct($str);
422 26         370 };
423             }
424              
425 22     22 1 402052 sub inf () { $int_class -> binf(); }
426 20     20 1 26211 sub NaN () { $int_class -> bnan(); }
427              
428             # This should depend on the current accuracy/precision. Fixme!
429 1     1 1 3815 sub PI () { $float_class -> new('3.141592653589793238462643383279502884197'); }
430 1     1 1 260455 sub e () { $float_class -> new('2.718281828459045235360287471352662497757'); }
431              
432             sub bpi ($) {
433 1     1 1 6 my $up = Math::BigFloat -> upgrade(); # get current upgrading, if any ...
434 1         30 Math::BigFloat -> upgrade(undef); # ... and disable
435 1         18 my $x = Math::BigFloat -> bpi(@_);
436 1         403 Math::BigFloat -> upgrade($up); # reset the upgrading
437 1         18 return $x;
438             }
439              
440             sub bexp ($$) {
441 1     1 1 1390 my $up = Math::BigFloat -> upgrade(); # get current upgrading, if any ...
442 1         21 Math::BigFloat -> upgrade(undef); # ... and disable
443 1         17 my $x = Math::BigFloat -> new(shift) -> bexp(@_);
444 1         2752 Math::BigFloat -> upgrade($up); # reset the upgrading
445 1         18 return $x;
446             }
447              
448             1;
449              
450             __END__