function pos_int2words(n, ones=['' ,'one' ,'two' ,'three' ,'four' ,'five' ,'six' ,'seven' ,'eight' ,'nine'], teens=['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'], tens=['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']) { if(n < 10) return ones[n]; else if(n < 20) return teens[n - 10]; else if(n < 100) return tens[Math.floor(n/10)] + (n%10 > 0 ? '-' + ones[n%10] : ''); else if(n < 1000) return ones[Math.floor(n/100)] + ' hundred ' + pos_int2words(n%100); else if(n < 1000000) return pos_int2words(Math.floor(n/1000)) + ' thousand ' + pos_int2words(n%1000); else return pos_int2words(Math.floor(n/1000000)) + ' million ' + pos_int2words(n%1000000); } export function int2words(n, ones=['' ,'one' ,'two' ,'three' ,'four' ,'five' ,'six' ,'seven' ,'eight' ,'nine'], teens=['ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'], tens=['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']) { if(n == 0) return 'zero'; else if(n < 0) return 'negative ' + pos_int2words(n, ones, teens, tens).replace(/\s+$/, ''); else return pos_int2words(n, ones, teens, tens).replace(/\s+$/, ''); } export function cardinal2ordinal(s, re_magnitudes=/(hundred|thousand|(m|b|tr|quadr)illion)$/, re_low=/(zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)$/, ordinals={ zero: 'zeroth', one: 'first', two: 'second', three: 'third', four: 'fourth', five: 'fifth', six: 'sixth', seven: 'seventh', eight: 'eighth', nine: 'ninth', ten: 'tenth', eleven: 'eleventh', twelve: 'twelfth' }) { if((s.slice(-4) == 'teen') || (re_magnitudes.test(s))) return s + 'th'; else if(s.charAt(s.length - 1) == 'y') return s.substring(0, s.length - 1) + 'ieth'; else return s.replace(re_low, (m, w) => ordinals[w]); } export function float2fraction(x, epsilon=0.0001) { if(x == 0) return [0, 1]; const a = Math.abs(x); let n = 0, d = 1, r; while(Math.abs((r = n/d) - a)/a >= epsilon) if(r < a) n++; else d++; return [x < 0 ? -n : n, d]; } function pos_fraction2words(x, epsilon=0.0001) { const frac = float2fraction(x, epsilon), n = frac[0], d = frac[1]; if(d == 1) return int2words(x); else if(d == 2) return int2words(n) + (n >= 2 ? ' halves' : ' half'); else if(d == 4) return int2words(n) + (n >= 2 ? ' quarters' : ' quarter'); else return int2words(n) + ' ' + cardinal2ordinal(int2words(d)) + (n >= 2 ? 's' : ''); } export function num2words(n, epsilon=0.0001) { if(n == 0) return 'zero'; else if(n < 0) return 'negative ' + num2words(-n); var intpart = n|0; if(n == intpart) return int2words(n); else if(intpart == 0) return pos_fraction2words(n, epsilon); else return num2words(intpart) + ' and ' + pos_fraction2words(n%1, epsilon); } export function translate(s, epsilon=0.0001) { return ('' + s).replace(/([+-]?(?:\d+|\d+\.\d+))/g, (m, w) => num2words(w, epsilon)); }