From 18868b9055d170f7a0fda0d649beeb9874b8e8b6 Mon Sep 17 00:00:00 2001 From: "sergey.volodzko" Date: Sun, 16 Dec 2018 18:33:36 +0300 Subject: [PATCH 1/4] implement all math operations --- final_task/program/program.py | 607 ++++++++++++++++++++++++++++++++++ final_task/setup.py | 23 ++ final_task/test_program.py | 165 +++++++++ 3 files changed, 795 insertions(+) create mode 100644 final_task/program/program.py create mode 100644 final_task/test_program.py diff --git a/final_task/program/program.py b/final_task/program/program.py new file mode 100644 index 0000000..57cf784 --- /dev/null +++ b/final_task/program/program.py @@ -0,0 +1,607 @@ +#!/usr/bin/env python3 + +import argparse +import math +import re + +# dict { name function: (execution priority, calculation)} +OPERATORS = {'+': (1, lambda x, y: x + y), + '-': (1, lambda x, y: x - y), + '*': (2, lambda x, y: x * y), + '/': (2, lambda x, y: x / y), + '//': (2, lambda x, y: x // y), + '%': (2, lambda x, y: x % y), + '^': (4, lambda x, y: x ** y), + 'atan2': (3, lambda x, y: math.atan2(x, y)), + 'copysign': (3, lambda x, y: math.copysign(x, y)), + 'fmod': (3, lambda x, y: math.fmod(x, y)), + 'gcd': (3, lambda x, y: math.gcd(x, y)), + 'hypot': (3, lambda x, y: math.hypot(x, y)), + 'isclose': (3, lambda x, y: math.isclose(x, y)), + 'ldexp': (3, lambda x, y: math.ldexp(x, y)), + 'pow': (3, lambda x, y: math.pow(x, y)), + 'log': (3, lambda x, y: math.log(x, y)), + 'acos': (3, lambda x: math.acos(x)), + 'acosh': (3, lambda x: math.acosh(x)), + 'asin': (3, lambda x: math.asin(x)), + 'asinh': (3, lambda x: math.asinh(x)), + 'atan': (3, lambda x: math.atan(x)), + 'atanh': (3, lambda x: math.atanh(x)), + 'ceil': (3, lambda x: math.ceil(x)), + 'cos': (3, lambda x: math.cos(x)), + 'cosh': (3, lambda x: math.cosh(x)), + 'degrees': (3, lambda x: math.degrees(x)), + 'erf': (3, lambda x: math.erf(x)), + 'erfc': (3, lambda x: math.erfc(x)), + 'exp': (3, lambda x: math.exp(x)), + 'expm1': (3, lambda x: math.expm1(x)), + 'fabs': (3, lambda x: math.fabs(x)), + 'factorial': (3, lambda x: math.factorial(x)), + 'floor': (3, lambda x: math.floor(x)), + 'frexp': (3, lambda x: math.frexp(x)), + 'gamma': (3, lambda x: math.gamma(x)), + 'isfinite': (3, lambda x: math.isfinite(x)), + 'isinf': (3, lambda x: math.isinf(x)), + 'isnan': (3, lambda x: math.isnan(x)), + 'lgamma': (3, lambda x: math.lgamma(x)), + 'log10': (3, lambda x: math.log10(x)), + 'log1p': (3, lambda x: math.log1p(x)), + 'log2': (3, lambda x: math.log2(x)), + 'modf': (3, lambda x: math.modf(x)), + 'radians': (3, lambda x: math.radians(x)), + 'sin': (3, lambda x: math.sin(x)), + 'sinh': (3, lambda x: math.sinh(x)), + 'sqrt': (3, lambda x: math.sqrt(x)), + 'tan': (3, lambda x: math.tan(x)), + 'tanh': (3, lambda x: math.tanh(x)), + 'trunc': (3, lambda x: math.trunc(x)), + 'round': (3, lambda x: round(x)), + 'abs': (3, lambda x: abs(x)) + } + +constant = {'pi': math.pi, + 'e': math.e, + 'inf': math.inf, + 'nan': math.nan, + 'tau': math.tau + } + +# all operators +OPERATORS_func = ['acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', + 'degrees', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', + 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', + 'log10', 'log1p', 'log2', 'modf', 'nan', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', + 'tau', 'trunc', 'round', 'abs', '+', '-', '*', '/', '%', '^', '//', '(', ')', 'e', 'pi', 'inf', + 'nan', 'tau'] + +# operators that take two values +OPERATORS_double = ['+', '-', '*', '/', '%', '^', '//', '%', 'atan2', 'copysign', 'fmod', 'gcd', 'hypot', 'isclose', + 'ldexp', 'pow', 'log'] + +OPERATORS_compare = {'==': (lambda x, y: x == y), + '>=': (lambda x, y: x >= y), + '<=': (lambda x, y: x <= y), + '!=': (lambda x, y: x != y), + '<': (lambda x, y: x < y), + '>': (lambda x, y: x > y) + } + + +def main_count(formula): + """Main function + + Pure command-line calculator using python. + Formula must include string positional arguments: + "EXPRESSION expression string to evaluate". + + """ + + def check_error(formula): + """Check error function + + Validation check + + """ + + if not formula: + return "ERROR: no value" + else: + + if formula[-1] in OPERATORS: + return "ERROR: syntax mistake" + if formula[0] in '^%*/= |\\': + return "ERROR: syntax mistake" + + if re.search(r'\d\s\d', formula): + return "ERROR: syntax mistake" + + if formula.count('(') != formula.count(')'): + return "ERROR: brackets are not balanced" + + if formula in OPERATORS: + return "ERROR: no function arguments" + + if '()' in formula or '[]' in formula: + return "ERROR: no function arguments" + + if re.search(r'[*,/,<,>,%,^][*,/,<,>,%,^]', formula): + return "ERROR: syntax error in statements" + + if re.search(r'[*,/,<,>,%,^,=]\s[*,/,<,>,%,^,=]', formula): + return "ERROR: syntax error in statements" + + if re.search(r'log[1,2][0,p][^(]', formula): + return "ERROR: unknown function" + + # function argument checking + for name_func in OPERATORS: + if name_func.isalpha() or name_func in ['log1p', 'log10', 'atan2', 'expm1', 'log2']: + # split by function name + '(' + name_func_bracket = '{}('.format(name_func) + + if name_func_bracket in formula: + + for expression in formula.split(name_func_bracket)[1:]: + count_brackets = 0 + count_arguments = 0 + for key in expression: + if key == ',' and not count_brackets: + # arguments are separated by commas + # count when no count_brackets + count_arguments += 1 + + if key == ')' and not count_brackets: + break + elif key == ')': + count_brackets -= 1 + elif key == '(': + count_brackets += 1 + + if count_arguments > 1: + return "ERROR: function arguments are not balanced" + # function pow has two or one argument + elif (count_arguments > 1 or count_arguments == 0) and name_func == 'pow': + return "ERROR: function arguments are not balanced" + # these functions have 2 arguments + elif count_arguments == 0 and name_func in ['atan2', 'copysign', 'fmod', 'gcd', 'hypot', + 'isclose', 'ldexp', 'pow']: + return "ERROR: function arguments are not balanced" + # other functions with 1 argument + elif count_arguments > 0 and name_func not in ['atan2', 'copysign', 'fmod', 'gcd', 'hypot', + 'isclose', 'ldexp', 'pow', 'log']: + return "ERROR: function arguments are not balanced" + + def function_format_two_arguments(formula, name_function): + """ Formatting formula with two arguments """ + + result_formula = formula + first_argument = [] + second_argument = [] + count_argument = True + count_bracket = 0 + + # find first and second argument + for key in (name_function).join(formula.split('{}'.format(name_function))[1:]): + + if key == '(': + count_bracket += 1 + if key == ')': + count_bracket -= 1 + if key == ',': + first_argument.pop(0) + count_argument = False + + if key == ')' and not count_bracket and not count_argument: + break + elif key == ')' and not count_bracket: + first_argument.append(key) + break + + elif count_argument: + first_argument.append(key) + + else: + if key != ',': + second_argument.append(key) + + # if not second argument into log - the base by log = e + # if log(x) format into log(x,e) + if not second_argument and name_function == 'log': + second_argument.append('e') + replace_expression = "{}{}".format(name_function, ''.join(first_argument), 1) + else: + replace_expression = "{}({},{})".format(name_function, ''.join(first_argument), + ''.join(second_argument), 1) + + # count every argument + x = main_count(''.join(first_argument)) + y = main_count(''.join(second_argument)) + + # function gcd have only int arguments + if name_function == 'gcd': + x = int(x) + y = int(y) + + result_function = OPERATORS[name_function][1](x, y) + # replace only 1 time + result_formula = result_formula.replace(replace_expression, str(result_function), 1) + + # double entry check + if name_function == 'log': + # there are log2p, log10 with 1 argument + if 'log(' in result_formula: + result_formula = function_format_two_arguments(result_formula, name_function) + else: + if name_function in result_formula: + result_formula = function_format_two_arguments(result_formula, name_function) + + return result_formula + + def format_degree_priority(formula): + """Function add priority degree + + If we have more than one degree - then add next degree with high priority. + """ + + result_formula = [] + + for i, key in enumerate(formula.split('^')): + result_formula.append(key) + if i != len(formula.split('^')) - 1: + result_formula.append('^' * (i + 1)) + # add in OPERATORS degree with high priority + OPERATORS.update({'^' * (i + 1): (4 + i, lambda x, y: x ** y)}) + OPERATORS_double.append('^' * (i + 1)) + OPERATORS_func.append('^' * (i + 1)) + + return ''.join(result_formula) + + def function_format_degree_replace_function(formula): + """Function replace degree by function into degree by function with brackets + + For example: 1^sin(pi) into ==> 1^(sin(pi)) + """ + + expression = [] + count_argument = False + count_bracket = 0 + for i, key in enumerate(formula): + + if key == '(': + count_bracket += 1 + elif key == ')': + count_bracket -= 1 + + if count_argument: + expression.append(key) + + if key == ')' and not count_bracket and count_argument: + break + + if key == '^' and formula[i + 1].isalpha(): + count_argument = True + # replace only 1 time + formula = formula.replace("{}".format(''.join(expression)), "({})".format(''.join(expression)), 1) + + # replace other instance + count = False + for i, key in enumerate(formula): + if key == '^' and formula[i + 1].isalpha(): + count = True + break + if count: + formula = function_format_degree_replace_function(formula) + + return formula + + def replace_degree_negative_number(formula): + """Function replace negative number with degree in formula into zero with brackets + + For example: + replace ^-5 ==> into ^(0-5) + ^-e == > into ^(0-e) + ^-cos() == > into ^(0-cos()) + ^-(1+2) == > into ^(0-(1+2)) + + """ + + expression = [] + count = False + bracket_count = 0 + for i, key in enumerate(formula): + + if count: + if key == ')' and not bracket_count: + expression.append(key) + break + elif key == ')': + bracket_count -= 1 + elif key == '(': + bracket_count += 1 + + if expression and (key in OPERATORS_double or key in OPERATORS_compare) and not bracket_count: + break + else: + expression.append(key) + + if key == '^' and formula[i + 1] == '-': + count = True + + # replace only first expression + formula = formula.replace(("^{}".format(''.join(expression))), "^(0{})".format(''.join(expression)), 1) + + # replace other expressions + if '^-' in formula: + formula = replace_degree_negative_number(formula) + + return formula + + def format_function_fsum(formula): + """Format function fsum + Calculate this function here because fsum has many arguments + + replace fsum([1,2]) ==> into 3 + + """ + + count_brackets = 0 + count_brackets_square = 0 + count_arguments = [] + add_argument = [] + + for key in formula.split('fsum([', 1)[1:][0]: + if key == "]" and not count_brackets_square: + break + + if key == ',' and not count_brackets: + count_arguments.append(''.join(add_argument)) + add_argument = [] + else: + add_argument.append(key) + + if key == ']': + count_brackets_square -= 1 + elif key == '[': + count_brackets_square += 1 + elif key == ')': + count_brackets -= 1 + elif key == '(': + count_brackets += 1 + + count_arguments.append(''.join(add_argument)) + + result_count = 0 + for argument in count_arguments: + result_count += float(main_count(argument)) + + replace_expression = "fsum([{}])".format(','.join(count_arguments), 1) + + result_formula = formula.replace(replace_expression, str(result_count), 1) + + return result_formula + + def formula_formatting(formula): + """Formula formatting + + Getting the formula to a readable form for reverse Polish notation. + + """ + + # delete backspace + formula = formula.replace(' ', '') + # replace )( into )*( for readable form + formula = formula.replace(')(', ')*(') + # replace (- into (0- for readable form + formula = '(0-'.join(formula.split('(-')) + + # formatting a formula with two arguments + for name_function in ['pow', 'log(', 'copysign', 'ldexp', 'atan2', 'hypot', 'fmod', 'gcd']: + if name_function in formula: + # use only log (without log10,log1p) + if name_function == 'log(': + name_function = 'log' + formula = function_format_two_arguments(formula, name_function) + + # find degree by function + for i, key in enumerate(formula): + if key == '^' and formula[i + 1].isalpha(): + formula = function_format_degree_replace_function(formula) + break + + # find the number of degrees + if formula.count("^") > 1: + formula = format_degree_priority(formula) + + # replace sign "+","-" in front formula + new_data = [] + new_data.append(formula[0]) + + for token in formula[1:]: + if new_data[-1] == '+' and token == '-': + new_data[-1] = '-' + elif new_data[-1] == '-' and token == '+': + new_data[-1] = '-' + elif new_data[-1] == '-' and token == '-': + new_data[-1] = '+' + elif new_data[-1] == '+' and token == '+': + new_data[-1] = '+' + else: + new_data.append(token) + if new_data[0] == '-': + new_data.insert(0, '0') + if new_data[0] == '+': + new_data.pop(0) + + formula = ''.join(new_data) + + # replace negative number in formula into zero with brackets + # replace 5/-1 into 5*(0-1)/1 + formula = formula.replace('/-', '*(0-1)/') + formula = formula.replace('*-', '*(0-1)*') + + # replace negative number with degree in formula into zero with brackets + if '^-' in formula: + formula = replace_degree_negative_number(formula) + + # format function fsum because fsum([1,2,3,4..]) has many arguments + if 'fsum' in formula: + formula = format_function_fsum(formula) + + return formula + + def split_formula(formatted_formula): + """Function splitting formula into keys + + Return iterator. + + """ + + stack = [] + for i, key in enumerate(formatted_formula): + + if key in '1234567890.': + # check log10 log1p log2 + if len(stack) > 2 and ''.join(stack[:3]) == 'log': + + stack.append(key) + if ''.join(stack) in OPERATORS_func: + yield ''.join(stack) + stack = [] + + elif stack and (stack[0] not in '1234567890.'): + yield ''.join(stack) + stack = [] + stack.append(key) + else: + stack.append(key) + + else: + if key in '()': + if stack and (stack[0] in '1234567890.'): + yield float(''.join(stack)) + elif stack: + yield ''.join(stack) + + stack = [] + stack.append(key) + + elif len(stack) > 2 and ''.join(stack[:3]) == 'log': + stack.append(key) + if ''.join(stack) in OPERATORS_func: + yield ''.join(stack) + stack = [] + + elif stack and stack[0] in '1234567890.': + yield float(''.join(stack)) + stack = [] + stack.append(key) + + else: + + if ''.join(stack) + key in OPERATORS_func: + stack.append(key) + + elif ''.join(stack) in constant: + yield constant.get(''.join(stack)) + stack = [] + stack.append(key) + + elif ''.join(stack) in OPERATORS_func: + + yield ''.join(stack) + stack = [] + stack.append(key) + else: + stack.append(key) + else: + + if stack and (stack[0] in '1234567890.'): + yield float(''.join(stack)) + elif ''.join(stack) in constant: + yield constant.get(''.join(stack)) + else: + yield ''.join(stack) + + def convert_into_reverse_pol_notation(splitted_formula): + """Covert formula(key) into Reverse Polish notation + + Return iterator + + """ + + stack = [] + for token in splitted_formula: + # replace constant + if token in constant: + token = constant.get(''.join(token)) + + if token in OPERATORS: + # choose by priority + while stack and stack[-1] != "(" and OPERATORS[token][0] <= OPERATORS[stack[-1]][0]: + yield stack.pop() + stack.append(token) + elif token == ")": + while stack: + x = stack.pop() + if x == "(": + break + yield x + elif token == "(": + stack.append(token) + else: + yield token + while stack: + yield stack.pop() + + def calc_reverse_pol_notation(converted_formula): + """Calculate Reverse Polish notation""" + + stack = [] + for token in converted_formula: + + if token in OPERATORS: + # check function with double arguments + if token in OPERATORS_double: + y, x = stack.pop(), stack.pop() + stack.append(OPERATORS[token][1](x, y)) + else: + # use function with 1 argument + x = stack.pop() + stack.append(OPERATORS[token][1](x)) + else: + stack.append(token) + + return stack[0] + + def check_comparison(formula): + """Function checks if there are comparison operations""" + + for compare in OPERATORS_compare: + if compare in formula: + return OPERATORS_compare.get(compare)( + main_count(formula.split(compare)[0]), + main_count(formula.split(compare)[1])) + + # start calculation + return calc_reverse_pol_notation(convert_into_reverse_pol_notation(split_formula(formula_formatting(formula)))) + + return check_error(formula) if check_error(formula) else check_comparison(formula) + + +def main(): + """Use of the module argparse + + For installing the utility pycalc + + """ + + parser = argparse.ArgumentParser() + parser.add_argument("EXPRESSION", help="expression string to evaluate", type=str) + args = parser.parse_args() + + # start main count with parsed arguments + print(main_count(args.EXPRESSION)) + + +if __name__ == '__main__': + print(main_count('fsum([9])')) diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..f9c82be 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup + +setup( + name = 'Calculator', + version = '0.01', + description = 'Pure command-line calculator using python', + author = 'S.Volodzko', + url = '', + license = 'MIT', + packages = ['program'], + entry_points = {'console_scripts': ['pycalc = program.program:main',],}, +) + + +''' +You have a package called the program and inside it a file called the program.py +using the main () parameter. Run setup.py like this: + +python3 setup.py install + +It installed it in the directory of package sites for your platform and will create a console script called a pycalc. +Then you can run the program with your item. +''' diff --git a/final_task/test_program.py b/final_task/test_program.py new file mode 100644 index 0000000..c73d6e3 --- /dev/null +++ b/final_task/test_program.py @@ -0,0 +1,165 @@ +import unittest + +from program.program import * +from math import * + + +class ErrorTest(unittest.TestCase): + """Check Error tests""" + + def test_normal(self): + result = main_count('2+2') + self.assertEqual(result, eval('2+2')) + + def test_calc(self): + result = main_count('2+4') + self.assertEqual(result, eval('2+4')) + + def test_check_error_null(self): + result = main_count('') + self.assertEqual(result, "ERROR: no value") + + def test_check_error_first_sign(self): + result = main_count('%12') + self.assertEqual(result, "ERROR: syntax mistake") + + def test_check_error_first_sign2(self): + result = main_count('\\12') + self.assertEqual(result, "ERROR: syntax mistake") + + def test_check_error_bad_param(self): + result = main_count('log(10,2,3)') + self.assertEqual(result, "ERROR: function arguments are not balanced") + + def test_check_error_not_param(self): + result = main_count('log()') + self.assertEqual(result, "ERROR: no function arguments") + + +class FunctionListTest(unittest.TestCase): + """Check function work""" + + def test_acos(self): + result = main_count('acos(1)') + self.assertEqual(result, eval('acos(1)')) + + def test_acosh(self): + result = main_count('acosh(1)') + self.assertEqual(result, eval('acosh(1)')) + + def test_asin(self): + result = main_count('asin(1)') + self.assertEqual(result, eval('asin(1)')) + + def test_asinh(self): + result = main_count('asinh(1)') + self.assertEqual(result, eval('asinh(1)')) + + def test_atan(self): + result = main_count('atan(0)') + self.assertEqual(result, eval('atan(0)')) + + def test_fsum_one_argument(self): + result = main_count('fsum([9])') + self.assertEqual(result, eval('fsum([9])')) + + def test_fsum_two_argument(self): + result = main_count('fsum([9,1])') + self.assertEqual(result, eval('fsum([9,1])')) + + def test_fsum_more_argument(self): + result = main_count('fsum([9,1,2,3])') + self.assertEqual(result, eval('fsum([9,1,2,3])')) + + def test_ceil(self): + result = main_count('ceil(1)') + self.assertEqual(result, eval('ceil(1)')) + + def test_degrees(self): + result = main_count('degrees(1)') + self.assertEqual(result, eval('degrees(1)')) + + # Check constant + def test_pi(self): + result = main_count('pi') + self.assertEqual(result, eval('pi')) + + def test_e(self): + result = main_count('e') + self.assertEqual(result, eval('e')) + + def test_inf(self): + result = main_count('inf') + self.assertEqual(result, eval('inf')) + + def test_nan(self): + result = main_count('nan') + self.assertEqual(isnan(result), isnan(eval('nan'))) + + def test_tau(self): + result = main_count('tau') + self.assertEqual(result, eval('tau')) + + +class CalculateListTest(unittest.TestCase): + """Check calculating""" + + def test_negative_number(self): + result = main_count('2+(-3*3-1)') + self.assertEqual(result, eval('2+(-3*3-1)')) + + def test_del_space(self): + result = main_count('3*3 -1') + self.assertEqual(result, eval('3*3 -1')) + + def test_replace_log_1param(self): + result = main_count('log(2+2)') + self.assertEqual(result, eval('log(2+2)')) + + def test_replace_log_2param(self): + result = main_count('log(2,2)') + self.assertEqual(result, eval('log(2,2)')) + + def test_replace_gcd_2param(self): + result = main_count('gcd(2,2)') + self.assertEqual(result, eval('gcd(2,2)')) + + def test_degree_function(self): + result = main_count('2^sin(2)') + self.assertEqual(result, eval('2**sin(2)')) + + def test_degree_priority(self): + result = main_count('2^3^4') + self.assertEqual(result, eval('2**3**4')) + + def test_degree_negative_number(self): + result = main_count('2^-3') + self.assertEqual(result, eval('2**(-3)')) + + def test_negative_numbers(self): + result = main_count('--+-2') + self.assertEqual(result, eval('--+-2')) + + def test_negative_numbers_multiplication(self): + result = main_count('3*-2') + self.assertEqual(result, eval('3*-2')) + + def test_float_numbers(self): + result = main_count('3.2-2.875') + self.assertEqual(result, eval('3.2-2.875')) + + def test_compare_function_more(self): + result = main_count('3>5') + self.assertEqual(result, eval('3>5')) + + def test_compare_function_less(self): + result = main_count('3<=5') + self.assertEqual(result, eval('3<=5')) + + def test_complex(self): + result = main_count('3.2+2*cos(e^log(7*e^e^sin(12.3),7.0) + cos(log10(e^-e))+3/2)') + self.assertEqual(result, eval('3.2+2*cos(e**log(7*e**e**sin(12.3),7.0) + cos(log10(e**-e))+3/2)')) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From ddd6fe0eefbcf49e4e07d94a4255d16247a1e192 Mon Sep 17 00:00:00 2001 From: "sergey.volodzko" Date: Sun, 16 Dec 2018 19:01:47 +0300 Subject: [PATCH 2/4] fix spaces in setup.py --- final_task/setup.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/final_task/setup.py b/final_task/setup.py index f9c82be..9eb2a9b 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -1,17 +1,16 @@ from setuptools import setup setup( - name = 'Calculator', - version = '0.01', - description = 'Pure command-line calculator using python', - author = 'S.Volodzko', - url = '', - license = 'MIT', - packages = ['program'], - entry_points = {'console_scripts': ['pycalc = program.program:main',],}, + name='Calculator', + version='0.01', + description='Pure command-line calculator using python', + author='S.Volodzko', + url='', + license='MIT', + packages=['program'], + entry_points={'console_scripts': ['pycalc = program.program:main']} ) - ''' You have a package called the program and inside it a file called the program.py using the main () parameter. Run setup.py like this: From 803ffc2654f05e8699ceda38d61a54339b4f3aa2 Mon Sep 17 00:00:00 2001 From: "sergey.volodzko" Date: Sun, 16 Dec 2018 19:18:54 +0300 Subject: [PATCH 3/4] fix blank line in test_program.py --- final_task/test_program.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/final_task/test_program.py b/final_task/test_program.py index c73d6e3..b66e719 100644 --- a/final_task/test_program.py +++ b/final_task/test_program.py @@ -1,5 +1,4 @@ import unittest - from program.program import * from math import * @@ -162,4 +161,5 @@ def test_complex(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + + unittest.main() From 538becbf36f82d5ded8419e8b26682de8d316be8 Mon Sep 17 00:00:00 2001 From: pittoly <43862358+pittoly@users.noreply.github.com> Date: Tue, 18 Dec 2018 15:51:10 +0300 Subject: [PATCH 4/4] Update program.py add rename variable --- final_task/program/program.py | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/final_task/program/program.py b/final_task/program/program.py index 57cf784..5a2245c 100644 --- a/final_task/program/program.py +++ b/final_task/program/program.py @@ -5,7 +5,7 @@ import re # dict { name function: (execution priority, calculation)} -OPERATORS = {'+': (1, lambda x, y: x + y), +operators = {'+': (1, lambda x, y: x + y), '-': (1, lambda x, y: x - y), '*': (2, lambda x, y: x * y), '/': (2, lambda x, y: x / y), @@ -67,7 +67,7 @@ } # all operators -OPERATORS_func = ['acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', +operators_func = ['acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', @@ -75,10 +75,10 @@ 'nan', 'tau'] # operators that take two values -OPERATORS_double = ['+', '-', '*', '/', '%', '^', '//', '%', 'atan2', 'copysign', 'fmod', 'gcd', 'hypot', 'isclose', +operators_double = ['+', '-', '*', '/', '%', '^', '//', '%', 'atan2', 'copysign', 'fmod', 'gcd', 'hypot', 'isclose', 'ldexp', 'pow', 'log'] -OPERATORS_compare = {'==': (lambda x, y: x == y), +operators_compare = {'==': (lambda x, y: x == y), '>=': (lambda x, y: x >= y), '<=': (lambda x, y: x <= y), '!=': (lambda x, y: x != y), @@ -107,7 +107,7 @@ def check_error(formula): return "ERROR: no value" else: - if formula[-1] in OPERATORS: + if formula[-1] in operators: return "ERROR: syntax mistake" if formula[0] in '^%*/= |\\': return "ERROR: syntax mistake" @@ -118,7 +118,7 @@ def check_error(formula): if formula.count('(') != formula.count(')'): return "ERROR: brackets are not balanced" - if formula in OPERATORS: + if formula in operators: return "ERROR: no function arguments" if '()' in formula or '[]' in formula: @@ -134,7 +134,7 @@ def check_error(formula): return "ERROR: unknown function" # function argument checking - for name_func in OPERATORS: + for name_func in operators: if name_func.isalpha() or name_func in ['log1p', 'log10', 'atan2', 'expm1', 'log2']: # split by function name + '(' name_func_bracket = '{}('.format(name_func) @@ -222,7 +222,7 @@ def function_format_two_arguments(formula, name_function): x = int(x) y = int(y) - result_function = OPERATORS[name_function][1](x, y) + result_function = operators[name_function][1](x, y) # replace only 1 time result_formula = result_formula.replace(replace_expression, str(result_function), 1) @@ -249,10 +249,10 @@ def format_degree_priority(formula): result_formula.append(key) if i != len(formula.split('^')) - 1: result_formula.append('^' * (i + 1)) - # add in OPERATORS degree with high priority - OPERATORS.update({'^' * (i + 1): (4 + i, lambda x, y: x ** y)}) - OPERATORS_double.append('^' * (i + 1)) - OPERATORS_func.append('^' * (i + 1)) + # add in operators degree with high priority + operators.update({'^' * (i + 1): (4 + i, lambda x, y: x ** y)}) + operators_double.append('^' * (i + 1)) + operators_func.append('^' * (i + 1)) return ''.join(result_formula) @@ -319,7 +319,7 @@ def replace_degree_negative_number(formula): elif key == '(': bracket_count += 1 - if expression and (key in OPERATORS_double or key in OPERATORS_compare) and not bracket_count: + if expression and (key in operators_double or key in operators_compare) and not bracket_count: break else: expression.append(key) @@ -464,7 +464,7 @@ def split_formula(formatted_formula): if len(stack) > 2 and ''.join(stack[:3]) == 'log': stack.append(key) - if ''.join(stack) in OPERATORS_func: + if ''.join(stack) in operators_func: yield ''.join(stack) stack = [] @@ -487,7 +487,7 @@ def split_formula(formatted_formula): elif len(stack) > 2 and ''.join(stack[:3]) == 'log': stack.append(key) - if ''.join(stack) in OPERATORS_func: + if ''.join(stack) in operators_func: yield ''.join(stack) stack = [] @@ -498,7 +498,7 @@ def split_formula(formatted_formula): else: - if ''.join(stack) + key in OPERATORS_func: + if ''.join(stack) + key in operators_func: stack.append(key) elif ''.join(stack) in constant: @@ -506,7 +506,7 @@ def split_formula(formatted_formula): stack = [] stack.append(key) - elif ''.join(stack) in OPERATORS_func: + elif ''.join(stack) in operators_func: yield ''.join(stack) stack = [] @@ -535,9 +535,9 @@ def convert_into_reverse_pol_notation(splitted_formula): if token in constant: token = constant.get(''.join(token)) - if token in OPERATORS: + if token in operators: # choose by priority - while stack and stack[-1] != "(" and OPERATORS[token][0] <= OPERATORS[stack[-1]][0]: + while stack and stack[-1] != "(" and operators[token][0] <= operators[stack[-1]][0]: yield stack.pop() stack.append(token) elif token == ")": @@ -559,15 +559,15 @@ def calc_reverse_pol_notation(converted_formula): stack = [] for token in converted_formula: - if token in OPERATORS: + if token in operators: # check function with double arguments - if token in OPERATORS_double: + if token in operators_double: y, x = stack.pop(), stack.pop() - stack.append(OPERATORS[token][1](x, y)) + stack.append(operators[token][1](x, y)) else: # use function with 1 argument x = stack.pop() - stack.append(OPERATORS[token][1](x)) + stack.append(operators[token][1](x)) else: stack.append(token) @@ -576,9 +576,9 @@ def calc_reverse_pol_notation(converted_formula): def check_comparison(formula): """Function checks if there are comparison operations""" - for compare in OPERATORS_compare: + for compare in operators_compare: if compare in formula: - return OPERATORS_compare.get(compare)( + return operators_compare.get(compare)( main_count(formula.split(compare)[0]), main_count(formula.split(compare)[1]))