Files
Dmitry Rudakov 7504a12f6a Upload to git
2026-06-05 20:25:41 +05:00

158 lines
4.2 KiB
C++

#pragma once
#include <cstdint>
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#include "pow.h"
#include "rational.h"
// Все ошибки вычислений
using Error = std::string;
namespace calculator_detail {
// Сравнивает значение с 0
template<class Number>
bool IsZero(const Number& value) {
return value == Number{};
}
// Вынесение 0 в отдельную перегрузку для Rational
inline bool IsZero(const Rational& value) {
return value == Rational{};
}
// Преобразует число в текст для отображения на экране
template<class Number>
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 Number>
class Calculator {
public:
// Устанавливает новое текущее значение
void Set(Number value) {
current_ = value;
}
// Возвращает текущее значение
Number GetNumber() const {
return current_;
}
// Сложение
std::optional<Error> Add(Number value) {
current_ += value;
return std::nullopt;
}
// Вычитание
std::optional<Error> Sub(Number value) {
current_ -= value;
return std::nullopt;
}
// Умножение
std::optional<Error> Mul(Number value) {
current_ *= value;
return std::nullopt;
}
// Деление провряет 0 только для типов без NaN
std::optional<Error> Div(Number value) {
if constexpr (std::is_integral_v<Number> || std::is_same_v<Number, Rational>) {
if (calculator_detail::IsZero(value)) {
return "Division by zero";
}
}
current_ /= value;
return std::nullopt;
}
// Возведение в степень выбирает нужную функцию и проверяет ошибки
std::optional<Error> Pow(Number value) {
if constexpr (std::is_floating_point_v<Number>) {
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<Number>) {
if constexpr (std::is_signed_v<Number>) {
if (value < 0) {
return "Integer negative power";
}
}
current_ = ::Pow(current_, value);
return std::nullopt;
}
if constexpr (std::is_same_v<Number, Rational>) {
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<Number> mem_ = std::nullopt;
};