Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
First solution in Clear category for Calculator-VI by TheRing
import decimal
import re
opmethod = {'+': decimal.Context.add, '-': decimal.Context.subtract,
'/': decimal.Context.divide, '*': decimal.Context.multiply,
'//': decimal.Context.divide_int, '%': decimal.Context.remainder,
'**': decimal.Context.power
}
def calculator(log: str) -> str:
decimal.getcontext().prec = 4
# Remove all occurrences of B following operator
operator_and_backspace = 1
while operator_and_backspace:
log, operator_and_backspace = re.subn(r'[+\-*/]B', '', log)
def clear():
nonlocal operand1, operand2, operator, displayed_number, displayed_number_str, last_char
operand1 = operator = operand2 = None
displayed_number = 0
displayed_number_str = '0'
last_char = ''
def calculate_result(op1, op, op2):
if None in (op1, op2):
return None
elif any(isinstance(any_op, decimal.Decimal) for any_op in (op1, op2)):
calculation_context = decimal.Context(prec=7, rounding=decimal.ROUND_HALF_EVEN)
result = opmethod[op](calculation_context, op1, op2)
result = decimal.getcontext().create_decimal(result)
int_result = result.to_integral(rounding=decimal.ROUND_DOWN)
return result if abs(int_result) <= 9999 else None
else:
result = eval(f"({op1}){op}({op2})")
if op == '/':
result = decimal.getcontext().create_decimal(result)
return result if abs(result) < 100000 else None
def string_to_number(numstring: str, strip_zeros: bool=True):
numstring = ('0' + numstring.lstrip('0'))[:6]
if '.' in numstring:
result = decimal.getcontext().create_decimal(
numstring.rstrip('0') if strip_zeros else numstring)
return None if result.to_integral(rounding=decimal.ROUND_DOWN) > 9999 else result
else:
result = int(numstring)
return None if result > 99999 else result
def number_to_string(number: decimal.Decimal|int) -> str:
if isinstance(number, decimal.Decimal):
# in case we have an exponent, remove it (from Decimal FAQ on docs.python.org)
return str(number.quantize(decimal.Decimal(1))) + '.' \
if number == number.to_integral() \
else str(number.normalize())
else:
return str(number)
clear()
for char in log:
if char.isdigit() or char == '.':
if last_char != '0':
displayed_number = 0 # ensure that displayed_number is not None
displayed_number_str = char
if last_char == '=': # number after equal sign -> new operation
operand1 = string_to_number(displayed_number_str)
operand2 = operator = None
else:
displayed_number_str += char
last_char = '0'
elif char == 'B':
if last_char == '0':
displayed_number_str = displayed_number_str[:-1]
else:
displayed_number_str = number_to_string(displayed_number)[:-1]
displayed_number = 0
last_char = '0'
elif char == 'C':
clear()
else:
if last_char == '0':
displayed_number = string_to_number(displayed_number_str)
if char == '=':
if operand2 is None:
operand2 = string_to_number(displayed_number_str)
if operator is not None:
displayed_number = operand1 = calculate_result(operand1, operator, operand2)
displayed_number_str = str(displayed_number)
elif char in '/*' and char == last_char:
operator *= 2
else:
if operand1 is None:
operand1 = displayed_number
if last_char == '0' and operator and operand2 is None:
# If not multiple signs in a row, then
# perform pending operation, e.g. "1+2-" == "3" when processing the terminal '-'
displayed_number = operand1 = calculate_result(operand1, operator, displayed_number)
displayed_number_str = str(displayed_number)
else:
# Delete 2nd operand from last operation
operand2 = None
operator = char
last_char = char
if last_char == '0':
check_number = string_to_number(displayed_number_str, strip_zeros=False)
if displayed_number == 0 and check_number:
return str(check_number).lstrip('0')
if displayed_number is None:
return 'error'
return number_to_string(displayed_number)
June 21, 2023
Comments: