File Coverage

blib/lib/Lingua/RU/Inflect.pm
Criterion Covered Total %
statement 164 173 94.8
branch 109 132 82.5
condition 16 21 76.1
subroutine 37 37 100.0
pod 3 3 100.0
total 329 366 89.8


line stmt bran cond sub pod time code
1             package Lingua::RU::Inflect;
2              
3 6     6   171013 use warnings;
  6         18  
  6         337  
4 6     6   41 use strict;
  6         18  
  6         197  
5 6     6   1138 use utf8;
  6         31  
  6         43  
6              
7             =encoding utf8
8              
9             =head1 NAME
10              
11             Lingua::RU::Inflect - Inflect russian names.
12              
13             =head1 VERSION
14              
15             Version 0.04
16              
17             =head1 DESCRIPTION
18              
19             Lingua::RU::Inflect is a perl module
20             that provides Russian linguistic procedures
21             such as declension of given names (with some nouns and adjectives too),
22             and gender detection by given name.
23              
24             Choosing of proper forms of varying prepositions
25             which added in 0.02 now DEPRECATED,
26             use L instead.
27              
28             =cut
29              
30             our ($REVISION, $DATE);
31             ($REVISION) = q$Revision$ =~ /(\d+)/g;
32             ($DATE)
33             = q$Date$ =~ /: (\d+)\s*$/g;
34              
35              
36             BEGIN {
37 6     6   718 use Exporter ();
  6         13  
  6         1453  
38 6     6   14 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
39              
40             # set the version for version checking
41 6         11 $VERSION = 0.04;
42              
43 6         91 @ISA = qw(Exporter);
44 6         28 @EXPORT = qw(
45             inflect_given_name detect_gender_by_given_name
46             choose_preposition_by_next_word
47             );
48              
49             # exported package globals
50 6         28 @EXPORT_OK = qw(
51             NOMINATIVE GENITIVE DATIVE
52             ACCUSATIVE INSTRUMENTAL PREPOSITIONAL
53             %CASES
54             MASCULINE FEMININE
55             izo ko nado ob oto predo peredo podo so vo
56             );
57              
58 6         320 %EXPORT_TAGS = (
59             'subs' => [ qw(
60             inflect_given_name detect_gender_by_given_name
61             choose_preposition_by_next_word
62             ) ],
63             'short' => [ qw( izo ko nado ob oto predo peredo podo so vo ) ],
64             'genders' => [ qw( MASCULINE FEMININE ) ],
65             'cases' => [ qw(
66             NOMINATIVE GENITIVE DATIVE ACCUSATIVE INSTRUMENTAL PREPOSITIONAL
67             %CASES
68             ) ],
69             'all' => [ @EXPORT, @EXPORT_OK ],
70             )
71              
72             }
73              
74             # Cases
75             # Why I can't use loop?!
76             use constant {
77 6         917 NOMINATIVE => -1,
78             GENITIVE => 0,
79             DATIVE => 1,
80             ACCUSATIVE => 2,
81             INSTRUMENTAL => 3,
82             PREPOSITIONAL => 4,
83 6     6   40 };
  6         10  
84              
85             my @CASE_NAMES = qw(
86             NOMINATIVE GENITIVE DATIVE ACCUSATIVE INSTRUMENTAL PREPOSITIONAL
87             );
88             my @CASE_NUMBERS = ( -1 .. 4 );
89              
90 6     6   6198 use List::MoreUtils 'mesh';
  6         8555  
  6         801  
91             our %CASES = mesh @CASE_NAMES, @CASE_NUMBERS;
92              
93             # Gender
94             use constant {
95 6         6046 FEMININE => 0,
96             MASCULINE => 1,
97 6     6   59 };
  6         12  
98              
99             =head1 SYNOPSIS
100              
101             Inflects russian names which represented in UTF-8.
102              
103             Perhaps a little code snippet.
104              
105             use Lingua::RU::Inflect;
106              
107             my @name = qw/Петрова Любовь Степановна/;
108             # Transliteration of above line is: Petrova Lyubov' Stepanovna
109              
110             my $gender = detect_gender_by_given_name(@name);
111             # $gender == FEMININE
112              
113             my @genitive = inflect_given_name(GENITIVE, @name);
114             # $genitive == qw/Петровой Любови Степановны/;
115             # Transliteration of above line is: Petrovoy Lyubovi Stepanovny
116              
117             =head1 TO DO
118              
119             1. Inflect any nouns, any words, anything...
120             2. Move preposition related function to L
121              
122             =head1 EXPORT
123              
124             Function C and
125             C are exported by default.
126              
127             Also you can export only case names:
128              
129             use Lingua::RU::Inflect qw/:cases/;
130              
131             Or only subs and genders
132              
133             use Lingua::RU::Inflect qw/:subs :genders/;
134              
135             Or only short aliases for subs
136              
137             use Lingua::RU::Inflect qw/:short/;
138              
139             Or everything: subs, aliases, genders and case names:
140              
141             use Lingua::RU::Inflect qw/:all/; # or
142             use Lingua::RU::Inflect qw/:cases :genders :subs :short/;
143              
144             =head1 FUNCTIONS
145              
146             =head2 detect_gender_by_given_name
147              
148             Try to detect gender by name. Up to three arguments expected:
149             lastname, firstname, patronym.
150              
151             Return C, C for successful detection
152             or C when function can't detect gender.
153              
154             =head3 Detection rules
155              
156             When name match some rule, rest of rules are ignored.
157              
158             =over 4
159              
160             =item 1
161              
162             Patronym (russian отчество — otchestvo), if presented, gives unambiguous
163             detection rules: feminine patronyms ends with “na”, masculine ones ends
164             with “ich” and “ych”.
165              
166             =item 2
167              
168             Most of russian feminine firstnames ends to vowels “a” and “ya”.
169             Most of russian masculine firstnames ends to consonants.
170              
171             There's exists exceptions for both rules: feminine names such as russian
172             name Lubov' (Любовь) and foreign names Ruf' (Руфь), Rachil' (Рахиль)
173             etc. Masculine names also often have affectionate diminutive forms:
174             Alyosha (Алёша) for Alexey (Алексей), Kolya (Коля) for Nickolay
175             (Николай) etc. Some affectionate diminutive names are ambiguous: Sasha
176             (Саша) is diminutive name for feminine name Alexandra (Александра) and
177             for masculine name Alexander (Александр), Zhenya (Женя) is diminutive
178             name for feminine name Eugenia (Евгения) and for masculine name Eugene
179             (Евгений) etc.
180              
181             These exceptions are processed.
182              
183             When got ambiguous result, function try to use next rule.
184              
185             =item 3
186              
187             Most of russian lastnames derived from possessive nouns (and names).
188             Feminine forms of these lastnames ends to “a”.
189             Some lastnames derived from adjectives. Feminine forms of these
190             lastnames ends to “ya”.
191              
192             =back
193              
194             =cut
195              
196             sub detect_gender_by_given_name {
197 43     43 1 977 my ( $lastname, $firstname, $patronym ) = @_;
198 43   100     99 map { $_ ||= '' } ( $lastname, $firstname, $patronym );
  129         510  
199 43         75 my $ambiguous = 0;
200              
201             # Detect by patronym
202 43 100       145 return FEMININE if $patronym =~ /на$/;
203 39 100       191 return MASCULINE if $patronym =~ /[иы]ч$/;
204              
205             # Detect by firstname
206             # Drop all names except first
207 31         133 $firstname =~ s/[\s\-].*//;
208              
209             # Process exceptions
210 2742 100       18988 map {
211 31         79 return MASCULINE if $firstname eq $_;
212             } ( &_MASCULINE_NAMES );
213              
214 2198 100       12318 map {
215 23         228 return FEMININE if $firstname eq $_;
216             } ( &_FEMININE_NAMES );
217              
218 108 100 50     355 map {
219 18         123 $ambiguous++ && last if $firstname eq $_;
220             } ( &_AMBIGUOUS_NAMES );
221              
222 18 100       58 unless ( $ambiguous ) {
223             # Feminine firstnames ends to vowels
224 12 100       101 return FEMININE if $firstname =~ /[ая]$/;
225             # Masculine firstnames ends to consonants
226 7 50       60 return MASCULINE if $firstname !~ /[аеёиоуыэюя]$/;
227             } # unless
228              
229             # Detect by lastname
230             # possessive names
231 6 100       77 return FEMININE if $lastname =~ /(ев|ин|ын|ёв|ов)а$/;
232 4 100       46 return MASCULINE if $lastname =~ /(ев|ин|ын|ёв|ов)$/;
233             # adjectives
234 2 50       22 return FEMININE if $lastname =~ /(ая|яя)$/;
235 2 50       15 return MASCULINE if $lastname =~ /(ий|ый)$/;
236              
237             # Unknown or ambiguous name
238 2         11 return undef;
239             }
240              
241             =head2 _inflect_given_name
242              
243             Inflects name of given gender to given case.
244             Up to 5 arguments expected:
245             I, I, I, I, I.
246             I, I, I must be in Nominative.
247              
248             Returns list which contains inflected I, I, I.
249              
250             =cut
251              
252             sub _inflect_given_name {
253 22     22   38 my $gender = shift;
254 22         61 my $case = shift;
255              
256 22 50       69 return @_ if $case eq NOMINATIVE;
257             return
258 22 50 33     129 if $case < GENITIVE
259             || $case > PREPOSITIONAL;
260              
261 22         48 my ( $lastname, $firstname, $patronym ) = @_;
262 22   100     36 map { $_ ||= '' } ( $lastname, $firstname, $patronym );
  66         277  
263              
264             # Patronyms
265             {
266 22 100       33 last unless $patronym;
  22         71  
267              
268 7 100       31 last if $patronym =~ s/на$/qw(ны не ну ной не)[$case]/e;
  2         11  
269 5 100       249 last if $patronym =~ s/ыч$/qw(ыча ычу ыча ычем ыче)[$case]/e;
  1         10  
270 4         25 $patronym =~ s/ич$/qw(ича ичу ича ичем иче)[$case]/e;
  4         18  
271 4         113 $patronym =~ s/(Иль|Кузьм|Фом)ичем$/$1ичом/;
272             }
273              
274             # Firstnames
275             {
276 22 100       39 last unless $firstname;
  22         53  
277              
278             # Exceptions
279 20         53 $firstname =~ s/^Пётр$/Петр/;
280              
281             # Names which ends to vowels “o”, “yo”, “u”, “yu”, “y”, “i”, “e”, “ye”
282             # and to pairs of vowels except “yeya”, “iya”
283             # can not be inflected
284              
285 6 100   6   113 last if $firstname =~ /[еёиоуыэю]$/i;
  6         15  
  6         92  
  20         217  
286 19 50       148 last if $firstname =~ /[аеёиоуыэюя]а$/i;
287 19 50       80 last if $firstname =~ /[аёоуыэюя]я$/i;
288             last
289             if (
290 19 100 100     2409 !defined $gender
      100        
291             || $gender == FEMININE
292             )
293             && $firstname =~ /[бвгджзклмнйпрстфхцчшщ]$/i;
294              
295 18 50       57 last if $firstname =~ s/ия$/qw(ии ии ию ией ие)[$case]/e;
  0         0  
296 18 100       74 last if $firstname =~ s/([гжйкхчшщ])а$/$1.qw(и е у ой е)[$case]/e;
  2         954  
297 16 100       73 last if $firstname =~ s/а$/qw(ы е у ой е)[$case]/e;
  6         37  
298 10 100       34 last if $firstname =~ s/мя$/qw(мени мени мя менем мени)[$case]/e; # common nouns such as “Imya” (Name)
  1         6  
299 9 100       41 last if $firstname =~ s/я$/qw(и е ю ей е)[$case]/e;
  3         25  
300 6 100       27 last if $firstname =~ s/й$/qw(я ю я ем е)[$case]/e;
  1         8  
301              
302             # Same endings, but different gender
303 5 100       20 if ( $gender == MASCULINE ) {
    50          
304 4 50       14 last if $firstname =~ s/ь$/qw(я ю я ем е)[$case]/e;
  0         0  
305             }
306             elsif ( $gender == FEMININE ) {
307 1 50       8 last if $firstname =~ s/ь$/qw(и и ь ью и)[$case]/e;
  1         8  
308             }
309              
310             # Rest of names which ends to consonants
311 4         14 $firstname .= qw(а у а ом е)[$case];
312             } # Firstnames
313              
314             # Lastnames
315             {
316 22 100       43 last unless $lastname;
  22         65  
317 20 100       51 last unless defined $gender;
318              
319             # Indeclinable
320 19 100       89 last if $lastname =~ /[еёиоуыэю]$/i;
321 18 50       97 last if $lastname =~ /[аеёиоуыэюя]а$/i;
322             # Lastnames such as “Belaya” and “Sinyaya”
323             # which ends to “aya” and “yaya” must be inflected
324 18 50       67 last if $lastname =~ /[ёоуыэю]я$/i;
325 18 100       70 last if $lastname =~ /[иы]х$/i;
326              
327             # Feminine lastnames
328             last
329 17 100 66     101 if $lastname =~ /(ин|ын|ев|ёв|ов)а$/
330 5         113 && $lastname =~ s/а$/qw(ой ой у ой ой)[$case]/e;
331             # TODO Does not process usual worls: Podkova, Sova etc
332             # TODO Decide/search what can I do with ambigous names: Mashina, Vagina etc
333              
334             # And masculine ones
335             last
336 12 100 66     93 if $lastname =~ /(ин|ын|ев|ёв|ов)$/
337             && ( $lastname .= qw(а у а ым е)[$case] );
338              
339             # As adjectives
340 8 50       40 last if $lastname =~ s/ая$/qw(ой ой ую ой ой)[$case]/e;
  0         0  
341 8 50       34 last if $lastname =~ s/яя$/qw(ей ей юю ей ей)[$case]/e;
  0         0  
342 8 100       29 last if $lastname =~ s/кий$/qw(кого кому кого ким ком)[$case]/e;
  1         7  
343 7 50       19 last if $lastname =~ s/ий$/qw(его ему его им ем)[$case]/e;
  0         0  
344 7 50       24 last if $lastname =~ s/ый$/qw(ого ому ого ым ом)[$case]/e;
  0         0  
345 7 100       25 last if $lastname =~ s/ой$/qw(ого ому ого ым ом)[$case]/e;
  1         13  
346              
347             # Rest of masculine lastnames
348 6 100       19 if ( $gender == MASCULINE ) {
349 4 100       16 last if $lastname =~ s/а$/qw(ы е у ой е)[$case]/e;
  1         7  
350 3 100       15 last if $lastname =~ s/мя$/qw(мени мени мя менем мени)[$case]/e;
  1         6  
351 2 50       11 last if $lastname =~ s/я$/qw(и е ю ёй е)[$case]/e;
  0         0  
352 2 50       8 last if $lastname =~ s/й$/qw(я ю й ем е)[$case]/e;
  0         0  
353 2 50       6 last if $lastname =~ s/ь$/qw(я ю я ем е)[$case]/e;
  0         0  
354 2         9 $lastname .= qw(а у а ом е)[$case];
355             } # if
356             } # Lastnames
357              
358 22         310 return ( $lastname, $firstname, $patronym );
359             } # sub _inflect_given_name
360              
361              
362             =head2 inflect_given_name
363              
364             Detects gender by given name and inflect parts of this name.
365              
366             Expects for up to 4 arguments:
367             I, I, I, I
368              
369             Available I are: C, C, C,
370             C, C, C.
371              
372             It returns list which contains
373             inflected I, I, I
374              
375             =cut
376              
377             sub inflect_given_name {
378 23     23 1 123 my $case = shift;
379 23 100       139 return @_ if $case eq NOMINATIVE;
380 22         69 my @name = _inflect_given_name(
381             detect_gender_by_given_name( @_ ), $case, @_
382             );
383             } # sub inflect_given_name
384              
385              
386             =head2 choose_preposition_by_next_word
387              
388             Chooses preposition by next word and returns chosen preposition.
389              
390             Expects 2 arguments: I and I.
391             I should be string with shortest of possible values.
392             Available values of I are:
393             C<'в'>, C<'из'>, C<'к'>, C<'над'>, C<'о'>, C<'от'>, C<'пред'>, C<'перед'>,
394             C<'под'> and C<'с'>.
395              
396             There is an aliases for calling this subroutine with common preposition:
397              
398             =head3 izo
399              
400             C is an alias for C
401              
402             =head3 ko
403              
404             C is an alias for C
405              
406             =head3 nado
407              
408             C is an alias for C
409              
410             =head3 ob
411              
412             C is an alias for C
413              
414             =head3 oto
415              
416             C is an alias for C
417              
418             =head3 podo
419              
420             C is an alias for C
421              
422             =head3 predo
423              
424             C is an alias for C
425              
426             =head3 peredo
427              
428             C is an alias for C
429              
430             =head3 so
431              
432             C is an alias for C
433              
434             =head3 vo
435              
436             C is an alias for C
437              
438             These aliases are not exported by default. They can be expored with tags C<:short> or C<:all>.
439              
440             Examples of code with these aliases:
441              
442             use Lingua::RU::Inflect qw/:short/;
443              
444             map {
445             print ob, $_;
446             } qw(
447             арбузе баране всём Елене ёлке игле йоде
448             мне многом огне паре ухе юге яблоке
449             );
450              
451             map {
452             print so, $_;
453             } qw(
454             огнём водой
455             зарёй зноем зрением зябликом
456             садом светом слоном спичками ссылкой
457             Стёпой стаканом сухарём сэром топором
458             жарой жбаном жратвой жуком
459             шаром шкафом шлангом шубой
460             );
461              
462             =cut
463              
464             sub choose_preposition_by_next_word ($$) {
465 47 50   47 1 277 my $preposition = lc shift or return undef;
466 47 50       13637 local $_ = lc shift or return undef;
467              
468             # Nested subroutine
469             local *_check_instrumental = sub {
470 13     13   30 for my $word (qw( льдом льном мной мною )) {
471 42 100       308 return $_[0] . 'о' if $word eq $_[1]
472             }
473 4         83 return $_[0]
474 47         226 }; # _check_instrumental
475              
476             # preposition => function
477             # TODO Check by dictionary
478             my %GRAMMAR = (
479             'в' => sub {
480 7     7   18 for my $word (qw( все всём мне мно )) {
481 27 100       496 return 'во' if /^$word/
482             }
483             /^[вф][^аеёиоуыэюя]/
484 5 100       132 ? 'во'
485             : 'в'
486             },
487             'из' => sub {
488 3     3   10 for my $word (qw( всех )) {
489 3 100       82 return 'изо' if $word eq $_
490             }
491             'из'
492 1         21 },
493             'к' => sub {
494 5     5   32 for my $word (qw( всем мне мно )) {
495 11 100       253 return 'ко' if /^$word/
496             }
497             'к'
498 1         23 },
499             'о' => sub {
500 5     5   12 for my $word (qw( всех всем всём мне )) {
501 20 100       75 return 'обо' if $word eq $_
502             }
503             return
504 4 100       96 /^[аиоуыэ]/
505             ? 'об'
506             : 'о'
507             },
508             'от' => sub {
509 3     3   7 for my $word (qw( всех )) {
510 3 100       49 return 'ото' if $word eq $_
511             }
512             'от'
513 1         21 },
514             'с' => sub {
515 11 100   11   55 return 'со' if /^мно/;
516             return
517 10 100       261 /^[жзсш][^аеёиоуыэюя]/i
518             ? 'со'
519             : 'с'
520             },
521             # Same rules:
522 3     3   37 'над' => sub { _check_instrumental('над', $_) },
523 4     4   11 'под' => sub { _check_instrumental('под', $_) },
524 3     3   8 'перед' => sub { _check_instrumental('перед', $_) },
525 3     3   8 'пред' => sub { _check_instrumental('пред', $_) },
526 47         873 );
527              
528 47 50       186 return undef unless exists $GRAMMAR{$preposition};
529              
530 47         123 $GRAMMAR{$preposition}->($_);
531              
532             } # sub choose_preposition_by_next_word
533              
534             # Aliases
535 1     1   11 *izo = sub { choose_preposition_by_next_word 'из', shift };
536 1     1   4 *ko = sub { choose_preposition_by_next_word 'к', shift };
537 1     1   4 *nado = sub { choose_preposition_by_next_word 'над', shift };
538 1     1   7 *ob = sub { choose_preposition_by_next_word 'о', shift };
539 1     1   5 *oto = sub { choose_preposition_by_next_word 'от', shift };
540 1     1   13 *predo = sub { choose_preposition_by_next_word 'пред', shift };
541 1     1   5 *peredo = sub { choose_preposition_by_next_word 'перед', shift };
542 1     1   5 *podo = sub { choose_preposition_by_next_word 'под', shift };
543 1     1   4 *so = sub { choose_preposition_by_next_word 'с', shift };
544 1     1   5 *vo = sub { choose_preposition_by_next_word 'в', shift };
545              
546             # Exceptions:
547              
548             # Masculine names which ends to vowels “a” and “ya”
549             sub _MASCULINE_NAMES () {
550 31     31   573 return qw(
551             Аба Азарья Акива Аккужа Аникита Алёша Андрюха Андрюша Аса Байгужа
552             Вафа Ваня Вася Витя Вова Володя Габдулла Габидулла Гаврила Гадельша
553             Гайнулла Гайса Гайфулла Галиулла Гарри Гата Гдалья Гийора Гиля Гошеа
554             Данила Джиханша Дима Зайнулла Закария Зия Зосима Зхарья Зыя Идельгужа
555             Иешуа Изя Ильмурза Илья Иона Исайя Иуда Йегошуа Йегуда Йедидья Карагужа Коля
556             Костя Кузьма Лёха Лёша Лука Ларри Марданша Микола Мирза Миха Миша Мойша Моня
557             Муртаза Муса Мусса Мустафа Никита Нэта Нэхэмья Овадья Петя Птахья
558             Рахматулла Риза Рома Савва Сафа Серёга Серёжа Сила Симха Сэадья Товия
559             Толя Федя Фима Фока Фома Хамза Хананья Цфанья Шалва Шахна Шрага Эзра
560             Элиша Элькана Юмагужа Ярулла Яхья Яша
561             )
562             }
563              
564             # Feminine names which ends to consonants
565             sub _FEMININE_NAMES () {
566 23     23   556 return qw(
567             Айгуль Айгюль Айзиряк Айрис Альфинур Асылгюль Бадар Бадиян Банат Бедер
568             Бибикамал Бибинур Гайниджамал Гайникамал Гаухар Гиффат Гулендем
569             Гульбадиян Гульдар Гульджамал Гульджихан Гульехан Гульзар Гулькей
570             Гульназ Гульнар Гульнур Гульсем Гульсесек Гульсибар Гульчачак Гульшат
571             Гульшаян Гульюзум Гульямал Гюзель Джамал Джаухар Джихан Дильбар Диляфруз
572             Зайнаб Зайнап Зейнаб Зубарджат Зуберьят Ильсёяр Камяр Карасес Кейт
573             Кэролайн Кэт Кэтрин Кямар Любовь Ляйсан Магинур Магруй Марьям Минджихан
574             Минлегюль Миньеган Наркас Нинель Нурджиган Райхан Раушан Рахель Рахиль
575             Рут Руфь Рэйчел Сагадат Сагдат Сарбиназ Сарвар Сафин Сахибджамал Сулпан
576             Сумбуль Сурур Сюмбель Сясак Тамар Тансулпан Умегульсум Уммегюльсем
577             Фарваз Фархинур Фирдаус Хаджар Хажар Хаят Хуршид Чечек Чулпан Шамсинур
578             Элис Энн Юдифь Юндуз Ямал
579             )
580             }
581              
582             # Ambiguous names which can be masculine and feminine
583             sub _AMBIGUOUS_NAMES () {
584 18     18   63 return qw(
585             Валя Женя Мина Паша Саша Шура
586             )
587             }
588              
589             =head1 AUTHOR
590              
591             Alexander Sapozhnikov, C<< >>
592              
593             =head1 BUGS
594              
595             Please report any bugs or feature requests
596             to C, or through the web interface
597             at L.
598             I will be notified, and then
599             you'll automatically be notified of progress on your bug as I make changes.
600              
601             =head1 SUPPORT
602              
603             You can find documentation for this module with the perldoc command.
604              
605             perldoc Lingua::RU::Inflect
606              
607             You can also look for information at:
608              
609             =over 4
610              
611             =item * RT: CPAN's request tracker
612              
613             L
614              
615             =item * AnnoCPAN: Annotated CPAN documentation
616              
617             L
618              
619             =item * CPAN Ratings
620              
621             L
622              
623             =item * Search CPAN
624              
625             L
626              
627             =item * Public repository at github
628              
629             L
630              
631             =back
632              
633             =head1 SEE ALSO
634              
635             Russian translation of this documentation available
636             at F
637              
638             =head1 ACKNOWLEDGEMENTS
639              
640             L (in Russian) for rules of declension.
641              
642             =head1 COPYRIGHT & LICENSE
643              
644             Copyright 2009-2012 Alexander Sapozhnikov.
645              
646             This program is free software; you can redistribute it and/or modify it
647             under the terms of either: the GNU General Public License as published
648             by the Free Software Foundation; or the Artistic License.
649              
650             See http://dev.perl.org/licenses/ for more information.
651              
652             =cut
653              
654             1;