File Coverage

blib/lib/bigrat.pm
Criterion Covered Total %
statement 124 166 74.7
branch 46 76 60.5
condition 4 20 20.0
subroutine 28 33 84.8
pod 15 15 100.0
total 217 310 70.0


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