#pragma once /// Original is here https://github.com/cerevra/int /// Distributed under the Boost Software License, Version 1.0. /// (See at http://www.boost.org/LICENSE_1_0.txt) #include "throwError.h" #include #include #include #include #include namespace wide { template struct IsWideInteger { static const constexpr bool value = false; }; template struct IsWideInteger> { static const constexpr bool value = true; }; template static constexpr bool ArithmeticConcept() noexcept { return std::is_arithmetic_v || IsWideInteger::value; } template static constexpr bool IntegralConcept() noexcept { return std::is_integral_v || IsWideInteger::value; } template class IsTupleLike { template static auto check(U * p) -> decltype(std::tuple_size::value, int()); template static void check(...); public: static constexpr const bool value = !std::is_void(nullptr))>::value; }; } namespace std { // numeric limits template class numeric_limits> { public: static constexpr bool is_specialized = true; static constexpr bool is_signed = is_same::value; static constexpr bool is_integer = true; static constexpr bool is_exact = true; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = true; static constexpr std::float_denorm_style has_denorm = std::denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr std::float_round_style round_style = std::round_toward_zero; static constexpr bool is_iec559 = false; static constexpr bool is_bounded = true; static constexpr bool is_modulo = true; static constexpr int digits = Bits - (is_same::value ? 1 : 0); static constexpr int digits10 = digits * 0.30103 /*std::log10(2)*/; static constexpr int max_digits10 = 0; static constexpr int radix = 2; static constexpr int min_exponent = 0; static constexpr int min_exponent10 = 0; static constexpr int max_exponent = 0; static constexpr int max_exponent10 = 0; static constexpr bool traps = true; static constexpr bool tinyness_before = false; static constexpr wide::integer min() noexcept { if (is_same::value) { using T = wide::integer; T res{}; res.items[T::_impl::big(0)] = std::numeric_limits::signed_base_type>::min(); return res; } return wide::integer(0); } static constexpr wide::integer max() noexcept { using T = wide::integer; T res{}; res.items[T::_impl::big(0)] = is_same::value ? std::numeric_limits::signed_base_type>::max() : std::numeric_limits::base_type>::max(); for (unsigned i = 1; i < wide::integer::_impl::item_count; ++i) { res.items[T::_impl::big(i)] = std::numeric_limits::base_type>::max(); } return res; } static constexpr wide::integer lowest() noexcept { return min(); } static constexpr wide::integer epsilon() noexcept { return 0; } static constexpr wide::integer round_error() noexcept { return 0; } static constexpr wide::integer infinity() noexcept { return 0; } static constexpr wide::integer quiet_NaN() noexcept { return 0; } static constexpr wide::integer signaling_NaN() noexcept { return 0; } static constexpr wide::integer denorm_min() noexcept { return 0; } }; // type traits template struct common_type, wide::integer> { using type = std::conditional_t < Bits == Bits2, wide::integer< Bits, std::conditional_t<(std::is_same_v && std::is_same_v), signed, unsigned>>, std::conditional_t, wide::integer>>; }; template struct common_type, Arithmetic> { static_assert(wide::ArithmeticConcept()); using type = std::conditional_t< std::is_floating_point_v, Arithmetic, std::conditional_t< sizeof(Arithmetic) < Bits * sizeof(long), wide::integer, std::conditional_t< Bits * sizeof(long) < sizeof(Arithmetic), Arithmetic, std::conditional_t< Bits * sizeof(long) == sizeof(Arithmetic) && (std::is_same_v || std::is_signed_v), Arithmetic, wide::integer>>>>; }; template struct common_type> : common_type, Arithmetic> { }; } namespace wide { template struct integer::_impl { static constexpr size_t _bits = Bits; static constexpr const unsigned byte_count = Bits / 8; static constexpr const unsigned item_count = byte_count / sizeof(base_type); static constexpr const unsigned base_bits = sizeof(base_type) * 8; static_assert(Bits % base_bits == 0); /// Simple iteration in both directions static constexpr unsigned little(unsigned idx) { return idx; } static constexpr unsigned big(unsigned idx) { return item_count - 1 - idx; } static constexpr unsigned any(unsigned idx) { return idx; } template constexpr static bool is_negative(const T & n) noexcept { if constexpr (std::is_signed_v) return n < 0; else return false; } template constexpr static bool is_negative(const integer & n) noexcept { if constexpr (std::is_same_v) return static_cast(n.items[integer::_impl::big(0)]) < 0; else return false; } template constexpr static auto make_positive(const T & n) noexcept { if constexpr (std::is_signed_v) return n < 0 ? -n : n; else return n; } template constexpr static integer make_positive(const integer & n) noexcept { return is_negative(n) ? integer(operator_unary_minus(n)) : n; } template __attribute__((no_sanitize("undefined"))) constexpr static auto to_Integral(T f) noexcept { if constexpr (std::is_signed_v) return static_cast(f); else return static_cast(f); } template constexpr static void wide_integer_from_builtin(integer & self, Integral rhs) noexcept { static_assert(sizeof(Integral) <= sizeof(base_type)); self.items[0] = _impl::to_Integral(rhs); if constexpr (std::is_signed_v) { if (rhs < 0) { for (size_t i = 1; i < item_count; ++i) self.items[i] = -1; return; } } for (size_t i = 1; i < item_count; ++i) self.items[i] = 0; } template constexpr static void wide_integer_from_tuple_like(integer & self, const TupleLike & tuple) noexcept { if constexpr (i < item_count) { if constexpr (i < std::tuple_size_v) self.items[i] = std::get(tuple); else self.items[i] = 0; wide_integer_from_tuple_like(self, tuple); } } /** * N.B. t is constructed from double, so max(t) = max(double) ~ 2^310 * the recursive call happens when t / 2^64 > 2^64, so there won't be more than 5 of them. * * t = a1 * max_int + b1, a1 > max_int, b1 < max_int * a1 = a2 * max_int + b2, a2 > max_int, b2 < max_int * a_(n - 1) = a_n * max_int + b2, a_n <= max_int <- base case. */ template constexpr static void set_multiplier(integer & self, T t) noexcept { constexpr uint64_t max_int = std::numeric_limits::max(); /// Implementation specific behaviour on overflow (if we don't check here, stack overflow will triggered in bigint_cast). if (!std::isfinite(t)) { self = 0; return; } const T alpha = t / static_cast(max_int); if (alpha <= static_cast(max_int)) self = static_cast(alpha); else // max(double) / 2^64 will surely contain less than 52 precision bits, so speed up computations. set_multiplier(self, alpha); self *= max_int; self += static_cast(t - floor(alpha) * static_cast(max_int)); // += b_i } constexpr static void wide_integer_from_builtin(integer & self, double rhs) noexcept { constexpr int64_t max_int = std::numeric_limits::max(); constexpr int64_t min_int = std::numeric_limits::lowest(); /// There are values in int64 that have more than 53 significant bits (in terms of double /// representation). Such values, being promoted to double, are rounded up or down. If they are rounded up, /// the result may not fit in 64 bits. /// The example of such a number is 9.22337e+18. /// As to_Integral does a static_cast to int64_t, it may result in UB. /// The necessary check here is that long double has enough significant (mantissa) bits to store the /// int64_t max value precisely. // TODO Be compatible with Apple aarch64 #if not (defined(__APPLE__) && defined(__aarch64__)) static_assert(LDBL_MANT_DIG >= 64, "On your system long double has less than 64 precision bits, " "which may result in UB when initializing double from int64_t"); #endif if (rhs > static_cast(min_int) && rhs < static_cast(max_int)) { self = static_cast(rhs); return; } const long double rhs_long_double = (static_cast(rhs) < 0) ? -static_cast(rhs) : rhs; set_multiplier(self, rhs_long_double); if (rhs < 0) self = -self; } template constexpr static void wide_integer_from_wide_integer(integer & self, const integer & rhs) noexcept { constexpr const unsigned min_bits = (Bits < Bits2) ? Bits : Bits2; constexpr const unsigned to_copy = min_bits / base_bits; for (unsigned i = 0; i < to_copy; ++i) self.items[i] = rhs.items[i]; if constexpr (Bits > Bits2) { if constexpr (std::is_signed_v) { if (rhs < 0) { for (unsigned i = to_copy; i < item_count; ++i) self.items[i] = -1; return; } } for (unsigned i = to_copy; i < item_count; ++i) self.items[i] = 0; } } template constexpr static bool should_keep_size() { return sizeof(T) <= byte_count; } constexpr static integer shift_left(const integer & rhs, unsigned n) noexcept { integer lhs; unsigned items_shift = n / base_bits; if (unsigned bit_shift = n % base_bits) { unsigned overflow_shift = base_bits - bit_shift; lhs.items[big(0)] = rhs.items[big(items_shift)] << bit_shift; for (unsigned i = 1; i < item_count - items_shift; ++i) { lhs.items[big(i - 1)] |= rhs.items[big(items_shift + i)] >> overflow_shift; lhs.items[big(i)] = rhs.items[big(items_shift + i)] << bit_shift; } } else { for (unsigned i = 0; i < item_count - items_shift; ++i) lhs.items[big(i)] = rhs.items[big(items_shift + i)]; } for (unsigned i = 0; i < items_shift; ++i) lhs.items[little(i)] = 0; return lhs; } constexpr static integer shift_right(const integer & rhs, unsigned n) noexcept { integer lhs; unsigned items_shift = n / base_bits; unsigned bit_shift = n % base_bits; if (bit_shift) { unsigned overflow_shift = base_bits - bit_shift; lhs.items[little(0)] = rhs.items[little(items_shift)] >> bit_shift; for (unsigned i = 1; i < item_count - items_shift; ++i) { lhs.items[little(i - 1)] |= rhs.items[little(items_shift + i)] << overflow_shift; lhs.items[little(i)] = rhs.items[little(items_shift + i)] >> bit_shift; } } else { for (unsigned i = 0; i < item_count - items_shift; ++i) lhs.items[little(i)] = rhs.items[little(items_shift + i)]; } if (is_negative(rhs)) { if (bit_shift) lhs.items[big(items_shift)] |= std::numeric_limits::max() << (base_bits - bit_shift); for (unsigned i = 0; i < items_shift; ++i) lhs.items[big(i)] = std::numeric_limits::max(); } else { for (unsigned i = 0; i < items_shift; ++i) lhs.items[big(i)] = 0; } return lhs; } private: template constexpr static base_type get_item(const T & x, unsigned idx) { if constexpr (IsWideInteger::value) { if (idx < T::_impl::item_count) return x.items[idx]; return 0; } else { if constexpr (sizeof(T) <= sizeof(base_type)) { if (0 == idx) return x; } else if (idx * sizeof(base_type) < sizeof(T)) return x >> (idx * base_bits); // & std::numeric_limits::max() return 0; } } template constexpr static integer minus(const integer & lhs, T rhs) { constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1; constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items; integer res(lhs); bool underflows[item_count] = {}; for (unsigned i = 0; i < op_items; ++i) { base_type rhs_item = get_item(rhs, i); base_type & res_item = res.items[little(i)]; underflows[i] = res_item < rhs_item; res_item -= rhs_item; } for (unsigned i = 1; i < item_count; ++i) { if (underflows[i - 1]) { base_type & res_item = res.items[little(i)]; if (res_item == 0) underflows[i] = true; --res_item; } } return res; } template constexpr static integer plus(const integer & lhs, T rhs) { constexpr const unsigned rhs_items = (sizeof(T) > sizeof(base_type)) ? (sizeof(T) / sizeof(base_type)) : 1; constexpr const unsigned op_items = (item_count < rhs_items) ? item_count : rhs_items; integer res(lhs); bool overflows[item_count] = {}; for (unsigned i = 0; i < op_items; ++i) { base_type rhs_item = get_item(rhs, i); base_type & res_item = res.items[little(i)]; res_item += rhs_item; overflows[i] = res_item < rhs_item; } for (unsigned i = 1; i < item_count; ++i) { if (overflows[i - 1]) { base_type & res_item = res.items[little(i)]; ++res_item; if (res_item == 0) overflows[i] = true; } } return res; } template constexpr static integer multiply(const integer & lhs, const T & rhs) { if constexpr (Bits == 256 && sizeof(base_type) == 8) { /// @sa https://github.com/abseil/abseil-cpp/blob/master/absl/numeric/int128.h using HalfType = unsigned __int128; HalfType a01 = (HalfType(lhs.items[little(1)]) << 64) + lhs.items[little(0)]; HalfType a23 = (HalfType(lhs.items[little(3)]) << 64) + lhs.items[little(2)]; HalfType a0 = lhs.items[little(0)]; HalfType a1 = lhs.items[little(1)]; HalfType b01 = rhs; uint64_t b0 = b01; uint64_t b1 = 0; HalfType b23 = 0; if constexpr (sizeof(T) > 8) b1 = b01 >> 64; if constexpr (sizeof(T) > 16) b23 = (HalfType(rhs.items[little(3)]) << 64) + rhs.items[little(2)]; HalfType r23 = a23 * b01 + a01 * b23 + a1 * b1; HalfType r01 = a0 * b0; HalfType r12 = (r01 >> 64) + (r23 << 64); HalfType r12_x = a1 * b0; integer res; res.items[little(0)] = r01; res.items[little(3)] = r23 >> 64; if constexpr (sizeof(T) > 8) { HalfType r12_y = a0 * b1; r12_x += r12_y; if (r12_x < r12_y) ++res.items[little(3)]; } r12 += r12_x; if (r12 < r12_x) ++res.items[little(3)]; res.items[little(1)] = r12; res.items[little(2)] = r12 >> 64; return res; } else if constexpr (Bits == 128 && sizeof(base_type) == 8) { using CompilerUInt128 = unsigned __int128; CompilerUInt128 a = (CompilerUInt128(lhs.items[1]) << 64) + lhs.items[0]; CompilerUInt128 b = (CompilerUInt128(rhs.items[1]) << 64) + rhs.items[0]; CompilerUInt128 c = a * b; integer res; res.items[0] = c; res.items[1] = c >> 64; return res; } else { integer res{}; #if 1 integer lhs2 = plus(lhs, shift_left(lhs, 1)); integer lhs3 = plus(lhs2, shift_left(lhs, 2)); #endif for (unsigned i = 0; i < item_count; ++i) { base_type rhs_item = get_item(rhs, i); unsigned pos = i * base_bits; while (rhs_item) { #if 1 /// optimization if ((rhs_item & 0x7) == 0x7) { res = plus(res, shift_left(lhs3, pos)); rhs_item >>= 3; pos += 3; continue; } if ((rhs_item & 0x3) == 0x3) { res = plus(res, shift_left(lhs2, pos)); rhs_item >>= 2; pos += 2; continue; } #endif if (rhs_item & 1) res = plus(res, shift_left(lhs, pos)); rhs_item >>= 1; ++pos; } } return res; } } public: constexpr static integer operator_unary_tilda(const integer & lhs) noexcept { integer res; for (unsigned i = 0; i < item_count; ++i) res.items[any(i)] = ~lhs.items[any(i)]; return res; } constexpr static integer operator_unary_minus(const integer & lhs) noexcept(std::is_same_v) { return plus(operator_unary_tilda(lhs), 1); } template constexpr static auto operator_plus(const integer & lhs, const T & rhs) noexcept(std::is_same_v) { if constexpr (should_keep_size()) { if (is_negative(rhs)) return minus(lhs, -rhs); else return plus(lhs, rhs); } else { static_assert(IsWideInteger::value); return std::common_type_t, integer>::_impl::operator_plus( integer(lhs), rhs); } } template constexpr static auto operator_minus(const integer & lhs, const T & rhs) noexcept(std::is_same_v) { if constexpr (should_keep_size()) { if (is_negative(rhs)) return plus(lhs, -rhs); else return minus(lhs, rhs); } else { static_assert(IsWideInteger::value); return std::common_type_t, integer>::_impl::operator_minus( integer(lhs), rhs); } } template constexpr static auto operator_star(const integer & lhs, const T & rhs) { if constexpr (should_keep_size()) { integer res; if constexpr (std::is_signed_v) { res = multiply((is_negative(lhs) ? make_positive(lhs) : lhs), (is_negative(rhs) ? make_positive(rhs) : rhs)); } else { res = multiply(lhs, (is_negative(rhs) ? make_positive(rhs) : rhs)); } if (std::is_same_v && is_negative(lhs) != is_negative(rhs)) res = operator_unary_minus(res); return res; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_star(T(lhs), rhs); } } template constexpr static bool operator_greater(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { if (std::numeric_limits::is_signed && (is_negative(lhs) != is_negative(rhs))) return is_negative(rhs); for (unsigned i = 0; i < item_count; ++i) { base_type rhs_item = get_item(rhs, big(i)); if (lhs.items[big(i)] != rhs_item) return lhs.items[big(i)] > rhs_item; } return false; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_greater(T(lhs), rhs); } } template constexpr static bool operator_less(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { if (std::numeric_limits::is_signed && (is_negative(lhs) != is_negative(rhs))) return is_negative(lhs); for (unsigned i = 0; i < item_count; ++i) { base_type rhs_item = get_item(rhs, big(i)); if (lhs.items[big(i)] != rhs_item) return lhs.items[big(i)] < rhs_item; } return false; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_less(T(lhs), rhs); } } template constexpr static bool operator_eq(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { for (unsigned i = 0; i < item_count; ++i) { base_type rhs_item = get_item(rhs, any(i)); if (lhs.items[any(i)] != rhs_item) return false; } return true; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_eq(T(lhs), rhs); } } template constexpr static auto operator_pipe(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { integer res; for (unsigned i = 0; i < item_count; ++i) res.items[little(i)] = lhs.items[little(i)] | get_item(rhs, i); return res; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_pipe(T(lhs), rhs); } } template constexpr static auto operator_amp(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { integer res; for (unsigned i = 0; i < item_count; ++i) res.items[little(i)] = lhs.items[little(i)] & get_item(rhs, i); return res; } else { static_assert(IsWideInteger::value); return std::common_type_t, T>::_impl::operator_amp(T(lhs), rhs); } } template constexpr static bool is_zero(const T & x) { bool is_zero = true; for (auto item : x.items) { if (item != 0) { is_zero = false; break; } } return is_zero; } /// returns quotient as result and remainder in numerator. template constexpr static integer divide(integer & numerator, integer denominator) { static_assert(std::is_unsigned_v); if constexpr (Bits == 128 && sizeof(base_type) == 8) { using CompilerUInt128 = unsigned __int128; CompilerUInt128 a = (CompilerUInt128(numerator.items[1]) << 64) + numerator.items[0]; CompilerUInt128 b = (CompilerUInt128(denominator.items[1]) << 64) + denominator.items[0]; CompilerUInt128 c = a / b; integer res; res.items[0] = c; res.items[1] = c >> 64; CompilerUInt128 remainder = a - b * c; numerator.items[0] = remainder; numerator.items[1] = remainder >> 64; return res; } if (is_zero(denominator)) throwError("Division by zero"); integer x = 1; integer quotient = 0; while (!operator_greater(denominator, numerator) && is_zero(operator_amp(shift_right(denominator, Bits2 - 1), 1))) { x = shift_left(x, 1); denominator = shift_left(denominator, 1); } while (!is_zero(x)) { if (!operator_greater(denominator, numerator)) { numerator = operator_minus(numerator, denominator); quotient = operator_pipe(quotient, x); } x = shift_right(x, 1); denominator = shift_right(denominator, 1); } return quotient; } template constexpr static auto operator_slash(const integer & lhs, const T & rhs) { if constexpr (should_keep_size()) { integer numerator = make_positive(lhs); integer denominator = make_positive(integer(rhs)); integer quotient = integer::_impl::divide(numerator, std::move(denominator)); if (std::is_same_v && is_negative(rhs) != is_negative(lhs)) quotient = operator_unary_minus(quotient); return quotient; } else { static_assert(IsWideInteger::value); return std::common_type_t, integer>::operator_slash(T(lhs), rhs); } } template constexpr static auto operator_percent(const integer & lhs, const T & rhs) { if constexpr (should_keep_size()) { integer remainder = make_positive(lhs); integer denominator = make_positive(integer(rhs)); integer::_impl::divide(remainder, std::move(denominator)); if (std::is_same_v && is_negative(lhs)) remainder = operator_unary_minus(remainder); return remainder; } else { static_assert(IsWideInteger::value); return std::common_type_t, integer>::operator_percent(T(lhs), rhs); } } // ^ template constexpr static auto operator_circumflex(const integer & lhs, const T & rhs) noexcept { if constexpr (should_keep_size()) { integer t(rhs); integer res = lhs; for (unsigned i = 0; i < item_count; ++i) res.items[any(i)] ^= t.items[any(i)]; return res; } else { static_assert(IsWideInteger::value); return T::operator_circumflex(T(lhs), rhs); } } constexpr static integer from_str(const char * c) { integer res = 0; bool is_neg = std::is_same_v && *c == '-'; if (is_neg) ++c; if (*c == '0' && (*(c + 1) == 'x' || *(c + 1) == 'X')) { // hex ++c; ++c; while (*c) { if (*c >= '0' && *c <= '9') { res = multiply(res, 16U); res = plus(res, *c - '0'); ++c; } else if (*c >= 'a' && *c <= 'f') { res = multiply(res, 16U); res = plus(res, *c - 'a' + 10U); ++c; } else if (*c >= 'A' && *c <= 'F') { // tolower must be used, but it is not constexpr res = multiply(res, 16U); res = plus(res, *c - 'A' + 10U); ++c; } else throwError("Invalid char from"); } } else { // dec while (*c) { if (*c < '0' || *c > '9') throwError("Invalid char from"); res = multiply(res, 10U); res = plus(res, *c - '0'); ++c; } } if (is_neg) res = operator_unary_minus(res); return res; } }; // Members template template constexpr integer::integer(T rhs) noexcept : items{} { if constexpr (IsWideInteger::value) _impl::wide_integer_from_wide_integer(*this, rhs); else if constexpr (IsTupleLike::value) _impl::wide_integer_from_tuple_like(*this, rhs); else _impl::wide_integer_from_builtin(*this, rhs); } template template constexpr integer::integer(std::initializer_list il) noexcept : items{} { if (il.size() == 1) { if constexpr (IsWideInteger::value) _impl::wide_integer_from_wide_integer(*this, *il.begin()); else if constexpr (IsTupleLike::value) _impl::wide_integer_from_tuple_like(*this, *il.begin()); else _impl::wide_integer_from_builtin(*this, *il.begin()); } else if (il.size() == 0) { _impl::wide_integer_from_builtin(*this, 0); } else { auto it = il.begin(); for (size_t i = 0; i < _impl::item_count; ++i) if (it < il.end()) items[i] = *it; } } template template constexpr integer & integer::operator=(const integer & rhs) noexcept { _impl::wide_integer_from_wide_integer(*this, rhs); return *this; } template template constexpr integer & integer::operator=(T rhs) noexcept { if constexpr (IsTupleLike::value) _impl::wide_integer_from_tuple_like(*this, rhs); else _impl::wide_integer_from_builtin(*this, rhs); return *this; } template template constexpr integer & integer::operator*=(const T & rhs) { *this = *this * rhs; return *this; } template template constexpr integer & integer::operator/=(const T & rhs) { *this = *this / rhs; return *this; } template template constexpr integer & integer::operator+=(const T & rhs) noexcept(std::is_same_v) { *this = *this + rhs; return *this; } template template constexpr integer & integer::operator-=(const T & rhs) noexcept(std::is_same_v) { *this = *this - rhs; return *this; } template template constexpr integer & integer::operator%=(const T & rhs) { *this = *this % rhs; return *this; } template template constexpr integer & integer::operator&=(const T & rhs) noexcept { *this = *this & rhs; return *this; } template template constexpr integer & integer::operator|=(const T & rhs) noexcept { *this = *this | rhs; return *this; } template template constexpr integer & integer::operator^=(const T & rhs) noexcept { *this = *this ^ rhs; return *this; } template constexpr integer & integer::operator<<=(int n) noexcept { if (static_cast(n) >= Bits) *this = 0; else if (n > 0) *this = _impl::shift_left(*this, n); return *this; } template constexpr integer & integer::operator>>=(int n) noexcept { if (static_cast(n) >= Bits) { if (_impl::is_negative(*this)) *this = -1; else *this = 0; } else if (n > 0) *this = _impl::shift_right(*this, n); return *this; } template constexpr integer & integer::operator++() noexcept(std::is_same_v) { *this = _impl::operator_plus(*this, 1); return *this; } template constexpr integer integer::operator++(int) noexcept(std::is_same_v) { auto tmp = *this; *this = _impl::operator_plus(*this, 1); return tmp; } template constexpr integer & integer::operator--() noexcept(std::is_same_v) { *this = _impl::operator_minus(*this, 1); return *this; } template constexpr integer integer::operator--(int) noexcept(std::is_same_v) { auto tmp = *this; *this = _impl::operator_minus(*this, 1); return tmp; } template constexpr integer::operator bool() const noexcept { return !_impl::operator_eq(*this, 0); } template template constexpr integer::operator T() const noexcept { static_assert(std::numeric_limits::is_integer); /// NOTE: memcpy will suffice, but unfortunately, this function is constexpr. using UnsignedT = std::make_unsigned_t; UnsignedT res{}; for (unsigned i = 0; i < _impl::item_count && i < (sizeof(T) + sizeof(base_type) - 1) / sizeof(base_type); ++i) res += UnsignedT(items[i]) << (sizeof(base_type) * 8 * i); return res; } template constexpr integer::operator long double() const noexcept { if (_impl::operator_eq(*this, 0)) return 0; integer tmp = *this; if (_impl::is_negative(*this)) tmp = -tmp; long double res = 0; for (unsigned i = 0; i < _impl::item_count; ++i) { long double t = res; res *= std::numeric_limits::max(); res += t; res += tmp.items[_impl::big(i)]; } if (_impl::is_negative(*this)) res = -res; return res; } template constexpr integer::operator double() const noexcept { return static_cast(*this); } template constexpr integer::operator float() const noexcept { return static_cast(*this); } // Unary operators template constexpr integer operator~(const integer & lhs) noexcept { return integer::_impl::operator_unary_tilda(lhs); } template constexpr integer operator-(const integer & lhs) noexcept(std::is_same_v) { return integer::_impl::operator_unary_minus(lhs); } template constexpr integer operator+(const integer & lhs) noexcept(std::is_same_v) { return lhs; } #define CT(x) \ std::common_type_t, std::decay_t> { x } // Binary operators template std::common_type_t, integer> constexpr operator*(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_star(lhs, rhs); } template std::common_type_t constexpr operator*(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) * CT(rhs); } template std::common_type_t, integer> constexpr operator/(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_slash(lhs, rhs); } template std::common_type_t constexpr operator/(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) / CT(rhs); } template std::common_type_t, integer> constexpr operator+(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_plus(lhs, rhs); } template std::common_type_t constexpr operator+(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) + CT(rhs); } template std::common_type_t, integer> constexpr operator-(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_minus(lhs, rhs); } template std::common_type_t constexpr operator-(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) - CT(rhs); } template std::common_type_t, integer> constexpr operator%(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_percent(lhs, rhs); } template std::common_type_t constexpr operator%(const Integral & lhs, const Integral2 & rhs) { return CT(lhs) % CT(rhs); } template std::common_type_t, integer> constexpr operator&(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_amp(lhs, rhs); } template std::common_type_t constexpr operator&(const Integral & lhs, const Integral2 & rhs) { return CT(lhs) & CT(rhs); } template std::common_type_t, integer> constexpr operator|(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_pipe(lhs, rhs); } template std::common_type_t constexpr operator|(const Integral & lhs, const Integral2 & rhs) { return CT(lhs) | CT(rhs); } template std::common_type_t, integer> constexpr operator^(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_circumflex(lhs, rhs); } template std::common_type_t constexpr operator^(const Integral & lhs, const Integral2 & rhs) { return CT(lhs) ^ CT(rhs); } template constexpr integer operator<<(const integer & lhs, int n) noexcept { if (static_cast(n) >= Bits) return integer(0); if (n <= 0) return lhs; return integer::_impl::shift_left(lhs, n); } template constexpr integer operator>>(const integer & lhs, int n) noexcept { if (static_cast(n) >= Bits) return integer(0); if (n <= 0) return lhs; return integer::_impl::shift_right(lhs, n); } template constexpr bool operator<(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_less(lhs, rhs); } template constexpr bool operator<(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) < CT(rhs); } template constexpr bool operator>(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_greater(lhs, rhs); } template constexpr bool operator>(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) > CT(rhs); } template constexpr bool operator<=(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_less(lhs, rhs) || std::common_type_t, integer>::_impl::operator_eq(lhs, rhs); } template constexpr bool operator<=(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) <= CT(rhs); } template constexpr bool operator>=(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_greater(lhs, rhs) || std::common_type_t, integer>::_impl::operator_eq(lhs, rhs); } template constexpr bool operator>=(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) >= CT(rhs); } template constexpr bool operator==(const integer & lhs, const integer & rhs) { return std::common_type_t, integer>::_impl::operator_eq(lhs, rhs); } template constexpr bool operator==(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) == CT(rhs); } template constexpr bool operator!=(const integer & lhs, const integer & rhs) { return !std::common_type_t, integer>::_impl::operator_eq(lhs, rhs); } template constexpr bool operator!=(const Arithmetic & lhs, const Arithmetic2 & rhs) { return CT(lhs) != CT(rhs); } #undef CT } namespace std { template struct hash> { std::size_t operator()(const wide::integer & lhs) const { static_assert(Bits % (sizeof(size_t) * 8) == 0); const auto * ptr = reinterpret_cast(lhs.items); unsigned count = Bits / (sizeof(size_t) * 8); size_t res = 0; for (unsigned i = 0; i < count; ++i) res ^= ptr[i]; return res; } }; }