Browse Source

Organize methods

Put common English methods to en/common.
master
TheoryOfNekomata 1 month ago
parent
commit
6dc41a3ca4
3 changed files with 254 additions and 416 deletions
  1. +43
    -207
      packages/core/src/systems/en-UK/long-count/stringify.ts
  2. +44
    -208
      packages/core/src/systems/en-US/short-count/stringify.ts
  3. +167
    -1
      packages/core/src/systems/en/common.ts

+ 43
- 207
packages/core/src/systems/en-UK/long-count/stringify.ts View File

@@ -6,158 +6,23 @@ import {
} from '../../../common';
import { numberToExponential } from '../../../exponent';
import {
CENTILLIONS_PREFIXES,
CentillionsPrefix,
DECILLIONS_PREFIXES,
DecillionsPrefix,
DECIMAL_POINT,
EMPTY_GROUP_DIGITS,
EXPONENT_DELIMITER, GROUP_SEPARATOR,
GROUPING_SYMBOL,
HUNDRED,
EXPONENT_DELIMITER,
DECILLIONS_PREFIXES,
EMPTY_GROUP_DIGITS,
ILLION_SUFFIX,
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,
makeHundredsName,
makeCentillionsPrefix,
repeatString,
StringifyGroupsOptions,
} 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) => {
if (place === BigInt(0)) {
return '' as const;
@@ -240,48 +105,6 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => {
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.
* @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';

+ 44
- 208
packages/core/src/systems/en-US/short-count/stringify.ts View File

@@ -6,158 +6,23 @@ import {
} from '../../../common';
import { numberToExponential } from '../../../exponent';
import {
CENTILLIONS_PREFIXES,
CentillionsPrefix,
DECILLIONS_PREFIXES,
DecillionsPrefix,
DECIMAL_POINT,
EMPTY_GROUP_DIGITS,
EXPONENT_DELIMITER, GROUP_SEPARATOR,
GROUPING_SYMBOL,
HUNDRED,
EXPONENT_DELIMITER,
DECILLIONS_PREFIXES,
EMPTY_GROUP_DIGITS,
ILLION_SUFFIX,
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,
makeHundredsName,
makeCentillionsPrefix,
repeatString,
StringifyGroupsOptions,
} 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) => {
if (place === BigInt(0)) {
return '' as const;
@@ -238,48 +103,6 @@ const getGroupName = (place: GroupPlace, shortenMillia: boolean) => {
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.
* @param value - The number string to group.
@@ -306,7 +129,7 @@ export const splitIntoGroups = (value: string): Group[] => {
if (lastGroup[GROUP_PLACE_INDEX] === currentPlace) {
const lastGroupDigits = lastGroup[0].split('');
lastGroupDigits[currentPlaceInGroup] = c;
return [...acc.slice(0, -1), [
return [...acc.slice(0, -1) ?? [], [
lastGroupDigits.join('') as GroupDigits,
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';

+ 167
- 1
packages/core/src/systems/en/common.ts View File

@@ -1,4 +1,4 @@
import { Group } from '../../common';
import { Group, GroupPlace } from '../../common';

export const GROUP_SEPARATOR = ' ' as const;

@@ -175,3 +175,169 @@ export const MILLIA_PREFIX = 'millia' as const;
* Suffix for -illion number names.
*/
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}`;
};

Loading…
Cancel
Save