File Coverage

lib/Math/String.pm
Criterion Covered Total %
statement 179 205 87.3
branch 65 94 69.1
condition 10 23 43.4
subroutine 31 36 86.1
pod 25 25 100.0
total 310 383 80.9


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