DragonComputer/Dragonfire

View on GitHub
dragonfire/arithmetic.py

Summary

Maintainability
A
3 hrs
Test Coverage
#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
.. module:: arithmetic
    :platform: Unix
    :synopsis: the top-level submodule of Dragonfire that provides basic methods to evaluate the algebraic expressions.

.. moduleauthor:: Mehmet Mert Yıldıran <mert.yildiran@bil.omu.edu.tr>
"""


def text2int(textnum, numwords={}):
    """Function that converts number descriptions written in English to integer.

    Args:
        textnum (str):  Number despcription written in English.

    Keyword Args:
        numwords (dict): Dictionary that maps words to numbers.

    Returns:
        int: Integer version of given number description in string format.
    """

    if not numwords:
        units = [
            "zero", "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", "ninety"]

        scales = ["hundred", "thousand", "million", "billion", "trillion"]

        numwords["and"] = (1, 0)
        for idx, word in enumerate(units):    numwords[word] = (1, idx)
        for idx, word in enumerate(tens):     numwords[word] = (1, idx * 10)
        for idx, word in enumerate(scales):   numwords[word] = (10 ** (idx * 3 or 2), 0)

    current = result = 0
    for word in textnum.split():
        if word not in numwords:
            raise Exception("Illegal word: " + word)

        scale, increment = numwords[word]
        current = current * scale + increment
        if scale > 100:
            result += current
            current = 0

    return result + current


def arithmetic_parse(com):
    """Method to evaluate the algebraic expressions written in English.

    Args:
        com (str):  User's command.

    Returns:
        str: Result of the given algebraic expression.
    """

    if not any(e in com.upper() for e in ['+', '-', '/', '*', '^', '=', 'x', 'y', 'z', 'PLUS', 'MINUS', 'DIVIDED BY', 'MULTIPLIED BY', 'TIMES', 'TO THE POWER OF', 'EQUAL']):
        return False
    com = com.lower()
    numwords = [
        "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
        "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
        "sixteen", "seventeen", "eighteen", "nineteen",

        "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety",

        "hundred", "thousand", "million", "billion", "trillion",

        "and"
    ]

    operators = {
        'plus': '+',
        'minus': '-',
        'divided_by': '/',
        'multiplied_by': '*',
        'times': '*',
        'to_the_power_of': '^',
        'equal': '='
    }

    com = com.replace("?", "")
    com = com.replace("divided by", "divided_by")
    com = com.replace("multiplied by", "multiplied_by")
    com = com.replace("to the power of", "to_the_power_of")
    words = com.split()
    stash = []
    expression = []
    for i in range(len(words)):
        word = words.pop(0)
        if stash and (word not in numwords):
            expression.append(str(text2int(' '.join(stash))))
            stash = []
        if word in numwords:
            stash.append(word)
        elif word in operators:
            expression.append(operators[word])
        elif word in ['+', '-', '/', '*', '^', '(', ')'] or word.isdigit():
            expression.append(word)
        elif word == '=' or 'equal' in word:
            break
    if stash:
        expression.append(str(text2int(' '.join(stash))))
        stash = []

    try:
        return ' '.join(expression) + ' = ' + str(eval(''.join(expression)))
    except ZeroDivisionError:
        return "Sorry, but that does not make sense as the divisor cannot be zero."
    except:
        return False