# Euler Problem 17¶

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of "and" when writing out numbers is in compliance with British usage.

I can see us doing a semi-automated approach here or we write a nice function. We probably write a nice function because we are geeks.

In [1]:

    def single_digit_integer_to_spoken_language(n):
        if n == 0:
            return ""
        assert(n > 0 and n < 10)
        return {1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five',
                6: 'six', 7: 'seven', 8: 'eight', 9: 'nine'}[n]

    def double_digit_integer_to_spoken_language(n):
        assert(n > 9 and n < 100)
        try:
            return {
                10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen',
                14: 'fourteen', 15: 'fifteen', 16: 'sixteen',
                17: 'seventeen', 18: 'eighteen', 19: 'nineteen'}[n]
        except KeyError:
            pass
        a, b = str(n)
        a = {2: 'twenty', 3: 'thirty', 4: 'forty', 5: 'fifty',
             6: 'sixty', 7: 'seventy', 8: 'eighty', 9: 'ninety'}[int(a)]
        b = integer_to_spoken_language(int(b))
        return a + '-' + b

    def triple_digit_integer_to_spoken_language(n):
        a, b = str(n)[0], str(n)[1:]
        a = single_digit_integer_to_spoken_language(int(a))
        b = integer_to_spoken_language(int(b))
        if not b:
            return a + " hundred"
        return a + " hundred and " + b

    def four_digit_integer_to_spoken_language(n):
        a, b = str(n)[0], str(n)[1:]
        a = single_digit_integer_to_spoken_language(int(a))
        b = integer_to_spoken_language(int(b))
        return a + " thousand " + b

    def integer_to_spoken_language(n):
        l = len(str(n))
        if l == 1:
            return single_digit_integer_to_spoken_language(n)
        elif l == 2:
            return double_digit_integer_to_spoken_language(n)
        elif l == 3:
            return triple_digit_integer_to_spoken_language(n)
        elif l == 4:
            return four_digit_integer_to_spoken_language(n)
        else:
            raise Exception("Length not supported.")

    assert(integer_to_spoken_language(5) == 'five')
    assert(integer_to_spoken_language(19) == 'nineteen')
    assert(integer_to_spoken_language(21) == 'twenty-one')
    assert(integer_to_spoken_language(210) == 'two hundred and ten')
    assert(integer_to_spoken_language(3000) == 'three thousand ')
    assert(integer_to_spoken_language(8333) == 'eight thousand three hundred and thirty-three')

Okay, I won't win a code golf contest but at least we can get the solution now.

In [2]:

    l = len("".join([integer_to_spoken_language(i) for i in range(1, 1001)]).replace(" ", "").replace("-", ""))
    assert(l == 21124)
    print(l)

    21124

Made the classical fourty/forty error I have already done four years ago. Some things don't change. Also I still do not really like this problem. The Haskell solution is actually way easier to read this time.