Completed
Push — master ( a3d81a...03367b )
by f
02:20
created

FirstNamesDeclension::getCases()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 39
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 29
nc 12
nop 2
dl 0
loc 39
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 FirstNamesDeclension extends \morphos\NamesDeclension implements Cases {
11
	use RussianLanguage, CasesHelper;
12
13
	protected $exceptions = array(
14
		'лев' => array(
15
			self::IMENIT => 'Лев',
16
			self::RODIT => 'Льва',
17
			self::DAT => 'Льву',
18
			self::VINIT => 'Льва',
19
			self::TVORIT => 'Львом',
20
			self::PREDLOJ => 'о Льве',
21
		),
22
		'павел' => array(
23
			self::IMENIT => 'Павел',
24
			self::RODIT => 'Павла',
25
			self::DAT => 'Павлу',
26
			self::VINIT => 'Павла',
27
			self::TVORIT => 'Павлом',
28
			self::PREDLOJ => 'о Павле',
29
		)
30
	);
31
32
	static protected $menNames = array(
33
		'абрам',
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
	static protected $womenNames = array(
191
		'авдотья',
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
	public function isMutable($name, $gender = null) {
355
		//var_dump(S::upper(S::slice($name, -1)));
356
		$name = S::lower($name);
357
		if ($gender === null) $gender = $this->detectGender($name);
358
		// man rules
359
		if ($gender === self::MAN) {
360
			// soft consonant
361
			if (S::lower(S::slice($name, -1)) == 'ь' && in_array(S::upper(S::slice($name, -2, -1)), self::$consonants)) {
362
				return true;
363
			} else if (in_array(S::upper(S::slice($name, -1)), array_diff(self::$consonants, array('Й', /*'Ч', 'Щ'*/)))) { // hard consonant
364
				return true;
365
			} else if (S::slice($name, -1) == 'й') {
366
				return true;
367
			}
368
		}
369
370
		// common rules
371
		if ((in_array(S::slice($name, -1), array('а', 'я')) && !in_array(S::upper(S::slice($name, -2, -1)), self::$vowels)) || in_array(S::slice($name, -2), array('ия', 'ья', 'ея'))) {
372
			return true;
373
		}
374
375
		return false;
376
	}
377
378
379
380
	public function detectGender($name) {
381
		$name = S::lower($name);
382
		if (in_array($name, self::$menNames))
383
			return self::MAN;
384
		else if (in_array($name, self::$womenNames))
385
			return self::WOMAN;
386
387
		$man = $woman = 0;
388
		$last1 = S::slice($name, -1);
389
		$last2 = S::slice($name, -2);
390
		$last3 = S::slice($name, -3);
391
392
		// try to detect gender by some statistical rules
393
		//
394
		if ($last1 == 'й') $man += 0.9;
395
		if ($last1 == 'ь') $man += 0.02;
396
		if (in_array($last1, self::$consonants)) $man += 0.01;
397
		if (in_array($last2, array('он', 'ов', 'ав', 'ам', 'ол', 'ан', 'рд', 'мп'))) $man += 0.3;
398
		if (in_array($last2, array('вь', 'фь', 'ль'))) $woman += 0.1;
399
		if (in_array($last2, array('ла'))) $woman += 0.04;
400
		if (in_array($last2, array('то', 'ма'))) $man += 0.01;
401 View Code Duplication
		if (in_array($last3, array('лья', 'вва', 'ока', 'ука', 'ита'))) $man += 0.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...
402
		if (in_array($last3, array('има'))) $woman += 0.15;
403 View Code Duplication
		if (in_array($last3, array('лия', 'ния', 'сия', 'дра', 'лла', 'кла', 'опа'))) $woman += 0.5;
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...
404 View Code Duplication
		if (in_array(S::slice($name, -4), array('льда', 'фира', 'нина', 'лита', 'алья'))) $woman += 0.5;
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...
405
406
		return $man == $woman ? null
407
			: ($man > $woman ? self::MAN : self::WOMAN);
408
	}
409
410
	public function getCases($name, $gender = null) {
411
		$name = S::lower($name);
412
413
		// common rules for ия and я
414
		if (S::slice($name, -2) == 'ия') {
415
			$prefix = S::name(S::slice($name, 0, -1));
416
			return array(
417
				self::IMENIT => $prefix.'я',
418
				self::RODIT => $prefix.'и',
419
				self::DAT => $prefix.'и',
420
				self::VINIT => $prefix.'ю',
421
				self::TVORIT => $prefix.'ей',
422
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
423
			);
424
		} else if (S::slice($name, -1) == 'я') {
425
			$prefix = S::name(S::slice($name, 0, -1));
426
			return array(
427
				self::IMENIT => $prefix.'я',
428
				self::RODIT => $prefix.'и',
429
				self::DAT => $prefix.'е',
430
				self::VINIT => $prefix.'ю',
431
				self::TVORIT => $prefix.'ей',
432
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
433
			);
434
		}
435
436
		if ($gender === null) $gender = $this->detectGender($name);
437
		if ($gender == self::MAN) {
438
			if (($result = $this->getCasesMan($name)) !== null)
439
				return $result;
440
		}
441
		else if ($gender == self::WOMAN) {
442
			if (($result = $this->getCasesWoman($name)) !== null)
443
				return $result;
444
		}
445
446
		$name = S::name($name);
447
		return array_fill_keys(array(self::IMENIT, self::RODIT, self::DAT, self::VINIT, self::TVORIT), $name) + array(self::PREDLOJ => $this->choosePrepositionByFirstLetter($name, 'об', 'о').' '.$name);
448
	}
449
450
	protected function getCasesMan($name) {
451
		// special cases for Лев, Павел
452
		if (isset($this->exceptions[$name]))
453
			return $this->exceptions[$name];
454
		else if (in_array(S::upper(S::slice($name, -1)), array_diff(self::$consonants, array('Й', /*'Ч', 'Щ'*/)))) { // hard consonant
455
			$prefix = S::name($name);
456
			return array(
457
				self::IMENIT => $prefix,
458
				self::RODIT => $prefix.'а',
459
				self::DAT => $prefix.'у',
460
				self::VINIT => $prefix.'а',
461
				self::TVORIT => RussianLanguage::isHissingConsonant(S::slice($name, -1)) || S::slice($name, -1) == 'ц' ? $prefix.'ем' : $prefix.'ом',
462
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
463
			);
464
		} else if (S::slice($name, -1) == 'ь' && in_array(S::upper(S::slice($name, -2, -1)), self::$consonants)) { // soft consonant
465
			$prefix = S::name(S::slice($name, 0, -1));
466
			return array(
467
				self::IMENIT => $prefix.'ь',
468
				self::RODIT => $prefix.'я',
469
				self::DAT => $prefix.'ю',
470
				self::VINIT => $prefix.'я',
471
				self::TVORIT => $prefix.'ем',
472
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
473
			);
474
		} else if (in_array(S::slice($name, -2), array('ай', 'ей', 'ой', 'уй', 'яй', 'юй', 'ий'))) {
475
			$prefix = S::name(S::slice($name, 0, -1));
476
			$postfix = S::slice($name, -2) == 'ий' ? 'и' : 'е';
477
			return array(
478
				self::IMENIT => $prefix.'й',
479
				self::RODIT => $prefix.'я',
480
				self::DAT => $prefix.'ю',
481
				self::VINIT => $prefix.'я',
482
				self::TVORIT => $prefix.'ем',
483
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.$postfix,
484
			);
485
		} else if (S::slice($name, -1) == 'а' && self::isConsonant($before = S::slice($name, -2, -1)) && !in_array($before, array(/*'г', 'к', 'х', */'ц'))) {
486
			$prefix = S::name(S::slice($name, 0, -1));
487
			$postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, array('г', 'к', 'х'))) ? 'и' : 'ы';
488
			return array(
489
				self::IMENIT => $prefix.'а',
490
				self::RODIT => $prefix.$postfix,
491
				self::DAT => $prefix.'е',
492
				self::VINIT => $prefix.'у',
493
				self::TVORIT => $prefix.'ой',
494
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
495
			);
496
		} else if (S::slice($name, -2) == 'ия') {
497
			$prefix = S::name(S::slice($name, 0, -1));
498
			return array(
499
				self::IMENIT => $prefix.'я',
500
				self::RODIT => $prefix.'и',
501
				self::DAT => $prefix.'и',
502
				self::VINIT => $prefix.'ю',
503
				self::TVORIT => $prefix.'ей',
504
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
505
			);
506
		} else if (S::slice($name, -2) == 'ло' || S::slice($name, -2) == 'ко') {
507
			$prefix = S::name(S::slice($name, 0, -1));
508
			$postfix = S::slice($name, -2, -1) == 'к' ? 'и' : 'ы';
509
			return array(
510
				self::IMENIT => $prefix.'о',
511
				self::RODIT =>  $prefix.$postfix,
512
				self::DAT => $prefix.'е',
513
				self::VINIT => $prefix.'у',
514
				self::TVORIT => $prefix.'ой',
515
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
516
			);
517
		}
518
519
		return null;
520
	}
521
522
	protected function getCasesWoman($name) {
523
		if (S::slice($name, -1) == 'а' && !in_array(S::upper($before = (S::slice($name, -2, -1))), self::$vowels)) {
524
			$prefix = S::name(S::slice($name, 0, -1));
525
			if ($before != 'ц') {
526
				$postfix = (RussianLanguage::isHissingConsonant($before) || in_array($before, array('г', 'к', 'х'))) ? 'и' : 'ы';
527
				return array(
528
					self::IMENIT => $prefix.'а',
529
					self::RODIT => $prefix.$postfix,
530
					self::DAT => $prefix.'е',
531
					self::VINIT => $prefix.'у',
532
					self::TVORIT => $prefix.'ой',
533
					self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
534
				);
535
			} else {
536
				return array(
537
					self::IMENIT => $prefix.'а',
538
					self::RODIT => $prefix.'ы',
539
					self::DAT => $prefix.'е',
540
					self::VINIT => $prefix.'у',
541
					self::TVORIT => $prefix.'ей',
542
					self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'е',
543
				);
544
			}
545
		} else if (S::slice($name, -1) == 'ь' && self::isConsonant(S::slice($name, -2, -1))) {
546
			$prefix = S::name(S::slice($name, 0, -1));
547
			return array(
548
				self::IMENIT => $prefix.'ь',
549
				self::RODIT => $prefix.'и',
550
				self::DAT => $prefix.'и',
551
				self::VINIT => $prefix.'ь',
552
				self::TVORIT => $prefix.'ью',
553
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
554
			);
555
		} else if (RussianLanguage::isHissingConsonant(S::slice($name, -1))) {
556
			$prefix = S::name($name);
557
			return array(
558
				self::IMENIT => $prefix,
559
				self::RODIT => $prefix.'и',
560
				self::DAT => $prefix.'и',
561
				self::VINIT => $prefix,
562
				self::TVORIT => $prefix.'ью',
563
				self::PREDLOJ => $this->choosePrepositionByFirstLetter($prefix, 'об', 'о').' '.$prefix.'и',
564
			);
565
		}
566
		return null;
567
	}
568
569
	public function getCase($name, $case, $gender = null) {
570
		$case = self::canonizeCase($case);
571
		$forms = $this->getCases($name, $gender);
572
		return $forms[$case];
573
	}
574
}
575