File Coverage

blib/lib/bigrat.pm
Criterion Covered Total %
statement 122 156 78.2
branch 45 72 62.5
condition 14 20 70.0
subroutine 28 31 90.3
pod 13 13 100.0
total 222 292 76.0


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