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.

146 lines
3.4 KiB

  1. /**
  2. * Group digits.
  3. */
  4. type GroupDigits = string;
  5. /**
  6. * Group place.
  7. */
  8. export type GroupPlace = bigint;
  9. /**
  10. * Group of digits and its place.
  11. *
  12. * The place refers to the order which the digits are grouped, e.g. for a number like
  13. *
  14. * 1,234,567
  15. *
  16. * The groups would be:
  17. *
  18. * ['001', 2]
  19. * ['234', 1]
  20. * ['567', 0]
  21. *
  22. * Note that groups do not necessarily have the same length of digits, such in the case of
  23. * South Asian numbering system:
  24. *
  25. * 1,00,00,000
  26. *
  27. * The groups would be:
  28. *
  29. * ['01', 3]
  30. * ['00', 2]
  31. * ['00', 1]
  32. * ['000', 0]
  33. */
  34. export type Group = [GroupDigits, GroupPlace];
  35. /**
  36. * Index of the group digits in a {@link Group|group}.
  37. */
  38. export const GROUP_DIGITS_INDEX = 0 as const;
  39. /**
  40. * Index of the group place in a {@link Group|group}.
  41. */
  42. export const GROUP_PLACE_INDEX = 1 as const;
  43. /**
  44. * Result of parsing a number string.
  45. */
  46. export interface ParseResult {
  47. /**
  48. * The parsed groups.
  49. */
  50. groups: Group[];
  51. /**
  52. * Whether the number is negative.
  53. */
  54. negative: boolean;
  55. }
  56. /**
  57. * System for stringifying and parsing numbers.
  58. */
  59. export interface NumberNameSystem {
  60. /**
  61. * Creates a negative string.
  62. * @param s - The string to make negative.
  63. * @returns string The negative string.
  64. */
  65. makeNegative: (s: string) => string;
  66. /**
  67. * Splits a string into groups.
  68. * @param value - The string to group.
  69. * @see {NumberNameSystem.combineGroups}
  70. * @returns Group[] The groups.
  71. */
  72. splitIntoGroups: (value: string) => Group[];
  73. /**
  74. * Creates a group string.
  75. * @param groups - The groups.
  76. * @param options - Options to use when creating the group.
  77. * @see {NumberNameSystem.parseGroups}
  78. * @returns string[] The groups represented into strings.
  79. */
  80. stringifyGroups: <T extends object>(groups: Group[], options?: T) => string[];
  81. /**
  82. * Merges tokens from stringified groups to a string.
  83. * @param tokens - The tokens to finalize.
  84. * @see {NumberNameSystem.tokenize}
  85. * @returns string The merged tokens.
  86. */
  87. mergeTokens: <T extends object>(tokens: string[], options?: T) => string;
  88. /**
  89. * Tokenizes a string.
  90. * @param value - The string to tokenize.
  91. * @see {NumberNameSystem.mergeTokens}
  92. * @returns string[] The tokens.
  93. */
  94. tokenize: (value: string) => string[];
  95. /**
  96. * Parses groups from a string.
  97. * @param tokens - The string to parse groups from.
  98. * @see {NumberNameSystem.stringifyGroups}
  99. * @returns Group[] The parsed groups.
  100. */
  101. parseGroups: (tokens: string[]) => ParseResult;
  102. /**
  103. * Combines groups into a string.
  104. * @param groups - The groups to combine.
  105. * @param negative - Whether the number is negative.
  106. * @see {NumberNameSystem.splitIntoGroups}
  107. * @returns string The combined groups in exponential form.
  108. */
  109. combineGroups: (groups: Group[], negative: boolean) => string;
  110. }
  111. /**
  112. * Error thrown when an invalid token is encountered.
  113. */
  114. export class InvalidTokenError extends Error {
  115. constructor(token: string) {
  116. super(`Invalid token: ${token}`);
  117. }
  118. }
  119. export const bigIntMax = (...b: bigint[]) => b.reduce(
  120. (previousMax, current) => {
  121. if (typeof previousMax === 'undefined') {
  122. return current;
  123. }
  124. return previousMax > current ? previousMax : current;
  125. },
  126. undefined as bigint | undefined,
  127. );
  128. export const bigIntMin = (...b: bigint[]) => b.reduce(
  129. (previousMin, current) => {
  130. if (typeof previousMin === 'undefined') {
  131. return current;
  132. }
  133. return previousMin < current ? previousMin : current;
  134. },
  135. undefined as bigint | undefined,
  136. );