File Coverage

blib/lib/Locale/CLDR/NumberFormatter.pm
Criterion Covered Total %
statement 267 367 72.7
branch 110 182 60.4
condition 47 71 66.2
subroutine 27 27 100.0
pod 0 7 0.0
total 451 654 68.9


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