descriptive_numeric_string.py

# =========================================================
# convert a positive integer entered by the user
# to descriptive numeric string
# For example:
#  106    --> one hundred and six
#  15,268 --> fifteen thousand two hundred and sixty eight
# ---------------------------------------------------------
# Notes:
# 1. the string entered by the user must be converted to
#    an integer for processing
# 2. commas may be entered as part of the input
# 3. commas are removed for processing
# 4. correct placement of commas in the input is not check
# =========================================================

# ---------------------------------------------------------
# --- import
# ---------------------------------------------------------

import sys
import math

if sys.version_info.major is 3:
    from tkinter import *
    py3 = True
else:
    from Tkinter import *
    py3 = False

# ---------------------------------------------------------
# --- global variables
# ---------------------------------------------------------

debug = False

tensdigits = [ '', 'one', 'two', 'three', 'four', 'five',
        'six', 'seven', 'eight', 'nine',  'ten',
        'eleven', 'twelve', 'thirteen', 'fourteen',
        'fifteen', 'sixteen', 'seventeen', 'eighteen',
        'nineteen' ]

tens = [ '', '', 'twenty', 'thirty', 'forty',
        'fifty', 'sixty', 'seventy', 'eighty', 'ninty' ] 

# ---------------------------------------------------------
# --- functions
# ---------------------------------------------------------


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# --- accumulate strings (concatination is not allowed)
# --- Based on: mindbending.org/en/
# ---       python-strings-accumulation-techniques
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def accumulate2Strings(str1,str2):
    return '{}{}'.format(str1,str2)

def accumulate3Strings(str1,str2,str3):
    return '{}{}{}'.format(str1,str2,str3)

def accumulate4Strings(str1,str2,str3,str4):
    return '{}{}{}{}'.format(str1,str2,str3,str4)

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# --- return tens/digits numeric string
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def getTenDigitString(td,t,d,h):

    ##print('getTenDigitString(td={},t={},d={})'.format(td,t,d,h))

    if td == 0:
         return ''

    if td < 20:
            return tensdigits[td]

    if d == 0:
            return tens[t]

    if t == 0:
            return tensdigits[td]

    return tens[t] + ' ' + tensdigits[d]

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# --- return hundreds/tens/digits numeric string
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def getHundredTenDigitString(n):

    ##print('getHundredTenDigitString({})'.format(n))

    h  = n // 100              # hundreds
    td = n  % 100              # tens/digits
    t  = td // 10              # tens
    d  = td % 10               # digits

    if debug:
        print('-------------')
        print('Hundreds = {}'.format(h))
        print('tens     = {}'.format(t))
        print('digits   = {}'.format(d))
        print('t/d      = {}'.format(td))
 
    # --- zero?

    if n == 0:
        return '' 

    # --- tens/digits only?

    tdstr = getTenDigitString(td,t,d,h)

    if h == 0:
        return tdstr

    # --- hundreds only?

    if tdstr == '':
        return tensdigits[h] + ' hundred'

    # --- hundreds and tens/digits

    return tensdigits[h]  + ' hundred' + ' and ' + tdstr

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# --- given a positive integer,
# --- return it's descriptive numeric string
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def intToDescriptiveNumericString(n):

    ##print('intToDescriptiveNumericString({})'.format(n))

    if n < 0:
        print'Error: integer negative'
        return ''

    if n == 0:
        return 'zero'

    if n >= 10**15:
        print('Error: integer is too large for program')
        return ''

    tr = n // 10**12           # trillions
    n = n - (tr * 10**12)      # remove trillions
    bb = n // 10**9            # billions
    n = n - (bb * 10**9)       # remove billions
    mm = n // 10**6            # millions
    n = n - (mm * 10**6)       # remove millions
    th = n // 10**3            # thousands
    n = n - (th * 10**3)       # remove thousands
    hu = n % 10**3             # hundreds

    if debug:
        print('trillion = {}'.format(tr))
        print('billion  = {}'.format(bb))
        print('Million  = {}'.format(mm))
        print('thousand = {}'.format(th))
        print('hundred  = {}'.format(hu))

    tdstr = getHundredTenDigitString(hu)

    str = ''

    if tr != 0:
        str = accumulate2Strings(
                getHundredTenDigitString(tr),' trillion')
    ##print('STR',str)

    if bb != 0:
        if len(str) == 0:
            str = accumulate2Strings(
                    getHundredTenDigitString(bb),' billion')
        else:
            str = accumulate4Strings(
                    str,', ',getHundredTenDigitString(bb),' billion')
    ##print('STR',str)

    if mm != 0:
        if len(str) == 0:
            str = accumulate2Strings(
                    getHundredTenDigitString(mm),' million')
        else:
            str = accumulate4Strings(
                    str,', ',getHundredTenDigitString(mm),' million')
    ##print('STR',str)

    if th != 0:
        if len(str) == 0:
            str = accumulate2Strings(
                    getHundredTenDigitString(th),' thousand')
        else:
            str = accumulate4Strings(
                    str,', ',getHundredTenDigitString(th),' thousand')
    ##print('STR',str)

    if len(tdstr) != 0:
        if len(str) == 0:
            return tdstr
        else:
            return str + ', ' + tdstr
    ##print('STR',str)

    return str


# ---------------------------------------------------------
# --- main
# ---------------------------------------------------------

if __name__ == '__main__':

    # - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # --- test functions
    # - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # --- ask the user for input

    def getUserInput(prompt,py3):
        if py3:
            return input(prompt)
        else:
            return raw_input(prompt)

    # --- pause the program

    def pause(py3):
        getUserInput('\nPress enter to continue ',py3)

    # --- clear the screen

    import os, platform

    def clearScreen():
        if platform.system() == 'Linux':
            os.system('clear')
        elif platform.system() == 'Windows':
            os.system('clear')
        else:
            os.system('cls')

    # - - - - - - - - - - - - - - - - - - - - - - - - - - -
    # --- process input until the user quits
    # - - - - - - - - - - - - - - - - - - - - - - - - - - -

    while True:

        # --- ask the user for an integer

        str = getUserInput('\nEnter an integer ',py3)

        # --- remove leading and trailing spaces
        # --- quit if there was no input

        s = str.strip()

        if s == '':
            break

        # --- remove commas from input

        s = s.replace(',','')

        # -- is the input an integer?

        if s.isdigit() != True:
            print('\nIllegal value entered ({})'.format(str))
            continue

        # --- get a description numeric string of the integer
        # --- on error, an empty string is returned

        str = intToDescriptiveNumericString(int(s))

        # --- display the results

        if str == '':
            print('\nOops! a conversion error')
        else:
            print('\n{}'.format(str))

            print('')
            for x in str.split():
                print('{},'.format(x.strip()))