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   10712 use Exporter;
  2         3  
  2         168  
21 2     2   6195 use Math::BigInt;
  2         117077  
  2         15  
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   46705 use Math::String::Charset;
  2         456  
  2         128  
27 2     2   12 use strict;
  2         4  
  2         84  
28 2     2   17 use vars qw($VERSION $AUTOLOAD $accuracy $precision $div_scale $round_mode);
  2         3  
  2         614  
29             $VERSION = '1.28'; # 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   8250 my $str = $_[0]->bstr();
40 163 50       351 return undef if !defined $str;
41 163 100       210 my $str1 = $_[1]; $str1 = $str1->bstr() if ref $str1;
  163         314  
42 163 50       316 return undef if !defined $str1;
43 163 50       631 $_[2] ? $str1 cmp $str : $str cmp $str1;
44             },
45             # can modify arg of ++ and --, so avoid a new-copy for speed
46 2         46 '++' => \&binc,
47             '--' => \&bdec,
48 2     2   14 ;
  2         3  
49              
50             my $CALC = 'Math::BigInt::Calc';
51              
52             sub import
53             {
54 3     3   95 my $self = shift;
55              
56 3   50     20 $CALC = Math::BigInt->config()->{lib} || 'Math::BigInt::Calc';
57              
58             # register us with MBI to get notified of future lib changes
59 3     5   378 Math::BigInt::_register_callback( $self, sub { $CALC = $_[0]; } );
  5         3194  
60              
61 3         35 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 21184 my $val = shift;
75            
76 51 50       271 $val = "" if !defined $val;
77 51 50 33     274 $val = shift if !ref($val) && $val eq $class;
78 51         72 my $set = shift;
79              
80             # make a new bigint (or copy the existing one)
81 51         171 my $self = Math::BigInt->new($val);
82 51 100 66     2417 if (ref($set) && (
      33        
83             ref($set) eq 'HASH' || UNIVERSAL::isa($set,'Math::String::Charset'))
84             )
85             {
86 5 50       25 $self->bdiv($set->{_scale}) if defined $set->{_scale}; # input is scaled?
87             }
88 51         412 bless $self, $class; # rebless
89 51         2383 $self->_set_charset($set);
90 51         752 $self;
91             }
92              
93             sub scale
94             {
95             # set/get the scale of the string (from the set)
96 1     1 1 136 my $self = shift;
97              
98 1         6 $self->{_set}->scale(@_);
99             }
100              
101             sub bzero
102             {
103 2     2 1 69 my $self = shift;
104 2 100       8 if (defined $self)
105             {
106             # $x->bzero(); (x) (M::S)
107             # $x->bzero(); (x) (M::bi or something)
108 1         9 $self->SUPER::bzero();
109 1 50       27 bless $self, $class if ref($self) ne $class; # convert aka rebless
110             }
111             else
112             {
113             # M::S::bzero(); ()
114 1         4 $self = Math::BigInt->bzero();
115 1         22 bless $self, $class; # rebless
116 1         4 $self->_set_charset(shift);
117             }
118 2         6 $self->{_cache} = undef; # invalidate cache
119 2         5 $self;
120             }
121              
122             sub bone
123             {
124 2     2 1 100 my $self = shift;
125 2 50       7 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         9 $self = Math::BigInt->bone();
136 2         64 bless $self, __PACKAGE__;
137 2         9 $self->_set_charset($_[0]);
138             }
139 2         12 my $min = $self->{_set}->minlen();
140 2 50       7 $min = 1 if $min <= 0;
141 2         292 $self->{_cache} = $self->{_set}->first($min); # first of minlen
142 2         7 $self;
143             }
144              
145             sub bnan
146             {
147 7     7 1 103 my $self = shift;
148 7 100       13 if (defined $self)
149             {
150             # $x->bnan(); (x) (M::S)
151             # $x->bnan(); (x) (M::bi or something)
152 6         32 $self->SUPER::bnan();
153 6 50       113 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         15 bless $self, __PACKAGE__;
160 1         4 $self->_set_charset(shift);
161             }
162 7         9 $self->{_cache} = undef;
163 7         26 $self;
164             }
165              
166             sub binf
167             {
168 2     2 1 65 my $self = shift;
169 2 100       11 if (defined $self)
170             {
171             # $x->bzero(); (x) (M::S)
172             # $x->bzero(); (x) (M::bi or something)
173 1         11 $self->SUPER::binf(shift);
174 1 50       23 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         17 bless $self, __PACKAGE__;
181 1         4 $self->_set_charset(shift);
182             }
183 2         5 $self->{_cache} = undef;
184 2         5 $self;
185             }
186              
187             ###############################################################################
188             # constructor
189              
190             sub new
191             {
192 120     120 1 13735 my $class = shift;
193 120   33     2766 $class = ref($class) || $class;
194 120 50       189 my $value = shift; $value = '' if !defined $value;
  120         277  
195              
196 120         186 my $self = {};
197 120 100       364 if (ref($value) eq 'HASH')
    100          
198             {
199 3         14 $self = Math::BigInt->new($value->{num}); # number form
200 3         94 bless $self, $class; # rebless
201 3         18 $self->_set_charset(shift); # if given charset, copy over
202 3 100       24 $self->bdiv($self->{_set}->{_scale})
203             if defined $self->{_set}->{_scale}; # input is scaled?
204 3         79 $self->{_cache} = $value->{str}; # string form
205             }
206             elsif (ref($value))
207             {
208 13         41 $self = $value->copy(); # got an object, so make copy
209 13         207 bless $self, $class; # rebless
210 13 50       36 $self->_set_charset(shift) if defined $_[0];# if given charset, copy over
211 13         26 $self->{_cache} = undef;
212             }
213             else
214             {
215 104         186 bless $self, $class;
216 104         310 $self->_set_charset(shift); # if given charset, copy over
217 104         6532 $self->_initialize($value);
218             }
219 120         951 $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 163     163   213 my $self = shift;
227 163         220 my $cs = shift;
228              
229 163 100       819 $cs = ['a'..'z'] if !defined $cs; # default a-z
230 163 100       1384 $cs = Math::String::Charset->new( $cs ) if ref($cs) =~ /^(ARRAY|HASH)$/;
231 163 50       500 die "charset '$cs' is not a reference" unless ref($cs);
232 163         508 $self->{_set} = $cs;
233 163         249 $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 112     112   206 my $self = shift;
243 112         151 my $value = shift;
244              
245 112         181 my $cs = $self->{_set};
246              
247 112 100       354 return $self->bnan() if !$cs->is_valid($value);
248            
249 108         1919 my $int = $cs->str2num($value);
250 108 50       336 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 108         320 foreach my $c (keys %$int) { $self->{$c} = $int->{$c}; }
  216         778  
256            
257 108         419 $self->{_cache} = $cs->norm($value); # caching normalized form
258 108         423 $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 942 my ($c,$x);
265 174 50       378 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         210 $x = shift;
273 174         251 $c = ref($x);
274             }
275 174 50       346 return unless ref($x); # only for objects
276            
277 174         257 my $self = {}; bless $self,$c;
  174         526  
278 174         580 foreach my $k (keys %$x)
279             {
280 689         1794 my $ref = ref($x->{$k});
281 689 100       2506 if ($k eq 'value')
    50          
    50          
    50          
    100          
    50          
282             {
283 174         553 $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         487 $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         958 $self->{$k} = $x->{$k};
318             }
319             }
320 174         639 $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 22 my $self = shift;
332 2         10 $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 101 my $x = shift;
351              
352 4         22 $x->{_set}->chars($x);
353             }
354              
355             sub bstr
356             {
357 241     241 1 2161 my $x = shift;
358              
359 241 50       550 return $x unless ref $x; # scalars get simple returned
360 241 100       906 return undef if $x->{sign} !~ /^[+-]$/; # short cut
361              
362 234 100       800 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         304 my $int = Math::BigInt->bzero();
367 93         2415 $int->{value} = $x->{value};
368 93         373 $x->{_cache} = $x->{_set}->num2str($int);
369              
370 93         616 $x->{_cache};
371             }
372              
373             sub as_number
374             {
375             # return yourself as MBI
376 71     71 1 412 my $self = shift;
377              
378             # make a copy of us and delete any specific (non-MBI) keys
379 71         173 my $x = $self->copy();
380 71         167 delete $x->{_cache};
381 71         137 delete $x->{_set};
382 71         176 bless $x, 'Math::BigInt'; # convert it to the new religion
383 71 100       227 $x->bmul($self->{_set}->{_scale})
384             if exists $self->{_set}->{_scale}; # scale it?
385 71         1463 $x;
386             }
387              
388             sub order
389             {
390 1     1 1 2 my $x = shift;
391 1         6 $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 10 my $x = $_[0];
403 5 100 66     28 if (!ref($_[0]) && $_[0] eq __PACKAGE__)
404             {
405             # Math::String length charset
406 3         13 $x = Math::String->new('',$_[2]); # Math::String->first(3,$set);
407             }
408 5         23 my $es = $x->{_set}->last($_[1]);
409 5         13 $x->_initialize($es);
410             }
411              
412             sub first
413             {
414 3     3 1 26 my $x = $_[0];
415 3 100 66     17 if (!ref($_[0]) && $_[0] eq __PACKAGE__)
416             {
417             # Math::String length charset
418 1         7 $x = Math::String->new('',$_[2]); # Math::String->first(3,$set);
419             }
420 3         15 my $es = $x->{_set}->first($_[1]);
421 3         12 $x->_initialize($es);
422             }
423              
424             sub error
425             {
426 1     1 1 36 my $x = shift;
427 1         9 $x->{_set}->error();
428             }
429              
430             sub is_valid
431             {
432 2     2 1 9 my $x = shift;
433              
434             # What does charset say to string?
435 2 50       9 if (defined $x->{_cache})
436             {
437             # XXX TODO: cached string should always be valid?
438 2         9 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 5906 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         211 my $str = $x->{_cache};
459 122         444 $x->SUPER::binc();
460              
461             # if old value cached and no rounding happens
462 122 100       4139 if ((defined $str)
463             # && (!defined $a) && (!defined $p)
464             # && (!defined $x->accuracy()) && (!defined $x->precision())
465             )
466             {
467 89         139 $x->{_cache} = $str; # restore cache
468 89         1642 $x->{_set}->next($x); # update string cache
469             }
470 122         800 $x;
471             }
472              
473             sub bdec
474             {
475 79 50   79 1 293 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         135 my $str = $x->{_cache};
480 79         744 $x->SUPER::bdec();
481              
482             # if old value cached and no rounding happens
483 79 100       4978 if ((defined $str)
484             # && (!defined $a) && (!defined $p)
485             # && (!defined $x->accuracy()) && (!defined $x->precision())
486             )
487             {
488 77         125 $x->{_cache} = $str; # restore cache
489 77         346 $x->{_set}->prev($x); # update string cache
490             }
491 79         551 $x;
492             }
493              
494             #############################################################################
495             # cache management
496              
497             sub modify
498             {
499 251     251 1 2141 $_[0]->{_cache} = undef; # invalidate cache
500 251         444 0; # go ahead, modify
501             }
502              
503             __END__