#pragma once #include #include #include #include namespace NYql { #ifndef _win_ typedef __int128 i128_t; typedef unsigned __int128 ui128_t; #endif template, typename TUnsignedPart = std::make_unsigned_t> class TWide { private: using THalf = TOneHalf; using TPart = TUnsignedPart; using TIsSigned = std::is_same; using TIsUnsigned = std::is_same; static_assert(TIsSigned::value || TIsUnsigned::value, "Invalid using of TWide."); static_assert(sizeof(TSignedPart) == sizeof(TUnsignedPart), "Different sizes of TWide parts."); TPart Lo; THalf Hi; static constexpr auto FullBitSize = sizeof(TPart) << 4U; static constexpr auto PartBitSize = sizeof(TPart) << 3U; static constexpr auto QuarterBitSize = sizeof(TPart) << 2U; static constexpr TPart GetUpperQuarter(TPart h) { return h >> QuarterBitSize; } static constexpr TPart GetLowerQuarter(TPart h) { const auto mask = TPart(~TPart(0U)) >> QuarterBitSize; return h & mask; } template constexpr std::enable_if_t CastImpl() const { return static_cast(Lo); } template constexpr std::enable_if_t CastImpl() const { return *reinterpret_cast(this); } template constexpr std::enable_if_t CastImpl() const { return (T(std::make_unsigned_t(Hi) << PartBitSize)) | Lo; } constexpr size_t GetBits() const { size_t out = Hi ? PartBitSize : 0U; for (auto up = TPart(out ? Hi : Lo); up; up >>= 1U) { ++out; } return out; } using TUnsigned = TWide; using TSibling = std::conditional_t::value, TWide, TWide>; friend TSibling; public: constexpr TWide() = default; constexpr TWide(const TWide& rhs) = default; constexpr TWide(TWide&& rhs) = default; constexpr TWide& operator=(const TWide& rhs) = default; constexpr TWide& operator=(TWide&& rhs) = default; constexpr TWide(const TSibling& rhs) : Lo(rhs.Lo), Hi(rhs.Hi) {} template constexpr TWide(const U upper_rhs, const L lower_rhs) : Lo(lower_rhs), Hi(upper_rhs) {} template ::value && sizeof(THalf) < sizeof(T), size_t> Shift = PartBitSize> constexpr TWide(const T rhs) : Lo(rhs), Hi(rhs >> Shift) {} template ::value && sizeof(T) <= sizeof(THalf), bool> Signed = std::is_signed::value> constexpr TWide(const T rhs) : Lo(rhs), Hi(Signed && rhs < 0 ? ~0 : 0) {} template ::value && std::is_same::value, THalf>> constexpr explicit TWide(const T& rhs) : Lo(rhs), Hi(TIsSigned::value && rhs < 0 ? ~0 : 0) {} template ::value && sizeof(THalf) < sizeof(T), size_t> Shift = PartBitSize> constexpr TWide& operator=(const T rhs) { Hi = rhs >> Shift; Lo = rhs; return *this; } template ::value && sizeof(T) <= sizeof(THalf), bool> Signed = std::is_signed::value> constexpr TWide& operator=(const T rhs) { Hi = Signed && rhs < 0 ? ~0 : 0; Lo = rhs; return *this; } constexpr explicit operator bool() const { return bool(Hi) || bool(Lo); } constexpr explicit operator i8() const { return CastImpl(); } constexpr explicit operator ui8() const { return CastImpl(); } constexpr explicit operator i16() const { return CastImpl(); } constexpr explicit operator ui16() const { return CastImpl(); } constexpr explicit operator i32() const { return CastImpl(); } constexpr explicit operator ui32() const { return CastImpl(); } constexpr explicit operator i64() const { return CastImpl(); } constexpr explicit operator ui64() const { return CastImpl(); } #ifndef _win_ constexpr explicit operator i128_t() const { return CastImpl(); } constexpr explicit operator ui128_t() const { return CastImpl(); } #endif constexpr explicit operator float() const { return TIsSigned::value && Hi < 0 ? -float(-*this) : std::fma(float(Hi), std::exp2(float(PartBitSize)), float(Lo)); } constexpr explicit operator double() const { return TIsSigned::value && Hi < 0 ? -double(-*this) : std::fma(double(Hi), std::exp2(double(PartBitSize)), double(Lo)); } constexpr TWide operator~() const { return TWide(~Hi, ~Lo); } constexpr TWide operator+() const { return TWide(Hi, Lo); } constexpr TWide operator-() const { const auto sign = THalf(1) << PartBitSize - 1U; if (TIsSigned::value && !Lo && Hi == sign) return *this; return ++~*this; } constexpr TWide& operator++() { if (!++Lo) ++Hi; return *this; } constexpr TWide& operator--() { if (!Lo--) --Hi; return *this; } constexpr TWide operator++(int) { const TWide r(*this); ++*this; return r; } constexpr TWide operator--(int) { const TWide r(*this); --*this; return r; } constexpr TWide& operator&=(const TWide& rhs) { Hi &= rhs.Hi; Lo &= rhs.Lo; return *this; } constexpr TWide& operator|=(const TWide& rhs) { Hi |= rhs.Hi; Lo |= rhs.Lo; return *this; } constexpr TWide& operator^=(const TWide& rhs) { Hi ^= rhs.Hi; Lo ^= rhs.Lo; return *this; } constexpr TWide& operator+=(const TWide& rhs) { const auto l = Lo; Lo += rhs.Lo; Hi += rhs.Hi; if (l > Lo) ++Hi; return *this; } constexpr TWide& operator-=(const TWide& rhs) { const auto l = Lo; Lo -= rhs.Lo; Hi -= rhs.Hi; if (l < Lo) --Hi; return *this; } constexpr TWide& operator<<=(const TWide& rhs) { if (const auto shift = size_t(rhs.Lo) % FullBitSize) { if (shift < PartBitSize) { Hi = TPart(Hi) << shift; Hi |= Lo >> (PartBitSize - shift); Lo <<= shift; } else { Hi = Lo << (shift - PartBitSize); Lo = 0; } } return *this; } constexpr TWide& operator>>=(const TWide& rhs) { if (const auto shift = size_t(rhs.Lo) % FullBitSize) { if (shift < PartBitSize) { Lo >>= shift; Lo |= TPart(Hi) << (PartBitSize - shift); Hi >>= shift; } else { Lo = Hi >> (shift - PartBitSize); Hi = TIsSigned::value && Hi < 0 ? ~0 : 0; } } return *this; } constexpr TWide& operator*=(const TWide& rhs) { return *this = Mul(*this, rhs); } constexpr TWide& operator/=(const TWide& rhs) { return *this = DivMod(*this, rhs).first; } constexpr TWide& operator%=(const TWide& rhs) { return *this = DivMod(*this, rhs).second; } constexpr TWide operator&(const TWide& rhs) const { return TWide(*this) &= rhs; } constexpr TWide operator|(const TWide& rhs) const { return TWide(*this) |= rhs; } constexpr TWide operator^(const TWide& rhs) const { return TWide(*this) ^= rhs; } constexpr TWide operator+(const TWide& rhs) const { return TWide(*this) += rhs; } constexpr TWide operator-(const TWide& rhs) const { return TWide(*this) -= rhs; } constexpr TWide operator*(const TWide& rhs) const { return Mul(*this, rhs); } constexpr TWide operator/(const TWide& rhs) const { return DivMod(*this, rhs).first; } constexpr TWide operator%(const TWide& rhs) const { return DivMod(*this, rhs).second; } constexpr TWide operator<<(const TWide& rhs) const { return TWide(*this) <<= rhs; } constexpr TWide operator>>(const TWide& rhs) const { return TWide(*this) >>= rhs; } template constexpr std::enable_if_t::value, T> operator&(const T rhs) const { return T(*this) & rhs; } constexpr bool operator==(const TWide& rhs) const { return std::tie(Hi, Lo) == std::tie(rhs.Hi, rhs.Lo); } constexpr bool operator!=(const TWide& rhs) const { return std::tie(Hi, Lo) != std::tie(rhs.Hi, rhs.Lo); } constexpr bool operator>=(const TWide& rhs) const { return std::tie(Hi, Lo) >= std::tie(rhs.Hi, rhs.Lo); } constexpr bool operator<=(const TWide& rhs) const { return std::tie(Hi, Lo) <= std::tie(rhs.Hi, rhs.Lo); } constexpr bool operator>(const TWide& rhs) const { return std::tie(Hi, Lo) > std::tie(rhs.Hi, rhs.Lo); } constexpr bool operator<(const TWide& rhs) const { return std::tie(Hi, Lo) < std::tie(rhs.Hi, rhs.Lo); } private: static constexpr TWide Mul(const TWide& lhs, const TWide& rhs) { const TPart lq[] = {GetLowerQuarter(lhs.Lo), GetUpperQuarter(lhs.Lo), GetLowerQuarter(lhs.Hi), GetUpperQuarter(lhs.Hi)}; const TPart rq[] = {GetLowerQuarter(rhs.Lo), GetUpperQuarter(rhs.Lo), GetLowerQuarter(rhs.Hi), GetUpperQuarter(rhs.Hi)}; const TPart prod0[] = {TPart(lq[0] * rq[0])}; const TPart prod1[] = {TPart(lq[0] * rq[1]), TPart(lq[1] * rq[0])}; const TPart prod2[] = {TPart(lq[0] * rq[2]), TPart(lq[1] * rq[1]), TPart(lq[2] * rq[0])}; const TPart prod3[] = {TPart(lq[0] * rq[3]), TPart(lq[1] * rq[2]), TPart(lq[2] * rq[1]), TPart(lq[3] * rq[0])}; const TPart fourthQ = GetLowerQuarter(prod0[0]); const TPart thirdQ = GetUpperQuarter(prod0[0]) + GetLowerQuarter(prod1[0]) + GetLowerQuarter(prod1[1]); const TPart secondQ = GetUpperQuarter(thirdQ) + GetUpperQuarter(prod1[0]) + GetUpperQuarter(prod1[1]) + GetLowerQuarter(prod2[0]) + GetLowerQuarter(prod2[1]) + GetLowerQuarter(prod2[2]); const TPart firstQ = GetUpperQuarter(secondQ) + GetUpperQuarter(prod2[0]) + GetUpperQuarter(prod2[1]) + GetUpperQuarter(prod2[2]) + GetLowerQuarter(prod3[0]) + GetLowerQuarter(prod3[1]) + GetLowerQuarter(prod3[2]) + GetLowerQuarter(prod3[3]); return TWide((firstQ << QuarterBitSize) | GetLowerQuarter(secondQ), (thirdQ << QuarterBitSize) | fourthQ); } static constexpr std::pair DivMod(const TWide& lhs, const TWide& rhs) { const bool nl = TIsSigned::value && lhs.Hi < 0; const bool nr = TIsSigned::value && rhs.Hi < 0; const TUnsigned l = nl ? -lhs : +lhs, r = nr ? -rhs : +rhs; TUnsigned div = 0, mod = 0; for (auto x = l.GetBits(); x;) { mod <<= 1; div <<= 1; if (--x < PartBitSize ? l.Lo & (TPart(1U) << x) : l.Hi & (TPart(1U) << x - PartBitSize)) { ++mod; } if (mod >= r) { mod -= r; ++div; } } if (nl) mod = -mod; if (nr != nl) div = -div; return {div, mod}; } }; template struct THalfOf; template<> struct THalfOf { typedef i8 Type; }; template<> struct THalfOf { typedef ui8 Type; }; template<> struct THalfOf { typedef i16 Type; }; template<> struct THalfOf { typedef ui16 Type; }; template<> struct THalfOf { typedef i32 Type; }; template<> struct THalfOf { typedef ui32 Type; }; #ifndef _win_ template<> struct THalfOf { typedef i64 Type; }; template<> struct THalfOf { typedef ui64 Type; }; #endif template struct THalfOf {}; template struct TPairOf; template<> struct TPairOf { typedef i16 Type; }; template<> struct TPairOf { typedef ui16 Type; }; template<> struct TPairOf { typedef i32 Type; }; template<> struct TPairOf { typedef ui32 Type; }; template<> struct TPairOf { typedef i64 Type; }; template<> struct TPairOf { typedef ui64 Type; }; #ifndef _win_ template<> struct TPairOf { typedef i128_t Type; }; template<> struct TPairOf { typedef ui128_t Type; }; #endif template struct TPairOf {}; }