File Coverage

blib/lib/Locale/CLDR/NumberFormatter.pm
Criterion Covered Total %
statement 264 366 72.1
branch 108 182 59.3
condition 46 71 64.7
subroutine 27 27 100.0
pod 0 7 0.0
total 445 653 68.1


line stmt bran cond sub pod time code
1             package Locale::CLDR::NumberFormatter;
2              
3 20     20   14359 use version;
  20         43  
  20         131  
4              
5             our $VERSION = version->declare('v0.28.2');
6              
7              
8 20     20   2015 use v5.10.1;
  20         73  
9 20     20   105 use mro 'c3';
  20         43  
  20         130  
10 20     20   704 use utf8;
  20         39  
  20         121  
11 20     20   660 use if $^V ge v5.12.0, feature => 'unicode_strings';
  20         35  
  20         254  
12              
13 20     20   1997 use Moose::Role;
  20         49  
  20         310  
14              
15             sub format_number {
16 769     769 0 1304 my ($self, $number, $format, $currency, $for_cash) = @_;
17            
18             # Check if the locales numbering system is algorithmic. If so ignore the format
19 769         2602 my $numbering_system = $self->default_numbering_system();
20 769 50       29236 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 769   100     2922 $format //= '0';
26            
27 769         1940 return $self->_format_number($number, $format, $currency, $for_cash);
28             }
29              
30             sub format_currency {
31 16     16 0 40 my ($self, $number, $for_cash) = @_;
32            
33 16         66 my $format = $self->currency_format;
34 16         64 return $self->format_number($number, $format, undef(), $for_cash);
35             }
36              
37             sub _format_number {
38 771     771   1336 my ($self, $number, $format, $currency, $for_cash) = @_;
39            
40             # First check to see if this is an algorithmic format
41 771         2088 my @valid_formats = $self->_get_valid_algorithmic_formats();
42            
43 771 100       1896 if (grep {$_ eq $format} @valid_formats) {
  15420         25936  
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 766 50       2225 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 766         816 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 766 100       2710 if ($format =~ tr/¤/¤/) {
58            
59 17   100     57 $for_cash //=0;
60            
61 17 50       91 $currency = $self->default_currency()
62             if ! defined $currency;
63            
64 17         62 $currency_data = $self->_get_currency_data($currency);
65            
66 17         71 $currency = $self->currency_symbol($currency);
67             }
68            
69 766         2159 $format = $self->parse_number_format($format, $currency, $currency_data, $for_cash);
70            
71 766         2002 $number = $self->get_formatted_number($number, $format, $currency_data, $for_cash);
72            
73 766         4218 return $number;
74             }
75              
76             sub add_currency_symbol {
77 17     17 0 31 my ($self, $format, $symbol) = @_;
78            
79            
80 17         130 $format =~ s/¤/'$symbol'/g;
81            
82 17         54 return $format;
83             }
84              
85             sub _get_currency_data {
86 17     17   30 my ($self, $currency) = @_;
87            
88 17         81 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   55 my ($self, $currency_data, $for_cash) = @_;
96            
97 34 100       66 my $rounder = $for_cash ? 'cashrounding' : 'rounding' ;
98            
99 34         79 return $currency_data->{$rounder};
100             }
101              
102             sub _get_currency_digits {
103 17     17   32 my ($self, $currency_data, $for_cash) = @_;
104            
105 17 100       47 my $digits = $for_cash ? 'cashdigits' : 'digits' ;
106            
107 17         52 return $currency_data->{$digits};
108             }
109              
110             sub parse_number_format {
111 768     768 0 1286 my ($self, $format, $currency, $currency_data, $for_cash) = @_;
112              
113 20     20   125192 use feature 'state';
  20         44  
  20         10952  
114            
115 768         903 state %cache;
116            
117 768 100       2736 return $cache{$format} if exists $cache{$format};
118            
119 26 100       100 $format = $self->add_currency_symbol($format, $currency)
120             if defined $currency;
121            
122 26         220 my ($positive, $negative) = $format =~ /^( (?: (?: ' [^']* ' )*+ | [^';]+ )+ ) (?: ; (.+) )? $/x;
123            
124 26   66     131 $negative //= "-$positive";
125            
126 26         68 my $type = 'positive';
127 26         52 foreach my $to_parse ( $positive, $negative ) {
128 52         69 my ($prefix, $suffix);
129 52 100       296 if (($prefix) = $to_parse =~ /^ ( (?: [^0-9@#.,E'*] | (?: ' [^']* ' )++ )+ ) /x) {
130 42         192 $to_parse =~ s/^ ( (?: [^0-9@#.,E'*] | (?: ' [^']* ' )++ )+ ) //x;
131             }
132 52 100       384 if( ($suffix) = $to_parse =~ / ( (?: [^0-9@#.,E'] | (?: ' [^']* ' )++ )+ ) $ /x) {
133 15         115 $to_parse =~ s/( (?:[^0-9@#.,E'] | (?: ' [^']* ' )++ )+ ) $//x;
134             }
135            
136             # Fix escaped ', - and +
137 52         92 foreach my $str ($prefix, $suffix) {
138 104   100     296 $str //= '';
139 104         338 $str =~ s/(?: ' (?: (?: '' )++ | [^']+ ) ' )*? \K ( [-+\\] ) /\\$1/gx;
140 104         294 $str =~ s/ ' ( (?: '' )++ | [^']++ ) ' /$1/gx;
141 104         198 $str =~ s/''/'/g;
142             }
143            
144             # Look for padding
145 52         77 my ($pad_character, $pad_location);
146 52 50       315 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         4 $suffix =~ s/\*(\p{Any})$//;
160 1         3 $pad_location = 'after suffix';
161             }
162            
163 52 100       113 my $pad_length = defined $pad_character
164             ? length($prefix) + length($to_parse) + length($suffix) + 2
165             : 0;
166            
167             # Check for a multiplier
168 52         64 my $multiplier = 1;
169 52 100 66     274 $multiplier = 100 if $prefix =~ tr/%/%/ || $suffix =~ tr/%/%/;
170 52 100 66     338 $multiplier = 1000 if $prefix =~ tr/‰/‰/ || $suffix =~ tr/‰/‰/;
171            
172 52         125 my $rounding = $to_parse =~ / ( [1-9] [0-9]* (?: \. [0-9]+ )? ) /x;
173 52   50     175 $rounding ||= 0;
174            
175 52 100       166 $rounding = $self->_get_currency_rounding($currency_data, $for_cash)
176             if defined $currency;
177            
178 52         209 my ($integer, $decimal) = split /\./, $to_parse;
179            
180 52         93 my ($minimum_significant_digits, $maximum_significant_digits, $minimum_digits);
181 52 50       132 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 52         94 $minimum_digits = $integer =~ tr/0-9/0-9/;
188             }
189            
190             # Check for exponent
191 52         77 my $exponent_digits = 0;
192 52         59 my $need_plus = 0;
193 52         60 my $exponent;
194             my $major_group;
195 0         0 my $minor_group;
196 52 50       112 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 52         163 my ($grouping) = split /\./, $to_parse;
203 52         161 my @groups = split /,/, $grouping;
204 52         71 shift @groups;
205 52         100 ($major_group, $minor_group) = map {length} @groups;
  48         130  
206 52   100     225 $minor_group //= $major_group;
207             }
208            
209 52   50     892 $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 52         204 $type = 'negative';
227             }
228            
229 26         89 return $cache{$format};
230             }
231              
232             # Rounding function
233             sub round {
234 17     17 0 33 my ($self, $number, $increment, $decimal_digits) = @_;
235              
236 17 50       38 if ($increment ) {
237 0         0 $number /= $increment;
238 0         0 $number = int ($number + .5 );
239 0         0 $number *= $increment;
240             }
241            
242 17 50       32 if ( $decimal_digits ) {
243 17         32 $number *= 10 ** $decimal_digits;
244 17         25 $number = int $number;
245 17         30 $number /= 10 ** $decimal_digits;
246            
247 17         131 my ($decimal) = $number =~ /(\..*)/;
248 17   100     47 $decimal //= '.'; # No fraction so add a decimal point
249            
250 17         71 $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 766     766 0 1222 my ($self, $number, $format, $currency_data, $for_cash) = @_;
262            
263 766         1644 my @digits = $self->get_digits;
264 766         2885 my @number_symbols_bundles = reverse $self->_find_bundle('number_symbols');
265 766         1306 my %symbols;
266 766         1478 foreach my $bundle (@number_symbols_bundles) {
267 1532         60903 my $current_symbols = $bundle->number_symbols;
268 1532         7460 foreach my $type (keys %$current_symbols) {
269 32938         35102 foreach my $symbol (keys %{$current_symbols->{$type}}) {
  32938         67935  
270 65876         161702 $symbols{$type}{$symbol} = $current_symbols->{$type}{$symbol};
271             }
272             }
273             }
274            
275 766         2766 my $symbols_type = $self->default_numbering_system;
276            
277 766 50       2502 $symbols_type = $symbols{$symbols_type}{alias} if exists $symbols{$symbols_type}{alias};
278            
279 766 100       2209 my $type = $number=~ s/^-// ? 'negative' : 'positive';
280            
281 766         1617 $number *= $format->{$type}{multiplier};
282            
283 766 100 66     3931 if ($format->{rounding} || defined $for_cash) {
284 17         25 my $decimal_digits = 0;
285            
286 17 50       45 if (defined $for_cash) {
287 17         53 $decimal_digits = $self->_get_currency_digits($currency_data, $for_cash)
288             }
289            
290 17         67 $number = $self->round($number, $format->{$type}{rounding}, $decimal_digits);
291             }
292            
293 766         1740 my $pad_zero = $format->{$type}{minimum_digits} - length "$number";
294 766 100       1704 if ($pad_zero > 0) {
295 4         13 $number = ('0' x $pad_zero) . $number;
296             }
297            
298             # Handle grouping
299 766         2186 my ($integer, $decimal) = split /\./, $number;
300              
301 766         2625 my $minimum_grouping_digits = $self->_find_bundle('minimum_grouping_digits');
302 766 50       32147 $minimum_grouping_digits = $minimum_grouping_digits
303             ? $minimum_grouping_digits->minimum_grouping_digits()
304             : 0;
305            
306 766         1993 my ($separator, $decimal_point) = ($symbols{$symbols_type}{group}, $symbols{$symbols_type}{decimal});
307 766 50 33     4312 if (($minimum_grouping_digits && length $integer >= $minimum_grouping_digits) || ! $minimum_grouping_digits) {
      33        
308 766         1731 my ($minor_group, $major_group) = ($format->{$type}{minor_group}, $format->{$type}{major_group});
309            
310 766 100 66     2157 if (defined $minor_group && $separator) {
311             # Fast commify using unpack
312 27         78 my $pattern = "(A$minor_group)(A$major_group)*";
313 27         178 $number = reverse join $separator, grep {length} unpack $pattern, reverse $integer;
  61         152  
314             }
315             }
316             else {
317 0         0 $number = $integer;
318             }
319            
320 766 100       1703 $number.= "$decimal_point$decimal" if defined $decimal;
321            
322             # Fix digits
323 766         3060 $number =~ s/([0-9])/$digits[$1]/eg;
  952         3297  
324            
325 766         2141 my ($prefix, $suffix) = ( $format->{$type}{prefix}, $format->{$type}{suffix});
326            
327             # This needs fixing for escaped symbols
328 766         1231 foreach my $string ($prefix, $suffix) {
329 1532         2136 $string =~ s/%/$symbols{$symbols_type}{percentSign}/;
330 1532         1809 $string =~ s/‰/$symbols{$symbols_type}{perMille}/;
331 1532 100       3078 if ($type eq 'negative') {
332 24         75 $string =~ s/(?: \\ \\ )*+ \K \\ - /$symbols{$symbols_type}{minusSign}/x;
333 24         44 $string =~ s/(?: \\ \\)*+ \K \\ + /$symbols{$symbols_type}{minusSign}/x;
334             }
335             else {
336 1508         1678 $string =~ s/(?: \\ \\ )*+ \K \\ - //x;
337 1508         1832 $string =~ s/(?: \\ \\ )*+ \K \\ + /$symbols{$symbols_type}{plusSign}/x;
338             }
339 1532         2372 $string =~ s/ \\ \\ /\\/gx;
340             }
341            
342 766         1569 $number = $prefix . $number . $suffix;
343            
344 766         15420 return $number;
345             }
346              
347             # Get the digits for the locale. Assumes a numeric numbering system
348             sub get_digits {
349 767     767 0 1113 my $self = shift;
350            
351 767         2339 my $numbering_system = $self->default_numbering_system();
352            
353 767 50       28433 $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 767         28134 my $digits = $self->numbering_system->{$numbering_system}{data};
356            
357 767         7273 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 771     771   1085 my $self = shift;
372            
373 771         2311 my @formats = map { @{$_->valid_algorithmic_formats()} } $self->_find_bundle('valid_algorithmic_formats');
  1542         1902  
  1542         62934  
374            
375 771         2149 my %seen;
376 771         1429 return sort grep { ! $seen{$_}++ } @formats;
  19275         52375  
377             }
378              
379             # Main entry point to RBNF
380             sub _algorithmic_number_format {
381 8     8   22 my ($self, $number, $format_name, $type) = @_;
382            
383 8         23 my $format_data = $self->_get_algorithmic_number_format_data_by_name($format_name, $type);
384            
385 8 50       19 return $number unless $format_data;
386            
387 8         47 return $self->_process_algorithmic_number_data($number, $format_data);
388             }
389              
390             sub _get_algorithmic_number_format_data_by_name {
391 8     8   15 my ($self, $format_name, $type) = @_;
392            
393             # Some of these algorithmic formats are in locale/type/name format
394 8 50       34 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     30 $type //= 'public';
403            
404 8         16 my %data = ();
405            
406 8         31 my @data_bundles = $self->_find_bundle('algorithmic_number_format_data');
407 8         22 foreach my $data_bundle (@data_bundles) {
408 10         418 my $data = $data_bundle->algorithmic_number_format_data();
409 10 100       39 next unless $data->{$format_name};
410 8 50       28 next unless $data->{$format_name}{$type};
411            
412 8         10 foreach my $rule (keys %{$data->{$format_name}{$type}}) {
  8         61  
413 186         400 $data{$rule} = $data->{$format_name}{$type}{$rule};
414             }
415            
416 8         25 last;
417             }
418            
419 8 50       35 return keys %data ? \%data : undef;
420             }
421              
422             sub _get_plural_form {
423 1     1   4 my ($self, $plural, $from) = @_;
424            
425 1         26 my ($result) = $from =~ /$plural\{(.+?)\}/;
426 1 50       5 ($result) = $from =~ /other\{(.+?)\}/ unless defined $result;
427            
428 1         5 return $result;
429             }
430              
431             sub _process_algorithmic_number_data {
432 14     14   27 my ($self, $number, $format_data, $plural, $in_fraction_rule_set) = @_;
433            
434 14   100     54 $in_fraction_rule_set //= 0;
435            
436 14         36 my $format = $self->_get_algorithmic_number_format($number, $format_data);
437            
438 14         1185 my $format_rule = $format->{rule};
439 14 100 66     94 if (! $plural && $format_rule =~ /(cardinal|ordinal)/) {
440 3         9 my $type = $1;
441 3         22 $plural = $self->plural($number, $type);
442 3         9 $plural = [$type, $plural];
443             }
444            
445             # Sort out plural forms
446 14 100       32 if ($plural) {
447 3         88 $format_rule =~ s/\$\($plural->[0],(.+)\)\$/$self->_get_plural_form($plural->[1],$1)/eg;
  1         6  
448             }
449            
450 14         27 my $divisor = $format->{divisor};
451 14   100     44 my $base_value = $format->{base_value} // '';
452            
453             # Negative numbers
454 14 100       50 if ($number =~ /^-/) {
    100          
455 2         4 my $positive_number = $number;
456 2         7 $positive_number =~ s/^-//;
457            
458 2 100       21 if ($format_rule =~ /→→/) {
    50          
    50          
    50          
    0          
459 1         5 $format_rule =~ s/→→/$self->_process_algorithmic_number_data($positive_number, $format_data, $plural)/e;
  1         5  
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 1         6 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
  1         4  
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 1         3 my $in_fraction_rule_set = 1;
489 1         7 my ($integer, $fraction) = $number =~ /^([^.]*)\.(.*)$/;
490            
491 1 50 33     10 if ($number >= 0 && $number < 1) {
492 1         6 $format_rule =~ s/\[.*\]//;
493             }
494             else {
495 0         0 $format_rule =~ s/[\[\]]//g;
496             }
497            
498 1 50       5 if ($format_rule =~ /→→/) {
    0          
499 1         5 $format_rule =~ s/→→/$self->_process_algorithmic_number_data_fractions($fraction, $format_data, $plural)/e;
  1         5  
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 1 50       7 if ($format_rule =~ /←←/) {
    0          
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 1 50       8 if($format_rule =~ /=.*=/) {
533 0 0       0 if($format_rule =~ /=%%.*=/) {
    0          
534 0         0 $format_rule =~ s/=%%(.*?)=/$self->_algorithmic_number_format($number, $1, 'private')/eg;
  0         0  
535             }
536             elsif($format_rule =~ /=%.*=/) {
537 0         0 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
  0         0  
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       37 if ($format_rule =~ /\[.*\]/) {
549 3 50 33     28 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         17 $format_rule =~ s/[\[\]]//g;
558             }
559             }
560            
561 11 100       23 if ($in_fraction_rule_set) {
562 2 50       15 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       47 if (my ($rule_name) = $format_rule =~ /→(.*)→/) {
586 3 50       10 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         11 $format_rule =~ s/→→/$self->_process_algorithmic_number_data($number % $divisor, $format_data, $plural)/e;
  3         14  
601             }
602             }
603            
604 9 50       30 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       39 if($format_rule =~ /=.*=/) {
624 4 50       22 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         12 $format_rule =~ s/=%(.*?)=/$self->_algorithmic_number_format($number, $1, 'public')/eg;
  2         12  
629             }
630             else {
631 2         11 $format_rule =~ s/=(.*?)=/$self->_format_number($number, $1)/eg;
  2         11  
632             }
633             }
634             }
635             }
636            
637 14         133 return $format_rule;
638             }
639              
640             sub _process_algorithmic_number_data_fractions {
641 1     1   3 my ($self, $fraction, $format_data, $plural) = @_;
642            
643 1         3 my $result = '';
644 1         3 foreach my $digit (split //, $fraction) {
645 1         17 $result .= $self->_process_algorithmic_number_data($digit, $format_data, $plural, 1);
646             }
647            
648 1         5 return $result;
649             }
650              
651             sub _get_algorithmic_number_format {
652 14     14   24 my ($self, $number, $format_data) = @_;
653            
654 20     20   155815 use bignum;
  20         129000  
  20         233  
655 14 100 66     49 return $format_data->{'-x'} if $number =~ /^-/ && exists $format_data->{'-x'};
656 13 100 66     51 return $format_data->{'x.x'} if $number =~ /\./ && exists $format_data->{'x.x'};
657 12 100 66     119 return $format_data->{0} if $number == 0 || $number =~ /^-/;
658 6 100       600 return $format_data->{max} if $number >= $format_data->{max}{base_value};
659            
660 4         7 my $previous = 0;
661 4         105 foreach my $key (sort { $a <=> $b } grep /^[0-9]+$/, keys %$format_data) {
  488         553  
662 71 100       693 next if $key == 0;
663 67 100       5364 return $format_data->{$key} if $number == $key;
664 66 100       139 return $format_data->{$previous} if $number < $key;
665 63         111 $previous = $key;
666             }
667             }
668              
669 20     20   1362514 no Moose::Role;
  20         56  
  20         263  
670              
671             1;
672              
673             # vim: tabstop=4