File Coverage

lib/Math/String.pm
Criterion Covered Total %
statement 177 203 87.1
branch 65 94 69.1
condition 11 23 47.8
subroutine 30 35 85.7
pod 25 25 100.0
total 308 380 81.0


line stmt bran cond sub pod time code
1             #############################################################################
2             # Math/String.pm -- package which defines a base class for calculating
3             # with big integers that are defined by arbitrary char sets.
4             #
5             # Copyright (C) 1999 - 2008 by Tels.
6             #############################################################################
7              
8             # see:
9             # http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2000-05/msg00974.html
10             # http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/1999-02/msg00812.html
11              
12             # the following hash values are used
13             # _set : ref to charset object
14             # sign, value, _a, _f, _p : from BigInt
15             # _cache : caches string form for speed
16              
17             package Math::String;
18              
19             require 5.008003; # requires this Perl version or later
20 3     3   140986 use strict;
  3         21  
  3         90  
21 3     3   14 use warnings;
  3         4  
  3         125  
22              
23 3     3   16 use Exporter;
  3         6  
  3         108  
24 3     3   3637 use Math::BigInt;
  3         87531  
  3         12  
25              
26             our ($VERSION, @ISA, @EXPORT_OK);
27             $VERSION = '1.30'; # Current version of this package
28             @ISA = qw(Exporter Math::BigInt);
29             @EXPORT_OK = qw( as_number last first string from_number
30             bzero bone binf bnan );
31              
32 3     3   72494 use Math::String::Charset;
  3         438  
  3         521  
33              
34             our ($accuracy, $precision, $div_scale, $round_mode);
35             $accuracy = undef;
36             $precision = undef;
37             $div_scale = 0;
38             $round_mode = 'even';
39              
40             my $class = "Math::String";
41              
42             use overload
43             'cmp' => sub {
44 163     163   34803 my $str = $_[0]->bstr();
45 163 50       329 return undef if !defined $str;
46 163 100       214 my $str1 = $_[1]; $str1 = $str1->bstr() if ref $str1;
  163         257  
47 163 50       256 return undef if !defined $str1;
48 163 50       1613 $_[2] ? $str1 cmp $str : $str cmp $str1;
49             },
50             # can modify arg of ++ and --, so avoid a new-copy for speed
51 3         51 '++' => \&binc,
52             '--' => \&bdec,
53 3     3   22 ;
  3         5  
54              
55             my $CALC = 'Math::BigInt::Calc';
56              
57             sub import
58             {
59 5     5   80 my $self = shift;
60              
61 5   50     19 $CALC = Math::BigInt->config()->{lib} || 'Math::BigInt::Calc';
62              
63 5         246 Math::BigInt::import($self, @_);
64             }
65              
66             sub string
67             {
68             # exportable version of new
69 0     0 1 0 $class->new(@_);
70             }
71              
72             sub from_number
73             {
74             # turn an integer into a string object
75             # catches also Math::String->from_number and make it work
76 51     51 1 30921 my $val = shift;
77              
78 51 50       134 $val = "" if !defined $val;
79 51 50 33     223 $val = shift if !ref($val) && $val eq $class;
80 51         73 my $set = shift;
81              
82             # make a new bigint (or copy the existing one)
83 51         168 my $self = Math::BigInt->new($val);
84 51 100 66     2465 if (ref($set) && (
      66        
85             ref($set) eq 'HASH' || UNIVERSAL::isa($set,'Math::String::Charset'))
86             )
87             {
88 5 50       21 $self->bdiv($set->{_scale}) if defined $set->{_scale}; # input is scaled?
89             }
90 51         733 bless $self, $class; # rebless
91 51         114 $self->_set_charset($set);
92 51         681 $self;
93             }
94              
95             sub scale
96             {
97             # set/get the scale of the string (from the set)
98 1     1 1 378 my $self = shift;
99              
100 1         7 $self->{_set}->scale(@_);
101             }
102              
103             sub bzero
104             {
105 3     3 1 597 my $self = shift;
106 3 100       10 if (defined $self)
107             {
108             # $x->bzero(); (x) (M::S)
109             # $x->bzero(); (x) (M::bi or something)
110 2         9 $self = $self->SUPER::bzero();
111 2 50       37 bless $self, $class if ref($self) ne $class; # convert aka rebless
112             }
113             else
114             {
115             # M::S::bzero(); ()
116 1         5 $self = Math::BigInt->bzero();
117 1         23 bless $self, $class; # rebless
118 1         4 $self->_set_charset(shift);
119             }
120 3         5 $self->{_cache} = undef; # invalidate cache
121 3         9 $self;
122             }
123              
124             sub bone
125             {
126 2     2 1 1125 my $self = shift;
127 2 50       9 if (defined $self)
128             {
129             # $x->bzero(); (x) (M::S)
130             # $x->bzero(); (x) (M::bi or something)
131 0         0 $self->SUPER::bone();
132 0 0       0 bless $self, $class if ref($self) ne $class; # convert aka rebless
133             }
134             else
135             {
136             # M::S::bzero(undef,charset);
137 2         9 $self = Math::BigInt->bone();
138 2         62 bless $self, __PACKAGE__;
139 2         7 $self->_set_charset($_[0]);
140             }
141 2         10 my $min = $self->{_set}->minlen();
142 2 50       7 $min = 1 if $min <= 0;
143 2         358 $self->{_cache} = $self->{_set}->first($min); # first of minlen
144 2         8 $self;
145             }
146              
147             sub bnan
148             {
149 7     7 1 864 my $self = shift;
150 7 100       14 if (defined $self)
151             {
152             # $x->bnan(); (x) (M::S)
153             # $x->bnan(); (x) (M::bi or something)
154 6         20 $self->SUPER::bnan();
155 6 50       75 bless $self, $class if ref($self) ne $class; # convert aka rebless
156             }
157             else
158             {
159             # M::S::bnan(); ()
160 1         4 $self = $class->SUPER::bnan();
161 1         27 bless $self, __PACKAGE__;
162 1         4 $self->_set_charset(shift);
163             }
164 7         11 $self->{_cache} = undef;
165 7         30 $self;
166             }
167              
168             sub binf
169             {
170 2     2 1 533 my $self = shift;
171 2 100       6 if (defined $self)
172             {
173             # $x->bzero(); (x) (M::S)
174             # $x->bzero(); (x) (M::bi or something)
175 1         8 $self->SUPER::binf(shift);
176 1 50       17 bless $self, $class if ref($self) ne $class; # convert aka rebless
177             }
178             else
179             {
180             # M::S::bzero(); ()
181 1         5 $self = $class->SUPER::binf(shift);
182 1         35 bless $self, __PACKAGE__;
183 1         3 $self->_set_charset(shift);
184             }
185 2         5 $self->{_cache} = undef;
186 2         4 $self;
187             }
188              
189             ###############################################################################
190             # constructor
191              
192             sub new
193             {
194 122     122 1 18457 my $class = shift;
195 122   33     390 $class = ref($class) || $class;
196 122 50       159 my $value = shift; $value = '' if !defined $value;
  122         226  
197              
198 122         175 my $self = {};
199 122 100       331 if (ref($value) eq 'HASH')
    100          
200             {
201 3         11 $self = Math::BigInt->new($value->{num}); # number form
202 3         127 bless $self, $class; # rebless
203 3         8 $self->_set_charset(shift); # if given charset, copy over
204             $self->bdiv($self->{_set}->{_scale})
205 3 100       22 if defined $self->{_set}->{_scale}; # input is scaled?
206 3         66 $self->{_cache} = $value->{str}; # string form
207             }
208             elsif (ref($value))
209             {
210 13         26 $self = $value->copy(); # got an object, so make copy
211 13         244 bless $self, $class; # rebless
212 13 50       29 $self->_set_charset(shift) if defined $_[0];# if given charset, copy over
213 13         30 $self->{_cache} = undef;
214             }
215             else
216             {
217 106         138 bless $self, $class;
218 106         213 $self->_set_charset(shift); # if given charset, copy over
219 106         207 $self->_initialize($value);
220             }
221 122         458 $self;
222             }
223              
224             sub _set_charset
225             {
226             # store reference to charset object, or make one if given array/hash ref
227             # first method should be prefered for speed/memory reasons
228 165     165   215 my $self = shift;
229 165         184 my $cs = shift;
230              
231 165 100       460 $cs = ['a'..'z'] if !defined $cs; # default a-z
232 165 100       1053 $cs = Math::String::Charset->new( $cs ) if ref($cs) =~ /^(ARRAY|HASH)$/;
233 165 50       335 die "charset '$cs' is not a reference" unless ref($cs);
234 165         353 $self->{_set} = $cs;
235 165         218 $self;
236             }
237              
238             #############################################################################
239             # private, initialize self
240              
241             sub _initialize
242             {
243             # set yourself to the value represented by the given string
244 114     114   141 my $self = shift;
245 114         146 my $value = shift;
246              
247 114         146 my $cs = $self->{_set};
248              
249 114 100       231 return $self->bnan() if !$cs->is_valid($value);
250              
251 110         1725 my $int = $cs->str2num($value);
252 110 50       299 if (!ref($int))
253             {
254 0         0 require Carp;
255 0         0 Carp::croak ("$int is not a reference to a Big* object");
256             }
257 110         318 foreach my $c (keys %$int) { $self->{$c} = $int->{$c}; }
  220         383  
258              
259 110         238 $self->{_cache} = $cs->norm($value); # caching normalized form
260 110         287 $self;
261             }
262              
263             sub copy
264             {
265             # for speed reasons, do not make a copy of a charset, but share it instead
266 174     174 1 316 my ($c,$x);
267 174 50       299 if (@_ > 1)
268             {
269             # if two arguments, the first one is the class to "swallow" subclasses
270 0         0 ($c,$x) = @_;
271             }
272             else
273             {
274 174         211 $x = shift;
275 174         222 $c = ref($x);
276             }
277 174 50       268 return unless ref($x); # only for objects
278              
279 174         229 my $self = {}; bless $self,$c;
  174         235  
280 174         425 foreach my $k (keys %$x)
281             {
282 689         1498 my $ref = ref($x->{$k});
283 689 100       1826 if ($k eq 'value')
    50          
    50          
    50          
    100          
    50          
284             {
285 174         412 $self->{$k} = $CALC->_copy($x->{$k});
286             }
287             #elsif (ref($x->{$k}) eq 'SCALAR')
288             elsif ($ref eq 'SCALAR')
289             {
290 0         0 $self->{$k} = \${$x->{$k}};
  0         0  
291             }
292             #elsif (ref($x->{$k}) eq 'ARRAY')
293             elsif ($ref eq 'ARRAY')
294             {
295 0         0 $self->{$k} = [ @{$x->{$k}} ];
  0         0  
296             }
297             #elsif (ref($x->{$k}) eq 'HASH')
298             elsif ($ref eq 'HASH')
299             {
300             # only one level deep!
301 0         0 foreach my $h (keys %{$x->{$k}})
  0         0  
302             {
303 0         0 $self->{$k}->{$h} = $x->{$k}->{$h};
304             }
305             }
306             #elsif (ref($x->{$k}) =~ /^Math::String::Charset/)
307             elsif ($ref =~ /^Math::String::Charset/)
308             {
309 174         341 $self->{$k} = $x->{$k}; # for speed reasons share this
310             }
311             #elsif (ref($x->{$k}))
312             elsif ($ref)
313             {
314             # my $c = ref($x->{$k});
315 0         0 $self->{$k} = $ref->new($x->{$k}); # no copy() due to deep rec
316             }
317             else
318             {
319 341         628 $self->{$k} = $x->{$k};
320             }
321             }
322 174         744 $self;
323             }
324              
325             sub charset
326             {
327 0     0 1 0 my $self = shift;
328 0         0 $self->{_set};
329             }
330              
331             sub class
332             {
333 2     2 1 29 my $self = shift;
334 2         8 $self->{_set}->class(@_);
335             }
336              
337             sub minlen
338             {
339 0     0 1 0 my $x = shift;
340 0         0 $x->{_set}->minlen();
341             }
342              
343             sub maxlen
344             {
345 0     0 1 0 my $x = shift;
346 0         0 $x->{_set}->minlen();
347             }
348              
349             sub length
350             {
351             # return number of characters in output
352 4     4 1 55 my $x = shift;
353              
354 4         12 $x->{_set}->chars($x);
355             }
356              
357             sub bstr
358             {
359 241     241 1 5203 my $x = shift;
360              
361 241 50       543 return $x unless ref $x; # scalars get simple returned
362 241 100       773 return undef if $x->{sign} !~ /^[+-]$/; # short cut
363              
364 234 100       711 return $x->{_cache} if defined $x->{_cache};
365              
366             # num2str needs (due to overloading "$x-1") a Math::BigInt object, so make it
367             # positively happy
368 93         266 my $int = Math::BigInt->bzero();
369 93         2029 $int->{value} = $x->{value};
370 93         264 $x->{_cache} = $x->{_set}->num2str($int);
371              
372 93         442 $x->{_cache};
373             }
374              
375             sub as_number
376             {
377             # return yourself as MBI
378 71     71 1 668 my $self = shift;
379              
380             # make a copy of us and delete any specific (non-MBI) keys
381 71         124 my $x = $self->copy();
382 71         130 delete $x->{_cache};
383 71         81 delete $x->{_set};
384 71         89 bless $x, 'Math::BigInt'; # convert it to the new religion
385             $x->bmul($self->{_set}->{_scale})
386 71 100       165 if exists $self->{_set}->{_scale}; # scale it?
387 71         1659 $x;
388             }
389              
390             sub order
391             {
392 1     1 1 3 my $x = shift;
393 1         5 $x->{_set}->order();
394             }
395              
396             sub type
397             {
398 0     0 1 0 my $x = shift;
399 0         0 $x->{_set}->type();
400             }
401              
402             sub last
403             {
404 5     5 1 10 my $x = $_[0];
405 5 100 66     23 if (!ref($_[0]) && $_[0] eq __PACKAGE__)
406             {
407             # Math::String length charset
408 3         10 $x = Math::String->new('',$_[2]); # Math::String->first(3,$set);
409             }
410 5         17 my $es = $x->{_set}->last($_[1]);
411 5         12 $x->_initialize($es);
412             }
413              
414             sub first
415             {
416 3     3 1 20 my $x = $_[0];
417 3 100 66     14 if (!ref($_[0]) && $_[0] eq __PACKAGE__)
418             {
419             # Math::String length charset
420 1         4 $x = Math::String->new('',$_[2]); # Math::String->first(3,$set);
421             }
422 3         11 my $es = $x->{_set}->first($_[1]);
423 3         8 $x->_initialize($es);
424             }
425              
426             sub error
427             {
428 1     1 1 16 my $x = shift;
429 1         5 $x->{_set}->error();
430             }
431              
432             sub is_valid
433             {
434 2     2 1 9 my $x = shift;
435              
436             # What does charset say to string?
437 2 50       9 if (defined $x->{_cache})
438             {
439             # XXX TODO: cached string should always be valid?
440 2         9 return $x->{_set}->is_valid($x->{_cache});
441             }
442             else
443             {
444 0         0 $x->{_cache} = $x->bstr(); # create cache
445             }
446 0         0 my $l = $x->length();
447 0 0 0     0 return 0 if ($l < $x->minlen() || $l > $x->maxlen());
448 0         0 1; # all okay
449             }
450              
451             #############################################################################
452             # binc/bdec for caching
453              
454             sub binc
455             {
456 122 50   122 1 9469 my ($self,$x) = ref($_[0]) ?
457             (ref($_[0]),@_) : (Math::BigInt::objectify(1,@_));
458              
459             # binc calls modify, and thus destroys the cache, so store it
460 122         179 my $str = $x->{_cache};
461 122         331 $x->SUPER::binc();
462              
463             # if old value cached and no rounding happens
464 122 100       4197 if ((defined $str)
465             # && (!defined $a) && (!defined $p)
466             # && (!defined $x->accuracy()) && (!defined $x->precision())
467             )
468             {
469 89         139 $x->{_cache} = $str; # restore cache
470 89         242 $x->{_set}->next($x); # update string cache
471             }
472 122         439 $x;
473             }
474              
475             sub bdec
476             {
477 79 50   79 1 552 my ($self,$x) = ref($_[0]) ?
478             (ref($_[0]),@_) : (Math::BigInt::objectify(1,@_));
479              
480             # bdec calls modify, and thus destroys the cache, so store it
481 79         125 my $str = $x->{_cache};
482 79         225 $x->SUPER::bdec();
483              
484             # if old value cached and no rounding happens
485 79 100       3225 if ((defined $str)
486             # && (!defined $a) && (!defined $p)
487             # && (!defined $x->accuracy()) && (!defined $x->precision())
488             )
489             {
490 76         117 $x->{_cache} = $str; # restore cache
491 76         229 $x->{_set}->prev($x); # update string cache
492             }
493 79         390 $x;
494             }
495              
496             #############################################################################
497             # cache management
498              
499             sub modify
500             {
501 249     249 1 1718 $_[0]->{_cache} = undef; # invalidate cache
502 249         404 0; # go ahead, modify
503             }
504              
505             __END__