File Coverage

blib/lib/Lingua/RU/Num2Word.pm
Criterion Covered Total %
statement 61 64 95.3
branch 20 26 76.9
condition 19 26 73.0
subroutine 7 7 100.0
pod 1 1 100.0
total 108 124 87.1


line stmt bran cond sub pod time code
1             package Lingua::RU::Num2Word;
2              
3 1     1   891 use strict;
  1         2  
  1         46  
4 1     1   4 use warnings;
  1         1  
  1         26  
5 1     1   3 use utf8;
  1         2  
  1         8  
6 1     1   499 use POSIX qw/floor/;
  1         6195  
  1         9  
7              
8             # ABSTRACT: Numbers to words in russian (without currency, but with specified gender)
9             our $VERSION = '0.02'; # VERSION
10             # AUTHORITY
11              
12 1     1   1456 use Exporter qw/import/;
  1         2  
  1         1396  
13             our @EXPORT_OK = qw(&num2rus_cardinal);
14              
15             my %diw = (
16             0 => {
17             0 => { 0 => "ноль", 1 => 1 },
18             1 => { 0 => "", 1 => 2 },
19             2 => { 0 => "", 1 => 3 },
20             3 => { 0 => "три", 1 => 0 },
21             4 => { 0 => "четыре", 1 => 0 },
22             5 => { 0 => "пять", 1 => 1 },
23             6 => { 0 => "шесть", 1 => 1 },
24             7 => { 0 => "семь", 1 => 1 },
25             8 => { 0 => "восемь", 1 => 1 },
26             9 => { 0 => "девять", 1 => 1 },
27             10 => { 0 => "десять", 1 => 1 },
28             11 => { 0 => "одинадцать", 1 => 1 },
29             12 => { 0 => "двенадцать", 1 => 1 },
30             13 => { 0 => "тринадцать", 1 => 1 },
31             14 => { 0 => "четырнадцать", 1 => 1 },
32             15 => { 0 => "пятнадцать", 1 => 1 },
33             16 => { 0 => "шестнадцать", 1 => 1 },
34             17 => { 0 => "семнадцать", 1 => 1 },
35             18 => { 0 => "восемнадцать", 1 => 1 },
36             19 => { 0 => "девятнадцать", 1 => 1 },
37             },
38              
39             1 => {
40             2 => { 0 => "двадцать", 1 => 1 },
41             3 => { 0 => "тридцать", 1 => 1 },
42             4 => { 0 => "сорок", 1 => 1 },
43             5 => { 0 => "пятьдесят", 1 => 1 },
44             6 => { 0 => "шестьдесят", 1 => 1 },
45             7 => { 0 => "семьдесят", 1 => 1 },
46             8 => { 0 => "восемьдесят", 1 => 1 },
47             9 => { 0 => "девяносто", 1 => 1 },
48             },
49             2 => {
50             1 => { 0 => "сто", 1 => 1 },
51             2 => { 0 => "двести", 1 => 1 },
52             3 => { 0 => "триста", 1 => 1 },
53             4 => { 0 => "четыреста", 1 => 1 },
54             5 => { 0 => "пятьсот", 1 => 1 },
55             6 => { 0 => "шестьсот", 1 => 1 },
56             7 => { 0 => "семьсот", 1 => 1 },
57             8 => { 0 => "восемьсот", 1 => 1 },
58             9 => { 0 => "девятьсот", 1 => 1 }
59             }
60              
61             );
62              
63             my %nom = (
64             0 => { 0 => "", 1 => "", 2 => "одна", 3 => "две" },
65             1 => { 0 => "", 1 => "", 2 => "один", 3 => "два" },
66             2 => { 0 => "тысячи", 1 => "тысяч", 2 => "одна тысяча", 3 => "две тысячи" },
67             3 => {
68             0 => "миллиона",
69             1 => "миллионов",
70             2 => "один миллион",
71             3 => "два миллиона"
72             },
73             4 => {
74             0 => "миллиарда",
75             1 => "миллиардов",
76             2 => "один миллиард",
77             3 => "два миллиарда"
78             },
79             5 => {
80             0 => "триллиона",
81             1 => "триллионов",
82             2 => "один триллион",
83             3 => "два триллиона"
84             }
85             );
86              
87             my %genders = (
88             'FEMININE' => { 0 => "", 1 => "", 2 => "одна", 3 => "две" },
89             'MASCULINE' => { 0 => "", 1 => "", 2 => "один", 3 => "два" },
90             'NEUTER' => { 0 => "", 1 => "", 2 => "одно", 3 => "два" },
91             );
92              
93             # Stolen from Lingua::RU::Number
94              
95              
96             sub num2rus_cardinal {
97 9     9 1 548 my ( $number, $gender ) = @_;
98              
99 9   100     30 $gender ||= 'MASCULINE'; # masculine by default
100 9         14 $gender = uc $gender;
101              
102 9 100       18 return _get_string( 0, 0, 0 ) unless $number; # no extra calculations for zero
103              
104 8         7 my ( $result, $negative );
105              
106             # Negative number, just add another word
107 8 50       19 if ( $number < 0 ) {
108 0         0 $number = abs( $number );
109 0         0 $negative = 1;
110             }
111              
112 8         8 $result = "";
113 8         36 my $int_number = floor( $number ); # no doubles
114              
115 8   100     35 for ( my $i = 1 ; $i < 6 && $int_number >= 1 ; $i++ ) {
116 15         15 my $tmp_number = $int_number / 1000;
117 15         130 my $number_part = sprintf( "%0.3f", $tmp_number - sprintf( "%d", $tmp_number ) ) * 1000;
118              
119 15         20 $int_number = floor $tmp_number; # no doubles again
120 15         20 $result = _get_string( $number_part, $i, $gender ) . " " . $result;
121             }
122              
123             # Clean the result
124 8         39 $result =~ s/\s+/ /g;
125 8         31 $result =~ s/\s+$//;
126              
127 8 50       15 if ( $negative ) {
128 0         0 $result = "минус $result";
129             }
130              
131 8         41 return $result;
132             }
133              
134             sub _get_string {
135 16     16   15 my $sum = shift;
136 16 50       26 return unless defined $sum;
137              
138 16         16 my $nominal = shift;
139 16         13 my $gender = shift;
140 16         17 my ( $result, $nom ) = ( '', -1 );
141              
142 16 50 66     106 if ( ( !$nominal && $sum < 100 ) || ( $nominal > 0 && $nominal < 6 && $sum < 1000 ) ) {
      33        
      33        
      66        
143 16         33 my $s2 = sprintf( "%d", $sum / 100 );
144              
145 16 100       25 if ( $s2 > 0 ) { # hundreds
146 6         17 $result .= ' ' . $diw{2}{$s2}{0};
147 6         9 $nom = $diw{2}{$s2}{1};
148             }
149              
150 16         35 my $sx = floor $sum - $s2 * 100;
151              
152 16 100 100     82 if ( ( $sx < 20 && $sx > 0 ) || ( $sx == 0 && !$nominal ) ) {
      100        
      66        
153 4         9 $result .= " " . $diw{0}{$sx}{0};
154 4         8 $nom = $diw{0}{$sx}{1};
155             }
156             else {
157 12         18 my $s1 = floor $sx / 10; # tens
158              
159 12         26 my $s0 = sprintf( "%d", $sum - $s2 * 100 - $s1 * 10 + 0.5 );
160              
161 12 100       21 if ( $s1 > 0 ) {
162 7         16 $result .= ' ' . $diw{1}{$s1}{0};
163 7         10 $nom = $diw{1}{$s1}{1};
164             }
165 12 100       20 if ( $s0 > 0 ) {
166 7         13 $result .= ' ' . $diw{0}{$s0}{0};
167 7         12 $nom = $diw{0}{$s0}{1};
168             }
169             }
170             }
171 16 100       28 if ( $nom >= 0 ) {
172              
173 11 100       17 if ( $nominal == 1 ) {
174 7 50       16 $result .= defined $nominal ? ' ' . $genders{$gender}{$nom} : '';
175             }
176             else {
177 4 50       13 $result .= defined $nominal ? ' ' . $nom{$nominal}{$nom} : '';
178             }
179             }
180 16         85 $result =~ s/^\s*//g;
181 16         99 $result =~ s/\s*$//g;
182              
183 16         97 return $result;
184             }
185              
186             1;
187              
188             __END__