Task

Show how to spell out a number in English.

You can use a preexisting implementation or roll your own, but you should support inputs up to at least one million (or the maximum value of your language's default bounded integer type, if that's less).

Support for inputs other than positive integers (like zero, negative integers, and floating-point numbers) is optional.



Related task

   Spelling of ordinal numbers.


get "libhdr"
manifest $( MAXLEN = 256/BYTESPERWORD $)
 
let append(v1, v2) be
$(  for i=1 to v2%0 do
        v1%(v1%0+i) := v2%i
    v1%0 := v1%0 + v2%0
$)
 
let spell(n, v) = valof
$(  let small(n) =
        n=0 -> "",          n=1 -> "one",       n=2 -> "two",   
        n=3 -> "three",     n=4 -> "four",      n=5 -> "five",  
        n=6 -> "six",       n=7 -> "seven",     n=8 -> "eight",     
        n=9 -> "nine",      n=10 -> "ten",      n=11 -> "eleven",
        n=12 -> "twelve",   n=13 -> "thirteen", n=14 -> "fourteen",
        n=15 -> "fifteen",  n=16 -> "sixteen",  n=17 -> "seventeen",
        n=18 -> "eighteen", n=19 -> "nineteen", valof finish
 
    let teens(n) =
        n=2 -> "twenty",    n=3 -> "thirty",    n=4 -> "forty",
        n=5 -> "fifty",     n=6 -> "sixty",     n=7 -> "seventy",
        n=8 -> "eighty",    n=9 -> "ninety",    valof finish
 
    let less100(n, v) be
    $(  if n >= 20
        $(  append(v, teens(n/10))
            n := n rem 10
            unless n=0 append(v, "-")
        $)
        append(v, small(n))
    $)
 
    let inner(n, v) be
    $(  let step(n, val, name, v) = valof
        $(  if n>=val
            $(  inner(n/val, v)
                append(v, name)
                unless n=val do append(v, " ")
            $)
            resultis n rem val
        $)
        test n<0
        $(  append(v, "minus ")
            inner(-n, v)
        $)
        or
        $(  n := step(n, 1000, " thousand", v)
            n := step(n, 100, " hundred", v)
            less100(n, v)
        $)  
    $)
 
    v%0 := 0
    test n=0 do append(v, "zero") or inner(n, v)
    resultis v
$)
 
let reads(v) be
$(  v%0 := 0
    $(  let c = rdch()
        if c='*N' | c=endstreamch break
        v%0 := v%0 + 1
        v%(v%0) := c
    $) repeat
$)
 
let atoi(v) = valof
$(  let r = 0 and neg = false and i = 1
    if v%1 = '-' then
    $(  i := 2
        neg := true
    $)
    while '0' <= v%i <= '9' & i <= v%0
    $(  r := r*10 + v%i-'0'
        i := i+1
    $)
    resultis neg -> -r, r
$)
 
let start() be
$(  let instr = vec MAXLEN and numstr = vec MAXLEN
    $(  writes("Number? ")
        reads(instr)
        if instr%0=0 finish
        writef("%N: %S*N", atoi(instr), spell(atoi(instr), numstr))
    $) repeat
$)