@@ -6,158 +6,23 @@ import { | |||||
} from '../../../common'; | } from '../../../common'; | ||||
import { numberToExponential } from '../../../exponent'; | import { numberToExponential } from '../../../exponent'; | ||||
import { | import { | ||||
CENTILLIONS_PREFIXES, | |||||
CentillionsPrefix, | |||||
DECILLIONS_PREFIXES, | |||||
DecillionsPrefix, | |||||
DECIMAL_POINT, | DECIMAL_POINT, | ||||
EMPTY_GROUP_DIGITS, | |||||
EXPONENT_DELIMITER, GROUP_SEPARATOR, | |||||
GROUPING_SYMBOL, | GROUPING_SYMBOL, | ||||
HUNDRED, | |||||
EXPONENT_DELIMITER, | |||||
DECILLIONS_PREFIXES, | |||||
EMPTY_GROUP_DIGITS, | |||||
ILLION_SUFFIX, | ILLION_SUFFIX, | ||||
MILLIA_PREFIX, | MILLIA_PREFIX, | ||||
MILLIONS_PREFIXES, | |||||
MILLIONS_SPECIAL_PREFIXES, | |||||
MillionsPrefix, | |||||
MillionsSpecialPrefix, | |||||
NEGATIVE, | |||||
ONES, | |||||
OnesName, | |||||
SHORT_MILLIA_DELIMITER, SHORT_MILLIA_ILLION_DELIMITER, T_AFFIX, | |||||
TEN_PLUS_ONES, | |||||
TenPlusOnesName, | |||||
TENS, | |||||
TENS_ONES_SEPARATOR, | |||||
TensName, | |||||
SHORT_MILLIA_DELIMITER, | |||||
SHORT_MILLIA_ILLION_DELIMITER, | |||||
T_AFFIX, | |||||
THOUSAND, | THOUSAND, | ||||
makeHundredsName, | |||||
makeCentillionsPrefix, | |||||
repeatString, | |||||
StringifyGroupsOptions, | |||||
} from '../../en/common'; | } from '../../en/common'; | ||||
/** | |||||
* Builds a name for numbers in tens and ones. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
const makeTensName = (tens: number, ones: number, addTensDashes: boolean) => { | |||||
if (tens === 0) { | |||||
return ONES[ones]; | |||||
} | |||||
if (tens === 1) { | |||||
return TEN_PLUS_ONES[ones] as unknown as TenPlusOnesName; | |||||
} | |||||
if (ones === 0) { | |||||
return TENS[tens]; | |||||
} | |||||
return `${TENS[tens] as Exclude<TensName, 'zero' | 'ten'>}${addTensDashes ? TENS_ONES_SEPARATOR : ' '}${ONES[ones] as Exclude<OnesName, 'zero'>}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in hundreds, tens, and ones. | |||||
* @param hundreds - Hundreds digit. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
const makeHundredsName = (hundreds: number, tens: number, ones: number, addTensDashes: boolean) => { | |||||
if (hundreds === 0) { | |||||
return makeTensName(tens, ones, addTensDashes); | |||||
} | |||||
if (tens === 0 && ones === 0) { | |||||
return `${ONES[hundreds]} ${HUNDRED}` as const; | |||||
} | |||||
return `${ONES[hundreds]} ${HUNDRED} ${makeTensName(tens, ones, addTensDashes)}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the millions. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The millions prefix. | |||||
*/ | |||||
const makeMillionsPrefix = ( | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (currentMillia === BigInt(0) && longestMilliaCount === BigInt(0)) { | |||||
return MILLIONS_SPECIAL_PREFIXES[millions] as MillionsSpecialPrefix; | |||||
} | |||||
return MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the decillions. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The decillions prefix. | |||||
*/ | |||||
const makeDecillionsPrefix = ( | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (decillions === 0) { | |||||
return makeMillionsPrefix(millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
return `${onesPrefix}${tensName}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the centillions. | |||||
* @param centillions - Centillions digit. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The centillions prefix. | |||||
*/ | |||||
const makeCentillionsPrefix = ( | |||||
centillions: number, | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (centillions === 0) { | |||||
return makeDecillionsPrefix(decillions, millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
const hundredsName = CENTILLIONS_PREFIXES[centillions] as CentillionsPrefix; | |||||
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) => { | |||||
let result = ''; | |||||
for (let i = BigInt(0); i < count; i += BigInt(1)) { | |||||
result += s; | |||||
} | |||||
return result; | |||||
}; | |||||
const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | ||||
if (place === BigInt(0)) { | if (place === BigInt(0)) { | ||||
return '' as const; | return '' as const; | ||||
@@ -240,48 +105,6 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | |||||
return `${isThousand ? `${THOUSAND} ` : ''}${groupGroups}${ILLION_SUFFIX}` as const; | return `${isThousand ? `${THOUSAND} ` : ''}${groupGroups}${ILLION_SUFFIX}` as const; | ||||
}; | }; | ||||
export interface StringifyGroupsOptions { | |||||
/** | |||||
* Whether to add dashes between tens and ones (e.g. "sixty-nine"). | |||||
*/ | |||||
addTensDashes?: boolean; | |||||
/** | |||||
* Use "millia^2-tillion" instead of "milliamilliatillion". | |||||
*/ | |||||
shortenMillia?: boolean; | |||||
} | |||||
/** | |||||
* 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]) => ( | |||||
place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | |||||
)); | |||||
return filteredGroups.map( | |||||
([group, place]) => { | |||||
const makeHundredsArgs = group | |||||
.padStart(3, '0') | |||||
.split('') | |||||
.map((s) => Number(s)) as [number, number, number]; | |||||
const groupDigitsName = makeHundredsName( | |||||
...makeHundredsArgs, | |||||
options?.addTensDashes ?? true, | |||||
); | |||||
const groupName = getGroupName(place, options?.shortenMillia ?? false); | |||||
if (groupName.length > 0) { | |||||
return `${groupDigitsName} ${groupName}`; | |||||
} | |||||
return groupDigitsName; | |||||
}, | |||||
); | |||||
}; | |||||
/** | /** | ||||
* 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. | ||||
@@ -319,27 +142,40 @@ export const splitIntoGroups = (value: string): Group[] => { | |||||
); | ); | ||||
}; | }; | ||||
export interface MergeTokensOptions { | |||||
oneGroupPerLine?: boolean; | |||||
} | |||||
/** | /** | ||||
* Formats the final tokenized string. | |||||
* @param tokens - The tokens to finalize. | |||||
* @param options - The options to use. | |||||
* 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 mergeTokens = (tokens: string[], options?: MergeTokensOptions) => ( | |||||
tokens | |||||
.map((t) => t.trim()) | |||||
.join(options?.oneGroupPerLine ? '\n' : GROUP_SEPARATOR) | |||||
.trim() | |||||
); | |||||
export const stringifyGroups = (groups: Group[], options?: StringifyGroupsOptions): string[] => { | |||||
const filteredGroups = groups.filter(([digits, place]) => ( | |||||
place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | |||||
)); | |||||
/** | |||||
* Makes a negative string. | |||||
* @param s - The string to make negative. | |||||
*/ | |||||
export const makeNegative = (s: string) => { | |||||
const negativePrefix = `${NEGATIVE} `; | |||||
return s.startsWith(negativePrefix) ? s.slice(negativePrefix.length) : `${negativePrefix}${s}`; | |||||
return filteredGroups.map( | |||||
([group, place]) => { | |||||
const makeHundredsArgs = group | |||||
.padStart(3, '0') | |||||
.split('') | |||||
.map((s) => Number(s)) as [number, number, number]; | |||||
const groupDigitsName = makeHundredsName( | |||||
...makeHundredsArgs, | |||||
options?.addTensDashes ?? true, | |||||
); | |||||
const groupName = getGroupName(place, options?.shortenMillia ?? false); | |||||
if (groupName.length > 0) { | |||||
return `${groupDigitsName} ${groupName}`; | |||||
} | |||||
return groupDigitsName; | |||||
}, | |||||
); | |||||
}; | }; | ||||
export { | |||||
StringifyGroupsOptions, | |||||
MergeTokensOptions, | |||||
mergeTokens, | |||||
makeNegative, | |||||
} from '../../en/common'; |
@@ -6,158 +6,23 @@ import { | |||||
} from '../../../common'; | } from '../../../common'; | ||||
import { numberToExponential } from '../../../exponent'; | import { numberToExponential } from '../../../exponent'; | ||||
import { | import { | ||||
CENTILLIONS_PREFIXES, | |||||
CentillionsPrefix, | |||||
DECILLIONS_PREFIXES, | |||||
DecillionsPrefix, | |||||
DECIMAL_POINT, | DECIMAL_POINT, | ||||
EMPTY_GROUP_DIGITS, | |||||
EXPONENT_DELIMITER, GROUP_SEPARATOR, | |||||
GROUPING_SYMBOL, | GROUPING_SYMBOL, | ||||
HUNDRED, | |||||
EXPONENT_DELIMITER, | |||||
DECILLIONS_PREFIXES, | |||||
EMPTY_GROUP_DIGITS, | |||||
ILLION_SUFFIX, | ILLION_SUFFIX, | ||||
MILLIA_PREFIX, | MILLIA_PREFIX, | ||||
MILLIONS_PREFIXES, | |||||
MILLIONS_SPECIAL_PREFIXES, | |||||
MillionsPrefix, | |||||
MillionsSpecialPrefix, | |||||
NEGATIVE, | |||||
ONES, | |||||
OnesName, | |||||
SHORT_MILLIA_DELIMITER, SHORT_MILLIA_ILLION_DELIMITER, T_AFFIX, | |||||
TEN_PLUS_ONES, | |||||
TenPlusOnesName, | |||||
TENS, | |||||
TENS_ONES_SEPARATOR, | |||||
TensName, | |||||
SHORT_MILLIA_DELIMITER, | |||||
SHORT_MILLIA_ILLION_DELIMITER, | |||||
T_AFFIX, | |||||
THOUSAND, | THOUSAND, | ||||
makeHundredsName, | |||||
makeCentillionsPrefix, | |||||
repeatString, | |||||
StringifyGroupsOptions, | |||||
} from '../../en/common'; | } from '../../en/common'; | ||||
/** | |||||
* Builds a name for numbers in tens and ones. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
const makeTensName = (tens: number, ones: number, addTensDashes: boolean) => { | |||||
if (tens === 0) { | |||||
return ONES[ones]; | |||||
} | |||||
if (tens === 1) { | |||||
return TEN_PLUS_ONES[ones] as unknown as TenPlusOnesName; | |||||
} | |||||
if (ones === 0) { | |||||
return TENS[tens]; | |||||
} | |||||
return `${TENS[tens] as Exclude<TensName, 'zero' | 'ten'>}${addTensDashes ? TENS_ONES_SEPARATOR : ' '}${ONES[ones] as Exclude<OnesName, 'zero'>}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in hundreds, tens, and ones. | |||||
* @param hundreds - Hundreds digit. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
const makeHundredsName = (hundreds: number, tens: number, ones: number, addTensDashes: boolean) => { | |||||
if (hundreds === 0) { | |||||
return makeTensName(tens, ones, addTensDashes); | |||||
} | |||||
if (tens === 0 && ones === 0) { | |||||
return `${ONES[hundreds]} ${HUNDRED}` as const; | |||||
} | |||||
return `${ONES[hundreds]} ${HUNDRED} ${makeTensName(tens, ones, addTensDashes)}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the millions. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The millions prefix. | |||||
*/ | |||||
const makeMillionsPrefix = ( | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (currentMillia === BigInt(0) && longestMilliaCount === BigInt(0)) { | |||||
return MILLIONS_SPECIAL_PREFIXES[millions] as MillionsSpecialPrefix; | |||||
} | |||||
return MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the decillions. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The decillions prefix. | |||||
*/ | |||||
const makeDecillionsPrefix = ( | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (decillions === 0) { | |||||
return makeMillionsPrefix(millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
return `${onesPrefix}${tensName}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the centillions. | |||||
* @param centillions - Centillions digit. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The centillions prefix. | |||||
*/ | |||||
const makeCentillionsPrefix = ( | |||||
centillions: number, | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (centillions === 0) { | |||||
return makeDecillionsPrefix(decillions, millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
const hundredsName = CENTILLIONS_PREFIXES[centillions] as CentillionsPrefix; | |||||
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) => { | |||||
let result = ''; | |||||
for (let i = BigInt(0); i < count; i += BigInt(1)) { | |||||
result += s; | |||||
} | |||||
return result; | |||||
}; | |||||
const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | ||||
if (place === BigInt(0)) { | if (place === BigInt(0)) { | ||||
return '' as const; | return '' as const; | ||||
@@ -238,48 +103,6 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => { | |||||
return `${groupGroups}${ILLION_SUFFIX}` as const; | return `${groupGroups}${ILLION_SUFFIX}` as const; | ||||
}; | }; | ||||
export interface StringifyGroupsOptions { | |||||
/** | |||||
* Whether to add dashes between tens and ones (e.g. "sixty-nine"). | |||||
*/ | |||||
addTensDashes?: boolean; | |||||
/** | |||||
* Use "millia^2-tillion" instead of "milliamilliatillion". | |||||
*/ | |||||
shortenMillia?: boolean; | |||||
} | |||||
/** | |||||
* 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]) => ( | |||||
place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | |||||
)); | |||||
return filteredGroups.map( | |||||
([group, place]) => { | |||||
const makeHundredsArgs = group | |||||
.padStart(3, '0') | |||||
.split('') | |||||
.map((s) => Number(s)) as [number, number, number]; | |||||
const groupDigitsName = makeHundredsName( | |||||
...makeHundredsArgs, | |||||
options?.addTensDashes ?? true, | |||||
); | |||||
const groupName = getGroupName(place, options?.shortenMillia ?? false); | |||||
if (groupName.length > 0) { | |||||
return `${groupDigitsName} ${groupName}`; | |||||
} | |||||
return groupDigitsName; | |||||
}, | |||||
); | |||||
}; | |||||
/** | /** | ||||
* 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. | ||||
@@ -306,7 +129,7 @@ export const splitIntoGroups = (value: string): Group[] => { | |||||
if (lastGroup[GROUP_PLACE_INDEX] === currentPlace) { | if (lastGroup[GROUP_PLACE_INDEX] === currentPlace) { | ||||
const lastGroupDigits = lastGroup[0].split(''); | const lastGroupDigits = lastGroup[0].split(''); | ||||
lastGroupDigits[currentPlaceInGroup] = c; | lastGroupDigits[currentPlaceInGroup] = c; | ||||
return [...acc.slice(0, -1), [ | |||||
return [...acc.slice(0, -1) ?? [], [ | |||||
lastGroupDigits.join('') as GroupDigits, | lastGroupDigits.join('') as GroupDigits, | ||||
currentPlace, | currentPlace, | ||||
]]; | ]]; | ||||
@@ -317,27 +140,40 @@ export const splitIntoGroups = (value: string): Group[] => { | |||||
); | ); | ||||
}; | }; | ||||
export interface MergeTokensOptions { | |||||
oneGroupPerLine?: boolean; | |||||
} | |||||
/** | /** | ||||
* Formats the final tokenized string. | |||||
* @param tokens - The tokens to finalize. | |||||
* @param options - The options to use. | |||||
* 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 mergeTokens = (tokens: string[], options?: MergeTokensOptions) => ( | |||||
tokens | |||||
.map((t) => t.trim()) | |||||
.join(options?.oneGroupPerLine ? '\n' : GROUP_SEPARATOR) | |||||
.trim() | |||||
); | |||||
export const stringifyGroups = (groups: Group[], options?: StringifyGroupsOptions): string[] => { | |||||
const filteredGroups = groups.filter(([digits, place]) => ( | |||||
place === BigInt(0) || digits !== EMPTY_GROUP_DIGITS | |||||
)); | |||||
/** | |||||
* Makes a negative string. | |||||
* @param s - The string to make negative. | |||||
*/ | |||||
export const makeNegative = (s: string) => { | |||||
const negativePrefix = `${NEGATIVE} `; | |||||
return s.startsWith(negativePrefix) ? s.slice(negativePrefix.length) : `${negativePrefix}${s}`; | |||||
return filteredGroups.map( | |||||
([group, place]) => { | |||||
const makeHundredsArgs = group | |||||
.padStart(3, '0') | |||||
.split('') | |||||
.map((s) => Number(s)) as [number, number, number]; | |||||
const groupDigitsName = makeHundredsName( | |||||
...makeHundredsArgs, | |||||
options?.addTensDashes ?? true, | |||||
); | |||||
const groupName = getGroupName(place, options?.shortenMillia ?? false); | |||||
if (groupName.length > 0) { | |||||
return `${groupDigitsName} ${groupName}`; | |||||
} | |||||
return groupDigitsName; | |||||
}, | |||||
); | |||||
}; | }; | ||||
export { | |||||
StringifyGroupsOptions, | |||||
MergeTokensOptions, | |||||
mergeTokens, | |||||
makeNegative, | |||||
} from '../../en/common'; |
@@ -1,4 +1,4 @@ | |||||
import { Group } from '../../common'; | |||||
import { Group, GroupPlace } from '../../common'; | |||||
export const GROUP_SEPARATOR = ' ' as const; | export const GROUP_SEPARATOR = ' ' as const; | ||||
@@ -175,3 +175,169 @@ export const MILLIA_PREFIX = 'millia' as const; | |||||
* Suffix for -illion number names. | * Suffix for -illion number names. | ||||
*/ | */ | ||||
export const ILLION_SUFFIX = 'illion' as const; | export const ILLION_SUFFIX = 'illion' as const; | ||||
/** | |||||
* Builds a name for numbers in tens and ones. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
const makeTensName = (tens: number, ones: number, addTensDashes: boolean) => { | |||||
if (tens === 0) { | |||||
return ONES[ones]; | |||||
} | |||||
if (tens === 1) { | |||||
return TEN_PLUS_ONES[ones] as unknown as TenPlusOnesName; | |||||
} | |||||
if (ones === 0) { | |||||
return TENS[tens]; | |||||
} | |||||
return `${TENS[tens] as Exclude<TensName, 'zero' | 'ten'>}${addTensDashes ? TENS_ONES_SEPARATOR : ' '}${ONES[ones] as Exclude<OnesName, 'zero'>}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in hundreds, tens, and ones. | |||||
* @param hundreds - Hundreds digit. | |||||
* @param tens - Tens digit. | |||||
* @param ones - Ones digit. | |||||
* @param addTensDashes - Whether to add dashes between the tens and ones. | |||||
* @returns string The name for the number. | |||||
*/ | |||||
export const makeHundredsName = ( | |||||
hundreds: number, | |||||
tens: number, | |||||
ones: number, | |||||
addTensDashes: boolean, | |||||
) => { | |||||
if (hundreds === 0) { | |||||
return makeTensName(tens, ones, addTensDashes); | |||||
} | |||||
if (tens === 0 && ones === 0) { | |||||
return `${ONES[hundreds]} ${HUNDRED}` as const; | |||||
} | |||||
return `${ONES[hundreds]} ${HUNDRED} ${makeTensName(tens, ones, addTensDashes)}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the millions. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The millions prefix. | |||||
*/ | |||||
const makeMillionsPrefix = ( | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (currentMillia === BigInt(0) && longestMilliaCount === BigInt(0)) { | |||||
return MILLIONS_SPECIAL_PREFIXES[millions] as MillionsSpecialPrefix; | |||||
} | |||||
return MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the decillions. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The decillions prefix. | |||||
*/ | |||||
const makeDecillionsPrefix = ( | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (decillions === 0) { | |||||
return makeMillionsPrefix(millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
return `${onesPrefix}${tensName}` as const; | |||||
}; | |||||
/** | |||||
* Builds a name for numbers in the centillions. | |||||
* @param centillions - Centillions digit. | |||||
* @param decillions - Decillions digit. | |||||
* @param millions - Millions digit. | |||||
* @param currentMillia - Current millia- group. | |||||
* @param longestMilliaCount - Number of millia- groups. | |||||
* @returns string The centillions prefix. | |||||
*/ | |||||
export const makeCentillionsPrefix = ( | |||||
centillions: number, | |||||
decillions: number, | |||||
millions: number, | |||||
currentMillia: GroupPlace, | |||||
longestMilliaCount: GroupPlace, | |||||
) => { | |||||
if (centillions === 0) { | |||||
return makeDecillionsPrefix(decillions, millions, currentMillia, longestMilliaCount); | |||||
} | |||||
const onesPrefix = MILLIONS_PREFIXES[millions] as MillionsPrefix; | |||||
const tensName = DECILLIONS_PREFIXES[decillions] as DecillionsPrefix; | |||||
const hundredsName = CENTILLIONS_PREFIXES[centillions] as CentillionsPrefix; | |||||
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. | |||||
*/ | |||||
export const repeatString = (s: string, count: GroupPlace) => { | |||||
let result = ''; | |||||
for (let i = BigInt(0); i < count; i += BigInt(1)) { | |||||
result += s; | |||||
} | |||||
return result; | |||||
}; | |||||
export interface StringifyGroupsOptions { | |||||
/** | |||||
* Whether to add dashes between tens and ones (e.g. "sixty-nine"). | |||||
*/ | |||||
addTensDashes?: boolean; | |||||
/** | |||||
* Use "millia^2-tillion" instead of "milliamilliatillion". | |||||
*/ | |||||
shortenMillia?: boolean; | |||||
} | |||||
export interface MergeTokensOptions { | |||||
oneGroupPerLine?: boolean; | |||||
} | |||||
/** | |||||
* Formats the final tokenized string. | |||||
* @param tokens - The tokens to finalize. | |||||
* @param options - The options to use. | |||||
*/ | |||||
export const mergeTokens = (tokens: string[], options?: MergeTokensOptions) => ( | |||||
tokens | |||||
.map((t) => t.trim()) | |||||
.join(options?.oneGroupPerLine ? '\n' : GROUP_SEPARATOR) | |||||
.trim() | |||||
); | |||||
/** | |||||
* Makes a negative string. | |||||
* @param s - The string to make negative. | |||||
*/ | |||||
export const makeNegative = (s: string) => { | |||||
const negativePrefix = `${NEGATIVE} `; | |||||
return s.startsWith(negativePrefix) ? s.slice(negativePrefix.length) : `${negativePrefix}${s}`; | |||||
}; |