<?php
abstract class Gender
{
const FEMININE = 0;
const MASCULINE = 1;
}
class Lexeme
{
public function __construct(string $form1, string $form2, string $form5, int $gender = Gender::MASCULINE)
{
$this->form1 = $form1;
$this->form2 = $form2;
$this->form5 = $form5;
$this->gender = $gender;
}
public function getGender(): int
{
return $this->gender;
}
public function getFormOf(int $number): string
{
$digit0 = $number % 10;
$digit1 = $number / 10 % 10;
if ($digit1 == 1) {
return $this->form5;
} elseif ($digit0 == 1) {
return $this->form1;
} elseif ($digit0 > 1 && $digit0 <= 4) {
return $this->form2;
} else {
return $this->form5;
}
}
public static function getEmpty(): Lexeme
{
return new Lexeme("", "", "");
}
private int $gender;
private string $form1;
private string $form2;
private string $form5;
}
class InWordsBuilder
{
public function __construct(Lexeme $quantity)
{
self::$thousand = new Lexeme("тысяча", "тысячи", "тысяч", Gender::FEMININE);
self::$million = new Lexeme("миллион", "миллиона", "миллионов", Gender::MASCULINE);
self::$billion = new Lexeme("миллиард", "миллиарда", "миллиардов", Gender::MASCULINE);
$this->quantity = $quantity;
}
public function build(int $number): string
{
if ($number == 0) {
$quantityStr = $this->quantity->getFormOf(0);
return "ноль" . ($quantityStr == "" ? "" : " $quantityStr");
}
$inParts = new IntegerInParts($number);
$partsOfRecord = [];
if ($number >= 1000000000) {
$partsOfRecord[0] = self::writePartInWords($inParts->getBillions(), self::$billion);
}
if ($number >= 1000000) {
$partsOfRecord[1] = self::writePartInWords($inParts->getMillions(), self::$million);
}
if ($number >= 1000) {
$partsOfRecord[2] = self::writePartInWords($inParts->getThousands(), self::$thousand);
}
$partsOfRecord[3] = self::writePartInWords($inParts->getUnits(), $this->quantity);
}
private static function writePartInWords(int $number, Lexeme $lexeme): string
{
$digits = [
$number / 100 % 10,
$number / 10 % 10,
$number % 10
];
$numberMod100 = $number % 100;
$parts = [self::$hundreds[$digits[0]]];
if ($digits[1] <= 1) {
if ($digits[2] < 3) {
$parts[1] = self::$oneToThree[$lexeme->getGender()][$digits[2]];
} else {
$parts[1] = self::$threeToNineteen[$numberMod100];
}
} else {
$parts[1] = self::$tens[$digits[1]];
if ($digits[2] < 3) {
$parts[2] = self::$oneToThree[$lexeme->getGender()][$digits[2]];
} else {
$parts[2] = self::$threeToNineteen[$digits[2]];
}
}
$lexemeInForm = $lexeme->getFormOf($number);
return $numeral . ($lexemeInForm == "" ? "" : " $lexemeInForm");
}
private static
array $hundreds = ["", "сто", "двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот"];
private static
array $tens = [2 => "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто"];
private static
array $threeToNineteen = [3 => 'три', 'четыре', 'пять', 'шесть', 'семь', 'восемь', 'девять', 'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать',
'восемнадцать', 'девятнадцать'];
private static
array $oneToThree = [ Gender::FEMININE => ['', 'одна', 'две'],
Gender::MASCULINE => ['', 'один', 'два'],
];
private static Lexeme $thousand;
private static Lexeme $million;
private static Lexeme $billion;
private Lexeme $quantity;
}
class IntegerInParts
{
public function __construct(int $number)
{
$this->billions = $number / 1000000000 % 1000;
$this->millions = $number / 1000000 % 1000;
$this->thousands = $number / 1000 % 1000;
$this->units = $number % 1000;
}
public function getBillions(): int
{
return $this->billions;
}
public function getMillions(): int
{
return $this->millions;
}
public function getThousands(): int
{
return $this->thousands;
}
public function getUnits(): int
{
return $this->units;
}
private int $billions;
private int $millions;
private int $thousands;
private int $units;
}
$builder = new InWordsBuilder(new Lexeme("штука", "штуки", "штук", Gender::FEMININE));
$numbers = [0, 4, 5, 9,
49, 55, 66, 28, 97, 18, 85, 96,
673, 242, 463, 592, 664, 259, 170, 382,
6428, 5454, 2175, 6234, 7091, 5236, 4533, 1244,
99189, 70540, 64063, 78756, 35080, 15626, 36327, 84276,
528070, 760165, 873158, 566844, 843370, 415865, 997742, 890570,
6855775, 4913491, 6181936, 7298680, 7625891, 7733251, 1001001, 9147526,
75269465, 98207441, 46370258, 26467052, 67385218, 74961612, 97937978, 63423923,
313248348, 604730178, 342676693, 965300262, 175878814, 796606802, 233396657, 213578532,
5281552249, 2361786413, 9463336049, 6982021856, 8707095076, 7869601075, 4398378123, 2223366077];
foreach ($numbers as $number) {
printf("%-12d= %s\n", $number, $builder->build($number)); }
PD9waHAKCmVycm9yX3JlcG9ydGluZygtMSk7Cm1iX2ludGVybmFsX2VuY29kaW5nKCd1dGYtOCcpOwoKYWJzdHJhY3QgY2xhc3MgR2VuZGVyCnsKICAgIGNvbnN0IEZFTUlOSU5FID0gMDsKICAgIGNvbnN0IE1BU0NVTElORSA9IDE7Cn0KCmNsYXNzIExleGVtZQp7CiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3Qoc3RyaW5nICRmb3JtMSwgc3RyaW5nICRmb3JtMiwgc3RyaW5nICRmb3JtNSwgaW50ICRnZW5kZXIgPSBHZW5kZXI6Ok1BU0NVTElORSkKICAgIHsKICAgICAgICAkdGhpcy0+Zm9ybTEgPSAkZm9ybTE7CiAgICAgICAgJHRoaXMtPmZvcm0yID0gJGZvcm0yOwogICAgICAgICR0aGlzLT5mb3JtNSA9ICRmb3JtNTsKICAgICAgICAkdGhpcy0+Z2VuZGVyID0gJGdlbmRlcjsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0R2VuZGVyKCk6IGludAogICAgewogICAgICAgIHJldHVybiAkdGhpcy0+Z2VuZGVyOwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBnZXRGb3JtT2YoaW50ICRudW1iZXIpOiBzdHJpbmcKICAgIHsKICAgICAgICAkZGlnaXQwID0gJG51bWJlciAlIDEwOwogICAgICAgICRkaWdpdDEgPSAkbnVtYmVyIC8gMTAgJSAxMDsKCiAgICAgICAgaWYgKCRkaWdpdDEgPT0gMSkgewogICAgICAgICAgICByZXR1cm4gJHRoaXMtPmZvcm01OwogICAgICAgIH0gZWxzZWlmICgkZGlnaXQwID09IDEpIHsKICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5mb3JtMTsKICAgICAgICB9IGVsc2VpZiAoJGRpZ2l0MCA+IDEgJiYgJGRpZ2l0MCA8PSA0KSB7CiAgICAgICAgICAgIHJldHVybiAkdGhpcy0+Zm9ybTI7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgcmV0dXJuICR0aGlzLT5mb3JtNTsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIHN0YXRpYyBmdW5jdGlvbiBnZXRFbXB0eSgpOiBMZXhlbWUKICAgIHsKICAgICAgICByZXR1cm4gbmV3IExleGVtZSgiIiwgIiIsICIiKTsKICAgIH0KCiAgICBwcml2YXRlIGludCAkZ2VuZGVyOwogICAgcHJpdmF0ZSBzdHJpbmcgJGZvcm0xOwogICAgcHJpdmF0ZSBzdHJpbmcgJGZvcm0yOwogICAgcHJpdmF0ZSBzdHJpbmcgJGZvcm01Owp9CgpjbGFzcyBJbldvcmRzQnVpbGRlcgp7CiAgICBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoTGV4ZW1lICRxdWFudGl0eSkKICAgIHsKICAgICAgICBzZWxmOjokdGhvdXNhbmQgPSBuZXcgTGV4ZW1lKCLRgtGL0YHRj9GH0LAiLCAi0YLRi9GB0Y/Rh9C4IiwgItGC0YvRgdGP0YciLCBHZW5kZXI6OkZFTUlOSU5FKTsKICAgICAgICBzZWxmOjokbWlsbGlvbiA9IG5ldyBMZXhlbWUoItC80LjQu9C70LjQvtC9IiwgItC80LjQu9C70LjQvtC90LAiLCAi0LzQuNC70LvQuNC+0L3QvtCyIiwgR2VuZGVyOjpNQVNDVUxJTkUpOwogICAgICAgIHNlbGY6OiRiaWxsaW9uID0gbmV3IExleGVtZSgi0LzQuNC70LvQuNCw0YDQtCIsICLQvNC40LvQu9C40LDRgNC00LAiLCAi0LzQuNC70LvQuNCw0YDQtNC+0LIiLCBHZW5kZXI6Ok1BU0NVTElORSk7CiAgICAgICAgJHRoaXMtPnF1YW50aXR5ID0gJHF1YW50aXR5OwogICAgfQoKICAgIHB1YmxpYyBmdW5jdGlvbiBidWlsZChpbnQgJG51bWJlcik6IHN0cmluZwogICAgewogICAgICAgIGlmICgkbnVtYmVyID09IDApIHsKICAgICAgICAgICAgJHF1YW50aXR5U3RyID0gJHRoaXMtPnF1YW50aXR5LT5nZXRGb3JtT2YoMCk7CiAgICAgICAgICAgIHJldHVybiAi0L3QvtC70YwiIC4gKCRxdWFudGl0eVN0ciA9PSAiIiA/ICIiIDogIiAkcXVhbnRpdHlTdHIiKTsKICAgICAgICB9CiAgICAgICAgJGluUGFydHMgPSBuZXcgSW50ZWdlckluUGFydHMoJG51bWJlcik7CiAgICAgICAgJHBhcnRzT2ZSZWNvcmQgPSBbXTsKICAgICAgICBpZiAoJG51bWJlciA+PSAxMDAwMDAwMDAwKSB7CiAgICAgICAgICAgICRwYXJ0c09mUmVjb3JkWzBdID0gc2VsZjo6d3JpdGVQYXJ0SW5Xb3JkcygkaW5QYXJ0cy0+Z2V0QmlsbGlvbnMoKSwgc2VsZjo6JGJpbGxpb24pOwogICAgICAgIH0KICAgICAgICBpZiAoJG51bWJlciA+PSAxMDAwMDAwKSB7CiAgICAgICAgICAgICRwYXJ0c09mUmVjb3JkWzFdID0gc2VsZjo6d3JpdGVQYXJ0SW5Xb3JkcygkaW5QYXJ0cy0+Z2V0TWlsbGlvbnMoKSwgc2VsZjo6JG1pbGxpb24pOwogICAgICAgIH0KICAgICAgICBpZiAoJG51bWJlciA+PSAxMDAwKSB7CiAgICAgICAgICAgICRwYXJ0c09mUmVjb3JkWzJdID0gc2VsZjo6d3JpdGVQYXJ0SW5Xb3JkcygkaW5QYXJ0cy0+Z2V0VGhvdXNhbmRzKCksIHNlbGY6OiR0aG91c2FuZCk7CiAgICAgICAgfQogICAgICAgICRwYXJ0c09mUmVjb3JkWzNdID0gc2VsZjo6d3JpdGVQYXJ0SW5Xb3JkcygkaW5QYXJ0cy0+Z2V0VW5pdHMoKSwgJHRoaXMtPnF1YW50aXR5KTsKICAgICAgICByZXR1cm4gdHJpbShpbXBsb2RlKCIgIiwgJHBhcnRzT2ZSZWNvcmQpKTsKICAgIH0KCiAgICBwcml2YXRlIHN0YXRpYyBmdW5jdGlvbiB3cml0ZVBhcnRJbldvcmRzKGludCAkbnVtYmVyLCBMZXhlbWUgJGxleGVtZSk6IHN0cmluZwogICAgewogICAgICAgICRkaWdpdHMgPSBbCiAgICAgICAgICAgICRudW1iZXIgLyAxMDAgJSAxMCwKICAgICAgICAgICAgJG51bWJlciAvIDEwICUgMTAsCiAgICAgICAgICAgICRudW1iZXIgJSAxMAogICAgICAgIF07CiAgICAgICAgJG51bWJlck1vZDEwMCA9ICRudW1iZXIgJSAxMDA7CgogICAgICAgICRwYXJ0cyA9IFtzZWxmOjokaHVuZHJlZHNbJGRpZ2l0c1swXV1dOwogICAgICAgIGlmICgkZGlnaXRzWzFdIDw9IDEpIHsKICAgICAgICAgICAgaWYgKCRkaWdpdHNbMl0gPCAzKSB7CiAgICAgICAgICAgICAgICAkcGFydHNbMV0gPSBzZWxmOjokb25lVG9UaHJlZVskbGV4ZW1lLT5nZXRHZW5kZXIoKV1bJGRpZ2l0c1syXV07CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAkcGFydHNbMV0gPSBzZWxmOjokdGhyZWVUb05pbmV0ZWVuWyRudW1iZXJNb2QxMDBdOwogICAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgJHBhcnRzWzFdID0gc2VsZjo6JHRlbnNbJGRpZ2l0c1sxXV07CiAgICAgICAgICAgIGlmICgkZGlnaXRzWzJdIDwgMykgewogICAgICAgICAgICAgICAgJHBhcnRzWzJdID0gc2VsZjo6JG9uZVRvVGhyZWVbJGxleGVtZS0+Z2V0R2VuZGVyKCldWyRkaWdpdHNbMl1dOwogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgJHBhcnRzWzJdID0gc2VsZjo6JHRocmVlVG9OaW5ldGVlblskZGlnaXRzWzJdXTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAkbnVtZXJhbCA9IHRyaW0oaW1wbG9kZSgiICIsICRwYXJ0cykpOwogICAgICAgICRsZXhlbWVJbkZvcm0gPSAkbGV4ZW1lLT5nZXRGb3JtT2YoJG51bWJlcik7CiAgICAgICAgcmV0dXJuICRudW1lcmFsIC4gKCRsZXhlbWVJbkZvcm0gPT0gIiIgPyAiIiA6ICIgJGxleGVtZUluRm9ybSIpOwogICAgfQoKICAgIHByaXZhdGUgc3RhdGljIGFycmF5ICRodW5kcmVkcyA9IFsiIiwgItGB0YLQviIsICLQtNCy0LXRgdGC0LgiLCAi0YLRgNC40YHRgtCwIiwgItGH0LXRgtGL0YDQtdGB0YLQsCIsICLQv9GP0YLRjNGB0L7RgiIsCiAgICAgICAgItGI0LXRgdGC0YzRgdC+0YIiLCAi0YHQtdC80YzRgdC+0YIiLCAi0LLQvtGB0LXQvNGM0YHQvtGCIiwgItC00LXQstGP0YLRjNGB0L7RgiJdOwogICAgcHJpdmF0ZSBzdGF0aWMgYXJyYXkgJHRlbnMgPSBbMiA9PiAi0LTQstCw0LTRhtCw0YLRjCIsICLRgtGA0LjQtNGG0LDRgtGMIiwgItGB0L7RgNC+0LoiLCAi0L/Rj9GC0YzQtNC10YHRj9GCIiwgItGI0LXRgdGC0YzQtNC10YHRj9GCIiwgItGB0LXQvNGM0LTQtdGB0Y/RgiIsCiAgICAgICAgItCy0L7RgdC10LzRjNC00LXRgdGP0YIiLCAi0LTQtdCy0Y/QvdC+0YHRgtC+Il07CiAgICBwcml2YXRlIHN0YXRpYyBhcnJheSAkdGhyZWVUb05pbmV0ZWVuID0gWzMgPT4gJ9GC0YDQuCcsICfRh9C10YLRi9GA0LUnLCAn0L/Rj9GC0YwnLCAn0YjQtdGB0YLRjCcsICfRgdC10LzRjCcsICfQstC+0YHQtdC80YwnLCAn0LTQtdCy0Y/RgtGMJywgJ9C00LXRgdGP0YLRjCcsCiAgICAgICAgJ9C+0LTQuNC90L3QsNC00YbQsNGC0YwnLCAn0LTQstC10L3QsNC00YbQsNGC0YwnLCAn0YLRgNC40L3QsNC00YbQsNGC0YwnLCAn0YfQtdGC0YvRgNC90LDQtNGG0LDRgtGMJywgJ9C/0Y/RgtC90LDQtNGG0LDRgtGMJywgJ9GI0LXRgdGC0L3QsNC00YbQsNGC0YwnLCAn0YHQtdC80L3QsNC00YbQsNGC0YwnLAogICAgICAgICfQstC+0YHQtdC80L3QsNC00YbQsNGC0YwnLCAn0LTQtdCy0Y/RgtC90LDQtNGG0LDRgtGMJ107CiAgICBwcml2YXRlIHN0YXRpYyBhcnJheSAkb25lVG9UaHJlZSA9IFsKICAgICAgICBHZW5kZXI6OkZFTUlOSU5FID0+IFsnJywgJ9C+0LTQvdCwJywgJ9C00LLQtSddLAogICAgICAgIEdlbmRlcjo6TUFTQ1VMSU5FID0+IFsnJywgJ9C+0LTQuNC9JywgJ9C00LLQsCddLAogICAgXTsKICAgIHByaXZhdGUgc3RhdGljIExleGVtZSAkdGhvdXNhbmQ7CiAgICBwcml2YXRlIHN0YXRpYyBMZXhlbWUgJG1pbGxpb247CiAgICBwcml2YXRlIHN0YXRpYyBMZXhlbWUgJGJpbGxpb247CiAgICBwcml2YXRlIExleGVtZSAkcXVhbnRpdHk7Cn0KCmNsYXNzIEludGVnZXJJblBhcnRzCnsKICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdChpbnQgJG51bWJlcikKICAgIHsKICAgICAgICAkdGhpcy0+YmlsbGlvbnMgPSAkbnVtYmVyIC8gMTAwMDAwMDAwMCAlIDEwMDA7CiAgICAgICAgJHRoaXMtPm1pbGxpb25zID0gJG51bWJlciAvIDEwMDAwMDAgJSAxMDAwOwogICAgICAgICR0aGlzLT50aG91c2FuZHMgPSAkbnVtYmVyIC8gMTAwMCAlIDEwMDA7CiAgICAgICAgJHRoaXMtPnVuaXRzID0gJG51bWJlciAlIDEwMDA7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldEJpbGxpb25zKCk6IGludAogICAgewogICAgICAgIHJldHVybiAkdGhpcy0+YmlsbGlvbnM7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldE1pbGxpb25zKCk6IGludAogICAgewogICAgICAgIHJldHVybiAkdGhpcy0+bWlsbGlvbnM7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGdldFRob3VzYW5kcygpOiBpbnQKICAgIHsKICAgICAgICByZXR1cm4gJHRoaXMtPnRob3VzYW5kczsKICAgIH0KCiAgICBwdWJsaWMgZnVuY3Rpb24gZ2V0VW5pdHMoKTogaW50CiAgICB7CiAgICAgICAgcmV0dXJuICR0aGlzLT51bml0czsKICAgIH0KCiAgICBwcml2YXRlIGludCAkYmlsbGlvbnM7CiAgICBwcml2YXRlIGludCAkbWlsbGlvbnM7CiAgICBwcml2YXRlIGludCAkdGhvdXNhbmRzOwogICAgcHJpdmF0ZSBpbnQgJHVuaXRzOwp9CgokYnVpbGRlciA9IG5ldyBJbldvcmRzQnVpbGRlcihuZXcgTGV4ZW1lKCLRiNGC0YPQutCwIiwgItGI0YLRg9C60LgiLCAi0YjRgtGD0LoiLCBHZW5kZXI6OkZFTUlOSU5FKSk7CiRudW1iZXJzID0gWzAsIDQsIDUsIDksCiAgICA0OSwgNTUsIDY2LCAyOCwgOTcsIDE4LCA4NSwgOTYsCiAgICA2NzMsIDI0MiwgNDYzLCA1OTIsIDY2NCwgMjU5LCAxNzAsIDM4MiwKICAgIDY0MjgsIDU0NTQsIDIxNzUsIDYyMzQsIDcwOTEsIDUyMzYsIDQ1MzMsIDEyNDQsCiAgICA5OTE4OSwgNzA1NDAsIDY0MDYzLCA3ODc1NiwgMzUwODAsIDE1NjI2LCAzNjMyNywgODQyNzYsCiAgICA1MjgwNzAsIDc2MDE2NSwgODczMTU4LCA1NjY4NDQsIDg0MzM3MCwgNDE1ODY1LCA5OTc3NDIsIDg5MDU3MCwKICAgIDY4NTU3NzUsIDQ5MTM0OTEsIDYxODE5MzYsIDcyOTg2ODAsIDc2MjU4OTEsIDc3MzMyNTEsIDEwMDEwMDEsIDkxNDc1MjYsCiAgICA3NTI2OTQ2NSwgOTgyMDc0NDEsIDQ2MzcwMjU4LCAyNjQ2NzA1MiwgNjczODUyMTgsIDc0OTYxNjEyLCA5NzkzNzk3OCwgNjM0MjM5MjMsCiAgICAzMTMyNDgzNDgsIDYwNDczMDE3OCwgMzQyNjc2NjkzLCA5NjUzMDAyNjIsIDE3NTg3ODgxNCwgNzk2NjA2ODAyLCAyMzMzOTY2NTcsIDIxMzU3ODUzMiwKICAgIDUyODE1NTIyNDksIDIzNjE3ODY0MTMsIDk0NjMzMzYwNDksIDY5ODIwMjE4NTYsIDg3MDcwOTUwNzYsIDc4Njk2MDEwNzUsIDQzOTgzNzgxMjMsIDIyMjMzNjYwNzddOwpmb3JlYWNoICgkbnVtYmVycyBhcyAkbnVtYmVyKSB7CiAgICBwcmludGYoIiUtMTJkPSAlc1xuIiwgJG51bWJlciwgJGJ1aWxkZXItPmJ1aWxkKCRudW1iZXIpKTsKfQo=