our
$VERSION
= version->declare(
'v0.34.4'
);
use
if
$^V ge v5.12.0,
feature
=>
'unicode_strings'
;
sub
format_number {
my
(
$self
,
$number
,
$format
,
$currency
,
$for_cash
) =
@_
;
my
$numbering_system
=
$self
->default_numbering_system();
if
(
$self
->numbering_system->{
$numbering_system
}{type} eq
'algorithmic'
) {
$format
=
$self
->numbering_system->{
$numbering_system
}{data};
return
$self
->_algorithmic_number_format(
$number
,
$format
);
}
$format
//=
'0'
;
return
$self
->_format_number(
$number
,
$format
,
$currency
,
$for_cash
);
}
sub
format_currency {
my
(
$self
,
$number
,
$for_cash
) =
@_
;
my
$format
=
$self
->currency_format;
return
$self
->format_number(
$number
,
$format
,
undef
(),
$for_cash
);
}
sub
_format_number {
my
(
$self
,
$number
,
$format
,
$currency
,
$for_cash
) =
@_
;
my
@valid_formats
=
$self
->_get_valid_algorithmic_formats();
if
(
grep
{
$_
eq
$format
}
@valid_formats
) {
return
$self
->_algorithmic_number_format(
$number
,
$format
);
}
if
(
my
(
$locale_id
,
$type
,
$format
) =
$format
=~ m(^(.*?)/(.*?)/(.*?)$)) {
my
$locale
= Locale::CLDR->new(
$locale_id
);
return
$locale
->format_number(
$number
,
$format
);
}
my
$currency_data
;
if
(
$format
=~
tr
/¤/¤/) {
$for_cash
//=0;
$currency
=
$self
->default_currency()
if
!
defined
$currency
;
$currency_data
=
$self
->_get_currency_data(
$currency
);
$currency
=
$self
->currency_symbol(
$currency
);
}
$format
=
$self
->parse_number_format(
$format
,
$currency
,
$currency_data
,
$for_cash
);
$number
=
$self
->get_formatted_number(
$number
,
$format
,
$currency_data
,
$for_cash
);
return
$number
;
}
sub
add_currency_symbol {
my
(
$self
,
$format
,
$symbol
) =
@_
;
$format
=~ s/¤/
'$symbol'
/g;
return
$format
;
}
sub
_get_currency_data {
my
(
$self
,
$currency
) =
@_
;
my
$currency_data
=
$self
->currency_fractions(
$currency
);
return
$currency_data
;
}
sub
_get_currency_rounding {
my
(
$self
,
$currency_data
,
$for_cash
) =
@_
;
my
$rounder
=
$for_cash
?
'cashrounding'
:
'rounding'
;
return
$currency_data
->{
$rounder
};
}
sub
_get_currency_digits {
my
(
$self
,
$currency_data
,
$for_cash
) =
@_
;
my
$digits
=
$for_cash
?
'cashdigits'
:
'digits'
;
return
$currency_data
->{
$digits
};
}
sub
parse_number_format {
my
(
$self
,
$format
,
$currency
,
$currency_data
,
$for_cash
) =
@_
;
state
%cache
;
return
$cache
{
$format
}
if
exists
$cache
{
$format
};
$format
=
$self
->add_currency_symbol(
$format
,
$currency
)
if
defined
$currency
;
my
(
$positive
,
$negative
) =
$format
=~ /^( (?: (?:
' [^'
]*
' )*+ | [^'
;]+ )+ ) (?: ; (.+) )? $/x;
$negative
//=
"-$positive"
;
my
$type
=
'positive'
;
foreach
my
$to_parse
(
$positive
,
$negative
) {
my
(
$prefix
,
$suffix
);
if
((
$prefix
) =
$to_parse
=~ /^ ( (?: [^0-9@
$to_parse
=~ s/^ ( (?: [^0-9@
}
if
( (
$suffix
) =
$to_parse
=~ / ( (?: [^0-9@
$to_parse
=~ s/( (?:[^0-9@
}
foreach
my
$str
(
$prefix
,
$suffix
) {
$str
//=
''
;
$str
=~ s/(?:
' (?: (?: '
' )++ | [^'
]+ ) ' )*? \K ( [-+\\] ) /\\$1/gx;
$str
=~ s/
' ( (?: '
' )++ | [^'
]++ ) ' /$1/gx;
$str
=~ s/
''
/'/g;
}
my
(
$pad_character
,
$pad_location
);
if
((
$pad_character
) =
$prefix
=~ /^\*(\p{Any})/ ) {
$prefix
=~ s/^\*(\p{Any})//;
$pad_location
=
'before prefix'
;
}
elsif
( (
$pad_character
) =
$prefix
=~ /\*(\p{Any})$/ ) {
$prefix
=~ s/\*(\p{Any})$//;
$pad_location
=
'after prefix'
;
}
elsif
((
$pad_character
) =
$suffix
=~ /^\*(\p{Any})/ ) {
$suffix
=~ s/^\*(\p{Any})//;
$pad_location
=
'before suffix'
;
}
elsif
((
$pad_character
) =
$suffix
=~ /\*(\p{Any})$/ ) {
$suffix
=~ s/\*(\p{Any})$//;
$pad_location
=
'after suffix'
;
}
my
$pad_length
=
defined
$pad_character
?
length
(
$prefix
) +
length
(
$to_parse
) +
length
(
$suffix
) + 2
: 0;
my
$multiplier
= 1;
$multiplier
= 100
if
$prefix
=~
tr
/%/%/ ||
$suffix
=~
tr
/%/%/;
$multiplier
= 1000
if
$prefix
=~
tr
/‰/‰/ ||
$suffix
=~
tr
/‰/‰/;
my
$rounding
=
$to_parse
=~ / ( [1-9] [0-9]* (?: \. [0-9]+ )? ) /x;
$rounding
||= 0;
$rounding
=
$self
->_get_currency_rounding(
$currency_data
,
$for_cash
)
if
defined
$currency
;
my
(
$integer
,
$decimal
) =
split
/\./,
$to_parse
;
my
(
$minimum_significant_digits
,
$maximum_significant_digits
,
$minimum_digits
);
if
(
my
(
$digits
) =
$to_parse
=~ /(\@+)/) {
$minimum_significant_digits
=
length
$digits
;
(
$digits
) =
$to_parse
=~ /\@(
$maximum_significant_digits
=
$minimum_significant_digits
+
length
(
$digits
//
''
);
}
else
{
$minimum_digits
=
$integer
=~
tr
/0-9/0-9/;
}
my
$exponent_digits
= 0;
my
$need_plus
= 0;
my
$exponent
;
my
$major_group
;
my
$minor_group
;
if
(
$to_parse
=~
tr
/E/E/) {
(
$need_plus
,
$exponent
) =
$to_parse
=~ m/ E ( \+? ) ( [0-9]+ ) /x;
$exponent_digits
=
length
$exponent
;
}
else
{
my
(
$grouping
) =
split
/\./,
$to_parse
;
my
@groups
=
split
/,/,
$grouping
;
shift
@groups
;
(
$major_group
,
$minor_group
) =
map
{
length
}
@groups
;
$minor_group
//=
$major_group
;
}
$cache
{
$format
}{
$type
} = {
prefix
=>
$prefix
//
''
,
suffix
=>
$suffix
//
''
,
pad_character
=>
$pad_character
,
pad_location
=>
$pad_location
//
'none'
,
pad_length
=>
$pad_length
,
multiplier
=>
$multiplier
,
rounding
=>
$rounding
,
minimum_significant_digits
=>
$minimum_significant_digits
,
maximum_significant_digits
=>
$maximum_significant_digits
,
minimum_digits
=>
$minimum_digits
// 0,
exponent_digits
=>
$exponent_digits
,
exponent_needs_plus
=>
$need_plus
,
major_group
=>
$major_group
,
minor_group
=>
$minor_group
,
};
$type
=
'negative'
;
}
return
$cache
{
$format
};
}
sub
round {
my
(
$self
,
$number
,
$increment
,
$decimal_digits
) =
@_
;
if
(
$increment
) {
$increment
/= 10 **
$decimal_digits
;
$number
/=
$increment
;
$number
=
int
(
$number
+ .5 );
$number
*=
$increment
;
}
if
(
$decimal_digits
) {
$number
*= 10 **
$decimal_digits
;
$number
=
int
$number
;
$number
/= 10 **
$decimal_digits
;
my
(
$decimal
) =
$number
=~ /(\..*)/;
$decimal
//=
'.'
;
$number
=
int
(
$number
) .
$decimal
. (
'0'
x (
$decimal_digits
-
length
(
$decimal
) +1 ));
}
else
{
$number
=
int
$number
;
}
return
$number
;
}
sub
get_formatted_number {
my
(
$self
,
$number
,
$format
,
$currency_data
,
$for_cash
) =
@_
;
my
@digits
=
$self
->get_digits;
my
@number_symbols_bundles
=
reverse
$self
->_find_bundle(
'number_symbols'
);
my
%symbols
;
foreach
my
$bundle
(
@number_symbols_bundles
) {
my
$current_symbols
=
$bundle
->number_symbols;
foreach
my
$type
(
keys
%$current_symbols
) {
foreach
my
$symbol
(
keys
%{
$current_symbols
->{
$type
}}) {
$symbols
{
$type
}{
$symbol
} =
$current_symbols
->{
$type
}{
$symbol
};
}
}
}
my
$symbols_type
=
$self
->default_numbering_system;
$symbols_type
=
$symbols
{
$symbols_type
}{alias}
if
exists
$symbols
{
$symbols_type
}{alias};
my
$type
=
$number
=~ s/^-// ?
'negative'
:
'positive'
;
$number
*=
$format
->{
$type
}{multiplier};
if
(
$format
->{rounding} ||
defined
$for_cash
) {
my
$decimal_digits
= 0;
if
(
defined
$for_cash
) {
$decimal_digits
=
$self
->_get_currency_digits(
$currency_data
,
$for_cash
)
}
$number
=
$self
->round(
$number
,
$format
->{
$type
}{rounding},
$decimal_digits
);
}
my
$pad_zero
=
$format
->{
$type
}{minimum_digits} -
length
"$number"
;
if
(
$pad_zero
> 0) {
$number
= (
'0'
x
$pad_zero
) .
$number
;
}
my
(
$integer
,
$decimal
) =
split
/\./,
$number
;
my
$minimum_grouping_digits
=
$self
->_find_bundle(
'minimum_grouping_digits'
);
$minimum_grouping_digits
=
$minimum_grouping_digits
?
$minimum_grouping_digits
->minimum_grouping_digits()
: 0;
my
(
$separator
,
$decimal_point
) = (
$symbols
{
$symbols_type
}{group},
$symbols
{
$symbols_type
}{decimal});
if
((
$minimum_grouping_digits
&&
length
$integer
>=
$minimum_grouping_digits
) || !
$minimum_grouping_digits
) {
my
(
$minor_group
,
$major_group
) = (
$format
->{
$type
}{minor_group},
$format
->{
$type
}{major_group});
if
(
defined
$minor_group
&&
$separator
) {
my
$pattern
=
"(A$minor_group)(A$major_group)*"
;
$number
=
reverse
join
$separator
,
grep
{
length
}
unpack
$pattern
,
reverse
$integer
;
}
else
{
$number
=
$integer
;
}
}
else
{
$number
=
$integer
;
}
$number
.=
"$decimal_point$decimal"
if
defined
$decimal
;
$number
=~ s/([0-9])/
$digits
[$1]/eg;
my
(
$prefix
,
$suffix
) = (
$format
->{
$type
}{prefix},
$format
->{
$type
}{suffix});
foreach
my
$string
(
$prefix
,
$suffix
) {
$string
=~ s/%/
$symbols
{
$symbols_type
}{percentSign}/;
$string
=~ s/‰/
$symbols
{
$symbols_type
}{perMille}/;
if
(
$type
eq
'negative'
) {
$string
=~ s/(?: \\ \\ )*+ \K \\ - /
$symbols
{
$symbols_type
}{minusSign}/x;
$string
=~ s/(?: \\ \\)*+ \K \\ + /
$symbols
{
$symbols_type
}{minusSign}/x;
}
else
{
$string
=~ s/(?: \\ \\ )*+ \K \\ - //x;
$string
=~ s/(?: \\ \\ )*+ \K \\ + /
$symbols
{
$symbols_type
}{plusSign}/x;
}
$string
=~ s/ \\ \\ /\\/gx;
}
$number
=
$prefix
.
$number
.
$suffix
;
return
$number
;
}
sub
get_digits {
my
$self
=
shift
;
my
$numbering_system
=
$self
->default_numbering_system();
$numbering_system
=
'latn'
unless
$self
->numbering_system->{
$numbering_system
}{type} eq
'numeric'
;
my
$digits
=
$self
->numbering_system->{
$numbering_system
}{data};
return
@$digits
;
}
sub
_get_valid_algorithmic_formats {
my
$self
=
shift
;
my
@formats
=
map
{ @{
$_
->valid_algorithmic_formats()} }
$self
->_find_bundle(
'valid_algorithmic_formats'
);
my
%seen
;
return
sort
grep
{ !
$seen
{
$_
}++ }
@formats
;
}
sub
_algorithmic_number_format {
my
(
$self
,
$number
,
$format_name
,
$type
) =
@_
;
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$format_name
,
$type
);
return
$number
unless
$format_data
;
return
$self
->_process_algorithmic_number_data(
$number
,
$format_data
);
}
sub
_get_algorithmic_number_format_data_by_name {
my
(
$self
,
$format_name
,
$type
) =
@_
;
if
(
my
(
$locale_id
,
undef
,
$format
) =
$format_name
=~ m(^(.*?)/(.*?)/(.*?)$)) {
my
$locale
= Locale::CLDR->new(
$locale_id
);
return
$locale
->_get_algorithmic_number_format_data_by_name(
$format
,
$type
)
if
$locale
;
return
undef
;
}
$type
//=
'public'
;
my
%data
= ();
my
@data_bundles
=
$self
->_find_bundle(
'algorithmic_number_format_data'
);
foreach
my
$data_bundle
(
@data_bundles
) {
my
$data
=
$data_bundle
->algorithmic_number_format_data();
next
unless
$data
->{
$format_name
};
next
unless
$data
->{
$format_name
}{
$type
};
foreach
my
$rule
(
keys
%{
$data
->{
$format_name
}{
$type
}}) {
$data
{
$rule
} =
$data
->{
$format_name
}{
$type
}{
$rule
};
}
last
;
}
return
keys
%data
? \
%data
:
undef
;
}
sub
_get_plural_form {
my
(
$self
,
$plural
,
$from
) =
@_
;
my
(
$result
) =
$from
=~ /
$plural
\{(.+?)\}/;
(
$result
) =
$from
=~ /other\{(.+?)\}/
unless
defined
$result
;
return
$result
;
}
sub
_process_algorithmic_number_data {
my
(
$self
,
$number
,
$format_data
,
$plural
,
$in_fraction_rule_set
) =
@_
;
$in_fraction_rule_set
//= 0;
my
$format
=
$self
->_get_algorithmic_number_format(
$number
,
$format_data
);
my
$format_rule
=
$format
->{rule};
if
(!
$plural
&&
$format_rule
=~ /(cardinal|ordinal)/) {
my
$type
= $1;
$plural
=
$self
->plural(
$number
,
$type
);
$plural
= [
$type
,
$plural
];
}
if
(
$plural
) {
$format_rule
=~ s/\$\(
$plural
->[0],(.+)\)\$/
$self
->_get_plural_form(
$plural
->[1],$1)/eg;
}
my
$divisor
=
$format
->{divisor};
my
$base_value
=
$format
->{base_value} //
''
;
if
(
$number
=~ /^-/) {
my
$positive_number
=
$number
;
$positive_number
=~ s/^-//;
if
(
$format_rule
=~ /→→/) {
$format_rule
=~ s/→→/
$self
->_process_algorithmic_number_data(
$positive_number
,
$format_data
,
$plural
)/e;
}
elsif
((
my
$rule_name
) =
$format_rule
=~ /→(.+)→/) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s/→(.+)→/
$self
->_process_algorithmic_number_data(
$positive_number
,
$format_data
,
$plural
)/e;
}
else
{
$format_rule
=~ s/→(.+)→/
$self
->_format_number(
$positive_number
, $1)/e;
}
}
elsif
(
$format_rule
=~ /=%%.*=/) {
$format_rule
=~ s/=%%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'private'
)/eg;
}
elsif
(
$format_rule
=~ /=%.*=/) {
$format_rule
=~ s/=%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'public'
)/eg;
}
elsif
(
$format_rule
=~ /=.*=/) {
$format_rule
=~ s/=(.*?)=/
$self
->_format_number(
$number
, $1)/eg;
}
}
elsif
(
$number
=~ /\./ ) {
my
$in_fraction_rule_set
= 1;
my
(
$integer
,
$fraction
) =
$number
=~ /^([^.]*)\.(.*)$/;
if
(
$number
>= 0 &&
$number
< 1) {
$format_rule
=~ s/\[.*\]//;
}
else
{
$format_rule
=~ s/[\[\]]//g;
}
if
(
$format_rule
=~ /→→/) {
$format_rule
=~ s/→→/
$self
->_process_algorithmic_number_data_fractions(
$fraction
,
$format_data
,
$plural
)/e;
}
elsif
((
my
$rule_name
) =
$format_rule
=~ /→(.*)→/) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s/→(.*)→/
$self
->_process_algorithmic_number_data_fractions(
$fraction
,
$format_data
,
$plural
)/e;
}
else
{
$format_rule
=~ s/→(.*)→/
$self
->_format_number(
$fraction
, $1)/e;
}
}
if
(
$format_rule
=~ /←←/) {
$format_rule
=~ s/←←/
$self
->_process_algorithmic_number_data(
$integer
,
$format_data
,
$plural
,
$in_fraction_rule_set
)/e;
}
elsif
((
my
$rule_name
) =
$format_rule
=~ /←(.+)←/) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s/←(.*)←/
$self
->_process_algorithmic_number_data(
$integer
,
$format_data
,
$plural
,
$in_fraction_rule_set
)/e;
}
else
{
$format_rule
=~ s/←(.*)←/
$self
->_format_number(
$integer
, $1)/e;
}
}
if
(
$format_rule
=~ /=.*=/) {
if
(
$format_rule
=~ /=%%.*=/) {
$format_rule
=~ s/=%%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'private'
)/eg;
}
elsif
(
$format_rule
=~ /=%.*=/) {
$format_rule
=~ s/=%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'public'
)/eg;
}
else
{
$format_rule
=~ s/=(.*?)=/
$self
->_format_number(
$integer
, $1)/eg;
}
}
}
else
{
if
(
$format_rule
=~ /\[.*\]/) {
if
(
$in_fraction_rule_set
&&
$number
*
$base_value
== 1) {
$format_rule
=~ s/\[.*\]//;
}
elsif
(!
$in_fraction_rule_set
&& ! (
$number
%
$divisor
) ) {
$format_rule
=~ s/\[.*\]//;
}
else
{
$format_rule
=~ s/[\[\]]//g;
}
}
if
(
$in_fraction_rule_set
) {
if
(
my
(
$rule_name
) =
$format_rule
=~ /←(.*)←/) {
if
(
length
$rule_name
) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s/←(.*)←/
$self
->_process_algorithmic_number_data(
$number
*
$base_value
,
$format_data
,
$plural
,
$in_fraction_rule_set
)/e;
}
else
{
$format_rule
=~ s/←(.*)←/
$self
->_format_number(
$number
*
$base_value
, $1)/e;
}
}
else
{
$format_rule
=~ s/←←/
$self
->_process_algorithmic_number_data(
$number
*
$base_value
,
$format_data
,
$plural
,
$in_fraction_rule_set
)/e;
}
}
elsif
(
$format_rule
=~ /=.*=/) {
$format_rule
=~ s/=(.*?)=/
$self
->_format_number(
$number
, $1)/eg;
}
}
else
{
if
(
my
(
$rule_name
) =
$format_rule
=~ /→(.*)→/) {
if
(
length
$rule_name
) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s/→(.+)→/
$self
->_process_algorithmic_number_data(
$number
%
$divisor
,
$format_data
,
$plural
)/e;
}
else
{
$format_rule
=~ s/→(.*)→/
$self
->_format_number(
$number
%
$divisor
, $1)/e;
}
}
else
{
$format_rule
=~ s/→→/
$self
->_process_algorithmic_number_data(
$number
%
$divisor
,
$format_data
,
$plural
)/e;
}
}
if
(
my
(
$rule_name
) =
$format_rule
=~ /←(.*)←/) {
if
(
length
$rule_name
) {
my
$type
=
'public'
;
if
(
$rule_name
=~ s/^%%/%/) {
$type
=
'private'
;
}
my
$format_data
=
$self
->_get_algorithmic_number_format_data_by_name(
$rule_name
,
$type
);
if
(
$format_data
) {
$format_rule
=~ s|←(.*)←|
$self
->_process_algorithmic_number_data(
int
(
$number
/
$divisor
),
$format_data
,
$plural
)|e;
}
else
{
$format_rule
=~ s|←(.*)←|
$self
->_format_number(
int
(
$number
/
$divisor
), $1)|e;
}
}
else
{
$format_rule
=~ s|←←|
$self
->_process_algorithmic_number_data(
int
(
$number
/
$divisor
),
$format_data
,
$plural
)|e;
}
}
if
(
$format_rule
=~ /=.*=/) {
if
(
$format_rule
=~ /=%%.*=/) {
$format_rule
=~ s/=%%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'private'
)/eg;
}
elsif
(
$format_rule
=~ /=%.*=/) {
$format_rule
=~ s/=%(.*?)=/
$self
->_algorithmic_number_format(
$number
, $1,
'public'
)/eg;
}
else
{
$format_rule
=~ s/=(.*?)=/
$self
->_format_number(
$number
, $1)/eg;
}
}
}
}
return
$format_rule
;
}
sub
_process_algorithmic_number_data_fractions {
my
(
$self
,
$fraction
,
$format_data
,
$plural
) =
@_
;
my
$result
=
''
;
foreach
my
$digit
(
split
//,
$fraction
) {
$result
.=
$self
->_process_algorithmic_number_data(
$digit
,
$format_data
,
$plural
, 1);
}
return
$result
;
}
sub
_get_algorithmic_number_format {
my
(
$self
,
$number
,
$format_data
) =
@_
;
return
$format_data
->{
'-x'
}
if
$number
=~ /^-/ &&
exists
$format_data
->{
'-x'
};
return
$format_data
->{
'x.x'
}
if
$number
=~ /\./ &&
exists
$format_data
->{
'x.x'
};
return
$format_data
->{0}
if
$number
== 0 ||
$number
=~ /^-/;
return
$format_data
->{max}
if
$number
>=
$format_data
->{max}{base_value};
my
$previous
= 0;
foreach
my
$key
(
sort
{
$a
<=>
$b
}
grep
/^[0-9]+$/,
keys
%$format_data
) {
next
if
$key
== 0;
return
$format_data
->{
$key
}
if
$number
==
$key
;
return
$format_data
->{
$previous
}
if
$number
<
$key
;
$previous
=
$key
;
}
}
no
Moo::Role;
1;