12.2 Solution

We begin by defining a few constants that will be useful later on.

const unitsAndTeens = Dict(0 => "zero", 1 => "one",
                     2 => "two", 3 => "three", 4 => "four",
                     5 => "five", 6 => "six", 7 => "seven",
                     8 => "eight", 9 => "nine", 10 => "ten",
                     11 => "eleven", 12 => "twelve",
                     13 => "thirteen", 14 => "fourteen",
                     15 => "fifteen", 16 => "sixteen",
                     17 => "seventeen", 18 => "eighteen",
                     19 => "nineteen")

const tens = Dict(20 => "twenty", 30 => "thrity",
            40 => "forty", 50 => "fifty", 60 => "sixty",
            70 => "seventy", 80 => "eigthy", 90 => "ninety")

The above are just mappings between the necessary basic key ingredients of our number soup. Let’s use them to get the transcript for numbers in the range of 0 to 99.

function getEngNumeralUpto99(n::Int)::Str
    @assert 0 <= n <= 99 "n must be in range [0-99]"
    if n < 20
        return unitsAndTeens[n]
    end
    u::Int = rem(n, 10) # u - units
    result::Str = tens[n-u]
    if u != 0
        result *= "-" * unitsAndTeens[u]
    end
    return result
end

Whenever a number (n) is below 20 (if n < 20) we just return its representation from the unitsAndTeens dictionary. Alternatively, for n in the tens, first we obtain the units (u) part, which is a reminder of dividing n by 10 (rem(n, 10)). We use it (u), to obtain the transcript for the tens (tens[n-u]). It becomes our result to which we attach the transcript for units separated by a hyphen ("-" * unitsAndTeens[u]), but only if the unit is different than zero (if u != 0).

Time for a test ride.

(
    getEngNumeralUpto99.([0, 5, 9, 11, 13, 20, 21]),
    getEngNumeralUpto99.([25, 32, 58, 64, 66]),
    getEngNumeralUpto99.([79, 83, 95, 99])
)
(["zero", "five", "nine", "eleven", "thirteen", "twenty", "twenty-one"],
["twenty-five", "thrity-two", "fifty-eight", "sixty-four", "sixty-six"],
["seventy-nine", "eigthy-three", "ninety-five", "ninety-nine"])

Good. It appears we got that one out of our way. Let’s move on to something bigger.

function getEngNumeralUpto999(n::Int)::Str
    @assert 0 <= n <= 999 "n must be in range [0-999]"
    if n < 100
        return getEngNumeralUpto99(n)
    end
    h::Int, t::Int = divrem(n, 100) # h - hundreds, t - tens
    result::Str = getEngNumeralUpto99(h) * " hundred"
    if t != 0
        result *= " and " * getEngNumeralUpto99(t)
    end
    return result
end

This time we deal with all the integers below one thousand. The previously defined getEngNumeralUpto99 is an important building block of that new function. If a number (n) is smaller than 100 (if n < 100) we just transcribe it as we did in the previous code snippet (return getEngNumeralUpto99(n)). Otherwise we split it into hundreds (h) and tens part (t, actually it may also contain units and teens). We start building our result by transcribing the hundreds part (result::Str = getEngNumeralUpto99(n) * " hundred"). When appropriate (t != 0) we append the transcript for the tens separated with "and" (British English convention) to our result.

Time for a test.

(
    getEngNumeralUpto999.([0, 9, 13, 20, 66, 95]),
    getEngNumeralUpto999.([101, 109, 110]),
    getEngNumeralUpto999.([320, 400, 500])
)
(["zero", "nine", "thirteen", "twenty", "sixty-six", "ninety-five"],
["one hundred and one", "one hundred and nine", "one hundred and ten"],
["three hundred and twenty", "four hundred", "five hundred"])

Since the previous part was so easy, there’s not reason to linger. Time for our final leap.

function getEngNumeralBelow1M(n::Int)::Str
    @assert 0 <= n <= 999_999 "n must be in range [0-999,999]"
    if n < 1000
        return getEngNumeralUpto999(n)
    end
    t::Int, h::Int = divrem(n, 1000) # t - thousands, h - hundreds
    result::Str = getEngNumeralUpto999(t) * " thousand"
    if h == 0
        return result
    elseif h < 100
        result *= " and "
    else
        result *= ", "
    end
    result *= getEngNumeralUpto999(h)
    return result
end

This time we also use our previously defined function (getEngNumeralUpto999) as an integral part of the bigger, more general solution. When a number (n) is small (if n < 1000) we write it down as we used to (return getEngNumeralUpto999(n)). Otherwise, we split it (n) to the thousands (t) and the hundreds (h) parts. Next, we build our result for the thousands (getEngNumeralUpto999(t) * " thousand). When the hundreds part is 0 (if h == 0) we just return our result. If the hundreds part is small (h < 100) we add the conjunction " and ", otherwise (large hundreds part), we separate the following words with ", ". Once again, we finish by using getEngNumeralUpto999(h) to transcribe the remaining part.

Let’s see our creation at work.

(
    getEngNumeralBelow1M.([0, 21, 64, 95]),
    getEngNumeralBelow1M.([101, 320, 500]),
    getEngNumeralBelow1M.([1_800]),
    getEngNumeralBelow1M.([96_779]),
    getEngNumeralBelow1M.([180_000]),
    getEngNumeralBelow1M.([889_308])
)
(["zero", "twenty-one", "sixty-four", "ninety-five"],
["one hundred and one", "three hundred and twenty", "five hundred"],
["one thousand, eight hundred"],
["ninety-six thousand, seven hundred and seventy-nine"],
["one hundred and eigthy thousand"],
["eight hundred and eigthy-nine thousand, three hundred and eight"])

Mission, completed. If you ever wanted to transcribe a number in the range of millions, you know what to do. Use getEngNumeralBelow1M as its building block, and write it according to the above-presented schema.



CC BY-NC-SA 4.0 Bartlomiej Lukaszuk