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.
 
 

373 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.longCount,
  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 long 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 thousand quadrillion',
  55. 'four hundred fifty-six quadrillion',
  56. 'seven hundred eighty-nine thousand trillion',
  57. 'twelve trillion',
  58. 'three hundred forty-five thousand billion',
  59. 'six hundred seventy-eight billion',
  60. 'nine hundred one thousand million',
  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. ${1000} | ${'one thousand'} | ${1000}
  69. ${10000} | ${'ten thousand'} | ${10000}
  70. ${12012} | ${'twelve thousand twelve'} | ${12012}
  71. ${12020} | ${'twelve thousand twenty'} | ${12020}
  72. ${100000} | ${'one hundred thousand'} | ${100000}
  73. ${123456} | ${'one hundred twenty-three thousand four hundred fifty-six'} | ${123456}
  74. ${'1000005000000'} | ${'one billion five million'} | ${'1.000005e+12'}
  75. ${longNumberValue1} | ${longNumberStringified1} | ${longNumberParsedValue}
  76. ${'1e6006'} | ${'one milliauntillion'} | ${'1e+6006'}
  77. `('converts $value to $stringified', ({
  78. value,
  79. stringified,
  80. parsedValue,
  81. }: { value: AllowedValue, stringified: string, parsedValue: AllowedValue }) => {
  82. doExpect(value, stringified, parsedValue);
  83. });
  84. it('converts one millia^1-tillion to 1e+3003', () => {
  85. expect(parse('one millia^1-tillion')).toBe('1e+3003');
  86. });
  87. });
  88. describe('blanket cases', () => {
  89. it('converts one millia^2-tillion', () => {
  90. const value = '1e+3000003';
  91. const stringified = 'one millia^2-tillion';
  92. expect(parse(stringified)).toBe(value);
  93. expect(stringify(value, { stringifyGroupsOptions: { shortenMillia: true } }))
  94. .toBe(stringified);
  95. });
  96. it('converts one millia^2-unmilliatillion', () => {
  97. const value = '1e+3003003';
  98. const stringified = 'one millia^2-unmilliatillion';
  99. expect(stringify(value, { stringifyGroupsOptions: { shortenMillia: true } }))
  100. .toBe(stringified);
  101. expect(parse(stringified)).toBe(value);
  102. });
  103. it('converts one milliamilliaunmilliatillion', () => {
  104. const value = '1e+3003003';
  105. const stringified = 'one milliamilliaunmilliatillion';
  106. expect(stringify(value)).toBe(stringified);
  107. expect(parse(stringified)).toBe('1e+3003003');
  108. });
  109. });
  110. describe('general', () => {
  111. describe('0-9', () => {
  112. it.each`
  113. value | numberName
  114. ${0} | ${'zero'}
  115. ${1} | ${'one'}
  116. ${2} | ${'two'}
  117. ${3} | ${'three'}
  118. ${4} | ${'four'}
  119. ${5} | ${'five'}
  120. ${6} | ${'six'}
  121. ${7} | ${'seven'}
  122. ${8} | ${'eight'}
  123. ${9} | ${'nine'}
  124. `('converts $ones to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  125. doExpect(value, numberName);
  126. });
  127. });
  128. describe('10-19', () => {
  129. it.each`
  130. value | numberName
  131. ${10} | ${'ten'}
  132. ${11} | ${'eleven'}
  133. ${12} | ${'twelve'}
  134. ${13} | ${'thirteen'}
  135. ${14} | ${'fourteen'}
  136. ${15} | ${'fifteen'}
  137. ${16} | ${'sixteen'}
  138. ${17} | ${'seventeen'}
  139. ${18} | ${'eighteen'}
  140. ${19} | ${'nineteen'}
  141. `('converts $tenPlusOnes to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  142. doExpect(value, numberName);
  143. });
  144. });
  145. describe.each`
  146. tensStart | tensEnd | tensBase
  147. ${20} | ${29} | ${'twenty'}
  148. ${30} | ${39} | ${'thirty'}
  149. ${40} | ${49} | ${'forty'}
  150. ${50} | ${59} | ${'fifty'}
  151. ${60} | ${69} | ${'sixty'}
  152. ${70} | ${79} | ${'seventy'}
  153. ${80} | ${89} | ${'eighty'}
  154. ${90} | ${99} | ${'ninety'}
  155. `('$tensStart-$tensEnd', ({
  156. tensStart, tensBase,
  157. }: { tensStart: number, tensBase: string }) => {
  158. it.each`
  159. value | numberName
  160. ${tensStart} | ${tensBase}
  161. ${tensStart + 1} | ${`${tensBase}-one`}
  162. ${tensStart + 2} | ${`${tensBase}-two`}
  163. ${tensStart + 3} | ${`${tensBase}-three`}
  164. ${tensStart + 4} | ${`${tensBase}-four`}
  165. ${tensStart + 5} | ${`${tensBase}-five`}
  166. ${tensStart + 6} | ${`${tensBase}-six`}
  167. ${tensStart + 7} | ${`${tensBase}-seven`}
  168. ${tensStart + 8} | ${`${tensBase}-eight`}
  169. ${tensStart + 9} | ${`${tensBase}-nine`}
  170. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  171. doExpect(value, numberName);
  172. });
  173. });
  174. describe.each`
  175. hundredsStart | hundredsEnd | hundredsBase
  176. ${100} | ${199} | ${'one hundred'}
  177. ${200} | ${299} | ${'two hundred'}
  178. ${300} | ${399} | ${'three hundred'}
  179. ${400} | ${499} | ${'four hundred'}
  180. ${500} | ${599} | ${'five hundred'}
  181. ${600} | ${699} | ${'six hundred'}
  182. ${700} | ${799} | ${'seven hundred'}
  183. ${800} | ${899} | ${'eight hundred'}
  184. ${900} | ${999} | ${'nine hundred'}
  185. `('$hundredsStart-$hundredsEnd', ({
  186. hundredsStart, hundredsBase,
  187. }: { hundredsStart: number, hundredsBase: string }) => {
  188. describe(`${hundredsStart}-${hundredsStart + 9}`, () => {
  189. it.each`
  190. value | numberName
  191. ${hundredsStart} | ${hundredsBase}
  192. ${hundredsStart + 1} | ${`${hundredsBase} one`}
  193. ${hundredsStart + 2} | ${`${hundredsBase} two`}
  194. ${hundredsStart + 3} | ${`${hundredsBase} three`}
  195. ${hundredsStart + 4} | ${`${hundredsBase} four`}
  196. ${hundredsStart + 5} | ${`${hundredsBase} five`}
  197. ${hundredsStart + 6} | ${`${hundredsBase} six`}
  198. ${hundredsStart + 7} | ${`${hundredsBase} seven`}
  199. ${hundredsStart + 8} | ${`${hundredsBase} eight`}
  200. ${hundredsStart + 9} | ${`${hundredsBase} nine`}
  201. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  202. doExpect(value, numberName);
  203. });
  204. });
  205. describe(`${hundredsStart + 10}-${hundredsStart + 19}`, () => {
  206. it.each`
  207. value | numberName
  208. ${hundredsStart + 10} | ${`${hundredsBase} ten`}
  209. ${hundredsStart + 11} | ${`${hundredsBase} eleven`}
  210. ${hundredsStart + 12} | ${`${hundredsBase} twelve`}
  211. ${hundredsStart + 13} | ${`${hundredsBase} thirteen`}
  212. ${hundredsStart + 14} | ${`${hundredsBase} fourteen`}
  213. ${hundredsStart + 15} | ${`${hundredsBase} fifteen`}
  214. ${hundredsStart + 16} | ${`${hundredsBase} sixteen`}
  215. ${hundredsStart + 17} | ${`${hundredsBase} seventeen`}
  216. ${hundredsStart + 18} | ${`${hundredsBase} eighteen`}
  217. ${hundredsStart + 19} | ${`${hundredsBase} nineteen`}
  218. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  219. doExpect(value, numberName);
  220. });
  221. });
  222. describe.each`
  223. start | end | base
  224. ${20} | ${29} | ${'twenty'}
  225. ${30} | ${39} | ${'thirty'}
  226. ${40} | ${49} | ${'forty'}
  227. ${50} | ${59} | ${'fifty'}
  228. ${60} | ${69} | ${'sixty'}
  229. ${70} | ${79} | ${'seventy'}
  230. ${80} | ${89} | ${'eighty'}
  231. ${90} | ${99} | ${'ninety'}
  232. `('$start-$end', ({
  233. start, base,
  234. }: { start: number, base: string }) => {
  235. it.each`
  236. value | numberName
  237. ${hundredsStart + start} | ${`${hundredsBase} ${base}`}
  238. ${hundredsStart + start + 1} | ${`${hundredsBase} ${base}-one`}
  239. ${hundredsStart + start + 2} | ${`${hundredsBase} ${base}-two`}
  240. ${hundredsStart + start + 3} | ${`${hundredsBase} ${base}-three`}
  241. ${hundredsStart + start + 4} | ${`${hundredsBase} ${base}-four`}
  242. ${hundredsStart + start + 5} | ${`${hundredsBase} ${base}-five`}
  243. ${hundredsStart + start + 6} | ${`${hundredsBase} ${base}-six`}
  244. ${hundredsStart + start + 7} | ${`${hundredsBase} ${base}-seven`}
  245. ${hundredsStart + start + 8} | ${`${hundredsBase} ${base}-eight`}
  246. ${hundredsStart + start + 9} | ${`${hundredsBase} ${base}-nine`}
  247. `('converts $value to $numberName', ({ value, numberName }: { value: number, numberName: string }) => {
  248. doExpect(value, numberName);
  249. });
  250. });
  251. });
  252. });
  253. it.each`
  254. value | numberName
  255. ${'1e+6'} | ${'one million'}
  256. ${'1e+9'} | ${'one thousand million'}
  257. ${'1e+12'} | ${'one billion'}
  258. ${'1e+15'} | ${'one thousand billion'}
  259. ${'1e+18'} | ${'one trillion'}
  260. ${'1e+21'} | ${'one thousand trillion'}
  261. ${'1e+24'} | ${'one quadrillion'}
  262. ${'1e+27'} | ${'one thousand quadrillion'}
  263. ${'1e+30'} | ${'one quintillion'}
  264. ${'1e+33'} | ${'one thousand quintillion'}
  265. ${'1e+36'} | ${'one sextillion'}
  266. ${'1e+39'} | ${'one thousand sextillion'}
  267. ${'1e+42'} | ${'one septillion'}
  268. ${'1e+45'} | ${'one thousand septillion'}
  269. ${'1e+48'} | ${'one octillion'}
  270. ${'1e+51'} | ${'one thousand octillion'}
  271. ${'1e+54'} | ${'one nonillion'}
  272. ${'1e+57'} | ${'one thousand nonillion'}
  273. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  274. doExpect(value, numberName);
  275. });
  276. it.each`
  277. value | numberName
  278. ${'1e+60'} | ${'one decillion'}
  279. ${'1e+66'} | ${'one undecillion'}
  280. ${'1e+72'} | ${'one duodecillion'}
  281. ${'1e+78'} | ${'one tredecillion'}
  282. ${'1e+84'} | ${'one quattuordecillion'}
  283. ${'1e+90'} | ${'one quindecillion'}
  284. ${'1e+96'} | ${'one sexdecillion'}
  285. ${'1e+102'} | ${'one septendecillion'}
  286. ${'1e+108'} | ${'one octodecillion'}
  287. ${'1e+114'} | ${'one novemdecillion'}
  288. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  289. doExpect(value, numberName);
  290. });
  291. it.each`
  292. value | numberName
  293. ${'1e+120'} | ${'one vigintillion'}
  294. ${'1e+126'} | ${'one unvigintillion'}
  295. ${'1e+132'} | ${'one duovigintillion'}
  296. ${'1e+138'} | ${'one trevigintillion'}
  297. ${'1e+144'} | ${'one quattuorvigintillion'}
  298. ${'1e+150'} | ${'one quinvigintillion'}
  299. ${'1e+156'} | ${'one sexvigintillion'}
  300. ${'1e+162'} | ${'one septenvigintillion'}
  301. ${'1e+168'} | ${'one octovigintillion'}
  302. ${'1e+174'} | ${'one novemvigintillion'}
  303. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  304. doExpect(value, numberName);
  305. });
  306. it.each`
  307. value | numberName
  308. ${'1e+180'} | ${'one trigintillion'}
  309. ${'1e+240'} | ${'one quadragintillion'}
  310. ${'1e+300'} | ${'one quinquagintillion'}
  311. ${'1e+360'} | ${'one sexagintillion'}
  312. ${'1e+420'} | ${'one septuagintillion'}
  313. ${'1e+480'} | ${'one octogintillion'}
  314. ${'1e+540'} | ${'one nonagintillion'}
  315. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  316. doExpect(value, numberName);
  317. });
  318. it.each`
  319. value | numberName
  320. ${'1e+600'} | ${'one centillion'}
  321. ${'1e+606'} | ${'one cenuntillion'}
  322. ${'1e+612'} | ${'one cenduotillion'}
  323. ${'1e+618'} | ${'one centretillion'}
  324. ${'1e+624'} | ${'one cenquattuortillion'}
  325. ${'1e+630'} | ${'one cenquintillion'}
  326. ${'1e+636'} | ${'one censextillion'}
  327. ${'1e+642'} | ${'one censeptentillion'}
  328. ${'1e+648'} | ${'one cenoctotillion'}
  329. ${'1e+654'} | ${'one cennovemtillion'}
  330. ${'1e+1200'} | ${'one duocentillion'}
  331. ${'1e+1800'} | ${'one trecentillion'}
  332. ${'1e+2400'} | ${'one quadringentillion'}
  333. ${'1e+3000'} | ${'one quingentillion'}
  334. ${'1e+3600'} | ${'one sescentillion'}
  335. ${'1e+4200'} | ${'one septingentillion'}
  336. ${'1e+4800'} | ${'one octingentillion'}
  337. ${'1e+5400'} | ${'one nongentillion'}
  338. `('converts $value to $numberName', ({ value, numberName }: { value: string, numberName: string }) => {
  339. doExpect(value, numberName);
  340. });
  341. it('converts \'1e+6000\' to \'one milliatillion\'', () => {
  342. doExpect('1e+6000', 'one milliatillion');
  343. });
  344. });