#pragma once #include "int.hpp" #include "storage.hpp" #include "utils.hpp" namespace mp { template constexpr size_t ResultMaxSize = std::max(SizeA, SizeB); template requires ElementTypesMatch using OpResult = BasicInt>; template class OverflowError : public std::runtime_error { public: OverflowError(T&& value) : std::runtime_error("Overflow"), m_value(std::move(value)) {} const T& value() const { return m_value; } private: T m_value; }; template using OverflowErrorOf = OverflowError>; struct Addition { template requires ElementTypesMatch && ElementTypesMatch static void invoke(const TLhs& lhs, const TRhs& rhs, TRes& res) { using ElementType = typename TRes::ElementType; res.zero(); ElementType carry = 0; size_t end = std::max(lhs.size_elems(), rhs.size_elems()); for (size_t i = 0; i < end; ++i) { ElementType a = lhs.get(i); ElementType b = rhs.get(i); ElementType c = carry + a; carry = (c < a) ? 1 : 0; c += b; if (c < b) carry = 1; res.set(i, c); } res.set(end, carry); } }; struct Multiplication { template requires ElementTypesMatch && ElementTypesMatch static void invoke(const TLhs& lhs, const TRhs& rhs, TRes& res) { using ElementType = typename TRes::ElementType; using DoubleType = DoubleWidthType; res.zero(); bool overflow = false; const size_t n = lhs.size_elems(); const size_t m = rhs.size_elems(); for (size_t i = 0; i < n; i++) { DoubleType carry = 0; ElementType a = lhs[i]; for (size_t j = 0; j < m; j++) { ElementType b = rhs[j]; DoubleType t = static_cast(a) * b + res.get(i + j) + carry; overflow |= !res.try_set(i + j, static_cast(t)); carry = t >> (sizeof(ElementType) * 8); } overflow |= !res.try_set(i + m, static_cast(carry)); } if (overflow) { throw std::overflow_error("Multiplication overflow"); } }; }; // Binary operation template requires ElementTypesMatch OpResult binary_op(const TLhs& lhs, const TRhs& rhs) { OpResult res{}; try { TOp::invoke(lhs, rhs, res); } catch (const std::overflow_error&) { throw OverflowErrorOf(std::move(res)); } return res; } // Compound assignment binary operation template requires ElementTypesMatch TLhs& ca_binary_op(TLhs& lhs, const TRhs& rhs) { TLhs res{}; try { TOp::invoke(lhs, rhs, res); } catch (const std::overflow_error&) { throw OverflowErrorOf(std::move(res)); } lhs = std::move(res); return lhs; } // Addition operators template requires ElementTypesMatch OpResult operator+(const TLhs& lhs, const TRhs& rhs) { return binary_op(lhs, rhs); } template requires ElementTypesMatch TLhs& operator+=(TLhs& lhs, const TRhs& rhs) { return ca_binary_op(lhs, rhs); } // Multiplication operators template requires ElementTypesMatch OpResult operator*(const TLhs& lhs, const TRhs& rhs) { return binary_op(lhs, rhs); } template requires ElementTypesMatch TLhs& operator*=(TLhs& lhs, const TRhs& rhs) { return ca_binary_op(lhs, rhs); } } // namespace mp