From 7504a12f6a5e40adff763892fb3f122ce3344f6d Mon Sep 17 00:00:00 2001 From: Dmitry Rudakov Date: Fri, 5 Jun 2026 20:25:41 +0500 Subject: [PATCH] Upload to git --- calculator.h | 157 +++++++++++ controller.h | 266 +++++++++++++++++++ enums.h | 30 +++ main.cpp | 46 ++++ mainwindow.cpp | 234 ++++++++++++++++ mainwindow.h | 58 ++++ mainwindow.ui | 709 +++++++++++++++++++++++++++++++++++++++++++++++++ pow.h | 43 +++ rational.h | 194 ++++++++++++++ submission.pro | 33 +++ 10 files changed, 1770 insertions(+) create mode 100644 calculator.h create mode 100644 controller.h create mode 100644 enums.h create mode 100644 main.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 mainwindow.ui create mode 100644 pow.h create mode 100644 rational.h create mode 100644 submission.pro 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