/// Original is here https://github.com/cerevra/int #pragma once #include "throwError.h" 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; } } 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 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[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) ? operator_unary_minus(n) : n; } template constexpr static auto to_Integral(T f) noexcept { if constexpr (std::is_same_v) return f; else if constexpr (std::is_signed_v) return static_cast(f); else return static_cast(f); } template constexpr static void wide_integer_from_bultin(integer & self, Integral rhs) noexcept { self.items[0] = _impl::to_Integral(rhs); if constexpr (std::is_same_v) self.items[1] = rhs >> base_bits; constexpr const unsigned start = (sizeof(Integral) == 16) ? 2 : 1; if constexpr (std::is_signed_v) { if (rhs < 0) { for (unsigned i = start; i < item_count; ++i) self.items[i] = -1; return; } } for (unsigned i = start; i < item_count; ++i) self.items[i] = 0; } constexpr static void wide_integer_from_bultin(integer & self, double rhs) noexcept { if ((rhs > 0 && rhs < std::numeric_limits::max()) || (rhs < 0 && rhs > std::numeric_limits::min())) { self = to_Integral(rhs); return; } long double r = rhs; if (r < 0) r = -r; size_t count = r / std::numeric_limits::max(); self = count; self *= std::numeric_limits::max(); long double to_diff = count; to_diff *= std::numeric_limits::max(); self += to_Integral(r - to_diff); 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 = item_count - items_shift; i < items_shift; ++i) lhs.items[little(i)] = std::numeric_limits::max(); } else { for (unsigned i = item_count - items_shift; i < items_shift; ++i) lhs.items[little(i)] = 0; } return lhs; } private: template constexpr static base_type get_item(const T & x, unsigned number) { if constexpr (IsWideInteger::value) { if (number < T::_impl::item_count) return x.items[number]; return 0; } else { if (number * sizeof(base_type) < sizeof(T)) return x >> (number * base_bits); // & std::numeric_limits::max() return 0; } } template constexpr static integer op_minus(const integer & lhs, T rhs) { integer res; bool is_underflow = false; for (unsigned i = 0; i < item_count; ++i) { base_type lhs_item = lhs.items[little(i)]; base_type rhs_item = get_item(rhs, i); if (is_underflow) { is_underflow = (lhs_item == 0); --lhs_item; } if (lhs_item < rhs_item) is_underflow = true; res.items[little(i)] = lhs_item - rhs_item; } return res; } template constexpr static integer op_plus(const integer & lhs, T rhs) { integer res; bool is_overflow = false; for (unsigned i = 0; i < item_count; ++i) { base_type lhs_item = lhs.items[little(i)]; base_type rhs_item = get_item(rhs, i); if (is_overflow) { ++lhs_item; is_overflow = (lhs_item == 0); } base_type & res_item = res.items[little(i)]; res_item = lhs_item + rhs_item; if (res_item < rhs_item) is_overflow = true; } return res; } template constexpr static auto op_multiply(const integer & lhs, const T & rhs) { integer res{}; #if 1 integer lhs2 = op_plus(lhs, shift_left(lhs, 1)); integer lhs3 = op_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 = op_plus(res, shift_left(lhs3, pos)); rhs_item >>= 3; pos += 3; continue; } if ((rhs_item & 0x3) == 0x3) { res = op_plus(res, shift_left(lhs2, pos)); rhs_item >>= 2; pos += 2; continue; } #endif if (rhs_item & 1) res = op_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 op_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 op_minus(lhs, -rhs); else return op_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 op_plus(lhs, -rhs); else return op_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 = op_multiply((is_negative(lhs) ? make_positive(lhs) : lhs), (is_negative(rhs) ? make_positive(rhs) : rhs)); } else { res = op_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_more(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_more(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); } } private: 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 T divide(T & numerator, T && denominator) { if (is_zero(denominator)) throwError("divide by zero"); T & n = numerator; T & d = denominator; T x = 1; T quotient = 0; while (!operator_more(d, n) && operator_eq(operator_amp(shift_right(d, base_bits * item_count - 1), 1), 0)) { x = shift_left(x, 1); d = shift_left(d, 1); } while (!operator_eq(x, 0)) { if (!operator_more(d, n)) { n = operator_minus(n, d); quotient = operator_pipe(quotient, x); } x = shift_right(x, 1); d = shift_right(d, 1); } return quotient; } public: template constexpr static auto operator_slash(const integer & lhs, const T & rhs) { if constexpr (should_keep_size()) { integer numerator = make_positive(lhs); integer quotient = divide(numerator, make_positive(integer(rhs))); 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); divide(remainder, make_positive(integer(rhs))); 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 = op_multiply(res, 16U); res = op_plus(res, *c - '0'); ++c; } else if (*c >= 'a' && *c <= 'f') { res = op_multiply(res, 16U); res = op_plus(res, *c - 'a' + 10U); ++c; } else if (*c >= 'A' && *c <= 'F') { // tolower must be used, but it is not constexpr res = op_multiply(res, 16U); res = op_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 = op_multiply(res, 10U); res = op_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 _impl::wide_integer_from_bultin(*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 _impl::wide_integer_from_bultin(*this, *il.begin()); } else _impl::wide_integer_from_bultin(*this, 0); } 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 { _impl::wide_integer_from_bultin(*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 (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 { if constexpr (std::is_same_v) { static_assert(Bits >= 128); return (__int128(items[1]) << 64) | items[0]; } else { static_assert(std::numeric_limits::is_integer); return items[0]; } } 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 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 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_more(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_more(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; } }; }