File Coverage

blib/lib/Locale/CLDR/NumberFormatter.pm
Criterion Covered Total %
statement 265 366 72.4
branch 110 182 60.4
condition 46 71 64.7
subroutine 27 27 100.0
pod 0 7 0.0
total 448 653 68.6


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