File Coverage

blib/lib/Locale/CLDR/NumberFormatter.pm
Criterion Covered Total %
statement 267 368 72.5
branch 110 182 60.4
condition 47 71 66.2
subroutine 27 27 100.0
pod 0 7 0.0
total 451 655 68.8


line stmt bran cond sub pod time code
1              
2             use version;
3 21     21   10446  
  21         44  
  21         113  
4             our $VERSION = version->declare('v0.34.1');
5              
6              
7             use v5.10.1;
8 21     21   1685 use mro 'c3';
  21         59  
9 21     21   81 use utf8;
  21         37  
  21         106  
10 21     21   411 use if $^V ge v5.12.0, feature => 'unicode_strings';
  21         33  
  21         97  
11 21     21   634  
  21         64  
  21         259  
12             use Moo::Role;
13 21     21   2346  
  21         36  
  21         126  
14             my ($self, $number, $format, $currency, $for_cash) = @_;
15            
16 775     775 0 2923 # Check if the locales numbering system is algorithmic. If so ignore the format
17             my $numbering_system = $self->default_numbering_system();
18             if ($self->numbering_system->{$numbering_system}{type} eq 'algorithmic') {
19 775         2124 $format = $self->numbering_system->{$numbering_system}{data};
20 775 50       2390 return $self->_algorithmic_number_format($number, $format);
21 0         0 }
22 0         0
23             $format //= '0';
24            
25 775   100     2448 return $self->_format_number($number, $format, $currency, $for_cash);
26             }
27 775         1678  
28             my ($self, $number, $for_cash) = @_;
29            
30             my $format = $self->currency_format;
31 16     16 0 61 return $self->format_number($number, $format, undef(), $for_cash);
32             }
33 16         105  
34 16         80 my ($self, $number, $format, $currency, $for_cash) = @_;
35            
36             # First check to see if this is an algorithmic format
37             my @valid_formats = $self->_get_valid_algorithmic_formats();
38 777     777   1186
39             if (grep {$_ eq $format} @valid_formats) {
40             return $self->_algorithmic_number_format($number, $format);
41 777         1332 }
42            
43 777 100       1236 # Some of these algorithmic formats are in locale/type/name format
  15540         17687  
44 5         15 if (my ($locale_id, $type, $format) = $format =~ m(^(.*?)/(.*?)/(.*?)$)) {
45             my $locale = Locale::CLDR->new($locale_id);
46             return $locale->format_number($number, $format);
47             }
48 772 50       1757
49 0         0 my $currency_data;
50 0         0
51             # Check if we need a currency and have not been given one.
52             # In that case we look up the default currency for the locale
53 772         906 if ($format =~ tr/¤/¤/) {
54            
55             $for_cash //=0;
56            
57 772 100       2073 $currency = $self->default_currency()
58             if ! defined $currency;
59 17   100     85
60             $currency_data = $self->_get_currency_data($currency);
61 17 50       107
62             $currency = $self->currency_symbol($currency);
63             }
64 17         61
65             $format = $self->parse_number_format($format, $currency, $currency_data, $for_cash);
66 17         66
67             $number = $self->get_formatted_number($number, $format, $currency_data, $for_cash);
68            
69 772         1697 return $number;
70             }
71 772         1637  
72             my ($self, $format, $symbol) = @_;
73 772         2901
74            
75             $format =~ s/¤/'$symbol'/g;
76            
77 17     17 0 51 return $format;
78             }
79              
80 17         127 my ($self, $currency) = @_;
81            
82 17         48 my $currency_data = $self->currency_fractions($currency);
83            
84             return $currency_data;
85             }
86 17     17   42  
87              
88 17         149 my ($self, $currency_data, $for_cash) = @_;
89            
90 17         33 my $rounder = $for_cash ? 'cashrounding' : 'rounding' ;
91            
92             return $currency_data->{$rounder};
93             }
94              
95 34     34   70 my ($self, $currency_data, $for_cash) = @_;
96            
97 34 100       60 my $digits = $for_cash ? 'cashdigits' : 'digits' ;
98            
99 34         89 return $currency_data->{$digits};
100             }
101              
102             my ($self, $format, $currency, $currency_data, $for_cash) = @_;
103 17     17   49  
104             use feature 'state';
105 17 100       52
106             state %cache;
107 17         46
108             return $cache{$format} if exists $cache{$format};
109            
110             $format = $self->add_currency_symbol($format, $currency)
111 774     774 0 1181 if defined $currency;
112            
113 21     21   20850 my ($positive, $negative) = $format =~ /^( (?: (?: ' [^']* ' )*+ | [^';]+ )+ ) (?: ; (.+) )? $/x;
  21         49  
  21         8615  
114            
115 774         888 $negative //= "-$positive";
116            
117 774 100       1944 my $type = 'positive';
118             foreach my $to_parse ( $positive, $negative ) {
119 27 100       116 my ($prefix, $suffix);
120             if (($prefix) = $to_parse =~ /^ ( (?: [^0-9@#.,E'*] | (?: ' [^']* ' )++ )+ ) /x) {
121             $to_parse =~ s/^ ( (?: [^0-9@#.,E'*] | (?: ' [^']* ' )++ )+ ) //x;
122 27         238 }
123             if( ($suffix) = $to_parse =~ / ( (?: [^0-9@#.,E'] | (?: ' [^']* ' )++ )+ ) $ /x) {
124 27   66     172 $to_parse =~ s/( (?:[^0-9@#.,E'] | (?: ' [^']* ' )++ )+ ) $//x;
125             }
126 27         53
127 27         69 # Fix escaped ', - and +
128 54         83 foreach my $str ($prefix, $suffix) {
129 54 100       263 $str //= '';
130 43         192 $str =~ s/(?: ' (?: (?: '' )++ | [^']+ ) ' )*? \K ( [-+\\] ) /\\$1/gx;
131             $str =~ s/ ' ( (?: '' )++ | [^']++ ) ' /$1/gx;
132 54 100       326 $str =~ s/''/'/g;
133 15         95 }
134            
135             # Look for padding
136             my ($pad_character, $pad_location);
137 54         116 if (($pad_character) = $prefix =~ /^\*(\p{Any})/ ) {
138 108   100     267 $prefix =~ s/^\*(\p{Any})//;
139 108         282 $pad_location = 'before prefix';
140 108         261 }
141 108         186 elsif ( ($pad_character) = $prefix =~ /\*(\p{Any})$/ ) {
142             $prefix =~ s/\*(\p{Any})$//;
143             $pad_location = 'after prefix';
144             }
145 54         77 elsif (($pad_character) = $suffix =~ /^\*(\p{Any})/ ) {
146 54 50       328 $suffix =~ s/^\*(\p{Any})//;
    50          
    50          
    100          
147 0         0 $pad_location = 'before suffix';
148 0         0 }
149             elsif (($pad_character) = $suffix =~ /\*(\p{Any})$/ ) {
150             $suffix =~ s/\*(\p{Any})$//;
151 0         0 $pad_location = 'after suffix';
152 0         0 }
153            
154             my $pad_length = defined $pad_character
155 0         0 ? length($prefix) + length($to_parse) + length($suffix) + 2
156 0         0 : 0;
157            
158             # Check for a multiplier
159 1         2 my $multiplier = 1;
160 1         2 $multiplier = 100 if $prefix =~ tr/%/%/ || $suffix =~ tr/%/%/;
161             $multiplier = 1000 if $prefix =~ tr/‰/‰/ || $suffix =~ tr/‰/‰/;
162            
163 54 100       106 my $rounding = $to_parse =~ / ( [1-9] [0-9]* (?: \. [0-9]+ )? ) /x;
164             $rounding ||= 0;
165            
166             $rounding = $self->_get_currency_rounding($currency_data, $for_cash)
167             if defined $currency;
168 54         79
169 54 100 66     226 my ($integer, $decimal) = split /\./, $to_parse;
170 54 100 66     276
171             my ($minimum_significant_digits, $maximum_significant_digits, $minimum_digits);
172 54         124 if (my ($digits) = $to_parse =~ /(\@+)/) {
173 54   50     178 $minimum_significant_digits = length $digits;
174             ($digits ) = $to_parse =~ /\@(#+)/;
175 54 100       159 $maximum_significant_digits = $minimum_significant_digits + length ($digits // '');
176             }
177             else {
178 54         164 $minimum_digits = $integer =~ tr/0-9/0-9/;
179             }
180 54         80
181 54 50       142 # Check for exponent
182 0         0 my $exponent_digits = 0;
183 0         0 my $need_plus = 0;
184 0   0     0 my $exponent;
185             my $major_group;
186             my $minor_group;
187 54         87 if ($to_parse =~ tr/E/E/) {
188             ($need_plus, $exponent) = $to_parse =~ m/ E ( \+? ) ( [0-9]+ ) /x;
189             $exponent_digits = length $exponent;
190             }
191 54         71 else {
192 54         63 # Check for grouping
193 54         110 my ($grouping) = split /\./, $to_parse;
194             my @groups = split /,/, $grouping;
195 54         0 shift @groups;
196 54 50       94 ($major_group, $minor_group) = map {length} @groups;
197 0         0 $minor_group //= $major_group;
198 0         0 }
199            
200             $cache{$format}{$type} = {
201             prefix => $prefix // '',
202 54         121 suffix => $suffix // '',
203 54         131 pad_character => $pad_character,
204 54         95 pad_location => $pad_location // 'none',
205 54         92 pad_length => $pad_length,
  48         122  
206 54   100     161 multiplier => $multiplier,
207             rounding => $rounding,
208             minimum_significant_digits => $minimum_significant_digits,
209 54   50     773 maximum_significant_digits => $maximum_significant_digits,
      50        
      100        
      50        
210             minimum_digits => $minimum_digits // 0,
211             exponent_digits => $exponent_digits,
212             exponent_needs_plus => $need_plus,
213             major_group => $major_group,
214             minor_group => $minor_group,
215             };
216            
217             $type = 'negative';
218             }
219            
220             return $cache{$format};
221             }
222              
223             # Rounding function
224             my ($self, $number, $increment, $decimal_digits) = @_;
225              
226 54         133 if ($increment ) {
227             $increment /= 10 ** $decimal_digits;
228             $number /= $increment;
229 27         93 $number = int ($number + .5 );
230             $number *= $increment;
231             }
232            
233             if ( $decimal_digits ) {
234 17     17 0 49 $number *= 10 ** $decimal_digits;
235             $number = int $number;
236 17 50       49 $number /= 10 ** $decimal_digits;
237 0         0
238 0         0 my ($decimal) = $number =~ /(\..*)/;
239 0         0 $decimal //= '.'; # No fraction so add a decimal point
240 0         0
241             $number = int ($number) . $decimal . ('0' x ( $decimal_digits - length( $decimal ) +1 ));
242             }
243 17 50       48 else {
244 17         48 # No decimal digits wanted
245 17         33 $number = int $number;
246 17         36 }
247            
248 17         123 return $number;
249 17   100     52 }
250              
251 17         86 my ($self, $number, $format, $currency_data, $for_cash) = @_;
252            
253             my @digits = $self->get_digits;
254             my @number_symbols_bundles = reverse $self->_find_bundle('number_symbols');
255 0         0 my %symbols;
256             foreach my $bundle (@number_symbols_bundles) {
257             my $current_symbols = $bundle->number_symbols;
258 17         53 foreach my $type (keys %$current_symbols) {
259             foreach my $symbol (keys %{$current_symbols->{$type}}) {
260             $symbols{$type}{$symbol} = $current_symbols->{$type}{$symbol};
261             }
262 772     772 0 1090 }
263             }
264 772         1304
265 772         1688 my $symbols_type = $self->default_numbering_system;
266 772         3931
267 772         1124 $symbols_type = $symbols{$symbols_type}{alias} if exists $symbols{$symbols_type}{alias};
268 1544         2780
269 1544         5225 my $type = $number=~ s/^-// ? 'negative' : 'positive';
270 36284         32911
  36284         49896  
271 69480         101885 $number *= $format->{$type}{multiplier};
272            
273             if ($format->{rounding} || defined $for_cash) {
274             my $decimal_digits = 0;
275            
276 772         1999 if (defined $for_cash) {
277             $decimal_digits = $self->_get_currency_digits($currency_data, $for_cash)
278 772 50       1852 }
279            
280 772 100       1907 $number = $self->round($number, $format->{$type}{rounding}, $decimal_digits);
281             }
282 772         1280
283             my $pad_zero = $format->{$type}{minimum_digits} - length "$number";
284 772 100 66     2636 if ($pad_zero > 0) {
285 17         29 $number = ('0' x $pad_zero) . $number;
286             }
287 17 50       40
288 17         77 # Handle grouping
289             my ($integer, $decimal) = split /\./, $number;
290              
291 17         95 my $minimum_grouping_digits = $self->_find_bundle('minimum_grouping_digits');
292             $minimum_grouping_digits = $minimum_grouping_digits
293             ? $minimum_grouping_digits->minimum_grouping_digits()
294 772         1477 : 0;
295 772 100       1414
296 4         7 my ($separator, $decimal_point) = ($symbols{$symbols_type}{group}, $symbols{$symbols_type}{decimal});
297             if (($minimum_grouping_digits && length $integer >= $minimum_grouping_digits) || ! $minimum_grouping_digits) {
298             my ($minor_group, $major_group) = ($format->{$type}{minor_group}, $format->{$type}{major_group});
299            
300 772         1908 if (defined $minor_group && $separator) {
301             # Fast commify using unpack
302 772         1742 my $pattern = "(A$minor_group)(A$major_group)*";
303 772 50       4626 $number = reverse join $separator, grep {length} unpack $pattern, reverse $integer;
304             }
305             else {
306             $number = $integer;
307 772         1557 }
308 772 50 33     2728 }
      33        
309 772         1369 else {
310             $number = $integer;
311 772 100 66     1577 }
312            
313 27         81 $number.= "$decimal_point$decimal" if defined $decimal;
314 27         234
  61         136  
315             # Fix digits
316             $number =~ s/([0-9])/$digits[$1]/eg;
317 745         916
318             my ($prefix, $suffix) = ( $format->{$type}{prefix}, $format->{$type}{suffix});
319            
320             # This needs fixing for escaped symbols
321 0         0 foreach my $string ($prefix, $suffix) {
322             $string =~ s/%/$symbols{$symbols_type}{percentSign}/;
323             $string =~ s/‰/$symbols{$symbols_type}{perMille}/;
324 772 100       1143 if ($type eq 'negative') {
325             $string =~ s/(?: \\ \\ )*+ \K \\ - /$symbols{$symbols_type}{minusSign}/x;
326             $string =~ s/(?: \\ \\)*+ \K \\ + /$symbols{$symbols_type}{minusSign}/x;
327 772         2503 }
  958         2432  
328             else {
329 772         1968 $string =~ s/(?: \\ \\ )*+ \K \\ - //x;
330             $string =~ s/(?: \\ \\ )*+ \K \\ + /$symbols{$symbols_type}{plusSign}/x;
331             }
332 772         1096 $string =~ s/ \\ \\ /\\/gx;
333 1544         1578 }
334 1544         1503
335 1544 100       2072 $number = $prefix . $number . $suffix;
336 24         68
337 24         46 return $number;
338             }
339              
340 1520         1529 # Get the digits for the locale. Assumes a numeric numbering system
341 1520         1453 my $self = shift;
342            
343 1544         1694 my $numbering_system = $self->default_numbering_system();
344            
345             $numbering_system = 'latn' unless $self->numbering_system->{$numbering_system}{type} eq 'numeric'; # Fall back to latn if the numbering system is not numeric
346 772         1323
347             my $digits = $self->numbering_system->{$numbering_system}{data};
348 772         9730
349             return @$digits;
350             }
351              
352             # RBNF
353 773     773 0 1775 # Note that there are a couple of assumptions with the way
354             # I handle Rule Base Number Formats.
355 773         1610 # 1) The number is treated as a string for as long as possible
356             # This allows things like -0.0 to be correctly formatted
357 773 50       2159 # 2) There is no fall back. All the rule sets are self contained
358             # in a bundle. Fall back is used to find a bundle but once a
359 773         1107 # bundle is found no further processing of the bundle chain
360             # is done. This was found by trial and error when attempting
361 773         1784 # to process -0.0 correctly into English.
362             my $self = shift;
363            
364             my @formats = map { @{$_->valid_algorithmic_formats()} } $self->_find_bundle('valid_algorithmic_formats');
365            
366             my %seen;
367             return sort grep { ! $seen{$_}++ } @formats;
368             }
369              
370             # Main entry point to RBNF
371             my ($self, $number, $format_name, $type) = @_;
372            
373             my $format_data = $self->_get_algorithmic_number_format_data_by_name($format_name, $type);
374            
375 777     777   989 return $number unless $format_data;
376            
377 777         1459 return $self->_process_algorithmic_number_data($number, $format_data);
  1554         4515  
  1554         5442  
378             }
379 777         1102  
380 777         989 my ($self, $format_name, $type) = @_;
  19425         34150  
381            
382             # Some of these algorithmic formats are in locale/type/name format
383             if (my ($locale_id, undef, $format) = $format_name =~ m(^(.*?)/(.*?)/(.*?)$)) {
384             my $locale = Locale::CLDR->new($locale_id);
385 8     8   18 return $locale->_get_algorithmic_number_format_data_by_name($format, $type)
386             if $locale;
387 8         20  
388             return undef;
389 8 50       15 }
390            
391 8         34 $type //= 'public';
392            
393             my %data = ();
394            
395 8     8   11 my @data_bundles = $self->_find_bundle('algorithmic_number_format_data');
396             foreach my $data_bundle (@data_bundles) {
397             my $data = $data_bundle->algorithmic_number_format_data();
398 8 50       28 next unless $data->{$format_name};
399 0         0 next unless $data->{$format_name}{$type};
400 0 0       0
401             foreach my $rule (keys %{$data->{$format_name}{$type}}) {
402             $data{$rule} = $data->{$format_name}{$type}{$rule};
403 0         0 }
404            
405             last;
406 8   100     24 }
407            
408 8         10 return keys %data ? \%data : undef;
409             }
410 8         19  
411 8         46 my ($self, $plural, $from) = @_;
412 10         22
413 10 100       22 my ($result) = $from =~ /$plural\{(.+?)\}/;
414 8 50       21 ($result) = $from =~ /other\{(.+?)\}/ unless defined $result;
415            
416 8         9 return $result;
  8         57  
417 196         262 }
418              
419             my ($self, $number, $format_data, $plural, $in_fraction_rule_set) = @_;
420 8         18
421             $in_fraction_rule_set //= 0;
422            
423 8 50       24 my $format = $self->_get_algorithmic_number_format($number, $format_data);
424            
425             my $format_rule = $format->{rule};
426             if (! $plural && $format_rule =~ /(cardinal|ordinal)/) {
427 1     1   4 my $type = $1;
428             $plural = $self->plural($number, $type);
429 1         12 $plural = [$type, $plural];
430 1 50       3 }
431            
432 1         3 # Sort out plural forms
433             if ($plural) {
434             $format_rule =~ s/\$\($plural->[0],(.+)\)\$/$self->_get_plural_form($plural->[1],$1)/eg;
435             }
436 14     14   25
437             my $divisor = $format->{divisor};
438 14   100     43 my $base_value = $format->{base_value} // '';
439            
440 14         27 # Negative numbers
441             if ($number =~ /^-/) {
442 14         1032 my $positive_number = $number;
443 14 100 66     58 $positive_number =~ s/^-//;
444 3         6
445 3         13 if ($format_rule =~ /→→/) {
446 3         7 $format_rule =~ s/→→/$self->_process_algorithmic_number_data($positive_number, $format_data, $plural)/e;
447             }
448             elsif((my $rule_name) = $format_rule =~ /→(.+)→/) {
449             my $type = 'public';
450 14 100       32 if ($rule_name =~ s/^%%/%/) {
451 3         43 $type = 'private';
  1         5  
452             }
453             my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
454 14         23 if($format_data) {
455 14   100     28 # was a valid name
456             $format_rule =~ s/→(.+)→/$self->_process_algorithmic_number_data($positive_number, $format_data, $plural)/e;
457             }
458 14 100       35 else {
    100          
459 1         1 # Assume a format
460 1         4 $format_rule =~ s/→(.+)→/$self->_format_number($positive_number, $1)/e;
461             }
462 1 50       7 }
    0          
    0          
    0          
    0          
463 1         4 elsif($format_rule =~ /=%%.*=/) {
  1         3  
464             $format_rule =~ s/=%%(.*?)=/$self->_algorithmic_number_format($number, $1, 'private')/eg;
465             }
466 0         0 elsif($format_rule =~ /=%.*=/) {
467 0 0       0 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
468 0         0 }
469             elsif($format_rule =~ /=.*=/) {
470 0         0 $format_rule =~ s/=(.*?)=/$self->_format_number($number, $1)/eg;
471 0 0       0 }
472             }
473 0         0 # Fractions
  0         0  
474             elsif( $number =~ /\./ ) {
475             my $in_fraction_rule_set = 1;
476             my ($integer, $fraction) = $number =~ /^([^.]*)\.(.*)$/;
477 0         0
  0         0  
478             if ($number >= 0 && $number < 1) {
479             $format_rule =~ s/\[.*\]//;
480             }
481 0         0 else {
  0         0  
482             $format_rule =~ s/[\[\]]//g;
483             }
484 0         0
  0         0  
485             if ($format_rule =~ /→→/) {
486             $format_rule =~ s/→→/$self->_process_algorithmic_number_data_fractions($fraction, $format_data, $plural)/e;
487 0         0 }
  0         0  
488             elsif((my $rule_name) = $format_rule =~ /→(.*)→/) {
489             my $type = 'public';
490             if ($rule_name =~ s/^%%/%/) {
491             $type = 'private';
492 2         4 }
493 2         8 my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
494             if ($format_data) {
495 2 50 33     30 $format_rule =~ s/→(.*)→/$self->_process_algorithmic_number_data_fractions($fraction, $format_data, $plural)/e;
496 2         8 }
497             else {
498             $format_rule =~ s/→(.*)→/$self->_format_number($fraction, $1)/e;
499 0         0 }
500             }
501            
502 2 100       10 if ($format_rule =~ /←←/) {
    50          
503 1         12 $format_rule =~ s/←←/$self->_process_algorithmic_number_data($integer, $format_data, $plural, $in_fraction_rule_set)/e;
  1         40  
504             }
505             elsif((my $rule_name) = $format_rule =~ /←(.+)←/) {
506 0         0 my $type = 'public';
507 0 0       0 if ($rule_name =~ s/^%%/%/) {
508 0         0 $type = 'private';
509             }
510 0         0 my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
511 0 0       0 if ($format_data) {
512 0         0 $format_rule =~ s/←(.*)←/$self->_process_algorithmic_number_data($integer, $format_data, $plural, $in_fraction_rule_set)/e;
  0         0  
513             }
514             else {
515 0         0 $format_rule =~ s/←(.*)←/$self->_format_number($integer, $1)/e;
  0         0  
516             }
517             }
518            
519 2 100       8 if($format_rule =~ /=.*=/) {
    50          
520 1         4 if($format_rule =~ /=%%.*=/) {
  1         3  
521             $format_rule =~ s/=%%(.*?)=/$self->_algorithmic_number_format($number, $1, 'private')/eg;
522             }
523 0         0 elsif($format_rule =~ /=%.*=/) {
524 0 0       0 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
525 0         0 }
526             else {
527 0         0 $format_rule =~ s/=(.*?)=/$self->_format_number($integer, $1)/eg;
528 0 0       0 }
529 0         0 }
  0         0  
530             }
531            
532 0         0 # Everything else
  0         0  
533             else {
534             # At this stage we have a non negative integer
535             if ($format_rule =~ /\[.*\]/) {
536 2 100       9 if ($in_fraction_rule_set && $number * $base_value == 1) {
537 1 50       5 $format_rule =~ s/\[.*\]//;
    50          
538 0         0 }
  0         0  
539             # Not fractional rule set Number is a multiple of $divisor and the multiple is even
540             elsif (! $in_fraction_rule_set && ! ($number % $divisor) ) {
541 1         5 $format_rule =~ s/\[.*\]//;
  1         9  
542             }
543             else {
544 0         0 $format_rule =~ s/[\[\]]//g;
  0         0  
545             }
546             }
547            
548             if ($in_fraction_rule_set) {
549             if (my ($rule_name) = $format_rule =~ /←(.*)←/) {
550             if (length $rule_name) {
551             my $type = 'public';
552 11 100       27 if ($rule_name =~ s/^%%/%/) {
553 3 50 33     17 $type = 'private';
    50 33        
554 0         0 }
555             my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
556             if ($format_data) {
557             $format_rule =~ s/←(.*)←/$self->_process_algorithmic_number_data($number * $base_value, $format_data, $plural, $in_fraction_rule_set)/e;
558 0         0 }
559             else {
560             $format_rule =~ s/←(.*)←/$self->_format_number($number * $base_value, $1)/e;
561 3         13 }
562             }
563             else {
564             $format_rule =~ s/←←/$self->_process_algorithmic_number_data($number * $base_value, $format_data, $plural, $in_fraction_rule_set)/e;
565 11 100       21 }
566 2 50       8 }
    50          
567 0 0       0 elsif($format_rule =~ /=.*=/) {
568 0         0 $format_rule =~ s/=(.*?)=/$self->_format_number($number, $1)/eg;
569 0 0       0 }
570 0         0 }
571             else {
572 0         0 if (my ($rule_name) = $format_rule =~ /→(.*)→/) {
573 0 0       0 if (length $rule_name) {
574 0         0 my $type = 'public';
  0         0  
575             if ($rule_name =~ s/^%%/%/) {
576             $type = 'private';
577 0         0 }
  0         0  
578             my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
579             if ($format_data) {
580             $format_rule =~ s/→(.+)→/$self->_process_algorithmic_number_data($number % $divisor, $format_data, $plural)/e;
581 0         0 }
  0         0  
582             else {
583             $format_rule =~ s/→(.*)→/$self->_format_number($number % $divisor, $1)/e;
584             }
585 0         0 }
  0         0  
586             else {
587             $format_rule =~ s/→→/$self->_process_algorithmic_number_data($number % $divisor, $format_data, $plural)/e;
588             }
589 9 100       29 }
590 3 50       8
591 0         0 if (my ($rule_name) = $format_rule =~ /←(.*)←/) {
592 0 0       0 if (length $rule_name) {
593 0         0 my $type = 'public';
594             if ($rule_name =~ s/^%%/%/) {
595 0         0 $type = 'private';
596 0 0       0 }
597 0         0 my $format_data = $self->_get_algorithmic_number_format_data_by_name($rule_name, $type);
  0         0  
598             if ($format_data) {
599             $format_rule =~ s|←(.*)←|$self->_process_algorithmic_number_data(int ($number / $divisor), $format_data, $plural)|e;
600 0         0 }
  0         0  
601             else {
602             $format_rule =~ s|←(.*)←|$self->_format_number(int($number / $divisor), $1)|e;
603             }
604 3         7 }
  3         10  
605             else {
606             $format_rule =~ s|←←|$self->_process_algorithmic_number_data(int($number / $divisor), $format_data, $plural)|e;
607             }
608 9 50       19 }
609 0 0       0
610 0         0 if($format_rule =~ /=.*=/) {
611 0 0       0 if($format_rule =~ /=%%.*=/) {
612 0         0 $format_rule =~ s/=%%(.*?)=/$self->_algorithmic_number_format($number, $1, 'private')/eg;
613             }
614 0         0 elsif($format_rule =~ /=%.*=/) {
615 0 0       0 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
616 0         0 }
  0         0  
617             else {
618             $format_rule =~ s/=(.*?)=/$self->_format_number($number, $1)/eg;
619 0         0 }
  0         0  
620             }
621             }
622             }
623 0         0
  0         0  
624             return $format_rule;
625             }
626              
627 9 100       24 my ($self, $fraction, $format_data, $plural) = @_;
628 4 50       13
    100          
629 0         0 my $result = '';
  0         0  
630             foreach my $digit (split //, $fraction) {
631             $result .= $self->_process_algorithmic_number_data($digit, $format_data, $plural, 1);
632 2         8 }
  2         10  
633            
634             return $result;
635 2         8 }
  2         7  
636              
637             my ($self, $number, $format_data) = @_;
638            
639             use bignum;
640             return $format_data->{'-x'} if $number =~ /^-/ && exists $format_data->{'-x'};
641 14         74 return $format_data->{'x.x'} if $number =~ /\./ && exists $format_data->{'x.x'};
642             return $format_data->{0} if $number == 0 || $number =~ /^-/;
643             return $format_data->{max} if $number >= $format_data->{max}{base_value};
644            
645 1     1   4 my $previous = 0;
646             foreach my $key (sort { $a <=> $b } grep /^[0-9]+$/, keys %$format_data) {
647 1         2 next if $key == 0;
648 1         3 return $format_data->{$key} if $number == $key;
649 1         31 return $format_data->{$previous} if $number < $key;
650             $previous = $key;
651             }
652 1         3 }
653              
654             no Moo::Role;
655              
656 14     14   20 1;
657              
658 21     21   112209 # vim: tabstop=4
  21         93309  
  21         114