File Coverage

blib/lib/Lingua/RU/Numeral.pm
Criterion Covered Total %
statement 221 242 91.3
branch 146 194 75.2
condition 67 106 63.2
subroutine 13 13 100.0
pod 1 1 100.0
total 448 556 80.5


line stmt bran cond sub pod time code
1             package Lingua::RU::Numeral;
2              
3 1     1   85006 use 5.010;
  1         13  
4 1     1   5 use strict;
  1         1  
  1         30  
5 1     1   4 use warnings;
  1         3  
  1         37  
6 1     1   6 use utf8;
  1         20  
  1         6  
7 1     1   38 use open qw(:std :utf8);
  1         1  
  1         9  
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12              
13             our %EXPORT_TAGS = ( 'all' => [ qw(
14             num2cardinal
15             ) ] );
16              
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18              
19             our @EXPORT = qw( );
20              
21             our $VERSION = '0.05';
22              
23              
24             ########################
25             # num2cardinal INPUT #
26             ########################
27             # SCALAR:
28             #~~~~~~~~
29             # 0. $number -- число для обработки, e.g: 1234567890
30              
31             #~~~~~~~~~~~
32             # HASH keys:
33             #~~~~~~~~~~~
34             # 1. 'gender' key -- род:
35             # (default) m = masculine (Мужской)
36             # f = feminine (Женский)
37             # n = neuter (Средний)
38              
39             # 2. 'case' key -- падеж: числительное: Количественное | Порядковое
40             # (default) n = nominative - Именительный: есть кто? что? | какой?
41             # g = genitive - Родительный: нет кого? чего? | какого?
42             # d = dative - Дательный: рад кому? чему? | какому?
43             # a = accusative - Винительный: вижу кого? что? | какой?
44             # i = instrumental - Творительный: оплачу кем? чем? | каким?
45             # p = prepositional - Предложный: думаю о ком? о чём?| о каком?
46              
47             # 3. 'multi' key -- единственное(undef|0|'singular') или множественное число (>0|'plural'). By default, 'singular'
48             # 4. 'object' key -- inanimate(неодушевлённый) или animate(одушевлённый) предмет. By default, 'inanimate'
49             # 5. 'prolog' key -- Preposition (prologue) of numeral
50             # 6. 'epilog' key -- Epilogue of numeral
51             # 7. 'alt' key
52             # 8. 'ucfirst' key
53              
54             sub num2cardinal {
55 1770     1770 1 414605 my( $number, %cfg ) = @_;
56 1770         4889 $number =~s/\D+//g;
57              
58             # extreme index for:
59 1     1   332 use constant VXN => 5; # numbers from 5 to <1000
  1         4  
  1         75  
60 1     1   7 use constant TMMT => 6; # тысячи | миллионы | миллиарды | триллионы
  1         2  
  1         3238  
61              
62 1770   100     5249 my $gender = $cfg{'gender'} || 'masculine';
63 1770 50       9762 $gender = $gender=~/^\s*(m|f|n)/i ? @{{'m'=>'masculine', 'f'=>'feminine', 'n'=>'neuter'}}{lc $1} : 'masculine';
  1770         6898  
64              
65 1770   100     5583 my $case = $cfg{'case'} || 'nominative';
66 1770 50       6234 $case = $case=~/^\s*([ngdaip])/i ? $1 : 'n';
67              
68 1770   100     4541 my $multi = $cfg{'multi'} || 'singular';
69 1770 100       4469 $multi = $multi=~/^\s*[p1-9]/i ? 'plural' : 'singular';
70              
71 1770   100     4041 my $object = $cfg{'object'} || 'inanimate';
72 1770 100       3874 $object = $object=~/^\s*[a1-9]/i ? 'animate' : 'inanimate';
73              
74             # Preposition (prologue) of numeral
75 1770         4328 my $prolog = &_ref_prolog( \%cfg, \$case );
76              
77             # Epilogue of numeral
78 1770         4632 my $epilog = &_ref_epilog( $case, $multi, \%cfg, \$object, \$gender );
79              
80 1770 100       3898 unless( $number ) {
81 55 100       175 my $oy = exists( $cfg{'alt'}{0} ) ? 'у' : 'о';
82             my $zero = ($case =~/^a/i && $object =~/^animate/i ) ?
83             "н${oy}ля" :
84 55 100 100     246 @{{'n'=>"н${oy}ль", 'g'=>"н${oy}ля", 'd'=>"н${oy}лю", 'a'=>"н${oy}ль", 'i'=>"н${oy}лём", 'p'=>"н${oy}ле"}}{ $case };
  52         428  
85              
86             # Add the numeral
87 55         190 my @s = ( $zero );
88              
89             # Add the prolog to numeral
90 55         108 my $i = &_add_prolog( $prolog, \@s );
91              
92             # Add the epilog to numeral
93 55 100 66     215 if( $epilog && exists( $epilog->{'root'} ) ) {
94 26         47 my $j = 0;
95 26         37 for my $root ( @{ $epilog->{'root'} } ) {
  26         54  
96 33         110 push @s, $root.$epilog->{'ends'}[ $j++ ][0];
97             }
98             }
99              
100 55 100       165 $s[$i] = ucfirst $s[$i] if exists $cfg{'ucfirst'};
101              
102 55         6981 return join ' ', @s;
103             }
104              
105 1715 100       3551 return "$number > 999_999_999_999_999 !" if length( $number ) > 15;
106              
107             # To get cardinal form DB (БД словоформ числительных)
108 1714         3530 my( $bsw, $plural, $Power ) = &_cardinal_form_db( $case, $multi, $object, $gender );
109              
110 1714         3041 my @Dcml = @{ $bsw->{'dcml'} };
  1714         5364  
111 1714         2392 my @Cent = @{ $bsw->{'cent'} };
  1714         4104  
112              
113 1714         3949 my @tmEnd; # for ends --- для окончаний 'тысяч','миллион','миллиард','триллион'
114             my @words; # for ends --- для искомой структуры слов числительных
115 1714         0 my @s; # Resulting string --- Результирующая строка
116              
117 1714         3726 while( length $number ) {
118              
119 3848 100       8935 if( $number =~/^.$/ ) { # 0..9
120 625 100       1272 last unless $number;
121              
122             push @s, ( $multi =~/^plural/i && $number == 1 ) ?
123             $plural->{1} :
124 409 100 66     2283 $bsw->{ $gender }{ ($case =~/^a/i && $object =~/^animate/i) ? 'animate' : 'unit'}[ $number ];
    100 100        
125              
126             # Add epilog to numeral
127 409 100 66     1569 if( $epilog && exists( $epilog->{'root'} ) ) {
128              
129 166         259 my $j = 0;
130 166         229 for my $root ( @{ $epilog->{'root'} } ) {
  166         349  
131 198 100       815 push @s, $root.$epilog->{'ends'}[ $j++ ][ $number < 5 ? $number : VXN ];
132             }
133              
134 166         380 $epilog = {}; # epilog END
135             }
136              
137 409         839 last;
138             }
139              
140 3223         6796 my $i = int length( $number ) / 3;
141              
142 3223 100       5721 if( $i < 2 ) { # $number = 10 .. 99_999
143             # Женский род, (т.к. может быть 'тысяч')
144 1295         1590 @words = @{ $bsw->{'feminine'}{'unit'} };
  1295         5026  
145 1295         1797 @tmEnd = @{ $bsw->{'eT'} }; # окончания для 'тысяч'
  1295         3164  
146             }
147             else { # Для >=100_000, 'миллион','миллиард','триллион'
148             # Мужской род
149 1928         2624 @words = @{ $bsw->{'masculine'}{'unit'} };
  1928         7053  
150 1928         2685 @tmEnd = @{ $bsw->{'eMT'} }; # окончания
  1928         4527  
151             }
152              
153 3223 100       6600 if( length( $number )%3 == 0 ) { # Сотни: 100 .. 999, 100_xxx .. 999_xxx, 100_xxx_xxx .. 999_xxx_xxx, etc.
154 768         2396 $number =~s/^\d//;
155 768 100       2406 push @s, $Cent[$&] if $&;
156 768         1717 next;
157             }
158              
159 2455 100       4610 if( length( $number )%3 == 2 ) { # Десятки: 10 .. 99, 10_xxx .. 99_xxx, 10_xxx_xxx .. 99_xxx_xxx, etc.
160 1123 100       2476 if( $number =~/^1/ ) { # 10... 19...
161 223         741 $number =~s/^\d\d//;
162 223 50       965 push @s, $words[$&] if $&;
163              
164 223 100       704 push @s, $Power->[$i].$tmEnd[0] if length $Power->[$i];
165             }
166             else { # 20... 99...
167 900         2546 $number =~s/^\d//;
168 900 100       2611 push @s, $Dcml[$&] if $&;
169             }
170              
171 1123         2313 next;
172             }
173              
174 1332 50       2637 if( length( $number )%3 == 1 ) { # Единицы: 0..., 1,..., 9...
175 1332         4381 $number =~s/^\d//;
176 1332         3006 my $d = $&;
177 1332 100       2431 if( $d ) {
178 941 100 66     3324 push @s, ( $multi =~/^plural/i && $d == 1 ) ? $plural->{1} : $words[$d];
179             }
180              
181 1332 100       4368 if( $s[-1] !~/^(?:м|трил)/ ) { # ещё не добавлено миллион | миллиард | триллион
182             my $w = ( $multi =~/^plural/i && $d == 1 ) ?
183 1185 100 66     3535 $plural->{ $i < 2 ? 'eT' : 'eMT'} :
    100          
184             $tmEnd[ $d ];
185              
186 1185         3162 push @s, $Power->[$i].$w;
187             }
188              
189 1332 100       4835 last if $number =~/^0+$/;
190             }
191             }
192              
193             # Add prolog to numeral
194 1714         3683 my $i = &_add_prolog( $prolog, \@s );
195              
196 1714 100       4664 $s[$i] = ucfirst $s[$i] if exists $cfg{'ucfirst'};
197              
198             # Add epilog to numeral
199 1714 100 66     5845 if( $epilog && exists( $epilog->{'root'} ) ) {
200              
201             # Choice between
202 380 100       1322 my $i = $s[-1] =~/^(?:ты|ми|трил)/ ?
203             TMMT : # тысяча | миллион | миллиард | триллион
204             VXN; # other
205              
206 380         564 my $j = 0;
207 380         541 for my $root ( @{ $epilog->{'root'} } ) {
  380         841  
208 447         1377 push @s, $root.$epilog->{'ends'}[ $j++ ][ $i ];
209             }
210              
211             }
212              
213 1714         29565 return join ' ', @s;
214             }
215              
216              
217             sub _cardinal_form_db {
218 1714     1714   3502 my( $case, $multi, $object, $gender ) = @_;
219              
220             # База словоформ числительных (singular -- единственно число, default)
221 1714         96647 my %bsw = (
222             'n' => { # nominative (Именительный падеж) кто? что?, default
223             'masculine' => { # Мужской род, default
224             'unit' => [ # 0..19
225             '','один','два','три','четыре','пять','шесть','семь','восемь','девять','десять',
226             'одиннадцать','двенадцать','тринадцать','четырнадцать','пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'
227             ],
228             },
229             'feminine' => { # Женский род
230             'unit' => [ # 0..19
231             '','одна','две' # остальные как для Мужского рода
232             ],
233             },
234             'neuter' => { # Средний род
235             'unit' => [ # 0..19
236             '','одно' # остальные как для Мужского рода
237             ],
238             },
239             'dcml' => [ # 20, 30,...,90
240             '','','двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят','восемьдесят','девяносто'
241             ],
242             'cent' => [ # 100..900
243             '','сто','двести','триста','четыреста','пятьсот','шестьсот','семьсот','восемьсот','девятьсот'
244             ],
245             'eT' => [ # окончания для 'тысяч' (0й - для 10..19; десятков: 10,20,...,90 и сотен: 100,200,...,900)
246             '','а', ('и') x 3, ('') x 5
247             ],
248             'eMT' => [ # окончания для 'миллион','миллиард','триллион' (0й - для 10..19; десятков: 10,20,...,90 и сотен: 100,200,...,900)
249             'ов','', ('а') x 3, ('ов') x 5
250             ],
251             }, #--------------------------------------------
252             'g' => { # genitive (Родительный падеж): кого? чего?
253             'masculine' => { # Мужской род, default
254             'unit' => [ # 0..19
255             '','одного','двух','трёх','четырёх','пяти','шести','семи','восьми','девяти','десяти',
256             'одиннадцати','двенадцати','тринадцати','четырнадцати','пятнадцати','шестнадцати','семнадцати','восемнадцати','девятнадцати'
257             ],
258             },
259             'feminine' => { # Женский род
260             'unit' => [ # 0..19
261             '','одной' # остальные как для Мужского рода
262             ],
263             },
264             'neuter' => { # Средний род
265             'unit' => [ # 0..19
266             '' # всё как для Мужского рода
267             ],
268             },
269             'dcml' => [ # 20, 30,...,90
270             '','','двадцати','тридцати','сорока','пятидесяти','шестидесяти','семидесяти','восьмидесяти','девяноста'
271             ],
272             'cent' => [ # 100..900
273             '','ста','двухсот','трёхсот','четырёхсот','пятисот','шестисот','семисот','восьмисот','девятисот'
274             ],
275             'eT' => [ # окончания для 'тысяч'
276             '','и', ('') x 8
277             ],
278             'eMT' => [ # окончания для 'миллион','миллиард','триллион'
279             'ов','а', ('ов') x 8
280             ],
281             }, #--------------------------------------------
282             'd' => { # dative (Дательный падеж): кому? чему?
283             'masculine' => { # Мужской род, default
284             'unit' => [ # 0..19
285             '','одному','двум','трём','четырём','пяти','шести','семи','восьми','девяти','десяти',
286             'одиннадцати','двенадцати','тринадцати','четырнадцати','пятнадцати','шестнадцати','семнадцати','восемнадцати','девятнадцати'
287             ],
288             },
289             'feminine' => { # Женский род
290             'unit' => [ # 0..19
291             '','одной' # остальные как для Мужского рода
292             ],
293             },
294             'neuter' => { # Средний род
295             'unit' => [ # 0..19
296             '' # всё как для Мужского рода
297             ],
298             },
299             'dcml' => [ # 20, 30,...,90 : как Родительный падеж
300             '','','двадцати','тридцати','сорока','пятидесяти','шестидесяти','семидесяти','восьмидесяти','девяноста'
301             ],
302             'cent' => [ # 100..900
303             '','ста','двумстам','трёхстам','четырёхстам','пятистам','шестистам','семистам','восьмистам','девятистам'
304             ],
305             'eT' => [ # окончания для 'тысяч'
306             'ам','е', ('ам') x 8
307             ],
308             'eMT' => [ # окончания для 'миллион','миллиард','триллион'
309             'ам','у', ('ам') x 8
310             ],
311             }, #--------------------------------------------
312             'a' => { # accusative (Винительный падеж): animate (одушевлённый объект): кого? | inanimate (неодушевлённый объект): что?
313             'masculine' => { # Мужской род, inanimate default
314             'unit' => [ # 0..19 : неодушевлённый объект, как Именительный падеж
315             '','один','два','три','четыре','пять','шесть','семь','восемь','девять','десять',
316             'одиннадцать','двенадцать','тринадцать','четырнадцать','пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'
317             ],
318             'animate' => [ # 0..19 : одушевлённый объект, как Родительный падеж (0..4), Именительный падеж (5..19)
319             '','одного','двух','трёх','четырёх', 'пять','шесть','семь','восемь','девять','десять',
320             'одиннадцать','двенадцать','тринадцать','четырнадцать','пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'
321             ],
322             },
323             'feminine' => { # Женский род
324             'unit' => [ # 0..19 : неодушевлённый объект
325             '','одну','две' # остальные как для Мужского рода
326             ],
327             'animate' => [ # 0..19 : одушевлённый объект
328             '','одну' # остальные как для одушевлённого Мужского рода
329             ],
330             },
331             'neuter' => { # Средний род
332             'unit' => [ # 0..19 : неодушевлённый объект
333             '','одно' # остальные как для Мужского рода
334             ],
335             'animate' => [ # 0..19 : одушевлённый объект
336             '','одно','два' # остальные как для одушевлённого Мужского рода
337             ],
338             },
339             'dcml' => [ # 20, 30,...,90
340             '','','двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят','восемьдесят','девяносто'
341             ],
342             'cent' => [ # 100..900
343             '','сто','двести','триста','четыреста','пятьсот','шестьсот','семьсот','восемьсот','девятьсот'
344             ],
345             'eT' => [ # окончания для 'тысяч' (0й - для 10..19 и десятков: 10,20,...,90)
346             '','у', ('и') x 3, ('') x 5
347             ],
348             'eMT' => [ # окончания для 'миллион','миллиард','триллион' (0й - для 10..19 и десятков: 10,20,...,90)
349             'ов','', ('а') x 3, ('ов') x 5
350             ],
351             }, #--------------------------------------------
352             'i' => { # instrumental (Творительный падеж) : кем? чем?
353             'masculine' => { # Мужской род, default
354             'unit' => [ # 0..19
355             '','одним','двумя','тремя','четырьмя','пятью','шестью','семью','восьмью','девятью','десятью',
356             'одиннадцатью','двенадцатью','тринадцатью','четырнадцатью','пятнадцатью','шестнадцатью','семнадцатью','восемнадцатью','девятнадцатью'
357             ],
358             },
359             'feminine' => { # Женский род
360             'unit' => [ # 0..19
361             '','одной' # остальные как для Мужского рода
362             ],
363             'alternative' => [ # 0..19
364             '','одною' # остальные как для Мужского рода
365             ],
366             },
367             'dcml' => [ # 20, 30,...,90
368             '','','двадцатью','тридцатью','сорока','пятьюдесятью','шестьюдесятью','семьюдесятью','восьмьюдесятью','девяноста'
369             ],
370             'cent' => [ # 100..900
371             '','ста','двумястами','тремястами','четырьмястами','пятьюстами','шестьюстами','семьюстами','восьмьюстами','девятьюстами'
372             ],
373             'eT' => [ # окончания для 'тысяч'
374             'ами','ей', ('ами') x 8
375             # 'ами','ью', ('ами') x 8
376             ],
377             'eMT' => [ # окончания для 'миллион','миллиард','триллион'
378             'ами','ом', ('ами') x 8
379             ],
380             }, #--------------------------------------------
381             'p' => { # prepositional (Предложный падеж) : о ком? о чём?
382             'masculine' => { # Мужской род, default
383             'unit' => [ # 0..19
384             '','одном','двух','трёх','четырёх','пяти','шести','семи','восьми','девяти','десяти',
385             'одиннадцати','двенадцати','тринадцати','четырнадцати','пятнадцати','шестнадцати','семнадцати','восемнадцати','девятнадцати'
386             ],
387             },
388             'feminine' => { # Женский род
389             'unit' => [ # 0..19
390             '','одной' # остальные как для Мужского рода
391             ],
392             },
393             'dcml' => [ # 20, 30,...,90
394             '','','двадцати','тридцати','сорока','пятидесяти','шестидесяти','семидесяти','восьмидесяти','девяноста'
395             ],
396             'cent' => [ # 100..900
397             '','ста','двухстах','трёхстах','четырёхстах','пятистах','шестистах','семистах','восьмистах','девятистах'
398             ],
399             'eT' => [ # окончания для 'тысяч'
400             'ах','е', ('ах') x 8
401             ],
402             'eMT' => [ # окончания для 'миллион','миллиард','триллион'
403             'ах','е', ('ах') x 8
404             ],
405             },
406             );
407              
408 1714         14925 my %plural = ( # множественное число
409             'n' => { # nominative (Именительный падеж)
410             1 => 'одни',
411             'eT' => 'и', # окончание для 'тысяч'
412             'eMT' => 'ы', # окончания для 'миллион','миллиард','триллион'
413             },
414             'g' => { # genitive (Родительный падеж)
415             1 => 'одних',
416             'eT' => '',
417             'eMT' => 'ов',
418             },
419             'd' => { # dative (Дательный падеж)
420             1 => 'одним',
421             'eT' => 'ам',
422             'eMT' => 'ам',
423             },
424             'a' => { # accusative (Винительный падеж):
425             1 => 'одни', # inanimate(неодушевлённый) объект. For animate(одушевлённый) -- see below
426             'eT' => 'и',
427             'eMT' => 'ы',
428             },
429             'i' => {# instrumental (Творительный падеж)
430             1 => 'одними',
431             'eT' => 'ами',
432             'eMT' => 'ами',
433             },
434             'p' => { # prepositional (Предложный падеж)
435             1 => 'одних',
436             'eT' => 'ах',
437             'eMT' => 'ах',
438             },
439             );
440              
441             # Дозаполняем необходимые структуры, кроме 'masculine'
442 1714         3426 my %gg = ('masculine' => undef );
443 1714         3104 for my $g ('feminine', $gender ) {
444 3428 100       8035 next if exists $gg{ $g };
445 1925         3152 $gg{ $g } = undef;
446              
447 1925         4059 for(0..19) {
448             # если НЕ определено
449 38500   100     135149 $bsw{ $case }{ $g }{'unit'}[$_] //= $bsw{ $case }{'masculine'}{'unit'}[$_];
450              
451 38500 100 66     100920 $bsw{'a'}{ $g }{'animate'}[$_] //= $bsw{'a'}{'masculine'}{'animate'}[$_]
      100        
452             if $case =~/^a/i && $object =~/^animate/i;
453             }
454             }
455              
456             # Корректируем окончания для accusative(Винительный падеж) + одушевлённый объект + множественное число
457 1714 100 100     4830 if( $case =~/^a/i && $object =~/^animate/i && $multi =~/^plural/i ) {
      100        
458 5         16 $plural{'a'}{1} = 'одних';
459 5         8 $plural{'a'}{'eT'} = '';
460 5         9 $plural{'a'}{'eMT'} = 'ов';
461             }
462              
463 1714         2361 return( \%{ $bsw{ $case } }, \%{ $plural{ $case } }, ['','тысяч','миллион','миллиард','триллион'] );
  1714         3897  
  1714         37905  
464             }
465              
466              
467             # Add prolog to numeral, e.g. ['с','со=с',...]
468             sub _add_prolog {
469 1769     1769   3491 my( $prolog, $s ) = @_;
470 1769 100 66     7122 return 0 if ! @$prolog or ! @$s or (~~@$prolog < 2 and ( ! defined( $prolog->[0] ) or ! length( $prolog->[0] ) ) );
      33        
      66        
      66        
471              
472 172         312 my $p = shift @$prolog; # get 1st element (e.g. 'с') for any numeral
473              
474 172         312 my $i = 0;
475 172         346 for( @$prolog ) { # ['со=с',...]
476 136 50       279 next unless $_;
477              
478 136         553 my( $k, $m ) = split '=';
479 136 50 33     487 next unless $k && $m;
480              
481 136 100       809 if( $s->[0] =~/^$m/ ) {
482 44         119 unshift @$s, $k;
483 44         89 undef $p;
484 44         69 $i = 1;
485 44         86 last;
486             }
487             }
488              
489 172 100 66     601 if( defined( $p ) && length $p ) {
490 128         338 unshift @$s, $p;
491 128         176 $i = 1;
492             }
493              
494 172         356 return $i;
495             }
496              
497              
498             sub _ref_prolog {
499 1770     1770   3063 my( $cfg, $case ) = @_;
500 1770   100     4681 my $prolog = $cfg->{'prolog'} // return [ ];
501              
502 1209 100       3226 if( ref($prolog) eq 'ARRAY') {
503 1062 100       3353 return ~~@$prolog ? [ @$prolog ] : [ ];
504             }
505              
506 147 50       300 if( ref($prolog) eq 'HASH') {
507 0         0 my @p;
508 0         0 for my $k (sort keys %$prolog ) {
509 0 0 0     0 if( defined( $prolog->{$k} ) && length( $prolog->{$k} ) ) {
510 0         0 my $v = $prolog->{$k};
511 0         0 $k =~s/^\s+|\s+$//g;
512 0         0 push @p, "$k=$v";
513             }
514             else {
515 0         0 $k =~s/^\s+|\s+$//g;
516 0         0 unshift @p, $k;
517             }
518             }
519 0 0       0 return ~~@p ? \@p : [ ];
520             }
521              
522 147 50       425 if( ref(\$prolog) eq 'SCALAR') {
523 147         470 $prolog =~s/^\s+|\s+$//g;
524              
525             # genitive - Родительный: кого? чего?
526 147 100       848 if( $prolog =~/^(?:
527             безо?|
528             в(?:близи|виду|доль|замен|круг|место|не|низу|нутр[иь]|переди?|роде|овнутрь|озле|округ|следствие|ыше)|
529             для|до|
530             из(?:о?|\-за|нутри|\-подо?)|
531             каса(?:ем|тельн)о|кроме|кругом|
532             мимо|
533             на(?:кануне|место|подобие|против|супротив|счет)|ниже|
534             о(?:бок|бочь|коло|крест|круг|причь|то?|тносительно)|
535             по(?:близости|верх|дле|зад[иь]|мимо|перек|се?реди(?:не)?|середь|сле|средством)|
536             пр(?:евыше|отив)|путем|
537             ради|
538             с(?:верху?|выше|ередь|зади|илами|наружи|низу|переди|ред[иь]|упротив)|
539             у
540 1     1   7 )$/ix
  1         2  
  1         26  
541             ) {
542 1         4 $$case = 'g'; # genitive
543 1         4 return [ $prolog ];
544             }
545              
546             # dative - Дательный: кому? чему?
547 146 100       525 if( $prolog =~ /^(?:
548             вдогон(?:ку|очку)?|вослед|вразрез|вслед|
549             ко?|
550             напере(?:кор|рез)|
551             подобно|противно|
552             соо(?:браз|тветствен)но|соразмерно
553             )$/ix
554             ) {
555 1         4 $$case = 'd'; # dative
556 1         4 return [ $prolog ];
557             }
558              
559             # accusative - Винительный: кого? что?
560 145 100       308 if( $prolog =~ /^(?:
561             (?:вы?|ис)ключая|про|сквозь|спустя|че?рез
562             )$/ix
563             ) {
564 1         5 $$case = 'a'; # accusative
565 1         5 return [ $prolog ];
566             }
567              
568             # instrumental - Творительный: кем? чем?
569 144 100       691 if( $prolog =~ /^(?:
570             кончая|надо?|начиная|передо?|по\-[зн]ад?
571             )$/ix
572             ) {
573 1         6 $$case = 'i'; # instrumental
574 1         5 return [ $prolog ];
575             }
576              
577             # prepositional - Предложный: о ком? о чём?
578 143 100       284 if( $prolog =~ /^при$/i ) {
579 1         4 $$case = 'p'; # prepositional
580 1         3 return [ $prolog ];
581             }
582              
583             # genitive (Родительный) || dative (Дательный, by default)
584 142 100       301 if( $prolog =~ /^согласно$/i ) {
585 1 50 33     9 $$case = 'd' if ! $$case or $$case !~/^[gd]/;
586 1         4 return [ $prolog ];
587             }
588              
589             # genitive (Родительный, by default) || instrumental (Творительный)
590 141 100       289 if( $prolog =~ /^(?:про)?меж(?:ду)?$/i ) {
591 1 50 33     10 $$case = 'g' if ! $$case or $$case !~/^[gi]/;
592 1         4 return [ $prolog ];
593             }
594              
595             # accusative (Винительный) || dative (Дательный, by default)
596 140 100       269 if( $prolog =~ /^благодаря$/i ) {
597 1 50 33     10 $$case = 'd' if ! $$case or $$case !~/^[ad]/;
598 1         4 return [ $prolog ];
599             }
600              
601             # accusative (Винительный) || instrumental (Творительный, by default)
602 139 100       319 if( $prolog =~ /^(?:за|подо?)$/i ) {
603 1 50 33     11 $$case = 'i' if ! $$case or $$case !~/^[ai]/;
604 1         4 return [ $prolog ];
605             }
606              
607             # accusative (Винительный) || prepositional (Предложный)
608 138 100       757 if( $prolog =~/^[вВB][ОоOo]?$/ ) {
    100          
    100          
609 1         12 $prolog =~tr/BOo/ВОо/;
610 1 50 33     10 $$case = 'a' if ! $$case or $$case !~/^[ap]/;
611 1         5 return [ $prolog ];
612             }
613             elsif( $prolog =~ /^[oOоО][бБ]?[oOоО]?$/ ) {
614 65 100 66     342 $$case = 'p' if ! $$case or $$case !~/^[ap]/;
615 65         229 return ['о','об=од'];
616             }
617             elsif( $prolog =~ /^на$/i ) {
618 1 50 33     13 $$case = 'a' if ! $$case or $$case !~/^[ap]/;
619 1         5 return [ $prolog ];
620             }
621              
622             # accusative (Винительный) || genitive (Родительный) || instrumental (Творительный, by default)
623 71 100       290 if( $prolog =~/^[cCсС][ОоOo]?$/ ) {
624 70 50 33     323 $$case = 'i' if ! $$case or $$case !~/^[agi]/;
625 70         247 return ['с','со=ст'];
626             }
627              
628             # accusative (Винительный) || dative (Дательный, by default) || prepositional (Предложный)
629 1 50       10 if( $prolog =~ /^по$/i ) {
630 1 50 33     10 $$case = 'd' if ! $$case or $$case !~/^[adp]/;
631 1         5 return [ $prolog ];
632             }
633              
634             }
635              
636 0         0 return [ ];
637             }
638              
639              
640             sub _ref_epilog {
641 1770     1770   3672 my( $case, $multi, $cfg, $object, $gender ) = @_;
642 1770 100       4074 my $epilog = $cfg->{'epilog'} or return {};
643              
644 1676 100 66     6762 if( ref(\$epilog) eq 'SCALAR') {
    100 66        
645 555         909 my %eRef;
646              
647 555 100       2385 if( $epilog =~/^(?:RUB|643)$/ ) { # Российский рубль
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
648 149         1913 %eRef = (
649             'object' => 'inanimate',
650             'gender' => 'masculine',
651             'root' => 'рубл',
652             'ends' => {
653             'n' => ['ей','ь', ('я') x 3, ('ей') x 2 ], # nominative - Именительный
654             'g' => ['ей','я', ('ей') x 5 ], # genitive - Родительный
655             'd' => ['ям','ю', ('ям') x 4, 'ей' ], # dative - Дательный
656             'a' => ['ей','ь', ('я') x 3, ('ей') x 2 ], # accusative - Винительный
657             'i' => ['ей','ём', ('ями') x 4, 'ей' ], # instrumental - Творительный
658             'p' => ['ей','е', ('ях') x 4, 'ей' ], # prepositional - Предложный
659             },
660             'plural' => {'n'=>'и', 'g'=>'ей', 'd'=>'ям', 'a'=>'и', 'i'=>'ями', 'p'=>'ях'},
661             );
662              
663             }
664             elsif( $epilog =~/^(?:BYR|974)$/) { # Белорусский рубль
665 97         2005 %eRef = (
666             'object' => 'inanimate',
667             'gender' => 'masculine',
668             'root' => ['белорусск','рубл'],
669             'ends' => {
670             'n' => [['их','ий', ('их') x 5 ], ['ей','ь', ('я') x 3, ('ей') x 2 ]], # nominative - Именительный
671             'g' => [['их','ого', ('их') x 5 ], ['ей','я', ('ей') x 5 ]], # genitive - Родительный
672             'd' => [['им','ому', ('им') x 4, 'их'], ['ям','ю', ('ям') x 4, 'ей' ]], # dative - Дательный
673             'a' => [['их','ий', ('их') x 5 ], ['ей','ь', ('я') x 3, ('ей') x 2 ]], # accusative - Винительный
674             'i' => [['их','им', ('ими') x 4, 'их'], ['ей','ём', ('ями') x 4, 'ей']], # instrumental - Творительный
675             'p' => [['их','ом', ('их') x 5], ['ей','е', ('ях') x 4, 'ей']], # prepositional - Предложный
676             },
677             'plural' => {'n'=>['ие','и'], 'g'=>['их','ей'], 'd'=>['им','ям'], 'a'=>['ие','и'], 'i'=>['ими','ями'], 'p'=>['их','ях']},
678             );
679              
680             }
681             elsif( $epilog =~/^(?:rub|\-643|byr|\-974)$/) { # Российская | Белорусская копейка
682 160         1992 %eRef = (
683             'object' => 'inanimate',
684             'gender' => 'feminine',
685             'root' => 'копе',
686             'ends' => {
687             'n' => ['ек','йка', ('йки') x 3, ('ек') x 2 ],
688             'g' => ['ек','йки', ('ек') x 5 ],
689             'd' => ['ек','йке', ('йкам') x 4, 'ек' ],
690             'a' => ['ек','йку', ('йки') x 3, ('ек') x 2 ],
691             'i' => ['ек','йкой', ('йками') x 4, 'ек' ],
692             'p' => ['ек','йке', ('йках') x 4, 'ек' ],
693             },
694             'plural' => {'n'=>'йки', 'g'=>'ек', 'd'=>'йкам', 'a'=>'йки', 'i'=>'йками', 'p'=>'йках'},
695             );
696              
697             }
698             elsif( $epilog =~/^(?:USD|840)$/) {
699 149         1802 %eRef = (
700             'object' => 'inanimate',
701             'gender' => 'masculine',
702             'root' => 'доллар',
703             'ends' => {
704             'n' => ['ов','', ('а') x 3, ('ов') x 2 ],
705             'g' => ['ов','а', ('ов') x 5 ],
706             'd' => ['ам','у', ('ам') x 4, 'ов' ],
707             'a' => ['ов','', ('а') x 3, ('ов') x 2 ],
708             'i' => ['ов','ом', ('ами') x 4, 'ов' ],
709             'p' => ['ов','е', ('ах') x 4, 'ов' ],
710             },
711             'plural' => {'n'=>'ы', 'g'=>'ов', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
712             );
713              
714             }
715             elsif( $epilog =~/^(?:usd|\-840)$/) {
716 0         0 %eRef = (
717             'object' => 'inanimate',
718             'gender' => 'masculine',
719             'root' => 'цент',
720             'ends' => {
721             'n' => ['ов','', ('а') x 3, ('ов') x 2 ],
722             'g' => ['ов','а', ('ов') x 5 ],
723             'd' => ['ам','у', ('ам') x 4, 'ов' ],
724             'a' => ['ов','', ('а') x 3, ('ов') x 2 ],
725             'i' => ['ов','ом', ('ами') x 4, 'ов' ],
726             'p' => ['ов','е', ('ах') x 4, 'ов' ],
727             },
728             'plural' => {'n'=>'ы', 'g'=>'ов', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
729             );
730              
731             }
732             elsif( $epilog =~/^(?:CNY|156)$/) {
733 0         0 %eRef = (
734             'object' => 'inanimate',
735             'gender' => 'masculine',
736             'root' => 'юан',
737             'ends' => {
738             'n' => ['ей','ь', ('я') x 3, ('ей') x 2 ],
739             'g' => ['ей','я', ('ей') x 5 ],
740             'd' => ['ям','ю', ('ям') x 5 ],
741             'a' => ['ей','ь', ('я') x 3, ('ей') x 2 ],
742             'i' => ['ей','ем', ('ями') x 4, 'ей' ],
743             'p' => ['ях','е', ('ях') x 4, 'ей' ],
744             },
745             'plural' => {'n'=>'и', 'g'=>'ей', 'd'=>'ям', 'a'=>'и', 'i'=>'ями', 'p'=>'ях'},
746             );
747              
748             }
749             # elsif( $epilog =~/^(?:cny|-156)$/) {
750             # %eRef = ('root'=>'фын', 'ends'=>['ей','ь', ('я') x 3, ('ей') x 5 ] );
751             # }
752             elsif( $epilog =~/^year$/i) {
753 0         0 %eRef = (
754             'object' => 'inanimate',
755             'gender' => 'masculine',
756             'root' => '',
757             'ends' => {
758             'n' => ['лет','год', ('года') x 3, ('лет') x 2 ],
759             'g' => ['лет','года', ('лет') x 5 ],
760             'd' => ['лет','году', ('годам') x 4, 'лет' ],
761             'a' => ['лет','год', ('года') x 3, ('лет') x 2 ],
762             'i' => ['лет','годом', ('годами') x 4, 'лет' ],
763             'p' => ['лет','годе', ('годах') x 4, 'лет' ],
764             },
765             'plural' => {'n'=>'годы', 'g'=>'лет', 'd'=>'годам', 'a'=>'годы', 'i'=>'годами', 'p'=>'годах'},
766             );
767              
768             }
769             elsif( $epilog =~/^month$/i) {
770 0         0 %eRef = (
771             'object' => 'inanimate',
772             'gender' => 'masculine',
773             'root' => 'месяц',
774             'ends' => {
775             'n' => ['ев','', ('а') x 3, ('ев') x 2 ],
776             'g' => ['ев','а', ('ев') x 5 ],
777             'd' => ['ам','у', ('ам') x 5 ],
778             'a' => ['ев','', ('а') x 3, ('ев') x 2 ],
779             'i' => ['ев','ем', ('ами') x 4, 'ев'],
780             'p' => ['ев','е', ('ах') x 4, 'ев'],
781             },
782             'plural' => {'n'=>'ы', 'g'=>'ев', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
783             );
784              
785             }
786             elsif( $epilog =~/^day$/i) {
787 0         0 %eRef = (
788             'object' => 'inanimate',
789             'gender' => 'masculine',
790             'root' => 'д',
791             'ends' => {
792             'n' => ['ней','ень', ('ня') x 3, ('ней') x 2 ],
793             'g' => ['ней','ня', ('ней') x 5 ],
794             'd' => ['ням','ню', ('ням') x 5 ],
795             'a' => ['ней','ень', ('ня') x 3, ('ней') x 2 ],
796             'i' => ['ней','нём', ('нями') x 4, 'ней'],
797             'p' => ['ней','не', ('нях') x 4, 'ней'],
798             },
799             'plural' => {'n'=>'ни', 'g'=>'ней', 'd'=>'ням', 'a'=>'ни', 'i'=>'нями', 'p'=>'нях'},
800             );
801              
802             }
803             elsif( $epilog =~/^hour$/i) {
804 0         0 %eRef = (
805             'object' => 'inanimate',
806             'gender' => 'masculine',
807             'root' => 'час',
808             'ends' => {
809             'n' => ['ов','', ('а') x 3, ('ов') x 2 ],
810             'g' => ['ов','а', ('ов') x 5 ],
811             'd' => ['ам','у', ('ам') x 5 ],
812             'a' => ['ов','', ('а') x 3, ('ов') x 2 ],
813             'i' => ['ов','ом', ('ами') x 4, 'ов'],
814             'p' => ['ов','е', ('ах') x 4, 'ов'],
815             },
816             'plural' => {'n'=>'ы', 'g'=>'ов', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
817             );
818              
819             }
820             elsif( $epilog =~/^min\.$/i) {
821 0         0 %eRef = (
822             'object' => 'inanimate',
823             'gender' => 'feminine',
824             'root' => 'минут',
825             'ends' => {
826             'n' => ['','а', ('ы') x 3, ('') x 2 ],
827             'g' => ['','ы', ('') x 5 ],
828             'd' => ['','е', ('ам') x 4, ''],
829             'a' => ['','у', ('ы') x 3, ('') x 2 ],
830             'i' => ['','ой', ('ами') x 4, ''],
831             'p' => ['','е', ('ах') x 4, ''],
832             },
833             'plural' => {'n'=>'ы', 'g'=>'', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
834             );
835              
836             }
837             elsif( $epilog =~/^sec\.$/i) {
838 0         0 %eRef = (
839             'object' => 'inanimate',
840             'gender' => 'feminine',
841             'root' => 'секунд',
842             'ends' => {
843             'n' => ['','а', ('ы') x 3, ('') x 2 ],
844             'g' => ['','ы', ('') x 5 ],
845             'd' => ['','е', ('ам') x 4, ''],
846             'a' => ['','у', ('ы') x 3, ('') x 2 ],
847             'i' => ['','ой', ('ами') x 4, ''],
848             'p' => ['','е', ('ах') x 4, ''],
849             },
850             'plural' => {'n'=>'ы', 'g'=>'', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
851             );
852              
853             }
854             elsif( $epilog =~/^meter$/i) {
855 0         0 %eRef = (
856             'object' => 'inanimate',
857             'gender' => 'masculine',
858             'root' => 'метр',
859             'ends' => {
860             'n' => ['ов','', ('а') x 3, ('ов') x 2 ],
861             'g' => ['ов','а', ('ов') x 5 ],
862             'd' => ['ов','у', ('ам') x 4, 'ов'],
863             'a' => ['ов','', ('а') x 3, ('ов') x 2 ],
864             'i' => ['ов','ом', ('ами') x 4, 'ов'],
865             'p' => ['ов','е', ('ах') x 4, 'ов'],
866             },
867             'plural' => {'n'=>'ы', 'g'=>'ов', 'd'=>'ам', 'a'=>'ы', 'i'=>'ами', 'p'=>'ах'},
868             );
869              
870             }
871             elsif( $epilog =~/^stamp$/i) {
872 0         0 %eRef = (
873             'object' => 'inanimate',
874             'gender' => 'feminine',
875             'root' => 'печат',
876             'ends' => {
877             'n' => ['ей','ь', ('и') x 3, ('ей') x 2 ],
878             'g' => ['ей','и', ('ей') x 5 ],
879             'd' => ['ей','и', ('ям') x 4, 'ей'],
880             'a' => ['ей','ь', ('и') x 3, ('ей') x 2 ],
881             'i' => ['ей','ью', ('ями') x 4, 'ей'],
882             'p' => ['ей','и', ('ях') x 4, 'ей'],
883             },
884             'plural' => {'n'=>'и', 'g'=>'ей', 'd'=>'ям', 'a'=>'и', 'i'=>'ями', 'p'=>'ях'},
885             );
886              
887             }
888              
889 555 50       1275 if( %eRef ) {
890 555         1123 $$object = $eRef{'object'};
891 555         890 $$gender = $eRef{'gender'};
892              
893             # To fix for plural '1' only
894 555 100       1556 if( $multi =~/^plural/i ) {
895 122 100       381 if( ref( \$eRef{'plural'}{ $case } ) eq 'SCALAR') {
    50          
896 92         210 $eRef{'ends'}{ $case }[1] = $eRef{'plural'}{ $case };
897             }
898             elsif( ref( $eRef{'plural'}{ $case } ) eq 'ARRAY') {
899 30         43 my $i = 0;
900 30         42 for( @{ $eRef{'plural'}{ $case } } ) {
  30         77  
901 60         130 $eRef{'ends'}{ $case }[ $i++ ][1] = $_;
902             }
903             }
904             }
905              
906 555         1000 $epilog = {};
907              
908 555 100       1466 if( ref( \$eRef{'root'} ) eq 'SCALAR') {
    50          
909 458         1137 $epilog->{'root'} = [ $eRef{'root'} ];
910 458         2525 $epilog->{'ends'}[0] = $eRef{'ends'}{ $case };
911             }
912             elsif( ref( $eRef{'root'} ) eq 'ARRAY') {
913 97         215 $epilog->{'root'} = $eRef{'root'};
914 97         703 $epilog->{'ends'} = $eRef{'ends'}{ $case };
915             }
916             }
917             else {
918 0         0 $epilog = {};
919             }
920              
921             }
922             elsif( ref($epilog) eq 'HASH' && exists( $epilog->{'root'} ) && exists( $epilog->{'ends'} ) ) {
923 17 50       42 if( exists $epilog->{'object'} ) {
924 17 50       55 $$object = $epilog->{'object'}=~/^\s*[a1-9]/i ? 'animate' : 'inanimate';
925             }
926              
927 17         57 $$gender = @{{'m'=>'masculine', 'f'=>'feminine', 'n'=>'neuter'}}{lc $1}
928 17 50 33     98 if exists( $epilog->{'gender'} ) && $epilog->{'gender'} =~/^\s*(m|f|n)/i;
929              
930             }
931             else {
932 1104         1877 $epilog = {};
933             }
934              
935 1676         3348 return $epilog;
936             }
937              
938             1;
939              
940             __END__