let's talk numbers

This commit is contained in:
Cory Sanin 2025-08-23 03:03:36 -05:00
parent 4a260f112f
commit 3b562116fd

View File

@ -1,4 +1,4 @@
import path from 'path';
interface Voice {
directory: string;
@ -7,4 +7,167 @@ interface Voice {
type Voices = { [name: string]: Voice };
const LINES = {
NEGATIVE: 'negative',
POINT: 'point',
ZERO: 'zero',
ONE: 'one',
TWO: 'two',
THREE: 'three',
FOUR: 'four',
FIVE: 'five',
SIX: 'six',
SEVEN: 'seven',
EIGHT: 'eight',
NINE: 'nine',
TEN: 'ten',
ELEVEN: 'eleven',
TWELVE: 'twelve',
THIRTEEN: 'thirteen',
FIFTEEN: 'fifteen',
TEEN: 'teen',
TWENTY: 'twenty',
THIRTY: 'thirty',
FORTY: 'forty',
FIFTY: 'fifty',
SIXTY: 'sixty',
SEVENTY: 'seventy',
EIGHTY: 'eighty',
NINETY: 'ninety',
HUNDRED: 'hundred',
THOUSAND: 'thousand',
MILLION: 'million',
BILLION: 'billion',
TRILLION: 'trillion'
}
function formatNumber(num: number): string {
const parts = num.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
function digitByDigit(str: string): string[] {
const tokens: string[] = [];
const input = str.replaceAll(',', '');
const map = [
LINES.ZERO,
LINES.ONE,
LINES.TWO,
LINES.THREE,
LINES.FOUR,
LINES.FIVE,
LINES.SIX,
LINES.SEVEN,
LINES.EIGHT,
LINES.NINE
]
for (let i = 0; i < input.length; i++) {
tokens.push(map[parseInt(input.charAt(i))]);
}
return tokens;
}
function tens(str: string): string[] {
if (str === '0') {
return [LINES.ZERO];
}
const tokens: string[] = [];
const num = parseInt(str);
const map = {
'2': LINES.TWENTY,
'3': LINES.THIRTY,
'4': LINES.FORTY,
'5': LINES.FIFTY,
'6': LINES.SIXTY,
'7': LINES.SEVENTY,
'8': LINES.EIGHTY,
'9': LINES.NINETY
};
const ones = str.charAt(str.length - 1);
if (num === 0) {
return [];
}
else if (num >= 20) {
tokens.push(map[str.charAt(0)]);
if (ones !== '0') {
tokens.push(...digitByDigit(ones));
}
}
else if (num < 10) {
tokens.push(...digitByDigit(ones));
}
else {
const weirdoNumberMap = [
[LINES.TEN],
[LINES.ELEVEN],
[LINES.TWELVE],
[LINES.THIRTEEN],
[LINES.FOUR, LINES.TEEN],
[LINES.FIFTEEN],
[LINES.SIX, LINES.TEEN],
[LINES.SEVEN, LINES.TEEN],
[LINES.EIGHT, LINES.TEEN],
[LINES.NINE, LINES.TEEN],
]
tokens.push(...weirdoNumberMap[num - 10]);
}
return tokens;
}
function hundreds(str: string): string[] {
const tokens: string[] = [];
if (str.length === 3 && str.charAt(0) !== '0') {
tokens.push(...digitByDigit(str.charAt(0)));
tokens.push(LINES.HUNDRED);
str = str.substring(1);
}
tokens.push(...tens(str));
return tokens;
}
function integer(str: string): string[] {
const tokens: string[] = [];
const numGroups = str.split(',');
const seperators = [LINES.TRILLION, LINES.BILLION, LINES.MILLION, LINES.THOUSAND];
if (numGroups.length > 5) {
return digitByDigit(str);
}
seperators.splice(0, seperators.length - numGroups.length + 1);
numGroups.forEach(g => {
if (g !== '000') {
tokens.push(...hundreds(g));
}
if (seperators.length === 0) {
return;
}
const sep = seperators.shift();
if (g !== '000') {
tokens.push(sep);
}
});
return tokens;
}
function voiceLines(voice: Voice, num: number): string[] {
if (Math.abs(num) > 999999999999999 || isNaN(num)) {
return [];
}
const tokens: string[] = [];
const str = formatNumber(num);
const parts = str.split('.');
if (parts[0].startsWith('-')) {
tokens.push(LINES.NEGATIVE);
parts[0] = parts[0].substring(1);
}
tokens.push(...integer(parts[0]));
if (parts.length > 1) {
tokens.push(LINES.POINT);
tokens.push(...digitByDigit(parts[1]));
}
return tokens.map(l => path.join(voice.directory, `${l}.${voice.extension}`));
}
export default voiceLines;
export { voiceLines };
export type { Voice, Voices };