Skip to content

Instantly share code, notes, and snippets.

@alexd1971
Last active June 28, 2019 19:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexd1971/0e661592c820cc9f881835eaa62f281f to your computer and use it in GitHub Desktop.
Save alexd1971/0e661592c820cc9f881835eaa62f281f to your computer and use it in GitHub Desktop.
import 'package:dartz/dartz.dart' show Tuple3;
/// Грамматический род
enum Gender { male, female, neuter }
/// Называет одну цифру (числа перого десятка)
///
/// [digit] - число первого десятка: 1 - 9.
/// [gender] - род числительного
String sayDigits(int digit, {Gender gender = Gender.male}) {
switch (digit) {
case 1:
switch (gender) {
case Gender.male:
return 'один';
case Gender.female:
return 'одна';
case Gender.neuter:
return 'одно';
}
break;
case 2:
switch (gender) {
case Gender.male:
case Gender.neuter:
return 'два';
case Gender.female:
return 'две';
}
break;
case 3:
return 'три';
case 4:
return 'четыре';
case 5:
return 'пять';
case 6:
return 'шесть';
case 7:
return 'семь';
case 8:
return 'восемь';
case 9:
return 'девять';
}
return '';
}
/// Называет числа второго десятка
///
/// [digit] принимает значение от 0 до 9.
String sayTeens(int digit) {
switch (digit) {
case 0:
return 'десять';
case 1:
return 'одиннадцать';
case 2:
return 'двенадцать';
case 3:
return 'тринадцать';
case 4:
return 'четырнадцать';
case 5:
return 'пятнадцать';
case 6:
return 'шестнадцать';
case 7:
return 'семнадцать';
case 8:
return 'восемнадцать';
case 9:
return 'девятнадцать';
default:
return '';
}
}
/// Называет десятки
///
/// [digit] принимает значения от 2 до 9.
String sayTens(int digit) {
switch (digit) {
case 2:
return 'двадцать';
case 3:
return 'тридцать';
case 4:
return 'сорок';
case 5:
return 'пятьдесят';
case 6:
return 'шестьдесят';
case 7:
return 'семьдесят';
case 8:
return 'восемьдесят';
case 9:
return 'девяносто';
default:
return '';
}
}
/// Называет сотни
///
/// [digit] принимает значения от 1 до 9.
String sayHundreds(int digit) {
switch (digit) {
case 1:
return 'сто';
case 2:
return 'двести';
case 3:
return 'триста';
case 4:
return 'четыреста';
case 5:
return 'пятьсот';
case 6:
return 'шестьсот';
case 7:
return 'семьсот';
case 8:
return 'восемьсот';
case 9:
return 'девятьсот';
default:
return '';
}
}
/// Называет трехзначное число (класс единиц)
///
/// Соответсвует словесному представлению класса единиц
/// - [digits] - кортеж из 3-х цифр класса числа.
/// - [gender] - род числительного
String sayOnes(Tuple3 digits, {Gender gender = Gender.male}) {
if (digits.value2 == 1) {
return [sayHundreds(digits.value1), sayTeens(digits.value3)].join(' ');
}
return [
sayHundreds(digits.value1),
sayTens(digits.value2),
sayDigits(digits.value3, gender: gender)
].join(' ');
}
/// Называет класс тысяч
///
/// - [digits] - кортеж из 3-х цифр класса числа.
String sayThousands(Tuple3 digits) {
if (digits == Tuple3(0, 0, 0)) {
return '';
}
final s = sayOnes(digits, gender: Gender.female);
if (digits.value2 == 1) {
return '$s тысяч';
} else if (digits.value3 == 1) {
return '$s тысяча';
} else if (digits.value3 > 1 && digits.value3 < 5) {
return '$s тысячи';
} else {
return '$s тысяч';
}
}
/// Называет класс миллионов
///
/// - [digits] - кортеж из 3-х цифр класса числа.
String sayMillions(Tuple3 digits) {
if (digits == Tuple3(0, 0, 0)) {
return '';
}
final s = sayOnes(digits);
if (digits.value2 == 1) {
return '$s миллионов';
} else if (digits.value3 == 1) {
return '$s миллион';
} else if (digits.value3 > 1 && digits.value3 < 5) {
return '$s миллиона';
} else {
return '$s миллионов';
}
}
/// Называет класс миллиардов
///
/// - [digits] - кортеж из 3-х цифр класса числа.
String sayBillions(Tuple3 digits) {
if (digits == Tuple3(0, 0, 0)) {
return '';
}
final s = sayOnes(digits);
if (digits.value2 == 1) {
return '$s миллиардов';
} else if (digits.value3 == 1) {
return '$s миллиард';
} else if (digits.value3 > 1 && digits.value3 < 5) {
return '$s миллиарда';
} else {
return '$s миллиардов';
}
}
/// Разбивает число на цифры
///
/// Например, `12345` -> `[1, 2, 3, 4, 5]`
List<int> splitByDigits(int x) {
if (x == 0) return [];
return []
..addAll(splitByDigits((x / 10).floor()))
..add(x % 10);
}
/// Формирует класс числа из трёх цифр
Tuple3 classFromList(List<int> digits) {
if (digits.isEmpty) {
return Tuple3(0, 0, 0);
} else if (digits.length == 1) {
return Tuple3(0, 0, digits[0]);
} else if (digits.length == 2) {
return Tuple3(0, digits[0], digits[1]);
} else if (digits.length == 3) {
return Tuple3(digits[0], digits[1], digits[2]);
} else {
throw (ArgumentError('Expected 3 digits'));
}
}
/// Разбивает число на классы
///
/// Например, `12345` -> `[(0, 1, 2), (3, 4, 5)]`
List<Tuple3> splitByClasses(int x) => x == 0
? []
: ([]
..addAll(splitByClasses((x / 1000).floor()))
..add(classFromList(splitByDigits(x % 1000))));
/// Называет число разбитое на классы
///
/// [x] - число разбитое на классы (например, `[(0, 1, 2), (3, 4, 5)]`)
/// [gender] - род числительного
String sayParsedNumber(List<Tuple3> x, {Gender gender = Gender.male}) {
if (x.length == 4) {
return ([sayBillions(x[0])]
..add(sayParsedNumber(x.sublist(1), gender: gender)))
.join(' ');
}
if (x.length == 3) {
return ([sayMillions(x[0])]
..add(sayParsedNumber(x.sublist(1), gender: gender)))
.join(' ');
}
if (x.length == 2) {
return ([sayThousands(x[0])]
..add(sayParsedNumber(x.sublist(1), gender: gender)))
.join(' ');
}
if (x.length == 1) {
return sayOnes(x[0], gender: gender);
}
return "";
}
/// Называет число [x]
///
/// [gender] - род числительного.
///
/// Например,
/// ```
/// print(sayNumber(12345)); // "двенадцать тысяч триста сорок пять"
/// ```
String sayNumber(int x, {Gender gender = Gender.male}) =>
x==0 ? 'ноль' : sayParsedNumber(splitByClasses(x), gender: gender)
.replaceAll(RegExp(r'\s+'), ' ');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment