/* * Copyright (c) 2019-2020, Sergey Bugaev * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include "Calculator.h" #include #include #include Optional Calculator::operation_with_literal_argument(Operation operation, Crypto::BigFraction argument) { // Support binary operations with percentages, for example "2+3%" == 2.06 if (m_binary_operation_in_progress != Operation::None && operation == Operation::Percent) { argument = m_binary_operation_saved_left_side * Crypto::BigFraction { 1, 100 } * argument; operation = Operation::None; // Don't apply the "%" operation twice } // If a previous operation is still in progress, finish it // Makes hitting "1+2+3=" equivalent to hitting "1+2=+3=" if (m_binary_operation_in_progress != Operation::None) { argument = finish_binary_operation(m_binary_operation_saved_left_side, m_binary_operation_in_progress, argument); } switch (operation) { case Operation::None: m_current_value = argument; break; case Operation::Add: case Operation::Subtract: case Operation::Multiply: case Operation::Divide: m_binary_operation_saved_left_side = argument; m_binary_operation_in_progress = operation; m_current_value = argument; break; case Operation::Sqrt: if (argument < Crypto::BigFraction {}) { m_has_error = true; m_current_value = argument; break; } m_current_value = argument.sqrt(); clear_operation(); break; case Operation::Inverse: if (argument == Crypto::BigFraction {}) { m_has_error = true; m_current_value = argument; break; } m_current_value = argument.invert(); clear_operation(); break; case Operation::Percent: m_current_value = argument * Crypto::BigFraction { 1, 100 }; break; case Operation::ToggleSign: m_current_value = -argument; break; case Operation::MemClear: m_mem.set_to_0(); m_current_value = argument; break; case Operation::MemRecall: m_current_value = m_mem; break; case Operation::MemSave: m_mem = argument; m_current_value = argument; break; case Operation::MemAdd: m_mem = m_mem + argument; // avoids the need for operator+=() m_current_value = m_mem; break; case Operation::Equals: m_current_value = argument; break; } return m_current_value; } static bool operation_is_binary(Calculator::Operation operation) { switch (operation) { case Calculator::Operation::Add: case Calculator::Operation::Subtract: case Calculator::Operation::Multiply: case Calculator::Operation::Divide: return true; default: return false; } } Optional Calculator::operation_without_argument(Operation operation) { bool in_binary_operation = m_binary_operation_in_progress != Operation::None; bool entering_new_binary_operation = operation_is_binary(operation); bool previous_operation_was_binary = operation_is_binary(m_previous_operation); if (in_binary_operation && entering_new_binary_operation) { m_binary_operation_in_progress = operation; return {}; } if (!in_binary_operation && previous_operation_was_binary && operation == Operation::Equals) { m_current_value = finish_binary_operation(m_current_value, m_previous_operation, m_previous_binary_operation_right_side); return m_current_value; } return operation_with_literal_argument(operation, m_current_value); } Crypto::BigFraction Calculator::finish_binary_operation(Crypto::BigFraction const& left_side, Operation operation, Crypto::BigFraction const& right_side) { Crypto::BigFraction res {}; m_previous_binary_operation_right_side = right_side; switch (operation) { case Operation::Add: res = left_side + right_side; break; case Operation::Subtract: res = left_side - right_side; break; case Operation::Multiply: res = left_side * right_side; break; case Operation::Divide: if (right_side == Crypto::BigFraction {}) { m_has_error = true; } else { res = left_side / right_side; } break; case Operation::None: case Operation::Sqrt: case Operation::Inverse: case Operation::Percent: case Operation::ToggleSign: case Operation::MemClear: case Operation::MemRecall: case Operation::MemSave: case Operation::MemAdd: case Operation::Equals: VERIFY_NOT_REACHED(); } clear_operation(); return res; } void Calculator::clear_operation() { if (m_binary_operation_in_progress != Operation::None) { m_previous_operation = m_binary_operation_in_progress; m_binary_operation_in_progress = Operation::None; } m_binary_operation_saved_left_side.set_to_0(); clear_error(); }