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.