File Coverage

blib/lib/Lingua/RU/Number.pm
Criterion Covered Total %
statement 101 106 95.2
branch 36 54 66.6
condition 24 48 50.0
subroutine 9 9 100.0
pod 0 3 0.0
total 170 220 77.2


line stmt bran cond sub pod time code
1             package Lingua::RU::Number;
2              
3 1     1   1001 use utf8;
  1         3  
  1         4  
4 1     1   25 use strict;
  1         1  
  1         30  
5 1     1   523 use POSIX qw/floor/;
  1         4841  
  1         5  
6 1     1   914 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         2  
  1         121  
7              
8             require Exporter;
9             require AutoLoader;
10              
11             @ISA = qw(Exporter AutoLoader);
12             # Items to export into callers namespace by default. Note: do not export
13             # names by default without a very good reason. Use EXPORT_OK instead.
14             # Do not simply export all your public functions/methods/constants.
15             @EXPORT = qw();
16             @EXPORT_OK = qw(rur_in_words num2words);
17              
18             $VERSION = '0.61';
19              
20             # Preloaded methods go here.
21 1     1   4 use vars qw(%diw %nom %genders);
  1         1  
  1         1597  
22              
23             %diw = (
24             0 => {
25             0 => { 0 => "ноль", 1 => 1},
26             1 => { 0 => "", 1 => 2},
27             2 => { 0 => "", 1 => 3},
28             3 => { 0 => "три", 1 => 0},
29             4 => { 0 => "четыре", 1 => 0},
30             5 => { 0 => "пять", 1 => 1},
31             6 => { 0 => "шесть", 1 => 1},
32             7 => { 0 => "семь", 1 => 1},
33             8 => { 0 => "восемь", 1 => 1},
34             9 => { 0 => "девять", 1 => 1},
35             10 => { 0 => "десять", 1 => 1},
36             11 => { 0 => "одиннадцать", 1 => 1},
37             12 => { 0 => "двенадцать", 1 => 1},
38             13 => { 0 => "тринадцать", 1 => 1},
39             14 => { 0 => "четырнадцать", 1 => 1},
40             15 => { 0 => "пятнадцать", 1 => 1},
41             16 => { 0 => "шестнадцать", 1 => 1},
42             17 => { 0 => "семнадцать", 1 => 1},
43             18 => { 0 => "восемнадцать", 1 => 1},
44             19 => { 0 => "девятнадцать", 1 => 1},
45             },
46             1 => {
47             2 => { 0 => "двадцать", 1 => 1},
48             3 => { 0 => "тридцать", 1 => 1},
49             4 => { 0 => "сорок", 1 => 1},
50             5 => { 0 => "пятьдесят", 1 => 1},
51             6 => { 0 => "шестьдесят", 1 => 1},
52             7 => { 0 => "семьдесят", 1 => 1},
53             8 => { 0 => "восемьдесят", 1 => 1},
54             9 => { 0 => "девяносто", 1 => 1},
55             },
56             2 => {
57             1 => { 0 => "сто", 1 => 1},
58             2 => { 0 => "двести", 1 => 1},
59             3 => { 0 => "триста", 1 => 1},
60             4 => { 0 => "четыреста", 1 => 1},
61             5 => { 0 => "пятьсот", 1 => 1},
62             6 => { 0 => "шестьсот", 1 => 1},
63             7 => { 0 => "семьсот", 1 => 1},
64             8 => { 0 => "восемьсот", 1 => 1},
65             9 => { 0 => "девятьсот", 1 => 1}
66             }
67             );
68              
69             %nom = (
70             0 => {0 => "копейки", 1 => "копеек", 2 => "одна копейка", 3 => "две копейки"},
71             1 => {0 => "рубля", 1 => "рублей", 2 => "один рубль", 3 => "два рубля"},
72             2 => {0 => "тысячи", 1 => "тысяч", 2 => "одна тысяча", 3 => "две тысячи"},
73             3 => {0 => "миллиона", 1 => "миллионов", 2 => "один миллион", 3 => "два миллиона"},
74             4 => {0 => "миллиарда",1 => "миллиардов",2 => "один миллиард",3 => "два миллиарда"},
75             5 => {0 => "триллиона",1 => "триллионов",2 => "один триллион",3 => "два триллиона"}
76             );
77              
78             %genders = (
79             0 => { 0 => "", 1 => "", 2 => "одна", 3 => "две" },
80             1 => { 0 => "", 1 => "", 2 => "один", 3 => "два" },
81             2 => { 0 => "", 1 => "", 2 => "одно", 3 => "два" },
82             );
83              
84             my $out_rub;
85              
86             sub rur_in_words
87             {
88 5     5 0 178 my ($sum) = shift;
89 5         6 my ($retval, $i, $sum_rub, $sum_kop);
90              
91 5         6 $retval = "";
92 5 50       13 $out_rub = ($sum >= 1) ? 0 : 1;
93 5         37 $sum_rub = sprintf("%0.0f", $sum);
94 5 100       17 $sum_rub-- if (($sum_rub - $sum) > 0);
95 5         20 $sum_kop = sprintf("%0.2f",($sum - $sum_rub))*100;
96              
97 5         11 my $kop = get_string($sum_kop, 0);
98              
99 5   66     24 for ($i=1; $i<6 && $sum_rub >= 1; $i++) {
100 6         8 my $sum_tmp = $sum_rub/1000;
101 6         33 my $sum_part = sprintf("%0.3f", $sum_tmp - int($sum_tmp))*1000;
102 6         15 $sum_rub = sprintf("%0.0f",$sum_tmp);
103              
104 6 100       49 $sum_rub-- if ($sum_rub - $sum_tmp > 0);
105 6         11 $retval = get_string($sum_part, $i)." ".$retval;
106             }
107 5 50       8 $retval .= " рублей" if ($out_rub == 0);
108 5         7 $retval .= " ".$kop;
109 5         27 $retval =~ s/\s+/ /g;
110 5         11 return $retval;
111             }
112              
113             sub get_string
114             {
115 11     11 0 11 my ($sum, $nominal) = @_;
116 11         10 my ($retval, $nom) = ('', -1);
117              
118 11 50 66     69 if (($nominal == 0 && $sum < 100) || ($nominal > 0 && $nominal < 6 && $sum < 1000)) {
      33        
      33        
      66        
119 11         14 my $s2 = int($sum/100);
120 11 100       17 if ($s2 > 0) {
121 1         7 $retval .= " ".$diw{2}{$s2}{0};
122 1         3 $nom = $diw{2}{$s2}{1};
123             }
124 11         39 my $sx = sprintf("%0.0f", $sum - $s2*100);
125 11 100       23 $sx-- if ($sx - ($sum - $s2*100) > 0);
126              
127 11 100 66     47 if (($sx<20 && $sx>0) || ($sx == 0 && $nominal == 0)) {
      33        
      66        
128 5         13 $retval .= " ".$diw{0}{$sx}{0};
129 5         21 $nom = $diw{0}{$sx}{1};
130             } else {
131 6         19 my $s1 = sprintf("%0.0f",$sx/10);
132 6 100       13 $s1-- if (($s1 - $sx/10) > 0);
133 6         10 my $s0 = int($sum - $s2*100 - $s1*10 + 0.5);
134 6 50       12 if ($s1 > 0) {
135 6         20 $retval .= " ".$diw{1}{$s1}{0};
136 6         9 $nom = $diw{1}{$s1}{1};
137             }
138 6 100       10 if ($s0 > 0) {
139 5         13 $retval .= " ".$diw{0}{$s0}{0};
140 5         10 $nom = $diw{0}{$s0}{1};
141             }
142             }
143             }
144 11 50       18 if ($nom >= 0) {
145 11         16 $retval .= " ".$nom{$nominal}{$nom};
146 11 100       29 $out_rub = 1 if ($nominal == 1);
147             }
148 11         51 $retval =~ s/^\s*//g;
149 11         59 $retval =~ s/\s*$//g;
150              
151 11         41 return $retval;
152             }
153              
154             sub num2words {
155 4     4 0 96 my ($number, $gender) = @_;
156              
157 4 100       15 $gender = 1 unless defined $gender; # male by default
158              
159 4 50       6 return _get_string(0, 0, 0) unless $number; # no extra calculations for zero
160              
161 4         2 my ($result, $negative);
162              
163             # Negative number, just add another word
164 4 50       8 if ($number < 0) {
165 0         0 $number = abs($number);
166 0         0 $negative = 1;
167             }
168              
169 4         4 $result = "";
170 4         17 my $int_number = floor($number); # no doubles
171              
172 4   66     18 for (my $i = 1; $i < 6 && $int_number >= 1; $i++) {
173 4         4 my $tmp_number = $int_number / 1000;
174 4         27 my $number_part = sprintf("%0.3f", $tmp_number - sprintf("%d", $tmp_number)) * 1000;
175              
176 4         6 $int_number = floor $tmp_number; # no doubles again
177 4         9 $result = _get_string($number_part, $i, $gender) . " " . $result;
178             }
179              
180             # Clean the result
181 4         14 $result =~ s/\s+/ /g;
182 4         12 $result =~ s/\s+$//;
183              
184 4 50       10 return ($negative) ? "минус $result" : $result;
185             }
186              
187             sub _get_string {
188 4     4   7 my $sum = shift;
189 4         5 my $nominal = shift;
190 4         3 my $gender = shift;
191 4         5 my ($result, $nom) = ('', -1);
192            
193 4 50       7 return unless defined $sum;
194              
195 4 50 33     29 if ( ( !$nominal && $sum < 100 ) || ( $nominal > 0 && $nominal < 6 && $sum < 1000 ) ) {
      33        
      33        
      33        
196 4         7 my $s2 = sprintf( "%d", $sum / 100 );
197              
198 4 50       7 if ( $s2 > 0 ) { # hundreds
199 0         0 $result .= ' ' . $diw{2}{$s2}{0};
200 0         0 $nom = $diw{2}{$s2}{1};
201             }
202              
203 4         7 my $sx = floor $sum - $s2 * 100;
204              
205 4 100 66     21 if ( ( $sx < 20 && $sx > 0 ) || ( $sx == 0 && !$nominal ) ) {
      33        
      66        
206 1         6 $result .= " " . $diw{0}{$sx}{0};
207 1         4 $nom = $diw{0}{$sx}{1};
208             }
209             else {
210 3         5 my $s1 = floor $sx / 10; # tens
211              
212 3         6 my $s0 = sprintf( "%d", $sum - $s2 * 100 - $s1 * 10 + 0.5 );
213              
214 3 50       6 if ( $s1 > 0 ) {
215 3         8 $result .= ' ' . $diw{1}{$s1}{0};
216 3         4 $nom = $diw{1}{$s1}{1};
217             }
218 3 50       6 if ( $s0 > 0 ) {
219 3         5 $result .= ' ' . $diw{0}{$s0}{0};
220 3         6 $nom = $diw{0}{$s0}{1};
221             }
222             }
223             }
224 4 50       8 if ( $nom >= 0 ) {
225              
226 4 50       7 if ( $nominal == 1 ) {
227 4 50       19 $result .= defined $nominal ? ' ' . $genders{$gender}{$nom} : '';
228             }
229             else {
230 0 0       0 $result .= defined $nominal ? ' ' . $nom{$nominal}{$nom} : '';
231             }
232             }
233 4         19 $result =~ s/^\s*//g;
234 4         29 $result =~ s/\s*$//g;
235              
236 4         26 return $result;
237             }
238              
239             # Autoload methods go after =cut, and are processed by the autosplit program.
240              
241             1;
242             __END__