Gets the name of a number, even if it's stupidly big. Supersedes TheoryOfNekomata/number-name.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

367 lines
13 KiB

  1. import { describe, it, expect } from 'vitest';
  2. import {
  3. AllowedValue,
  4. parse,
  5. stringify,
  6. systems,
  7. } from '../../../src';
  8. const options = {
  9. system: systems.enUK.shortCount,
  10. };
  11. const doExpect = (
  12. value: AllowedValue,
  13. stringified: string,
  14. parsedValue: AllowedValue = value,
  15. ) => {
  16. const stringifyOptions = {
  17. ...options,
  18. };
  19. const parseOptions = {
  20. ...options,
  21. type: typeof value as ('number' | 'string' | 'bigint'),
  22. };
  23. expect(stringify(value, stringifyOptions)).toBe(stringified);
  24. expect(parse(stringified, parseOptions)).toBe(parsedValue);
  25. };
  26. describe('British short count', () => {
  27. it('returns empty string for empty input', () => {
  28. expect(parse('')).toBe('');
  29. });
  30. it('formats doubly negative numbers to be positive', () => {
  31. expect(stringify('--1')).toBe('one');
  32. expect(stringify('----1')).toBe('one');
  33. });
  34. describe('error handling', () => {
  35. it('throws an error when given fully unparseable input', () => {
  36. expect(() => parse('foo')).toThrow();
  37. });
  38. it('throws an error when given partially unparseable input', () => {
  39. expect(() => parse('one milliamilliafootillion')).toThrow();
  40. expect(() => parse('one millia^foo-tillion')).toThrow();
  41. expect(() => parse('one millia^0-tillion')).toThrow();
  42. });
  43. });
  44. describe('options', () => {
  45. it('formats one group per line', () => {
  46. expect(stringify(1234567, { mergeTokensOptions: { oneGroupPerLine: true } })).toBe(`one million
  47. two hundred thirty-four thousand
  48. five hundred sixty-seven`);
  49. });
  50. });
  51. describe('individual cases', () => {
  52. const longNumberValue1 = '123,456,789,012,345,678,901,234,567,890';
  53. const longNumberStringified1 = [
  54. 'one hundred twenty-three octillion',
  55. 'four hundred fifty-six septillion',
  56. 'seven hundred eighty-nine sextillion',
  57. 'twelve quintillion',
  58. 'three hundred forty-five quadrillion',
  59. 'six hundred seventy-eight trillion',
  60. 'nine hundred one billion',
  61. 'two hundred thirty-four million',
  62. 'five hundred sixty-seven thousand',
  63. 'eight hundred ninety',
  64. ].join(' ');
  65. const longNumberParsedValue = '1.2345678901234567890123456789e+29';
  66. it.each`
  67. value | stringified | parsedValue
  68. ${-1000000} | ${'negative one million'} | ${-1000000}
  69. ${-1000500} | ${'negative one million five hundred'} | ${-1000500}
  70. ${1000} | ${'one thousand'} | ${1000}
  71. ${10000} | ${'ten thousand'} | ${10000}
  72. ${12012} | ${'twelve thousand twelve'} | ${12012}
  73. ${12020} | ${'twelve thousand twenty'} | ${12020}
  74. ${20000} | ${'twenty thousand'} | ${20000}
  75. ${100000} | ${'one hundred thousand'} | ${100000}
  76. ${123456} | ${'one hundred twenty-three thousand four hundred fifty-six'} | ${123456}
  77. ${'1000005000000'} | ${'one trillion five million'} | ${'1.000005e+12'}
  78. ${longNumberValue1} | ${longNumberStringified1} | ${longNumberParsedValue}
  79. ${'1e3006'} | ${'one milliauntillion'} | ${'1e+3006'}
  80. `('converts $value to $stringified', ({
  81. value,
  82. stringified,
  83. parsedValue,
  84. }: { value: AllowedValue, stringified: string, parsedValue: AllowedValue }) => {
  85. doExpect(value, stringified, parsedValue);
  86. });
  87. it('converts one millia^1-tillion to 1e+3003', () => {
  88. expect(parse('one millia^1-tillion')).toBe('1e+3003');
  89. });
  90. });
  91. describe('blanket cases', () => {
  92. it('converts one millia^2-tillion', () => {
  93. const value = '1e+3000003';
  94. const stringified = 'one millia^2-tillion';
  95. expect(parse(stringified)).toBe(value);
  96. expect(stringify(value, { stringifyGroupsOptions: { shortenMillia: true } }))
  97. .toBe(stringified);
  98. });
  99. it('converts one millia^2-unmilliatillion', () => {
  100. const value = '1e+3003003';
  101. const stringified = 'one millia^2-unmilliatillion';
  102. expect(stringify(value, { stringifyGroupsOptions: { shortenMillia: true } }))
  103. .toBe(stringified);
  104. expect(parse(stringified)).toBe(value);
  105. });
  106. it('converts one milliamilliaunmilliatillion', () => {
  107. const value = '1e+3003003';
  108. const stringified = 'one milliamilliaunmilliatillion';
  109. expect(stringify(value)).toBe(stringified);
  110. expect(parse(stringified)).toBe('1e+3003003');
  111. });
  112. });
  113. describe('general', () => {
  114. describe('0-9', () => {
  115. it.each`
  116. value | numberName
  117. ${0} | ${'zero'}
  118. ${1} | ${'one'}
  119. ${2} | ${'two'}
  120. ${3} | ${'three'}
  121. ${4} | ${'four'}
  122. ${5} | ${'five'}
  123. ${6} | ${'six'}
  124. ${7} | ${'seven'}
  125. ${8} | ${'eight'}
  126. ${9} | ${'nine'}
  127. `('converts $ones to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  128. doExpect(value, numberName);
  129. });
  130. });
  131. describe('10-19', () => {
  132. it.each`
  133. value | numberName
  134. ${10} | ${'ten'}
  135. ${11} | ${'eleven'}
  136. ${12} | ${'twelve'}
  137. ${13} | ${'thirteen'}
  138. ${14} | ${'fourteen'}
  139. ${15} | ${'fifteen'}
  140. ${16} | ${'sixteen'}
  141. ${17} | ${'seventeen'}
  142. ${18} | ${'eighteen'}
  143. ${19} | ${'nineteen'}
  144. `('converts $tenPlusOnes to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  145. doExpect(value, numberName);
  146. });
  147. });
  148. describe.each`
  149. tensStart | tensEnd | tensBase
  150. ${20} | ${29} | ${'twenty'}
  151. ${30} | ${39} | ${'thirty'}
  152. ${40} | ${49} | ${'forty'}
  153. ${50} | ${59} | ${'fifty'}
  154. ${60} | ${69} | ${'sixty'}
  155. ${70} | ${79} | ${'seventy'}
  156. ${80} | ${89} | ${'eighty'}
  157. ${90} | ${99} | ${'ninety'}
  158. `('$tensStart-$tensEnd', ({
  159. tensStart, tensBase,
  160. }: { tensStart: number, tensBase: string }) => {
  161. it.each`
  162. value | numberName
  163. ${tensStart} | ${tensBase}
  164. ${tensStart + 1} | ${`${tensBase}-one`}
  165. ${tensStart + 2} | ${`${tensBase}-two`}
  166. ${tensStart + 3} | ${`${tensBase}-three`}
  167. ${tensStart + 4} | ${`${tensBase}-four`}
  168. ${tensStart + 5} | ${`${tensBase}-five`}
  169. ${tensStart + 6} | ${`${tensBase}-six`}
  170. ${tensStart + 7} | ${`${tensBase}-seven`}
  171. ${tensStart + 8} | ${`${tensBase}-eight`}
  172. ${tensStart + 9} | ${`${tensBase}-nine`}
  173. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  174. doExpect(value, numberName);
  175. });
  176. });
  177. describe.each`
  178. hundredsStart | hundredsEnd | hundredsBase
  179. ${100} | ${199} | ${'one hundred'}
  180. ${200} | ${299} | ${'two hundred'}
  181. ${300} | ${399} | ${'three hundred'}
  182. ${400} | ${499} | ${'four hundred'}
  183. ${500} | ${599} | ${'five hundred'}
  184. ${600} | ${699} | ${'six hundred'}
  185. ${700} | ${799} | ${'seven hundred'}
  186. ${800} | ${899} | ${'eight hundred'}
  187. ${900} | ${999} | ${'nine hundred'}
  188. `('$hundredsStart-$hundredsEnd', ({
  189. hundredsStart, hundredsBase,
  190. }: { hundredsStart: number, hundredsBase: string }) => {
  191. describe(`${hundredsStart}-${hundredsStart + 9}`, () => {
  192. it.each`
  193. value | numberName
  194. ${hundredsStart} | ${hundredsBase}
  195. ${hundredsStart + 1} | ${`${hundredsBase} one`}
  196. ${hundredsStart + 2} | ${`${hundredsBase} two`}
  197. ${hundredsStart + 3} | ${`${hundredsBase} three`}
  198. ${hundredsStart + 4} | ${`${hundredsBase} four`}
  199. ${hundredsStart + 5} | ${`${hundredsBase} five`}
  200. ${hundredsStart + 6} | ${`${hundredsBase} six`}
  201. ${hundredsStart + 7} | ${`${hundredsBase} seven`}
  202. ${hundredsStart + 8} | ${`${hundredsBase} eight`}
  203. ${hundredsStart + 9} | ${`${hundredsBase} nine`}
  204. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  205. doExpect(value, numberName);
  206. });
  207. });
  208. describe(`${hundredsStart + 10}-${hundredsStart + 19}`, () => {
  209. it.each`
  210. value | numberName
  211. ${hundredsStart + 10} | ${`${hundredsBase} ten`}
  212. ${hundredsStart + 11} | ${`${hundredsBase} eleven`}
  213. ${hundredsStart + 12} | ${`${hundredsBase} twelve`}
  214. ${hundredsStart + 13} | ${`${hundredsBase} thirteen`}
  215. ${hundredsStart + 14} | ${`${hundredsBase} fourteen`}
  216. ${hundredsStart + 15} | ${`${hundredsBase} fifteen`}
  217. ${hundredsStart + 16} | ${`${hundredsBase} sixteen`}
  218. ${hundredsStart + 17} | ${`${hundredsBase} seventeen`}
  219. ${hundredsStart + 18} | ${`${hundredsBase} eighteen`}
  220. ${hundredsStart + 19} | ${`${hundredsBase} nineteen`}
  221. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  222. doExpect(value, numberName);
  223. });
  224. });
  225. describe.each`
  226. start | end | base
  227. ${20} | ${29} | ${'twenty'}
  228. ${30} | ${39} | ${'thirty'}
  229. ${40} | ${49} | ${'forty'}
  230. ${50} | ${59} | ${'fifty'}
  231. ${60} | ${69} | ${'sixty'}
  232. ${70} | ${79} | ${'seventy'}
  233. ${80} | ${89} | ${'eighty'}
  234. ${90} | ${99} | ${'ninety'}
  235. `('$start-$end', ({
  236. start, base,
  237. }: { start: number, base: string }) => {
  238. it.each`
  239. value | numberName
  240. ${hundredsStart + start} | ${`${hundredsBase} ${base}`}
  241. ${hundredsStart + start + 1} | ${`${hundredsBase} ${base}-one`}
  242. ${hundredsStart + start + 2} | ${`${hundredsBase} ${base}-two`}
  243. ${hundredsStart + start + 3} | ${`${hundredsBase} ${base}-three`}
  244. ${hundredsStart + start + 4} | ${`${hundredsBase} ${base}-four`}
  245. ${hundredsStart + start + 5} | ${`${hundredsBase} ${base}-five`}
  246. ${hundredsStart + start + 6} | ${`${hundredsBase} ${base}-six`}
  247. ${hundredsStart + start + 7} | ${`${hundredsBase} ${base}-seven`}
  248. ${hundredsStart + start + 8} | ${`${hundredsBase} ${base}-eight`}
  249. ${hundredsStart + start + 9} | ${`${hundredsBase} ${base}-nine`}
  250. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  251. doExpect(value, numberName);
  252. });
  253. });
  254. });
  255. });
  256. it.each`
  257. value | numberName
  258. ${'1e+6'} | ${'one million'}
  259. ${'1e+9'} | ${'one billion'}
  260. ${'1e+12'} | ${'one trillion'}
  261. ${'1e+15'} | ${'one quadrillion'}
  262. ${'1e+18'} | ${'one quintillion'}
  263. ${'1e+21'} | ${'one sextillion'}
  264. ${'1e+24'} | ${'one septillion'}
  265. ${'1e+27'} | ${'one octillion'}
  266. ${'1e+30'} | ${'one nonillion'}
  267. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  268. doExpect(value, numberName);
  269. });
  270. it.each`
  271. value | numberName
  272. ${'1e+33'} | ${'one decillion'}
  273. ${'1e+36'} | ${'one undecillion'}
  274. ${'1e+39'} | ${'one duodecillion'}
  275. ${'1e+42'} | ${'one tredecillion'}
  276. ${'1e+45'} | ${'one quattuordecillion'}
  277. ${'1e+48'} | ${'one quindecillion'}
  278. ${'1e+51'} | ${'one sexdecillion'}
  279. ${'1e+54'} | ${'one septendecillion'}
  280. ${'1e+57'} | ${'one octodecillion'}
  281. ${'1e+60'} | ${'one novemdecillion'}
  282. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  283. doExpect(value, numberName);
  284. });
  285. it.each`
  286. value | numberName
  287. ${'1e+63'} | ${'one vigintillion'}
  288. ${'1e+66'} | ${'one unvigintillion'}
  289. ${'1e+69'} | ${'one duovigintillion'}
  290. ${'1e+72'} | ${'one trevigintillion'}
  291. ${'1e+75'} | ${'one quattuorvigintillion'}
  292. ${'1e+78'} | ${'one quinvigintillion'}
  293. ${'1e+81'} | ${'one sexvigintillion'}
  294. ${'1e+84'} | ${'one septenvigintillion'}
  295. ${'1e+87'} | ${'one octovigintillion'}
  296. ${'1e+90'} | ${'one novemvigintillion'}
  297. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  298. doExpect(value, numberName);
  299. });
  300. it.each`
  301. value | numberName
  302. ${'1e+93'} | ${'one trigintillion'}
  303. ${'1e+123'} | ${'one quadragintillion'}
  304. ${'1e+153'} | ${'one quinquagintillion'}
  305. ${'1e+183'} | ${'one sexagintillion'}
  306. ${'1e+213'} | ${'one septuagintillion'}
  307. ${'1e+243'} | ${'one octogintillion'}
  308. ${'1e+273'} | ${'one nonagintillion'}
  309. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  310. doExpect(value, numberName);
  311. });
  312. it.each`
  313. value | numberName
  314. ${'1e+303'} | ${'one centillion'}
  315. ${'1e+306'} | ${'one cenuntillion'}
  316. ${'1e+309'} | ${'one cenduotillion'}
  317. ${'1e+312'} | ${'one centretillion'}
  318. ${'1e+315'} | ${'one cenquattuortillion'}
  319. ${'1e+318'} | ${'one cenquintillion'}
  320. ${'1e+321'} | ${'one censextillion'}
  321. ${'1e+324'} | ${'one censeptentillion'}
  322. ${'1e+327'} | ${'one cenoctotillion'}
  323. ${'1e+330'} | ${'one cennovemtillion'}
  324. ${'1e+603'} | ${'one duocentillion'}
  325. ${'1e+903'} | ${'one trecentillion'}
  326. ${'1e+1203'} | ${'one quadringentillion'}
  327. ${'1e+1503'} | ${'one quingentillion'}
  328. ${'1e+1803'} | ${'one sescentillion'}
  329. ${'1e+2103'} | ${'one septingentillion'}
  330. ${'1e+2403'} | ${'one octingentillion'}
  331. ${'1e+2703'} | ${'one nongentillion'}
  332. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  333. doExpect(value, numberName);
  334. });
  335. it('converts \'1e+3003\' to \'one milliatillion\'', () => {
  336. doExpect('1e+3003', 'one milliatillion');
  337. });
  338. });