Gets the name of a number, even if it's stupidly big. Supersedes TheoryOfNekomata/number-name.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

105 regels
2.5 KiB

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