Completed
Push — master ( 708613...9f9161 )
by f
01:19
created

FirstNamesInflection::getCases()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 43
Code Lines 30

Duplication

Lines 30
Ratio 69.77 %

Importance

Changes 0
Metric Value
cc 8
eloc 30
nc 12
nop 2
dl 30
loc 43
rs 5.3846
c 0
b 0
f 0
1
<?php
2
namespace morphos\Russian;
3
4
use morphos\S;
5
6
/**
7
 * Rules are from: http://www.imena.org/decl_mn.html
8
 * and http://www.imena.org/decl_fn.html
9
 */
10
class FirstNamesInflection extends \morphos\NamesInflection implements Cases
11
{
12
    use RussianLanguage, CasesHelper;
13
14
    protected static $exceptions = array(
15
        'лев' => array(
16
            self::IMENIT => 'Лев',
17
            self::RODIT => 'Льва',
18
            self::DAT => 'Льву',
19
            self::VINIT => 'Льва',
20
            self::TVORIT => 'Львом',
21
            self::PREDLOJ => 'о Льве',
22
        ),
23
        'павел' => array(
24
            self::IMENIT => 'Павел',
25
            self::RODIT => 'Павла',
26
            self::DAT => 'Павлу',
27
            self::VINIT => 'Павла',
28
            self::TVORIT => 'Павлом',
29
            self::PREDLOJ => 'о Павле',
30
        )
31
    );
32
33
    protected static $menNames = array(
34
        'абрам',
35
        'аверьян',
36
        'авраам',
37
        'агафон',
38
        'адам',
39
        'азар',
40
        'акакий',
41
        'аким',
42
        'аксён',
43
        'александр',
44
        'алексей',
45
        'альберт',
46
        'анатолий',
47
        'андрей',
48
        'андрон',
49
        'антип',
50
        'антон',
51
        'аполлон',
52
        'аристарх',
53
        'аркадий',
54
        'арнольд',
55
        'арсений',
56
        'арсентий',
57
        'артем',
58
        'артём',
59
        'артемий',
60
        'артур',
61
        'аскольд',
62
        'афанасий',
63
        'богдан',
64
        'борис',
65
        'борислав',
66
        'бронислав',
67
        'вадим',
68
        'валентин',
69
        'валерий',
70
        'варлам',
71
        'василий',
72
        'венедикт',
73
        'вениамин',
74
        'веньямин',
75
        'венцеслав',
76
        'виктор',
77
        'виген',
78
        'вилен',
79
        'виталий',
80
        'владилен',
81
        'владимир',
82
        'владислав',
83
        'владлен',
84
        'вова',
85
        'всеволод',
86
        'всеслав',
87
        'вячеслав',
88
        'гавриил',
89
        'геннадий',
90
        'георгий',
91
        'герман',
92
        'глеб',
93
        'григорий',
94
        'давид',
95
        'даниил',
96
        'данил',
97
        'данила',
98
        'демьян',
99
        'денис',
100
        'димитрий',
101
        'дмитрий',
102
        'добрыня',
103
        'евгений',
104
        'евдоким',
105
        'евсей',
106
        'егор',
107
        'емельян',
108
        'еремей',
109
        'ермолай',
110
        'ерофей',
111
        'ефим',
112
        'захар',
113
        'иван',
114
        'игнат',
115
        'игорь',
116
        'илларион',
117
        'иларион',
118
        'илья',
119
        'иосиф',
120
        'казимир',
121
        'касьян',
122
        'кирилл',
123
        'кондрат',
124
        'константин',
125
        'кузьма',
126
        'лавр',
127
        'лаврентий',
128
        'лазарь',
129
        'ларион',
130
        'лев',
131
        'леонард',
132
        'леонид',
133
        'лука',
134
        'максим',
135
        'марат',
136
        'мартын',
137
        'матвей',
138
        'мефодий',
139
        'мирон',
140
        'михаил',
141
        'моисей',
142
        'назар',
143
        'никита',
144
        'николай',
145
        'олег',
146
        'осип',
147
        'остап',
148
        'павел',
149
        'панкрат',
150
        'пантелей',
151
        'парамон',
152
        'пётр',
153
        'петр',
154
        'платон',
155
        'потап',
156
        'прохор',
157
        'роберт',
158
        'ростислав',
159
        'савва',
160
        'савелий',
161
        'семён',
162
        'семен',
163
        'сергей',
164
        'сидор',
165
        'спартак',
166
        'тарас',
167
        'терентий',
168
        'тимофей',
169
        'тимур',
170
        'тихон',
171
        'ульян',
172
        'фёдор',
173
        'федор',
174
        'федот',
175
        'феликс',
176
        'фирс',
177
        'фома',
178
        'харитон',
179
        'харлам',
180
        'эдуард',
181
        'эммануил',
182
        'эраст',
183
        'юлиан',
184
        'юлий',
185
        'юрий',
186
        'яков',
187
        'ян',
188
        'ярослав',
189
    );
190
191
    protected static $womenNames = array(
192
        'авдотья',
193
        'аврора',
194
        'агата',
195
        'агния',
196
        'агриппина',
197
        'ада',
198
        'аксинья',
199
        'алевтина',
200
        'александра',
201
        'алёна',
202
        'алена',
203
        'алина',
204
        'алиса',
205
        'алла',
206
        'альбина',
207
        'амалия',
208
        'анастасия',
209
        'ангелина',
210
        'анжела',
211
        'анжелика',
212
        'анна',
213
        'антонина',
214
        'анфиса',
215
        'арина',
216
        'белла',
217
        'божена',
218
        'валентина',
219
        'валерия',
220
        'ванда',
221
        'варвара',
222
        'василина',
223
        'василиса',
224
        'вера',
225
        'вероника',
226
        'виктория',
227
        'виола',
228
        'виолетта',
229
        'вита',
230
        'виталия',
231
        'владислава',
232
        'власта',
233
        'галина',
234
        'глафира',
235
        'дарья',
236
        'диана',
237
        'дина',
238
        'ева',
239
        'евгения',
240
        'евдокия',
241
        'евлампия',
242
        'екатерина',
243
        'елена',
244
        'елизавета',
245
        'ефросиния',
246
        'ефросинья',
247
        'жанна',
248
        'зиновия',
249
        'злата',
250
        'зоя',
251
        'ивонна',
252
        'изольда',
253
        'илона',
254
        'инга',
255
        'инесса',
256
        'инна',
257
        'ирина',
258
        'ия',
259
        'капитолина',
260
        'карина',
261
        'каролина',
262
        'кира',
263
        'клавдия',
264
        'клара',
265
        'клеопатра',
266
        'кристина',
267
        'ксения',
268
        'лада',
269
        'лариса',
270
        'лиана',
271
        'лидия',
272
        'лилия',
273
        'лина',
274
        'лия',
275
        'лора',
276
        'любава',
277
        'любовь',
278
        'людмила',
279
        'майя',
280
        'маргарита',
281
        'марианна',
282
        'мариетта',
283
        'марина',
284
        'мария',
285
        'марья',
286
        'марта',
287
        'марфа',
288
        'марьяна',
289
        'матрёна',
290
        'матрена',
291
        'матрона',
292
        'милена',
293
        'милослава',
294
        'мирослава',
295
        'муза',
296
        'надежда',
297
        'настасия',
298
        'настасья',
299
        'наталия',
300
        'наталья',
301
        'нелли',
302
        'ника',
303
        'нина',
304
        'нинель',
305
        'нонна',
306
        'оксана',
307
        'олимпиада',
308
        'ольга',
309
        'пелагея',
310
        'полина',
311
        'прасковья',
312
        'раиса',
313
        'рената',
314
        'римма',
315
        'роза',
316
        'роксана',
317
        'руфь',
318
        'сарра',
319
        'светлана',
320
        'серафима',
321
        'снежана',
322
        'софья',
323
        'софия',
324
        'стелла',
325
        'степанида',
326
        'стефания',
327
        'таисия',
328
        'таисья',
329
        'тамара',
330
        'татьяна',
331
        'ульяна',
332
        'устиния',
333
        'устинья',
334
        'фаина',
335
        'фёкла',
336
        'фекла',
337
        'феодора',
338
        'хаврония',
339
        'христина',
340
        'эвелина',
341
        'эдита',
342
        'элеонора',
343
        'элла',
344
        'эльвира',
345
        'эмилия',
346
        'эмма',
347
        'юдифь',
348
        'юлиана',
349
        'юлия',
350
        'ядвига',
351
        'яна',
352
        'ярослава',
353
    );
354
355
    public static function isMutable($name, $gender = null)
356
    {
357
        //var_dump(S::upper(S::slice($name, -1)));
358
        $name = S::lower($name);
359
        if ($gender === null) {
360
            $gender = self::detectGender($name);
361
        }
362
        // man rules
363
        if ($gender === self::MALE) {
364
            // soft consonant
365
            if (S::lower(S::slice($name, -1)) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
366
                return true;
367
            } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, array('й', /*'Ч', 'Щ'*/)))) { // hard consonant
368
                return true;
369
            } elseif (S::slice($name, -1) == 'й') {
370
                return true;
371
            }
372
        }
373
374
        // common rules
375 View Code Duplication
        if ((in_array(S::slice($name, -1), array('а', 'я')) && !self::isVowel(S::slice($name, -2, -1))) || in_array(S::slice($name, -2), array('ия', 'ья', 'ея'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
376
            return true;
377
        }
378
379
        return false;
380
    }
381
382 View Code Duplication
    public static function detectGender($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383
    {
384
        $name = S::lower($name);
385
        if (in_array($name, self::$menNames)) {
386
            return self::MALE;
387
        } elseif (in_array($name, self::$womenNames)) {
388
            return self::FEMALE;
389
        }
390
391
        $man = $woman = 0;
392
        $last1 = S::slice($name, -1);
393
        $last2 = S::slice($name, -2);
394
        $last3 = S::slice($name, -3);
395
396
        // try to detect gender by some statistical rules
397
        //
398
        if ($last1 == 'й') {
399
            $man += 0.9;
400
        }
401
        if ($last1 == 'ь') {
402
            $man += 0.02;
403
        }
404
        if (in_array($last1, self::$consonants)) {
405
            $man += 0.01;
406
        }
407
        if (in_array($last2, array('он', 'ов', 'ав', 'ам', 'ол', 'ан', 'рд', 'мп'))) {
408
            $man += 0.3;
409
        }
410
        if (in_array($last2, array('вь', 'фь', 'ль'))) {
411
            $woman += 0.1;
412
        }
413
        if (in_array($last2, array('ла'))) {
414
            $woman += 0.04;
415
        }
416
        if (in_array($last2, array('то', 'ма'))) {
417
            $man += 0.01;
418
        }
419
        if (in_array($last3, array('лья', 'вва', 'ока', 'ука', 'ита'))) {
420
            $man += 0.2;
421
        }
422
        if (in_array($last3, array('има'))) {
423
            $woman += 0.15;
424
        }
425
        if (in_array($last3, array('лия', 'ния', 'сия', 'дра', 'лла', 'кла', 'опа'))) {
426
            $woman += 0.5;
427
        }
428
        if (in_array(S::slice($name, -4), array('льда', 'фира', 'нина', 'лита', 'алья'))) {
429
            $woman += 0.5;
430
        }
431
432
        return $man == $woman ? null
433
            : ($man > $woman ? self::MALE : self::FEMALE);
434
    }
435
436
    public static function getCases($name, $gender = null)
437
    {
438
        $name = S::lower($name);
439
440
        // common rules for ия and я
441 View Code Duplication
        if (S::slice($name, -2) == 'ия') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
            $prefix = S::name(S::slice($name, 0, -1));
443
            return array(
444
                self::IMENIT => $prefix.'я',
445
                self::RODIT => $prefix.'и',
446
                self::DAT => $prefix.'и',
447
                self::VINIT => $prefix.'ю',
448
                self::TVORIT => $prefix.'ей',
449
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
450
            );
451
        } elseif (S::slice($name, -1) == 'я') {
452
            $prefix = S::name(S::slice($name, 0, -1));
453
            return array(
454
                self::IMENIT => $prefix.'я',
455
                self::RODIT => $prefix.'и',
456
                self::DAT => $prefix.'е',
457
                self::VINIT => $prefix.'ю',
458
                self::TVORIT => $prefix.'ей',
459
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
460
            );
461
        }
462
463
        if ($gender === null) {
464
            $gender = self::detectGender($name);
465
        }
466 View Code Duplication
        if ($gender == self::MALE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
467
            if (($result = self::getCasesMan($name)) !== null) {
468
                return $result;
469
            }
470
        } elseif ($gender == self::FEMALE) {
471
            if (($result = self::getCasesWoman($name)) !== null) {
472
                return $result;
473
            }
474
        }
475
476
        $name = S::name($name);
477
        return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT), $name) + array(self::PREDLOJ => self::choosePrepositionByFirstLetter($name, 'об', 'о').' '.$name);
478
    }
479
480
    protected static function getCasesMan($name)
481
    {
482
        // special cases for Лев, Павел
483
        if (isset(self::$exceptions[$name])) {
484
            return self::$exceptions[$name];
485
        } elseif (in_array(S::slice($name, -1), array_diff(self::$consonants, array('й', /*'Ч', 'Щ'*/)))) { // hard consonant
486 View Code Duplication
			if (in_array(S::slice($name, -2), ['ек', 'ёк'])) // Витек, Санек
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
487
				$prefix = S::name(S::slice($name, 0, -2)).'ьк';
488
			else
489
				$prefix = S::name($name);
490
            return array(
491
                self::IMENIT => S::name($name),
492
                self::RODIT => $prefix.'а',
493
                self::DAT => $prefix.'у',
494
                self::VINIT => $prefix.'а',
495
                self::TVORIT => RussianLanguage::isHissingConsonant(S::slice($name, -1)) || S::slice($name, -1) == 'ц' ? $prefix.'ем' : $prefix.'ом',
496
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
497
            );
498
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) { // soft consonant
499
            $prefix = S::name(S::slice($name, 0, -1));
500
            return array(
501
                self::IMENIT => $prefix.'ь',
502
                self::RODIT => $prefix.'я',
503
                self::DAT => $prefix.'ю',
504
                self::VINIT => $prefix.'я',
505
                self::TVORIT => $prefix.'ем',
506
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
507
            );
508
        } elseif (in_array(S::slice($name, -2), array('ай', 'ей', 'ой', 'уй', 'яй', 'юй', 'ий'))) {
509
            $prefix = S::name(S::slice($name, 0, -1));
510
            $postfix = S::slice($name, -2) == 'ий' ? 'и' : 'е';
511
            return array(
512
                self::IMENIT => $prefix.'й',
513
                self::RODIT => $prefix.'я',
514
                self::DAT => $prefix.'ю',
515
                self::VINIT => $prefix.'я',
516
                self::TVORIT => $prefix.'ем',
517
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.$postfix,
518
            );
519
        } elseif (S::slice($name, -1) == 'а' && self::isConsonant($before = S::slice($name, -2, -1)) && !in_array($before, array(/*'г', 'к', 'х', */'ц'))) {
520
            $prefix = S::name(S::slice($name, 0, -1));
521
            $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, array('г', 'к', 'х'))) ? 'и' : 'ы';
522
            return array(
523
                self::IMENIT => $prefix.'а',
524
                self::RODIT => $prefix.$postfix,
525
                self::DAT => $prefix.'е',
526
                self::VINIT => $prefix.'у',
527
                self::TVORIT => $prefix.'ой',
528
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
529
            );
530 View Code Duplication
        } elseif (S::slice($name, -2) == 'ло' || S::slice($name, -2) == 'ко') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531
            $prefix = S::name(S::slice($name, 0, -1));
532
            $postfix = S::slice($name, -2, -1) == 'к' ? 'и' : 'ы';
533
            return array(
534
                self::IMENIT => $prefix.'о',
535
                self::RODIT =>  $prefix.$postfix,
536
                self::DAT => $prefix.'е',
537
                self::VINIT => $prefix.'у',
538
                self::TVORIT => $prefix.'ой',
539
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
540
            );
541
        }
542
543
        return null;
544
    }
545
546
    protected static function getCasesWoman($name)
547
    {
548
        if (S::slice($name, -1) == 'а' && !self::isVowel($before = (S::slice($name, -2, -1)))) {
549
            $prefix = S::name(S::slice($name, 0, -1));
550 View Code Duplication
            if ($before != 'ц') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
551
                $postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, array('г', 'к', 'х'))) ? 'и' : 'ы';
552
                return array(
553
                    self::IMENIT => $prefix.'а',
554
                    self::RODIT => $prefix.$postfix,
555
                    self::DAT => $prefix.'е',
556
                    self::VINIT => $prefix.'у',
557
                    self::TVORIT => $prefix.'ой',
558
                    self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
559
                );
560
            } else {
561
                return array(
562
                    self::IMENIT => $prefix.'а',
563
                    self::RODIT => $prefix.'ы',
564
                    self::DAT => $prefix.'е',
565
                    self::VINIT => $prefix.'у',
566
                    self::TVORIT => $prefix.'ей',
567
                    self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
568
                );
569
            }
570
        } elseif (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
571
            $prefix = S::name(S::slice($name, 0, -1));
572
            return array(
573
                self::IMENIT => $prefix.'ь',
574
                self::RODIT => $prefix.'и',
575
                self::DAT => $prefix.'и',
576
                self::VINIT => $prefix.'ь',
577
                self::TVORIT => $prefix.'ью',
578
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
579
            );
580
        } elseif (RussianLanguage::isHissingConsonant(S::slice($name, -1))) {
581
            $prefix = S::name($name);
582
            return array(
583
                self::IMENIT => $prefix,
584
                self::RODIT => $prefix.'и',
585
                self::DAT => $prefix.'и',
586
                self::VINIT => $prefix,
587
                self::TVORIT => $prefix.'ью',
588
                self::PREDLOJ => self::choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
589
            );
590
        }
591
        return null;
592
    }
593
594
    public static function getCase($name, $case, $gender = null)
595
    {
596
        $case = self::canonizeCase($case);
597
        $forms = self::getCases($name, $gender);
598
        return $forms[$case];
599
    }
600
}
601