commit 7504a12f6a5e40adff763892fb3f122ce3344f6d Author: Dmitry Rudakov Date: Fri Jun 5 20:25:41 2026 +0500 Upload to git diff --git a/calculator.h b/calculator.h new file mode 100644 index 0000000..8967cd1 --- /dev/null +++ b/calculator.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "pow.h" +#include "rational.h" + +// Все ошибки вычислений +using Error = std::string; + +namespace calculator_detail { + +// Сравнивает значение с 0 +template +bool IsZero(const Number& value) { + return value == Number{}; +} + +// Вынесение 0 в отдельную перегрузку для Rational +inline bool IsZero(const Rational& value) { + return value == Rational{}; +} + +// Преобразует число в текст для отображения на экране +template +std::string NumberToString(const Number& value) { + std::ostringstream out; + out << value; + return out.str(); +} + +// Вывод uint8_t как число +inline std::string NumberToString(std::uint8_t value) { + std::ostringstream out; + out << +value; + return out.str(); +} + +} + +template +class Calculator { +public: + // Устанавливает новое текущее значение + void Set(Number value) { + current_ = value; + } + + // Возвращает текущее значение + Number GetNumber() const { + return current_; + } + + // Сложение + std::optional Add(Number value) { + current_ += value; + return std::nullopt; + } + + // Вычитание + std::optional Sub(Number value) { + current_ -= value; + return std::nullopt; + } + + // Умножение + std::optional Mul(Number value) { + current_ *= value; + return std::nullopt; + } + + // Деление провряет 0 только для типов без NaN + std::optional Div(Number value) { + if constexpr (std::is_integral_v || std::is_same_v) { + if (calculator_detail::IsZero(value)) { + return "Division by zero"; + } + } + + current_ /= value; + return std::nullopt; + } + + // Возведение в степень выбирает нужную функцию и проверяет ошибки + std::optional Pow(Number value) { + if constexpr (std::is_floating_point_v) { + current_ = ::Pow(current_, value); + return std::nullopt; + } + + if (calculator_detail::IsZero(current_) && calculator_detail::IsZero(value)) { + return "Zero power to zero"; + } + + if constexpr (std::is_integral_v) { + if constexpr (std::is_signed_v) { + if (value < 0) { + return "Integer negative power"; + } + } + + current_ = ::Pow(current_, value); + return std::nullopt; + } + + if constexpr (std::is_same_v) { + if (value.GetDenominator() != 1) { + return "Fractional power is not supported"; + } + if (calculator_detail::IsZero(current_) && value.GetNumerator() < 0) { + return "Division by zero"; + } + + current_ = ::Pow(current_, value); + return std::nullopt; + } + + return std::nullopt; + } + + // Сохраняет текущее значение в память + void Save() { + mem_ = current_; + } + + // Загружает текущее значение из памяти + void Load() { + if (mem_) { + current_ = *mem_; + } + } + + // Очищает память калькулятора + void ClearMem() { + mem_ = std::nullopt; + } + + // Значение сохранено в памяти? Если Да выводить индикатор + bool GetHasMem() const { + return mem_.has_value(); + } + + // Возвращает текущее значение в том же формате, что используется в интерфейсе + std::string GetNumberRepr() const { + return calculator_detail::NumberToString(current_); + } + +private: + // Текущее значение + Number current_ = {}; + // Ячейка памяти + std::optional mem_ = std::nullopt; +}; diff --git a/controller.h b/controller.h new file mode 100644 index 0000000..520e209 --- /dev/null +++ b/controller.h @@ -0,0 +1,266 @@ +#pragma once + +#include +#include +#include "calculator.h" +#include "mainwindow.h" +#include "rational.h" + +template +std::string ToString(T number) { + std::ostringstream tmp_str; + tmp_str << number; + return tmp_str.str(); +} + +template +T FromString(const std::string& number) { + std::istringstream tmp_str{number}; + T result{}; + tmp_str >> result; + return result; +} + +inline std::string ToString(std::uint8_t number) { + std::ostringstream tmp_str; + tmp_str << +number; + return tmp_str.str(); +} + +template<> +std::uint8_t FromString(const std::string& number) { + std::istringstream tmp_str{number}; + unsigned result{}; + tmp_str >> result; + return static_cast(result); +} + +template +class Controller +{ +public: + Controller() {} + + void BindWithMainWindow(MainWindow* main_window) { + view_ = main_window; + if (!view_) { + return; + } + + view_->SetDigitKeyCallback([this](int key){ + PressDigitKey(key); + }); + view_->SetProcessOperationKeyCallback([this](Operation key){ + ProcessOperationKey(key); + }); + view_->SetProcessControlKeyCallback([this](ControlKey key){ + ProcessControlKey(key); + }); + + RevealView(); + } + +private: + void PressDigitKey(int digit) { + AddChar(digit + '0'); + } + + void ProcessOperationKey(Operation operation) { + switch(operation) { + case Operation::ADDITION: + OnOperation([this](Number x){return calculator_.Add(x);}, " + "); + break; + case Operation::SUBTRACTION: + OnOperation([this](Number x){return calculator_.Sub(x);}, " − "); + break; + case Operation::MULTIPLICATION: + OnOperation([this](Number x){return calculator_.Mul(x);}, " × "); + break; + case Operation::DIVISION: + OnOperation([this](Number x){return calculator_.Div(x);}, " ÷ "); + break; + case Operation::POWER: + OnOperation([this](Number x){return calculator_.Pow(x);}, " ^ "); + break; + } + } + + void ProcessControlKey(ControlKey key) { + switch(key) { + case ControlKey::EQUALS: { + if (!operation_) { + return; + } + std::string formula = ToString(calculator_.GetNumber()) + operation_name_ + ToString(active_number_) + " = "; + + auto error = operation_(active_number_); + if (error.has_value()) { + SetErrorInView(error.value()); + break; + } + + SetFormulaInView(formula); + SetInputAsNumber(calculator_.GetNumber()); + operation_ = {}; + break; + } + case ControlKey::CLEAR: + SetInputAsNumber(Number{}); + SetFormulaInView(""); + operation_ = {}; + break; + case ControlKey::MEM_SAVE: + mem_ = active_number_; + SetMemInView("M"); + break; + case ControlKey::MEM_LOAD: + if (!mem_.has_value()) { + return; + } + SetInputAsNumber(mem_.value()); + SetFormulaInView(""); + break; + case ControlKey::MEM_CLEAR: + mem_ = std::nullopt; + SetMemInView(""); + break; + case ControlKey::PLUS_MINUS: + if (input_as_number_) { + active_number_ = -active_number_; + SetInputAsNumber(active_number_); + } else { + if (input_.size() && input_.front() == '-') { + SetInputAsString(input_.substr(1)); + } else { + SetInputAsString("-" + input_); + } + } + break; + case ControlKey::BACKSPACE: + if (input_.size() > 0) { + SetInputAsString(input_.substr(0, input_.size() - 1)); + } + break; + case ControlKey::EXTRA_KEY: + DoExtraAction(); + break; + } + } + + std::optional GetExtraKey() const { + if constexpr (std::is_integral_v) { + return std::nullopt; + } + if constexpr (std::is_same_v) { + return "/"; + } + return "."; + } + + void OnOperation(std::function (Number)> action, std::string text) { + if (!operation_) { + calculator_.Set(active_number_); + } + + operation_name_ = text; + operation_ = action; + input_ = ""; + + SetFormulaInView(ToString(calculator_.GetNumber()) + text); + } + + void SetInputAsString(const std::string& new_input) { + input_as_number_ = false; + input_ = new_input; + active_number_ = FromString(input_); + UpdateInputInView(new_input); + } + + void SetInputAsNumber(Number number) { + input_as_number_ = true; + input_ = ""; + active_number_ = number; + UpdateInputInView(ToString(active_number_)); + } + + void AddChar(char character) { + SetInputAsString(input_ + character); + } + + void DoExtraAction() { + if constexpr (std::is_integral_v) { + return; + } + if constexpr (std::is_same_v) { + if (input_.size() == 0 || input_.find('/') != std::string::npos) { + return; + } + AddChar('/'); + } else { + if (input_.find('.') != std::string::npos) { + return; + } + AddChar('.'); + } + } + + void UpdateInputInView(const std::string& string) { + if (!view_) { + return; + } + view_->SetInputText(string); + text_ = string; + } + + void SetErrorInView(const std::string& string) { + if (!view_) { + return; + } + view_->SetErrorText(string); + } + + void SetFormulaInView(const std::string& string) { + if (!view_) { + return; + } + view_->SetFormulaText(string); + formula_ = string; + } + + void SetMemInView(const std::string& string) { + if (!view_) { + return; + } + view_->SetMemText(string); + mem_text_ = string; + } + + void RevealView() { + if (!view_) { + return; + } + view_->SetInputText(text_); + view_->SetFormulaText(formula_); + view_->SetMemText(mem_text_); + view_->SetExtraKey(GetExtraKey()); + } + +private: + std::function(Number number)> operation_; + std::string operation_name_; + + Calculator calculator_; + + Number active_number_ = {}; + std::string input_; + + MainWindow* view_ = nullptr; + + std::optional mem_ = {}; + + bool input_as_number_ = true; + + std::string text_; + std::string formula_; + std::string mem_text_; +}; \ No newline at end of file diff --git a/enums.h b/enums.h new file mode 100644 index 0000000..29501bc --- /dev/null +++ b/enums.h @@ -0,0 +1,30 @@ +#pragma once + +enum class Operation { + MULTIPLICATION, + DIVISION, + SUBTRACTION, + ADDITION, + POWER, +}; + +enum class ControlKey { + EQUALS, // Кнопка "Равно". + CLEAR, // Кнопка "C". + MEM_SAVE, // Кнопка "MS". + MEM_LOAD, // Кнопка "ML". + MEM_CLEAR, // Кнопка "MC". + PLUS_MINUS, // Кнопка "+-". + BACKSPACE, // Кнопка "Стереть последний символ". + EXTRA_KEY, // Дополнительная экстра-кнопка. +}; + +enum class ControllerType { + UINT8_T, + INT, + INT64_T, + SIZE_T, + DOUBLE, + FLOAT, + RATIONAL, +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3df4630 --- /dev/null +++ b/main.cpp @@ -0,0 +1,46 @@ +#include "mainwindow.h" + +#include "controller.h" + +#include + +int main(int argc, char *argv[]) +{ + Controller double_controller; + Controller int_controller; + Controller float_controller; + Controller size_t_controller; + Controller int64_t_controller; + Controller byte_controller; + Controller rational_controller; + QApplication a(argc, argv); + MainWindow w; + w.SetControllerCallback([&](ControllerType controller) { + switch(controller) { + case ControllerType::DOUBLE: + double_controller.BindWithMainWindow(&w); + break; + case ControllerType::FLOAT: + float_controller.BindWithMainWindow(&w); + break; + case ControllerType::INT: + int_controller.BindWithMainWindow(&w); + break; + case ControllerType::INT64_T: + int64_t_controller.BindWithMainWindow(&w); + break; + case ControllerType::SIZE_T: + size_t_controller.BindWithMainWindow(&w); + break; + case ControllerType::UINT8_T: + byte_controller.BindWithMainWindow(&w); + break; + case ControllerType::RATIONAL: + rational_controller.BindWithMainWindow(&w); + break; + } + }); + double_controller.BindWithMainWindow(&w); + w.show(); + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..3014d76 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,234 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include + +#include + +namespace { + +QPushButton* AsButton(QObject* object) { + return qobject_cast(object); +} + +} + +MainWindow::MainWindow(QWidget* parent) + : QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); + + // Все кнопки с цифрами + const QList digit_buttons = { + ui->tb_zero, ui->tb_one, ui->tb_two, ui->tb_three, ui->tb_four, + ui->tb_five, ui->tb_six, ui->tb_seven, ui->tb_eight, ui->tb_nine + }; + for (QPushButton* button : digit_buttons) { + connect(button, &QPushButton::clicked, this, &MainWindow::OnDigitClicked); + } + + // Арифметичсекие операции + const QList operation_buttons = { + ui->tb_add, ui->tb_substract, ui->tb_multiplicate, ui->tb_divide, ui->tb_power + }; + for (QPushButton* button : operation_buttons) { + connect(button, &QPushButton::clicked, this, &MainWindow::OnOperationClicked); + } + + // Управляющие кнопки: равно, память, знак, стирание символа и точки + const QList control_buttons = { + ui->tb_equal, ui->tb_reset, ui->tb_ms, ui->tn_mr, ui->tb_mc, + ui->tb_negate, ui->tb_backspace, ui->tb_extra + }; + for (QPushButton* button : control_buttons) { + connect(button, &QPushButton::clicked, this, &MainWindow::OnControlClicked); + } + + // Выпадающие кнопки с выбором типа числа + connect(ui->cmb_controller, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &MainWindow::OnControllerChanged); + + // Дефолтные значения при запуске + SetInputText("0"); + SetFormulaText(""); + SetMemText(""); +} + +MainWindow::~MainWindow() { + delete ui; +} + +void MainWindow::SetInputText(const std::string& text) { + // Показываем обычный результат вычислений + ui->l_result->setStyleSheet(""); + ui->l_result->setText(QString::fromStdString(text)); +} + +void MainWindow::SetErrorText(const std::string& text) { + // Ошибки выводим в том же поле, но красным цветом + ui->l_result->setStyleSheet("color: red;"); + ui->l_result->setText(QString::fromStdString(text)); +} + +void MainWindow::SetFormulaText(const std::string& text) { + // Отображение текущей формулы + ui->l_formula->setText(QString::fromStdString(text)); +} + +void MainWindow::SetMemText(const std::string& text) { + // Отображение значения в памяти + ui->l_memory->setText(QString::fromStdString(text)); +} + +void MainWindow::SetExtraKey(const std::optional& key) { + // В некоторых режимах точка не используется + if (!key) { + ui->tb_extra->hide(); + return; + } + // Если точка используется + ui->tb_extra->setText(QString::fromStdString(*key)); + ui->tb_extra->show(); +} + +void MainWindow::SetDigitKeyCallback(std::function cb) { + digit_cb_ = std::move(cb); +} + +void MainWindow::SetProcessOperationKeyCallback(std::function cb) { + operation_cb_ = std::move(cb); +} + +void MainWindow::SetProcessControlKeyCallback(std::function cb) { + control_cb_ = std::move(cb); +} + +void MainWindow::SetControllerCallback(std::function cb) { + controller_cb_ = std::move(cb); +} + +void MainWindow::OnDigitClicked() { + // Игнорируем если контролер еще не подклчил обработчик + const QPushButton* button = AsButton(sender()); + if (!button || !digit_cb_) { + return; + } + + // Передаем нажатую цифру в контролер + digit_cb_(button->text().toInt()); +} + +void MainWindow::OnOperationClicked() { + // Определяем нажатую кнопку с цифрой + const QPushButton* button = AsButton(sender()); + if (!button || !operation_cb_) { + return; + } + + const auto operation = OperationFromObjectName(button->objectName()); + if (operation) { + operation_cb_(*operation); + } +} + +void MainWindow::OnControlClicked() { + // Определяем нажатую кнопку управляющих кнопок + const QPushButton* button = AsButton(sender()); + if (!button || !control_cb_) { + return; + } + + const auto key = ControlFromObjectName(button->objectName()); + if (key) { + control_cb_(*key); + } +} + +void MainWindow::OnControllerChanged() { + // Смена режима работы + if (!controller_cb_) { + return; + } + + const auto controller = ControllerFromText(ui->cmb_controller->currentText()); + if (controller) { + controller_cb_(*controller); + } +} + +std::optional MainWindow::OperationFromObjectName(const QString& object_name) { + // Определяем какая арифметичская кнопка была нажата + if (object_name == "tb_add") { + return Operation::ADDITION; + } + if (object_name == "tb_substract") { + return Operation::SUBTRACTION; + } + if (object_name == "tb_multiplicate") { + return Operation::MULTIPLICATION; + } + if (object_name == "tb_divide") { + return Operation::DIVISION; + } + if (object_name == "tb_power") { + return Operation::POWER; + } + return std::nullopt; +} + +std::optional MainWindow::ControlFromObjectName(const QString& object_name) { + // Определяем какая управляющая кнопка была нажата + if (object_name == "tb_equal") { + return ControlKey::EQUALS; + } + if (object_name == "tb_reset") { + return ControlKey::CLEAR; + } + if (object_name == "tb_ms") { + return ControlKey::MEM_SAVE; + } + if (object_name == "tn_mr") { + return ControlKey::MEM_LOAD; + } + if (object_name == "tb_mc") { + return ControlKey::MEM_CLEAR; + } + if (object_name == "tb_negate") { + return ControlKey::PLUS_MINUS; + } + if (object_name == "tb_backspace") { + return ControlKey::BACKSPACE; + } + if (object_name == "tb_extra") { + return ControlKey::EXTRA_KEY; + } + return std::nullopt; +} + +std::optional MainWindow::ControllerFromText(const QString& text) { + // Определяем выбранный тип + if (text == "double") { + return ControllerType::DOUBLE; + } + if (text == "float") { + return ControllerType::FLOAT; + } + if (text == "uint8_t") { + return ControllerType::UINT8_T; + } + if (text == "int") { + return ControllerType::INT; + } + if (text == "int64_t") { + return ControllerType::INT64_T; + } + if (text == "size_t") { + return ControllerType::SIZE_T; + } + if (text == "Rational") { + return ControllerType::RATIONAL; + } + return std::nullopt; +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..8ea843a --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,58 @@ +#pragma once + +#include "enums.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow() override; + + // Набор методов отображения, который используют типизированные контроллеры. + void SetInputText(const std::string& text); + void SetErrorText(const std::string& text); + void SetFormulaText(const std::string& text); + void SetMemText(const std::string& text); + void SetExtraKey(const std::optional& key); + + // Набор методов регистрации функций обратного вызова, который используют контроллеры и `main`. + void SetDigitKeyCallback(std::function cb); + void SetProcessOperationKeyCallback(std::function cb); + void SetProcessControlKeyCallback(std::function cb); + void SetControllerCallback(std::function cb); + +private slots: + // Слоты переводят Qt-сигналы в перечисления проекта и простые значения. + void OnDigitClicked(); + void OnOperationClicked(); + void OnControlClicked(); + void OnControllerChanged(); + +private: + // Вспомогательные функции оставляют детали имен виджетов внутри окна. + static std::optional OperationFromObjectName(const QString& object_name); + static std::optional ControlFromObjectName(const QString& object_name); + static std::optional ControllerFromText(const QString& text); + + Ui::MainWindow* ui; + + // Сохраненные функции обратного вызова вызываются только при соответствующем действии в интерфейсе. + std::function operation_cb_; + std::function digit_cb_; + std::function control_cb_; + std::function controller_cb_; +}; diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..566c286 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,709 @@ + + + MainWindow + + + + 0 + 0 + 390 + 377 + + + + + 0 + 0 + + + + + 14 + + + + Калькулятор + + + + + + + + 10 + + + + l_formula + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 4 + + + 4 + + + + + + + + 0 + 0 + + + + + double + + + + + float + + + + + uint8_t + + + + + int + + + + + int64_t + + + + + size_t + + + + + Rational + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 1 + + + 1 + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 6 + + + 6 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 3 + + + 3 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + + + + - + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 9 + + + 9 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 2 + + + 2 + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + ± + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 8 + + + 8 + + + + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + MC + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + . + + + + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + MS + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 0 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + ÷ + + + / + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 5 + + + 5 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + C + + + Esc + + + + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + MR + + + + + + + + + + 0 + 0 + + + + + 14 + + + + l_memory + + + + + + + + 0 + 0 + + + + + 14 + + + + l_result + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + 7 + + + 7 + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + = + + + Enter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + Arial Narrow + 14 + + + + × + + + * + + + + + + + + + diff --git a/pow.h b/pow.h new file mode 100644 index 0000000..c219ebd --- /dev/null +++ b/pow.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "rational.h" + +// Возведение в степень для целочисленных показателей +template +Number IntegerPow(Number l_op, Number r_op) { + Number res = 1; + while (r_op > 0) { + if (r_op & 1) { + res *= l_op; + } + r_op >>= 1; + l_op *= l_op; + } + return res; +} + +// Возведение в степень для всех типов кроме Rational +template +Number Pow(Number lhs, Number rhs) { + if constexpr (std::is_floating_point_v) { + return static_cast(std::pow(lhs, rhs)); + } else { + return IntegerPow(lhs, rhs); + } +} + +// Возведение в степень для типа Rational +inline Rational Pow(Rational lhs, Rational rhs) { + if (rhs.GetDenominator() != 1) { + std::abort(); + } + + const auto pow = rhs.GetNumerator(); + if (pow >= 0) { + return {IntegerPow(lhs.GetNumerator(), pow), IntegerPow(lhs.GetDenominator(), pow)}; + } + + return {IntegerPow(lhs.GetDenominator(), -pow), IntegerPow(lhs.GetNumerator(), -pow)}; +} diff --git a/rational.h b/rational.h new file mode 100644 index 0000000..25e3134 --- /dev/null +++ b/rational.h @@ -0,0 +1,194 @@ +#pragma once + +#include +#include +#include +#include + +class Rational +{ +public: + // Конструктор по умолчанию: 0/1 + Rational() = default; + + // Конструктор числителя + Rational(int numerator) + : numerator_{ numerator }, + denominator_{ 1 } { + Reduction(); + } + + // Конструктор из числителя и знаменателя + Rational(int numerator, int denominator) + : numerator_{ numerator }, + denominator_{ denominator } { + Reduction(); + } + + // Копирующий конструктор + Rational(const Rational& other) + : numerator_{other.numerator_}, + denominator_(other.denominator_) { + } + + // Возвращает обратную дробь + Rational Inv() const { + return Rational(denominator_, numerator_); + } + + // Получить числитель + int GetNumerator() const { + return numerator_; + } + + // Получить знаменатель + int GetDenominator() const { + return denominator_; + } + + // Перегрузка оператора присваивания + Rational& operator=(const Rational&) = default; + + // Унарный плюс + Rational operator+() const { + return *this; + return {numerator_, denominator_}; + + } + + // Унарный минус + Rational operator-() const { + return Rational(-numerator_, denominator_); + } + + // Сложение с присваиванием + Rational& operator+=(const Rational& other) { + numerator_ = numerator_ * other.denominator_ + + other.numerator_ * denominator_; + denominator_ = denominator_ * other.denominator_; + Reduction(); + return *this; + } + + // Вычитание с присваиванием + Rational& operator-=(const Rational& other) { + numerator_ = numerator_ * other.denominator_ - + other.numerator_ * denominator_; + denominator_ = denominator_ * other.denominator_; + Reduction(); + return *this; + } + + // Умножение с присваиванием + Rational& operator*=(const Rational& other) { + numerator_ = numerator_ * other.numerator_; + denominator_ = denominator_ * other.denominator_; + Reduction(); + return *this; + } + + // Деление с присваиванием + Rational& operator/=(const Rational& other) { + numerator_ *= other.denominator_; + denominator_ *= other.numerator_; + Reduction(); + return *this; + } + + // Сложение + Rational operator+(const Rational& other) const { + Rational result(*this); + result += other; + return result; + } + + // Вычитание + Rational operator-(const Rational& other) const { + Rational result(*this); + result -= other; + return result; + } + + // Умножение + Rational operator*(const Rational& other) const { + Rational result(*this); + result *= other; + return result; + } + + // Деление + Rational operator/(const Rational& other) const { + Rational result(*this); + result /= other; + return result; + } + + // Проверка равенства дробей + bool operator==(const Rational& other) const { + return static_cast(numerator_) * other.denominator_ == + static_cast(other.numerator_) * denominator_; + } + + // Трехстороннее сравнение + auto operator<=>(const Rational& other) const { + return static_cast(numerator_) * other.denominator_ <=> + static_cast(other.numerator_) * denominator_; + } + + // Вывод дроби в поток + friend std::ostream& operator<<(std::ostream& os, const Rational& r) { + if (r.denominator_ == 1) { + os << r.numerator_; + } else { + os << r.numerator_ << " / " << r.denominator_; + } + return os; + } + + // Чтение дроби из потока + friend std::istream& operator>>(std::istream& is, Rational& r) { + int n; + int d; + char div; + + is >> n; + if (!(is >> std::ws >> div)) { + r = Rational(n, 1); + return is; + } + + if (div != '/') { + is.unget(); + r = Rational(n, 1); + return is; + } + + is >> d; + if (!is || d == 0) { + is.setstate(std::ios::failbit); + r = Rational(n, 1); + } else { + r = Rational(n, d); + } + return is; + } + +private: + int numerator_ = 0; + int denominator_ = 1; + + // Нормализация дроби + void Reduction() { + if (numerator_ == 0) { + denominator_ = 1; + return; + } + if (denominator_ < 0) { + numerator_ = -numerator_; + denominator_ = -denominator_; + } + const int divisor = std::gcd(numerator_, denominator_); + numerator_ /= divisor; + denominator_ /= divisor; + } +}; diff --git a/submission.pro b/submission.pro new file mode 100644 index 0000000..5968499 --- /dev/null +++ b/submission.pro @@ -0,0 +1,33 @@ +######## +MOCK_LIB=../../../../mocks_library +######## + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++20 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + calculator.h \ + controller.h \ + enums.h \ + mainwindow.h \ + pow.h \ + rational.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target