@@ -48,44 +48,56 @@ export const GROUP_PLACE_INDEX = 1 as const; | |||||
/** | /** | ||||
* System for stringifying and parsing numbers. | * System for stringifying and parsing numbers. | ||||
*/ | */ | ||||
export interface StringifySystem { | |||||
export interface NumberNameSystem { | |||||
/** | /** | ||||
* Creates a negative string. | * Creates a negative string. | ||||
* @param s - The string to make negative. | * @param s - The string to make negative. | ||||
* @returns string The negative string. | |||||
*/ | */ | ||||
makeNegative: (s: string) => string; | makeNegative: (s: string) => string; | ||||
/** | /** | ||||
* Creates a group string. | |||||
* @param group - The group digits. | |||||
* @param place - The group place. | |||||
* @param options - Options to use when creating the group. | |||||
* Splits a string into groups. | |||||
* @param value - The string to group. | |||||
* @see {NumberNameSystem.combineGroups} | |||||
* @returns Group[] The groups. | |||||
*/ | */ | ||||
makeGroups: <T extends object>(groups: Group[], options?: T) => string[]; | |||||
splitIntoGroups: (value: string) => Group[]; | |||||
/** | /** | ||||
* Groups a string. | |||||
* @param value - The string to group. | |||||
* Creates a group string. | |||||
* @param groups - The groups. | |||||
* @param options - Options to use when creating the group. | |||||
* @see {NumberNameSystem.parseGroups} | |||||
* @returns string[] The groups represented into strings. | |||||
*/ | */ | ||||
group: (value: string) => Group[]; | |||||
stringifyGroups: <T extends object>(groups: Group[], options?: T) => string[]; | |||||
/** | /** | ||||
* Finalizes a string. | |||||
* Merges tokens from stringified groups to a string. | |||||
* @param tokens - The tokens to finalize. | * @param tokens - The tokens to finalize. | ||||
* @see {NumberNameSystem.tokenize} | |||||
* @returns string The merged tokens. | |||||
*/ | */ | ||||
finalize: <T extends object>(tokens: string[], options?: T) => string; | |||||
mergeTokens: <T extends object>(tokens: string[], options?: T) => string; | |||||
/** | /** | ||||
* Tokenizes a string. | * Tokenizes a string. | ||||
* @param value - The string to tokenize. | * @param value - The string to tokenize. | ||||
* @see {NumberNameSystem.mergeTokens} | |||||
* @returns string[] The tokens. | |||||
*/ | */ | ||||
tokenize: (value: string) => string[]; | tokenize: (value: string) => string[]; | ||||
/** | /** | ||||
* Parses groups from a string. | * Parses groups from a string. | ||||
* @param value - The string to parse groups from. | |||||
* @param tokens - The string to parse groups from. | |||||
* @see {NumberNameSystem.stringifyGroups} | |||||
* @returns Group[] The parsed groups. | |||||
*/ | */ | ||||
parseGroups: (value: string[]) => Group[]; | |||||
parseGroups: (tokens: string[]) => Group[]; | |||||
/** | /** | ||||
* Combines groups into a string. | * Combines groups into a string. | ||||
* @param value - The groups to combine. | |||||
* @param groups - The groups to combine. | |||||
* @see {NumberNameSystem.splitIntoGroups} | |||||
* @returns string The combined groups in exponential form. | |||||
*/ | */ | ||||
combineGroups: (value: Group[]) => string; | |||||
combineGroups: (groups: Group[]) => string; | |||||
} | } | ||||
/** | /** | ||||
@@ -1,5 +1,5 @@ | |||||
import { enUS } from './systems'; | import { enUS } from './systems'; | ||||
import { StringifySystem } from './common'; | |||||
import { NumberNameSystem } from './common'; | |||||
import { exponentialToNumberString, extractExponentialComponents, numberToExponential } from './exponent'; | import { exponentialToNumberString, extractExponentialComponents, numberToExponential } from './exponent'; | ||||
/** | /** | ||||
@@ -35,20 +35,23 @@ type ParseResult = typeof ALLOWED_PARSE_RESULT_TYPES[number]; | |||||
* Options to use when converting a value to a string. | * Options to use when converting a value to a string. | ||||
*/ | */ | ||||
export interface StringifyOptions< | export interface StringifyOptions< | ||||
TMakeGroupOptions extends object = object, | |||||
TFinalizeOptions extends object = object, | |||||
TStringifyGroupsOptions extends object, | |||||
TMergeTokensOptions extends object, | |||||
> { | > { | ||||
/** | /** | ||||
* The system to use when converting a value to a string. | * The system to use when converting a value to a string. | ||||
* | * | ||||
* Defaults to en-US (American short count). | * Defaults to en-US (American short count). | ||||
*/ | */ | ||||
system?: StringifySystem; | |||||
system?: NumberNameSystem; | |||||
/** | /** | ||||
* Options to use when making a group. This is used to override the default options for a group. | |||||
* Options to use when stringifying a single group. | |||||
*/ | */ | ||||
makeGroupOptions?: TMakeGroupOptions; | |||||
finalizeOptions?: TFinalizeOptions; | |||||
stringifyGroupsOptions?: TStringifyGroupsOptions; | |||||
/** | |||||
* Options to use when merging tokens. | |||||
*/ | |||||
mergeTokensOptions?: TMergeTokensOptions; | |||||
} | } | ||||
/** | /** | ||||
@@ -57,10 +60,14 @@ export interface StringifyOptions< | |||||
* @param options - Options to use when converting a value to its name. | * @param options - Options to use when converting a value to its name. | ||||
* @returns string The name of the value. | * @returns string The name of the value. | ||||
*/ | */ | ||||
export const stringify = ( | |||||
value: AllowedValue, | |||||
options = {} as StringifyOptions, | |||||
): string => { | |||||
export const stringify = < | |||||
TStringifyGroupsOptions extends object, | |||||
TMergeTokensOptions extends object | |||||
> | |||||
( | |||||
value: AllowedValue, | |||||
options = {} as StringifyOptions<TStringifyGroupsOptions, TMergeTokensOptions>, | |||||
): string => { | |||||
if (!( | if (!( | ||||
(ALLOWED_PARSE_RESULT_TYPES as unknown as string[]) | (ALLOWED_PARSE_RESULT_TYPES as unknown as string[]) | ||||
.includes(typeof (value as unknown)) | .includes(typeof (value as unknown)) | ||||
@@ -69,18 +76,15 @@ export const stringify = ( | |||||
} | } | ||||
const valueStr = value.toString().replace(/\s/g, ''); | const valueStr = value.toString().replace(/\s/g, ''); | ||||
const { system = enUS.shortCount, makeGroupOptions, finalizeOptions } = options; | |||||
const { system = enUS.shortCount, stringifyGroupsOptions, mergeTokensOptions } = options; | |||||
if (valueStr.startsWith(NEGATIVE_SYMBOL)) { | if (valueStr.startsWith(NEGATIVE_SYMBOL)) { | ||||
return system.makeNegative(stringify(valueStr.slice(NEGATIVE_SYMBOL.length), options)); | return system.makeNegative(stringify(valueStr.slice(NEGATIVE_SYMBOL.length), options)); | ||||
} | } | ||||
const groups = system.group(valueStr); | |||||
const groupNames = system.makeGroups( | |||||
groups, | |||||
makeGroupOptions, | |||||
); | |||||
return system.finalize(groupNames, finalizeOptions); | |||||
const groups = system.splitIntoGroups(valueStr); | |||||
const groupNames = system.stringifyGroups(groups, stringifyGroupsOptions); | |||||
return system.mergeTokens(groupNames, mergeTokensOptions); | |||||
}; | }; | ||||
/** | /** | ||||
@@ -92,7 +96,7 @@ export interface ParseOptions { | |||||
* | * | ||||
* Defaults to en-US (American short count). | * Defaults to en-US (American short count). | ||||
*/ | */ | ||||
system?: StringifySystem; | |||||
system?: NumberNameSystem; | |||||
/** | /** | ||||
* The type to parse the value as. | * The type to parse the value as. | ||||
*/ | */ | ||||
@@ -16,12 +16,16 @@ export const POSITIVE_SYMBOL = '+' as const; | |||||
export const SHORT_MILLIA_DELIMITER = '^' as const; | export const SHORT_MILLIA_DELIMITER = '^' as const; | ||||
export const SHORT_MILLIA_ILLION_DELIMITER = '-' as const; | |||||
export const EXPONENT_DELIMITER = 'e' as const; | export const EXPONENT_DELIMITER = 'e' as const; | ||||
export const EMPTY_GROUP_DIGITS = '000' as const; | export const EMPTY_GROUP_DIGITS = '000' as const; | ||||
export const EMPTY_PLACE: Group = [EMPTY_GROUP_DIGITS, BigInt(0)]; | export const EMPTY_PLACE: Group = [EMPTY_GROUP_DIGITS, BigInt(0)]; | ||||
export const T_AFFIX = 't' as const; | |||||
/** | /** | ||||
* Ones number names. | * Ones number names. | ||||
*/ | */ | ||||
@@ -21,6 +21,8 @@ import { | |||||
OnesName, | OnesName, | ||||
POSITIVE_SYMBOL, | POSITIVE_SYMBOL, | ||||
SHORT_MILLIA_DELIMITER, | SHORT_MILLIA_DELIMITER, | ||||
SHORT_MILLIA_ILLION_DELIMITER, | |||||
T_AFFIX, | |||||
TEN_PLUS_ONES, | TEN_PLUS_ONES, | ||||
TenPlusOnesName, | TenPlusOnesName, | ||||
TENS, | TENS, | ||||
@@ -31,12 +33,22 @@ import { | |||||
const FINAL_TOKEN = '' as const; | const FINAL_TOKEN = '' as const; | ||||
export const tokenize = (stringValue: string) => ( | |||||
stringValue | |||||
/** | |||||
* Tokenizes a string. | |||||
* @param value - The string to tokenize. | |||||
* @see {NumberNameSystem.mergeTokens} | |||||
* @returns string[] The tokens. | |||||
*/ | |||||
export const tokenize = (value: string) => ( | |||||
value | |||||
.toLowerCase() | .toLowerCase() | ||||
.trim() | .trim() | ||||
.replace(/\n+/gs, ' ') | .replace(/\n+/gs, ' ') | ||||
.replace(/\s+/g, ' ') | .replace(/\s+/g, ' ') | ||||
.replace( | |||||
new RegExp(`${SHORT_MILLIA_ILLION_DELIMITER}${T_AFFIX}${ILLION_SUFFIX}`, 'g'), | |||||
`${T_AFFIX}${ILLION_SUFFIX}`, | |||||
) | |||||
.replace(new RegExp(`${TENS_ONES_SEPARATOR}`, 'g'), ' ') | .replace(new RegExp(`${TENS_ONES_SEPARATOR}`, 'g'), ' ') | ||||
.split(' ') | .split(' ') | ||||
.filter((maybeToken) => maybeToken.length > 0) | .filter((maybeToken) => maybeToken.length > 0) | ||||
@@ -49,19 +61,29 @@ interface DoParseState { | |||||
done: boolean; | done: boolean; | ||||
} | } | ||||
/** | |||||
* Deconstructs a group name token (e.g. "million", "duodecillion", etc.) to its affixes and | |||||
* parses them. | |||||
* @param result - The current state of the parser. | |||||
* @returns DoParseState The next state of the parser. | |||||
*/ | |||||
const doParseGroupName = (result: DoParseState): DoParseState => { | const doParseGroupName = (result: DoParseState): DoParseState => { | ||||
if (result.groupNameCurrent.length < 1) { | |||||
return { | |||||
...result, | |||||
done: true, | |||||
}; | |||||
} | |||||
if ( | |||||
result.groupNameCurrent.length < 1 | |||||
if (result.groupNameCurrent === 't') { | |||||
// If the current group name is "t", then we're done. | // If the current group name is "t", then we're done. | ||||
// We use the -t- affix to attach the group prefix to the -illion suffix, except for decillion. | // We use the -t- affix to attach the group prefix to the -illion suffix, except for decillion. | ||||
|| result.groupNameCurrent === T_AFFIX | |||||
) { | |||||
return { | return { | ||||
...result, | ...result, | ||||
// Fill the gaps of millias with zeros. | |||||
millias: new Array(result.millias.length) | |||||
.fill(0) | |||||
.map((z, i) => ( | |||||
result.millias[i] ?? z | |||||
)), | |||||
done: true, | done: true, | ||||
}; | }; | ||||
} | } | ||||
@@ -120,29 +142,23 @@ const doParseGroupName = (result: DoParseState): DoParseState => { | |||||
}; | }; | ||||
} | } | ||||
if (result.groupNameCurrent.startsWith(`${MILLIA_PREFIX}${SHORT_MILLIA_DELIMITER}`)) { | |||||
// short millia | |||||
// FIXME | |||||
const matchedMilliaArray = result.groupNameCurrent.match(/^\d+/); | |||||
if (!matchedMilliaArray) { | |||||
throw new InvalidTokenError(result.groupNameCurrent); | |||||
} | |||||
const matchedMillia = matchedMilliaArray[0]; | |||||
const newMillia = Number(matchedMillia); | |||||
const oldMillia = result.milliaIndex; | |||||
const newMillias = [...result.millias]; | |||||
newMillias[newMillia] = newMillias[oldMillia] || 1; | |||||
newMillias[oldMillia] = 0; | |||||
return { | |||||
milliaIndex: newMillia, | |||||
millias: newMillias, | |||||
groupNameCurrent: result.groupNameCurrent.slice(MILLIA_PREFIX.length), | |||||
done: false, | |||||
}; | |||||
} | |||||
if (result.groupNameCurrent.startsWith(MILLIA_PREFIX)) { | if (result.groupNameCurrent.startsWith(MILLIA_PREFIX)) { | ||||
const newMillia = result.milliaIndex + 1; | |||||
let newMillia: number; | |||||
let prefix: string; | |||||
const isShortMillia = result.groupNameCurrent.startsWith(`${MILLIA_PREFIX}${SHORT_MILLIA_DELIMITER}`); | |||||
if (isShortMillia) { | |||||
const matchedMilliaArray = result.groupNameCurrent | |||||
.match(new RegExp(`^${MILLIA_PREFIX}\\${SHORT_MILLIA_DELIMITER}(\\d+)`)); | |||||
if (!matchedMilliaArray) { | |||||
throw new InvalidTokenError(result.groupNameCurrent); | |||||
} | |||||
const [wholeMilliaPrefix, matchedMillia] = matchedMilliaArray; | |||||
newMillia = Number(matchedMillia); | |||||
prefix = wholeMilliaPrefix; | |||||
} else { | |||||
newMillia = result.milliaIndex + 1; | |||||
prefix = MILLIA_PREFIX; | |||||
} | |||||
const oldMillia = result.milliaIndex; | const oldMillia = result.milliaIndex; | ||||
const newMillias = [...result.millias]; | const newMillias = [...result.millias]; | ||||
newMillias[newMillia] = newMillias[oldMillia] || 1; | newMillias[newMillia] = newMillias[oldMillia] || 1; | ||||
@@ -150,7 +166,7 @@ const doParseGroupName = (result: DoParseState): DoParseState => { | |||||
return { | return { | ||||
milliaIndex: newMillia, | milliaIndex: newMillia, | ||||
millias: newMillias, | millias: newMillias, | ||||
groupNameCurrent: result.groupNameCurrent.slice(MILLIA_PREFIX.length), | |||||
groupNameCurrent: result.groupNameCurrent.slice(prefix.length), | |||||
done: false, | done: false, | ||||
}; | }; | ||||
} | } | ||||
@@ -158,6 +174,11 @@ const doParseGroupName = (result: DoParseState): DoParseState => { | |||||
throw new InvalidTokenError(result.groupNameCurrent); | throw new InvalidTokenError(result.groupNameCurrent); | ||||
}; | }; | ||||
/** | |||||
* Gets the place of a group name (e.g. "million", "duodecillion", etc.). | |||||
* @param groupName - The group name. | |||||
* @returns bigint The place of the group name. | |||||
*/ | |||||
const getGroupPlaceFromGroupName = (groupName: string) => { | const getGroupPlaceFromGroupName = (groupName: string) => { | ||||
if (groupName === THOUSAND) { | if (groupName === THOUSAND) { | ||||
return BigInt(1); | return BigInt(1); | ||||
@@ -191,16 +212,43 @@ const getGroupPlaceFromGroupName = (groupName: string) => { | |||||
return bigGroupPlace + BigInt(1); | return bigGroupPlace + BigInt(1); | ||||
}; | }; | ||||
/** | |||||
* Mode of the group parser. | |||||
*/ | |||||
enum ParseGroupsMode { | enum ParseGroupsMode { | ||||
INITIAL = 'unknown', | |||||
/** | |||||
* Initial mode. | |||||
*/ | |||||
INITIAL = 'initial', | |||||
/** | |||||
* Has parsed a ones name. | |||||
*/ | |||||
ONES_MODE = 'ones', | ONES_MODE = 'ones', | ||||
/** | |||||
* Has parsed a tens name. | |||||
*/ | |||||
TENS_MODE = 'tens', | TENS_MODE = 'tens', | ||||
/** | |||||
* Has parsed a ten-plus-ones name. | |||||
*/ | |||||
TEN_PLUS_ONES_MODE = 'tenPlusOnes', | TEN_PLUS_ONES_MODE = 'tenPlusOnes', | ||||
/** | |||||
* Has parsed a "hundred" token. | |||||
*/ | |||||
HUNDRED_MODE = 'hundred', | HUNDRED_MODE = 'hundred', | ||||
/** | |||||
* Has parsed a "thousand" or any "-illion" token. | |||||
*/ | |||||
THOUSAND_MODE = 'thousand', | THOUSAND_MODE = 'thousand', | ||||
/** | |||||
* Done parsing. | |||||
*/ | |||||
DONE = 'done', | DONE = 'done', | ||||
} | } | ||||
/** | |||||
* State of the group parser. | |||||
*/ | |||||
interface ParserState { | interface ParserState { | ||||
lastToken?: string; | lastToken?: string; | ||||
groups: Group[]; | groups: Group[]; | ||||
@@ -340,6 +388,12 @@ const parseTens = (acc: ParserState, token: string): ParserState => { | |||||
}; | }; | ||||
}; | }; | ||||
/** | |||||
* Parses groups from a string. | |||||
* @param tokens - The string to parse groups from. | |||||
* @see {NumberNameSystem.stringifyGroups} | |||||
* @returns Group[] The parsed groups. | |||||
*/ | |||||
export const parseGroups = (tokens: string[]) => { | export const parseGroups = (tokens: string[]) => { | ||||
// We add a final token which is an empty string to parse whatever the last non-empty token is. | // We add a final token which is an empty string to parse whatever the last non-empty token is. | ||||
const tokensToParse = [...tokens, FINAL_TOKEN]; | const tokensToParse = [...tokens, FINAL_TOKEN]; | ||||
@@ -404,6 +458,12 @@ const bigIntMin = (...b: bigint[]) => b.reduce( | |||||
undefined as bigint | undefined, | undefined as bigint | undefined, | ||||
); | ); | ||||
/** | |||||
* Combines groups into a string. | |||||
* @param groups - The groups to combine. | |||||
* @see {NumberNameSystem.splitIntoGroups} | |||||
* @returns string The combined groups in exponential form. | |||||
*/ | |||||
export const combineGroups = (groups: Group[]) => { | export const combineGroups = (groups: Group[]) => { | ||||
const places = groups.map((g) => g[GROUP_PLACE_INDEX]); | const places = groups.map((g) => g[GROUP_PLACE_INDEX]); | ||||
if (places.length < 1) { | if (places.length < 1) { | ||||
@@ -24,7 +24,7 @@ import { | |||||
NEGATIVE, | NEGATIVE, | ||||
ONES, | ONES, | ||||
OnesName, | OnesName, | ||||
SHORT_MILLIA_DELIMITER, | |||||
SHORT_MILLIA_DELIMITER, SHORT_MILLIA_ILLION_DELIMITER, T_AFFIX, | |||||
TEN_PLUS_ONES, | TEN_PLUS_ONES, | ||||
TenPlusOnesName, | TenPlusOnesName, | ||||
TENS, | TENS, | ||||
@@ -135,6 +135,12 @@ const makeCentillionsPrefix = ( | |||||
return `${hundredsName}${onesPrefix}${decillions > 0 ? tensName : ''}` as const; | return `${hundredsName}${onesPrefix}${decillions > 0 ? tensName : ''}` as const; | ||||
}; | }; | ||||
/** | |||||
* Repeats a string a given number of times. | |||||
* @param s - String to repeat. | |||||
* @param count - Number of times to repeat the string. | |||||
* @returns string The repeated string. | |||||
*/ | |||||
const repeatString = (s: string, count: GroupPlace) => { | const repeatString = (s: string, count: GroupPlace) => { | ||||
let result = ''; | let result = ''; | ||||
for (let i = BigInt(0); i < count; i += BigInt(1)) { | for (let i = BigInt(0); i < count; i += BigInt(1)) { | ||||
@@ -183,13 +189,20 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | |||||
.filter(([groupDigits]) => groupDigits !== EMPTY_GROUP_DIGITS) | .filter(([groupDigits]) => groupDigits !== EMPTY_GROUP_DIGITS) | ||||
.map(([groupDigits, groupPlace], _index, millias) => { | .map(([groupDigits, groupPlace], _index, millias) => { | ||||
const [hundreds, tens, ones] = groupDigits.split('').map(Number); | const [hundreds, tens, ones] = groupDigits.split('').map(Number); | ||||
const centillionsPrefix = makeCentillionsPrefix( | |||||
hundreds, | |||||
tens, | |||||
ones, | |||||
BigInt(millias.length - 1) | |||||
); | |||||
if (groupPlace < 1) { | if (groupPlace < 1) { | ||||
return makeCentillionsPrefix(hundreds, tens, ones, BigInt(millias.length - 1)); | |||||
return centillionsPrefix; | |||||
} | } | ||||
const milliaSuffix = ( | const milliaSuffix = ( | ||||
shortenMillia && groupPlace > 1 | shortenMillia && groupPlace > 1 | ||||
? `${MILLIA_PREFIX}${SHORT_MILLIA_DELIMITER}${groupPlace}` | |||||
? `${MILLIA_PREFIX}${SHORT_MILLIA_DELIMITER}${groupPlace}${SHORT_MILLIA_ILLION_DELIMITER}` | |||||
: repeatString(MILLIA_PREFIX, groupPlace) | : repeatString(MILLIA_PREFIX, groupPlace) | ||||
); | ); | ||||
@@ -197,7 +210,7 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | |||||
return milliaSuffix; | return milliaSuffix; | ||||
} | } | ||||
return makeCentillionsPrefix(hundreds, tens, ones, BigInt(millias.length - 1)) + milliaSuffix; | |||||
return `${centillionsPrefix}${milliaSuffix}`; | |||||
}) | }) | ||||
.join(''); | .join(''); | ||||
@@ -206,18 +219,31 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | |||||
} | } | ||||
if (bigGroupPlace > 10) { | if (bigGroupPlace > 10) { | ||||
return `${groupGroups}t${ILLION_SUFFIX}` as const; | |||||
// vigin - t - illion, cen - t - illion, etc. | |||||
return `${groupGroups}${T_AFFIX}${ILLION_SUFFIX}` as const; | |||||
} | } | ||||
return `${groupGroups}${ILLION_SUFFIX}` as const; | return `${groupGroups}${ILLION_SUFFIX}` as const; | ||||
}; | }; | ||||
export interface MakeGroupsOptions { | |||||
export interface StringifyGroupsOptions { | |||||
/** | |||||
* Whether to add dashes between tens and ones (e.g. "sixty-nine"). | |||||
*/ | |||||
addTensDashes?: boolean; | addTensDashes?: boolean; | ||||
/** | |||||
* Use "millia^2-tillion" instead of "milliamilliatillion". | |||||
*/ | |||||
shortenMillia?: boolean; | shortenMillia?: boolean; | ||||
} | } | ||||
export const makeGroups = (groups: Group[], options?: MakeGroupsOptions): string[] => { | |||||
/** | |||||
* Creates a group string. | |||||
* @param groups - The groups. | |||||
* @param options - Options to use when creating the group. | |||||
* @returns string[] The groups represented into strings. | |||||
*/ | |||||
export const stringifyGroups = (groups: Group[], options?: StringifyGroupsOptions): string[] => { | |||||
const filteredGroups = groups.filter(([digits, place]) => ( | const filteredGroups = groups.filter(([digits, place]) => ( | ||||
place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | ||||
)); | )); | ||||
@@ -245,8 +271,9 @@ export const makeGroups = (groups: Group[], options?: MakeGroupsOptions): string | |||||
/** | /** | ||||
* Group a number string into groups of three digits, starting from the decimal point. | * Group a number string into groups of three digits, starting from the decimal point. | ||||
* @param value - The number string to group. | * @param value - The number string to group. | ||||
* @returns Group[] The groups. | |||||
*/ | */ | ||||
export const group = (value: string): Group[] => { | |||||
export const splitIntoGroups = (value: string): Group[] => { | |||||
const [significand, exponentString] = numberToExponential( | const [significand, exponentString] = numberToExponential( | ||||
value, | value, | ||||
{ | { | ||||
@@ -277,7 +304,7 @@ export const group = (value: string): Group[] => { | |||||
); | ); | ||||
}; | }; | ||||
export interface FinalizeOptions { | |||||
export interface MergeTokensOptions { | |||||
oneGroupPerLine?: boolean; | oneGroupPerLine?: boolean; | ||||
} | } | ||||
@@ -286,7 +313,7 @@ export interface FinalizeOptions { | |||||
* @param tokens - The tokens to finalize. | * @param tokens - The tokens to finalize. | ||||
* @param options - The options to use. | * @param options - The options to use. | ||||
*/ | */ | ||||
export const finalize = (tokens: string[], options?: FinalizeOptions) => ( | |||||
export const mergeTokens = (tokens: string[], options?: MergeTokensOptions) => ( | |||||
tokens | tokens | ||||
.map((t) => t.trim()) | .map((t) => t.trim()) | ||||
.join(options?.oneGroupPerLine ? '\n' : GROUP_SEPARATOR) | .join(options?.oneGroupPerLine ? '\n' : GROUP_SEPARATOR) | ||||
@@ -8,7 +8,7 @@ const options = { | |||||
const stringifyOptions = { | const stringifyOptions = { | ||||
...options, | ...options, | ||||
makeGroupOptions: { | |||||
stringifyGroupsOptions: { | |||||
addTensDashes: false, | addTensDashes: false, | ||||
}, | }, | ||||
}; | }; | ||||
@@ -286,4 +286,14 @@ describe('numerica', () => { | |||||
const value5 = '1e3006'; | const value5 = '1e3006'; | ||||
expect(stringify(value5)).toBe('one milliauntillion'); | expect(stringify(value5)).toBe('one milliauntillion'); | ||||
}); | }); | ||||
it('converts short millia', () => { | |||||
const shortMillia1 = 'one millia^1-tillion'; | |||||
expect(parse(shortMillia1)).toBe('1e+3003'); | |||||
const shortMillia2 = 'one millia^2-tillion'; | |||||
expect(parse(shortMillia2)).toBe('1e+3000003'); | |||||
expect(stringify(parse(shortMillia2), { stringifyGroupsOptions: { shortenMillia: true } })) | |||||
.toBe(shortMillia2); | |||||
}); | |||||
}); | }); |
@@ -3,7 +3,7 @@ import { stringify, parse } from '../../../src'; | |||||
import { numberToExponential } from '../../../src/exponent'; | import { numberToExponential } from '../../../src/exponent'; | ||||
const stringifyOptions = { | const stringifyOptions = { | ||||
makeGroupOptions: { | |||||
stringifyGroupsOptions: { | |||||
addTensDashes: false, | addTensDashes: false, | ||||
}, | }, | ||||
}; | }; | ||||