Gets the name of a number, even if it's stupidly big. Supersedes TheoryOfNekomata/number-name.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 

114 rader
3.0 KiB

  1. import BigNumber from 'bignumber.js';
  2. import {
  3. BLANK_DIGIT,
  4. createBlankDigits,
  5. deconstructNumeric,
  6. groupDigits,
  7. NEGATIVE_SIGN,
  8. normalizeNumeric,
  9. Numeric,
  10. } from '../../../utils/numeric';
  11. import getLatinPowerName from '../../../utils/common/latinPowers';
  12. import {getGroupDigitsName} from '../../common/de';
  13. const config = {
  14. thousandName: 'tausend',
  15. thousandOrdinalName: 'tausendste',
  16. millia: 'millia',
  17. illion: 'illion',
  18. illionth: 'illionste',
  19. illiard: 'illiarde',
  20. illiardth: 'illiardste',
  21. hundredsLatinNames: [
  22. '',
  23. 'cen',
  24. 'duocen',
  25. 'trecen',
  26. 'quadringen',
  27. 'quingen',
  28. 'sescen',
  29. 'septingen',
  30. 'octingen',
  31. 'nongen',
  32. ],
  33. onesLatinNames: ['', 'un', 'duo', 'tre', 'quattuor', 'quin', 'sex', 'septen', 'octo', 'novem'],
  34. tensLatinNames: [
  35. '',
  36. 'dec',
  37. 'vigin',
  38. 'trigin',
  39. 'quadragin',
  40. 'quinquagin',
  41. 'sexagin',
  42. 'septuagin',
  43. 'octogin',
  44. 'nonagin',
  45. ],
  46. onesSpecialLatinNames: ['', 'm', 'b', 'tr', 'quadr', 'quin', 'sex', 'sep', 'oct', 'non'],
  47. negative: 'negative',
  48. grouping: 3,
  49. };
  50. const getGroupIndexName = (index: BigNumber, digits: string, ordinal: boolean) => {
  51. if (index.eq(1)) {
  52. return ordinal ? config.thousandOrdinalName : config.thousandName;
  53. }
  54. const basicIndex = index.dividedToIntegerBy(2);
  55. const isOdd = index.mod(2).eq(1);
  56. const latinPowerName = getLatinPowerName(basicIndex, isOdd, ordinal, config);
  57. const latinPowerNameWithCase = latinPowerName.slice(0, 1).toUpperCase() + latinPowerName.slice(1);
  58. if (digits.padStart(config.grouping, BLANK_DIGIT) === '001' || ordinal) {
  59. return latinPowerNameWithCase;
  60. }
  61. if (latinPowerNameWithCase.endsWith('e')) {
  62. return latinPowerNameWithCase + 'n';
  63. }
  64. return latinPowerNameWithCase + 'en';
  65. };
  66. const getGroupName = (g: [string, BigNumber], ordinal: boolean) => {
  67. const [digits, index] = g;
  68. if (index.lt(1)) {
  69. return getGroupDigitsName(digits, index, ordinal);
  70. }
  71. if (index.lt(2)) {
  72. return [getGroupDigitsName(digits, index, false), getGroupIndexName(index, digits, ordinal)].join('');
  73. }
  74. return [getGroupDigitsName(digits, index, false), getGroupIndexName(index, digits, ordinal)].join(' ');
  75. };
  76. type Options = {
  77. groupSeparator: string,
  78. ordinal: boolean,
  79. }
  80. const getLocalizedNumberName = (xRaw: Numeric, options = {} as Partial<Options>) => {
  81. const {
  82. groupSeparator = ' ',
  83. ordinal = false,
  84. } = options;
  85. const x = normalizeNumeric(xRaw);
  86. const {significandDigits, exponent} = deconstructNumeric(x);
  87. const blankDigits = createBlankDigits(config.grouping);
  88. const groups = groupDigits(significandDigits, exponent, config.grouping);
  89. if (groups.length === 1) {
  90. return getGroupName(groups[0], ordinal);
  91. }
  92. const base = groups
  93. .filter(([digits]) => digits !== blankDigits)
  94. .map((g, i, gg) => getGroupName(g, ordinal ? i === gg.length - 1 : false))
  95. .join(groupSeparator);
  96. if (x.startsWith(NEGATIVE_SIGN)) {
  97. return [config.negative, base].join(' ');
  98. }
  99. return base;
  100. };
  101. export default getLocalizedNumberName;