diff --git a/final_task/calc/__init__.py b/final_task/calc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/calc/main.py b/final_task/calc/main.py new file mode 100644 index 0000000..b97bbc0 --- /dev/null +++ b/final_task/calc/main.py @@ -0,0 +1,13 @@ +import argparse +from calc.main_functions import reduction_expression, check_compared + + +def main(): + try: + parser = argparse.ArgumentParser(description='Takes mathematical expression') + parser.add_argument('string') + s = parser.parse_args().string + composition = reduction_expression(s) + print(check_compared(composition)) + except Exception as e: + print('ERROR: ', e) diff --git a/final_task/calc/main_functions.py b/final_task/calc/main_functions.py new file mode 100644 index 0000000..adafbfd --- /dev/null +++ b/final_task/calc/main_functions.py @@ -0,0 +1,116 @@ +from calc import other_functions +from calc.math_functions import decide_func + + +class ExpressionError(Exception): + pass + + +def reduction_expression(string): + lis = other_functions.finding_elements(string) + lis = other_functions.additions(lis) + return lis + + +def check_compared(composition): + i = 0 + while i < len(composition)-1: + if composition[i] == '==': + a, b = cut(i, composition) + return a == b + elif composition[i] == '<=': + a, b = cut(i, composition) + return a <= b + elif composition[i] == '>=': + a, b = cut(i, composition) + return a >= b + elif composition[i] == '!=': + a, b = cut(i, composition) + return a != b + elif composition[i] == '>': + a, b = cut(i, composition) + return a > b + elif composition[i] == '<': + a, b = cut(i, composition) + return a < b + + i += 1 + + return decide_expression(composition) + + +def cut(i, lis): + a = decide_expression(lis[:i]) + b = decide_expression(lis[i+1:]) + return a, b + + +def decide_expression(s): + s.insert(0, '(') + s.append(')') + st_nums = [] + st_ops = [] + i = 0 + + while i < len(s): + verify(s, i, st_nums, st_ops) + i += 1 + + if len(st_nums) > 1 or len(st_ops): + print('ERROR: not necessary operation') + raise ExpressionError + + return st_nums[0] + + +def verify(string, index, st_nums, st_ops): + if type(string[index]) == float: + st_nums.append(string[index]) + + elif string[index] == '(': + st_ops.append('(') + if other_functions.get_prior(string[index+1]) == 1: + st_nums.append(0) + + elif string[index] == ')': + if st_ops[-1] == '(': + del st_ops[-1] + else: + try: + st_nums[-2] = other_functions.perform_bin_operate(st_nums[-2], st_nums[-1], st_ops[-1]) + except Exception: + print('ERROR: not necessary element') + raise ExpressionError + del st_ops[-1] + del st_nums[-1] + verify(string, index, st_nums, st_ops) + + elif other_functions.get_prior(string[index]) == 5: + args = other_functions.decide_function(index, string) + ready_args = decide_args(args) + string[index] = decide_func(string[index], ready_args) + verify(string, index, st_nums, st_ops) + + elif other_functions.get_prior(string[index]) <= other_functions.get_prior(st_ops[-1]): + if string[index] == '^' and st_ops[-1] == '^': + st_ops.append(string[index]) + else: + try: + st_nums[-2] = other_functions.perform_bin_operate(st_nums[-2], st_nums[-1], st_ops[-1]) + except Exception: + print('ERROR: not necessary element') + raise ExpressionError + del st_nums[-1] + del st_ops[-1] + verify(string, index, st_nums, st_ops) + + elif other_functions.get_prior(string[index]) > other_functions.get_prior(st_ops[-1]): + st_ops.append(string[index]) + + +def decide_args(args): + ready_args = [] + for s in args: + ready_args.append(decide_expression(s)) + + return ready_args diff --git a/final_task/calc/math_functions.py b/final_task/calc/math_functions.py new file mode 100644 index 0000000..52a8b2d --- /dev/null +++ b/final_task/calc/math_functions.py @@ -0,0 +1,44 @@ +import math + + +functions_mapping = { + 'abs': math.fabs, + 'acos': math.acos, + 'acosh': math.acosh, + 'asin': math.asin, + 'asinh': math.asinh, + 'atan': math.atan, + 'atanh': math.atanh, + 'ceil': math.ceil, + 'cos': math.cos, + 'degrees': math.degrees, + 'exp': math.exp, + 'expm1': math.expm1, + 'fabs': math.fabs, + 'factorial': math.factorial, + 'log10': math.log10, + 'log2': math.log2, + 'radians': math.radians, + 'sin': math.sin, + 'round': round, + 'log': math.log, + 'pow': math.pow, +} + + +def decide_func(function, ready_args): + for name, func in functions_mapping.items(): + if function == name: + try: + if len(ready_args) == 1: + return float(func(ready_args[0])) + elif len(ready_args) == 2: + return float(func(ready_args[0], ready_args[1])) + else: + print('ERROR: problem with arguments in function "' + function + '"!') + exit() + except Exception: + print('ERROR: problem with arguments in function "' + function + '"!') + exit() + print('ERROR: not find function "' + function + '"!') + exit() diff --git a/final_task/calc/other_functions.py b/final_task/calc/other_functions.py new file mode 100644 index 0000000..ede8e43 --- /dev/null +++ b/final_task/calc/other_functions.py @@ -0,0 +1,227 @@ +from math import pi, e, tau + + +def finding_elements(s): + lis = [] + ord_a = 97 + ord_z = 122 + ord_0 = 48 + ord_9 = 57 + unit_num = '' + unit_fun = '' + s += ' ' + for element in s: + + if ord_a <= ord(element) <= ord_z: + if unit_num: + unit_num = verify_num(unit_num) + lis.append(unit_num) + unit_num = '' + unit_fun += element + unit_fun = verify_pi_e(unit_fun, lis) + + elif (ord_0 <= ord(element) <= ord_9) or element == '.': + if unit_fun: + lis.append(unit_fun) + unit_fun = '' + unit_num += element + + else: + if unit_num: + unit_num = verify_num(unit_num) + lis.append(unit_num) + unit_num = '' + if unit_fun: + lis.append(unit_fun) + unit_fun = '' + lis.append(element) + + return lis + + +def verify_num(str_num): + try: + float_num = float(str_num) + return float_num + except Exception: + print('ERROR: incorrect value "' + str_num + '"') + exit() + + +def verify_pi_e(unit_func, lis): + if unit_func == 'e': + lis.append(e) + return '' + elif unit_func == 'pi': + lis.append(pi) + return '' + elif unit_func == 'tau': + lis.append(tau) + return '' + else: + return unit_func + + +def additions(lis): + i = 0 + while i < len(lis)-1: + if lis[i] == '/' and lis[i+1] == '/': + lis[i] = '//' + del lis[i+1] + + elif lis[i] == 'expm' and lis[i+1] == 1: + lis[i] = 'expm1' + del lis[i+1] + + elif lis[i] == 'atan' and lis[i+1] == 2: + lis[i] = 'atan2' + del lis[i+1] + + elif lis[i] == 'log': + if lis[i+1] == 10: + lis[i] = 'log10' + del lis[i+1] + elif lis[i+1] == 2: + lis[i] = 'log2' + del lis[i+1] + + elif lis[i] == '=' and lis[i+1] != '=': + del lis[i] + lis[i-1] += '=' + continue + + elif type(lis[i]) == float: + if get_prior(lis[i + 1]) == 5 or lis[i + 1] == '(': + lis.insert(i + 1, '*') + elif lis[i] == ')': + if lis[i + 1] == '(' or get_prior(lis[i + 1]) == 5: + lis.insert(i + 1, '*') + + i += 1 + + i = 0 + while i < len(lis) - 1: + + if get_prior(lis[i]) == 1 and get_prior(lis[i+1]) == 1: + if lis[i] != lis[i+1]: + lis[i] = '-' + else: + lis[i] = '+' + del lis[i+1] + continue + + elif type(lis[i]) == float: + if (get_prior(lis[i + 1]) == 5 or lis[i + 1] == '(') and type(lis[i + 1]) != float: + lis.insert(i + 1, '*') + elif lis[i] == ')': + if lis[i + 1] == '(' or get_prior(lis[i + 1]) == 5: + lis.insert(i + 1, '*') + + elif (get_prior(lis[i]) == 2 or get_prior(lis[i]) == 3) and get_prior(lis[i+1]) == 1: + del lis[i+1] + lis[i+1] *= -1 + continue + + elif lis[i] == ' ': + del lis[i] + continue + + if lis[i+1] == ' ': + del lis[i+1] + continue + + i += 1 + + return lis + + +def get_prior(op): + four_4 = [',', ' ', '<', '>', '=', '!', '>=', '<=', '==', '!='] + if op == '(' or op == ')': + return 0 + elif op == '+' or op == '-': + return 1 + elif op == '/' or op == '*' or op == '//' or op == '%': + return 2 + elif op == '^': + return 3 + elif op in four_4: + return 4 + else: + return 5 + + +def perform_bin_operate(a, b, op): + if op == '+': + return a + b + elif op == '-': + return a - b + elif op == '*': + return a * b + elif op == '%': + return a % b + elif op == '^': + return a ** b + elif op == '/': + if b != 0: + return a / b + else: + print('ERROR: divide by zero') + exit() + elif op == '//': + if b != 0: + return a // b + else: + print('ERROR: divide by zero') + exit() + else: + print('ERROR: unknown operator "' + op + '"') + exit() + + +def decide_function(i, s): + + line = get_line_args(i, s) + args = get_args(line) + return args + + +def get_line_args(i, s): + hooks = 1 + i = i + 1 + if s[i] != '(': + print('ERROR: arguments of function "' + str(s[i-1]) + '" should be between hooks') + exit() + line = [s[i]] + del s[i] + + while hooks != 0 and i < len(s): + if s[i] == '(': + hooks += 1 + elif s[i] == ')': + hooks -= 1 + line.append(s[i]) + del s[i] + + return line + + +def get_args(line): + + args = [] + h = 0 + arg = [] + + for i in line[1:-1]: + if i == ',' and h == 0: + args.append(arg) + arg = [] + else: + arg.append(i) + if i == '(': + h += 1 + elif i == ')': + h -= 1 + if arg: + args.append(arg) + return args diff --git a/final_task/calc/unit_tests.py b/final_task/calc/unit_tests.py new file mode 100644 index 0000000..4a531c4 --- /dev/null +++ b/final_task/calc/unit_tests.py @@ -0,0 +1,129 @@ +import unittest +from calc.other_functions import * +from calc.math_functions import decide_func + + +class TestFindingElements(unittest.TestCase): + """Test for function 'finding_elements' from other_functions.py""" + + def test_fe1(self): + formated_expression = finding_elements('3 >= 5') + self.assertEqual(formated_expression, [3, ' ', '>', '=', ' ', 5, ' ']) + + def test_fe2(self): + formated_expression = finding_elements('3.234') + self.assertEqual(formated_expression, [3.234, ' ']) + + def test_fe3(self): + formated_expression = finding_elements('epi') + self.assertEqual(formated_expression, [2.718281828459045, 3.141592653589793, ' ']) + + def test_fe4(self): + formated_expression = finding_elements('log()') + self.assertEqual(formated_expression, ['log', '(', ')', ' ']) + + +class TestAdditions(unittest.TestCase): + """Test for function 'additions' from other_functions.py""" + + def test_ad1(self): + formated_expression = additions([3, ' ', '>', '=', ' ', 5, ' ']) + self.assertEqual(formated_expression, [3, '>=', 5]) + + def test_ad2(self): + formated_expression = additions([3.234, ' ']) + self.assertEqual(formated_expression, [3.234]) + + def test_ad3(self): + formated_expression = additions([2.718281828459045, 3.141592653589793, ' ']) + self.assertEqual(formated_expression, [2.718281828459045, '*', 3.141592653589793]) + + def test_ad4(self): + formated_expression = additions(['log', '(', ')', ' ']) + self.assertEqual(formated_expression, ['log', '(', ')']) + + def test_ad5(self): + formated_expression = additions(['log', 10, '(', ')', ' ']) + self.assertEqual(formated_expression, ['log10', '(', ')']) + + def test_ad6(self): + formated_expression = additions([' ', '-', '+', '-', '-']) + self.assertEqual(formated_expression, ['-']) + + +class TestGetLineArgs(unittest.TestCase): + """Test for function 'get_line_args' from other_functions.py""" + + def test_gl1(self): + formated_expression = get_line_args(6, [0, 1, 2, 3, 4, 5, 'log', '(', 2, '*', '(', ')', 4, ')', ')', '(', ')']) + self.assertEqual(formated_expression, ['(', 2, '*', '(', ')', 4, ')']) + + +class TestGetArgs(unittest.TestCase): + """Test for function 'get_args' from other_functions.py""" + + def test_ga1(self): + formated_expression = get_args(['(', 2, '*', '(', ')', 4, ')']) + self.assertEqual(formated_expression, [[2, '*', '(', ')', 4]]) + + def test_ga2(self): + """Few arguments""" + + formated_expression = get_args(['(', '(', 2, ')', ',', 4, ',', '(', ')', ')']) + self.assertEqual(formated_expression, [['(', 2, ')'], [4], ['(', ')']]) + + def test_ga3(self): + """Function in function""" + + formated_expression = get_args(['(', 'log', '(', 2, ',', 2, ')', ',', 4, ')']) + self.assertEqual(formated_expression, [['log', '(', 2, ',', 2, ')'], [4]]) + + +class TestMiniFunctions1(unittest.TestCase): + """Test for minifunction from other_functions.py""" + + def test_vn(self): + """Function 'verify_num' verify performance""" + + formated_expression = verify_num('12.3456') + self.assertEqual(formated_expression, 12.3456) + + def test_pe1(self): + """Function 'verify_pi_e' verify performance""" + + formated_expression = verify_pi_e('pi', []) + self.assertEqual(formated_expression, '') + + def test_pe2(self): + """Function 'verify_pi_e' verify performance""" + + formated_expression = verify_pi_e('pow', []) + self.assertEqual(formated_expression, 'pow') + + def test_pr1(self): + """Function 'prior' verify performance""" + + formated_expression = get_prior(12.34) + self.assertEqual(formated_expression, 5) + + def test_pr2(self): + """Function 'prior' verify performance""" + + formated_expression = get_prior('log') + self.assertEqual(formated_expression, 5) + + def test_bo(self): + """Function 'bin_operate' verify performance""" + + formated_expression = perform_bin_operate(2, 2, '^') + self.assertEqual(formated_expression, 4) + + +class TestDecideFunction(unittest.TestCase): + """Test for function 'decide_function' from math_functions.py""" + + def test_mdf(self): + """verify performance""" + + formated_expression = decide_func('pow', [2, 2]) + self.assertEqual(formated_expression, 4) diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..f44b5ba 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,26 @@ +from setuptools import setup, find_packages + +setup( + name='pycalc', + version='2.0', + description='Decide different expressions', + long_description='No fake', + classifiers=[ + 'Development Status :: Beta test', + 'License :: None :: None', + 'Programming Language :: Python :: 3.6', + ], + keywords='calculator', + url='not add', + author='Efi-fi', + author_email='efimprostopro@gmail.com', + license='None', + packages=find_packages(), + install_requires=[], + include_package_data=True, + zip_safe=False, + entry_points={ + 'console_scripts': + ['pycalc = calc.main:main'] + } + )