CardinalNumeralGenerator::getCase()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
namespace morphos\Russian;
3
4
use morphos\NumeralGenerator;
5
use morphos\S;
6
use RuntimeException;
7
8
/**
9
 * Rules are from http://www.fio.ru/pravila/grammatika/sklonenie-imen-chislitelnykh/
10
 */
11
class CardinalNumeralGenerator extends NumeralGenerator implements Cases
12
{
13
    use RussianLanguage, CasesHelper;
14
15
    /**
16
     * @var string[]
17
     * @phpstan-var array<int, string>
18
     */
19
    protected static $words = [
20
        1 => 'один',
21
        2 => 'два',
22
        3 => 'три',
23
        4 => 'четыре',
24
        5 => 'пять',
25
        6 => 'шесть',
26
        7 => 'семь',
27
        8 => 'восемь',
28
        9 => 'девять',
29
        10 => 'десять',
30
        11 => 'одиннадцать',
31
        12 => 'двенадцать',
32
        13 => 'тринадцать',
33
        14 => 'четырнадцать',
34
        15 => 'пятнадцать',
35
        16 => 'шестнадцать',
36
        17 => 'семнадцать',
37
        18 => 'восемнадцать',
38
        19 => 'девятнадцать',
39
        20 => 'двадцать',
40
        30 => 'тридцать',
41
        40 => 'сорок',
42
        50 => 'пятьдесят',
43
        60 => 'шестьдесят',
44
        70 => 'семьдесят',
45
        80 => 'восемьдесят',
46
        90 => 'девяносто',
47
        100 => 'сто',
48
        200 => 'двести',
49
        300 => 'триста',
50
        400 => 'четыреста',
51
        500 => 'пятьсот',
52
        600 => 'шестьсот',
53
        700 => 'семьсот',
54
        800 => 'восемьсот',
55
        900 => 'девятьсот',
56
    ];
57
58
    /**
59
     * @var string[]
60
     * @phpstan-var array<int, string>
61
     */
62
    protected static $exponents = [
63
        1000 => 'тысяча',
64
        1000000 => 'миллион',
65
        1000000000 => 'миллиард',
66
        1000000000000 => 'триллион',
67
        1000000000000000 => 'квадриллион',
68
    ];
69
70
    /**
71
     * @var array
72
     * @phpstan-var array<string, array<string, array<string, string>|string>>
73
     */
74
    protected static $precalculated = [
75
        'один' => [
76
            self::MALE => [
77
                self::IMENIT => 'один',
78
                self::RODIT => 'одного',
79
                self::DAT => 'одному',
80
                self::VINIT => 'один',
81
                self::TVORIT => 'одним',
82
                self::PREDLOJ => 'одном',
83
            ],
84
            self::FEMALE => [
85
                self::IMENIT => 'одна',
86
                self::RODIT => 'одной',
87
                self::DAT => 'одной',
88
                self::VINIT => 'одну',
89
                self::TVORIT => 'одной',
90
                self::PREDLOJ => 'одной',
91
            ],
92
            self::NEUTER => [
93
                self::IMENIT => 'одно',
94
                self::RODIT => 'одного',
95
                self::DAT => 'одному',
96
                self::VINIT => 'одно',
97
                self::TVORIT => 'одним',
98
                self::PREDLOJ => 'одном',
99
            ],
100
        ],
101
        'два' => [
102
            self::MALE => [
103
                self::IMENIT => 'два',
104
                self::RODIT => 'двух',
105
                self::DAT => 'двум',
106
                self::VINIT => 'два',
107
                self::TVORIT => 'двумя',
108
                self::PREDLOJ => 'двух',
109
            ],
110
            self::FEMALE => [
111
                self::IMENIT => 'две',
112
                self::RODIT => 'двух',
113
                self::DAT => 'двум',
114
                self::VINIT => 'две',
115
                self::TVORIT => 'двумя',
116
                self::PREDLOJ => 'двух',
117
            ],
118
            self::NEUTER => [
119
                self::IMENIT => 'два',
120
                self::RODIT => 'двух',
121
                self::DAT => 'двум',
122
                self::VINIT => 'два',
123
                self::TVORIT => 'двумя',
124
                self::PREDLOJ => 'двух',
125
            ],
126
        ],
127
        'три' => [
128
            self::IMENIT => 'три',
129
            self::RODIT => 'трех',
130
            self::DAT => 'трем',
131
            self::VINIT => 'три',
132
            self::TVORIT => 'тремя',
133
            self::PREDLOJ => 'трех',
134
        ],
135
        'четыре' => [
136
            self::IMENIT => 'четыре',
137
            self::RODIT => 'четырех',
138
            self::DAT => 'четырем',
139
            self::VINIT => 'четыре',
140
            self::TVORIT => 'четырьмя',
141
            self::PREDLOJ => 'четырех',
142
        ],
143
        'двести' => [
144
            self::IMENIT => 'двести',
145
            self::RODIT => 'двухсот',
146
            self::DAT => 'двумстам',
147
            self::VINIT => 'двести',
148
            self::TVORIT => 'двумястами',
149
            self::PREDLOJ => 'двухстах',
150
        ],
151
        'восемьсот' => [
152
            self::IMENIT => 'восемьсот',
153
            self::RODIT => 'восьмисот',
154
            self::DAT => 'восьмистам',
155
            self::VINIT => 'восемьсот',
156
            self::TVORIT => 'восьмистами',
157
            self::PREDLOJ => 'восьмистах',
158
        ],
159
        'тысяча' => [
160
            self::IMENIT => 'тысяча',
161
            self::RODIT => 'тысяч',
162
            self::DAT => 'тысячам',
163
            self::VINIT => 'тысяч',
164
            self::TVORIT => 'тысячей',
165
            self::PREDLOJ => 'тысячах',
166
        ],
167
    ];
168
169
    /**
170
     * @param int $number
171
     * @param string $gender
172
     * @return string[]
173
     * @phpstan-return array<string, string>
174
     * @throws \Exception
175
     */
176 33
    public static function getCases($number, $gender = self::MALE)
177
    {
178
        // simple numeral
179 33
        if (isset(static::$words[$number]) || isset(static::$exponents[$number])) {
180 33
            $word = isset(static::$words[$number]) ? static::$words[$number] : static::$exponents[$number];
181 33
            if (isset(static::$precalculated[$word])) {
182 21
                if (isset(static::$precalculated[$word][static::MALE])) {
183 10
                    return static::$precalculated[$word][$gender];
184
                } else {
185 18
                    return static::$precalculated[$word];
186
                }
187 24
            } elseif (($number >= 5 && $number <= 20) || $number == 30) {
188 14
                $prefix = S::slice($word, 0, -1);
189
                return [
190 14
                    static::IMENIT => $word,
191 14
                    static::RODIT => $prefix.'и',
192 14
                    static::DAT => $prefix.'и',
193 14
                    static::VINIT => $word,
194 14
                    static::TVORIT => $prefix.'ью',
195 14
                    static::PREDLOJ => $prefix.'и',
196
                ];
197 21
            } elseif (in_array($number, [40, 90, 100])) {
198 11
                $prefix = $number == 40 ? $word : S::slice($word, 0, -1);
199
                return [
200 11
                    static::IMENIT => $word,
201 11
                    static::RODIT => $prefix.'а',
202 11
                    static::DAT => $prefix.'а',
203 11
                    static::VINIT => $word,
204 11
                    static::TVORIT => $prefix.'а',
205 11
                    static::PREDLOJ => $prefix.'а',
206
                ];
207 18
            } elseif (($number >= 50 && $number <= 80)) {
208 10
                $prefix = S::slice($word, 0, -6);
209
                return [
210 10
                    static::IMENIT => $prefix.'ьдесят',
211 10
                    static::RODIT => $prefix.'идесяти',
212 10
                    static::DAT => $prefix.'идесяти',
213 10
                    static::VINIT => $prefix.'ьдесят',
214 10
                    static::TVORIT => $prefix.'ьюдесятью',
215 10
                    static::PREDLOJ => $prefix.'идесяти',
216
                ];
217 18
            } elseif (in_array($number, [300, 400])) {
218 6
                $prefix = S::slice($word, 0, -4);
219
                return [
220 6
                    static::IMENIT => $word,
221 6
                    static::RODIT => $prefix.'ехсот',
222 6
                    static::DAT => $prefix.'емстам',
223 6
                    static::VINIT => $word,
224 6
                    static::TVORIT => $prefix.($number == 300 ? 'е' : 'ь').'мястами',
225 6
                    static::PREDLOJ => $prefix.'ехстах',
226
                ];
227 12
            } elseif ($number >= 500 && $number <= 900) {
228 10
                $prefix = S::slice($word, 0, -4);
229
                return [
230 10
                    static::IMENIT => $word,
231 10
                    static::RODIT => $prefix.'исот',
232 10
                    static::DAT => $prefix.'истам',
233 10
                    static::VINIT => $word,
234 10
                    static::TVORIT => $prefix.'ьюстами',
235 10
                    static::PREDLOJ => $prefix.'истах',
236
                ];
237 2
            } elseif (isset(static::$exponents[$number])) {
238 2
                return NounDeclension::getCases($word, false);
239
            }
240
241
            throw new RuntimeException('Unreachable');
242
        }
243
244 21
        if ($number == 0) {
245
            return [
246
                static::IMENIT => 'ноль',
247
                static::RODIT => 'ноля',
248
                static::DAT => 'нолю',
249
                static::VINIT => 'ноль',
250
                static::TVORIT => 'нолём',
251
                static::PREDLOJ => 'ноле',
252
            ];
253
        } // compound numeral
254
255 21
        $parts = [];
256 21
        $result = [];
257
258 21
        foreach (array_reverse(static::$exponents, true) as $word_number => $word) {
259 21
            if ($number >= $word_number) {
260 13
                $count = (int)floor($number / $word_number);
261 13
                $parts[] = static::getCases($count, ($word_number == 1000 ? static::FEMALE : static::MALE));
262
263 13
                switch (NounPluralization::getNumeralForm($count)) {
264 13
                    case NounPluralization::ONE:
265 4
                        $parts[] = NounDeclension::getCases($word, false);
266 4
                        break;
267
268 12 View Code Duplication
                    case NounPluralization::TWO_FOUR:
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...
269 7
                        $part = NounPluralization::getCases($word);
270 7
                        if ($word_number != 1000) { // get dative case of word for 1000000, 1000000000 and 1000000000000
271 4
                            $part[Cases::IMENIT] = $part[Cases::VINIT] = NounDeclension::getCase($word, Cases::RODIT);
272
                        }
273 7
                        $parts[] = $part;
274 7
                        break;
275
276 9 View Code Duplication
                    case NounPluralization::FIVE_OTHER:
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...
277 9
                        $part = NounPluralization::getCases($word);
278 9
                        $part[Cases::IMENIT] = $part[Cases::VINIT] = $part[Cases::RODIT];
279 9
                        $parts[] = $part;
280 9
                        break;
281
                }
282
283 21
                $number = $number % ($count * $word_number);
284
            }
285
        }
286
287 21 View Code Duplication
        foreach (array_reverse(static::$words, true) as $word_number => $word) {
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...
288 21
            if ($number >= $word_number) {
289 21
                $parts[] = static::getCases($word_number, $gender);
290 21
                $number %= $word_number;
291
            }
292
        }
293
294
        // make one array with cases and delete 'o/об' prepositional from all parts except the last one
295 21
        foreach (array(static::IMENIT, static::RODIT, static::DAT, static::VINIT, static::TVORIT, static::PREDLOJ) as $case) {
296 21
            $result[$case] = [];
297 21
            foreach ($parts as $partN => $part) {
298 21
                $result[$case][] = $part[$case];
299
            }
300 21
            $result[$case] = implode(' ', $result[$case]);
301
        }
302
303 21
        return $result;
304
    }
305
306
    /**
307
     * @param int $number
308
     * @param string $case
309
     * @param string $gender
310
     *
311
     * @return string
312
     * @throws \Exception
313
     */
314 25
    public static function getCase($number, $case, $gender = self::MALE)
315
    {
316 25
        $case = static::canonizeCase($case);
317 25
        $forms = static::getCases($number, $gender);
318 25
        return $forms[$case];
319
    }
320
}
321