mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:02:44 +00:00 
			
		
		
		
	 6124050187
			
		
	
	
		6124050187
		
	
	
	
	
		
			
			Instead of using the specified type U like we want, we were using the type T all along when comparing with smaller integers. This is now fixed.
		
			
				
	
	
		
			777 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			777 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/BuiltinWrappers.h>
 | |
| #include <AK/Checked.h>
 | |
| #include <AK/Concepts.h>
 | |
| #include <AK/Format.h>
 | |
| #include <AK/NumericLimits.h>
 | |
| #include <AK/StdLibExtraDetails.h>
 | |
| #include <AK/StdLibExtras.h>
 | |
| #include <AK/String.h>
 | |
| #include <AK/StringBuilder.h>
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| template<typename T>
 | |
| requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt;
 | |
| 
 | |
| // FIXME: This breaks formatting
 | |
| // template<typename T>
 | |
| // constexpr inline bool Detail::IsIntegral<UFixedBigInt<T>> = true;
 | |
| 
 | |
| template<typename T>
 | |
| constexpr inline bool Detail::IsUnsigned<UFixedBigInt<T>> = true;
 | |
| template<typename T>
 | |
| constexpr inline bool Detail::IsSigned<UFixedBigInt<T>> = false;
 | |
| 
 | |
| template<typename T>
 | |
| struct NumericLimits<UFixedBigInt<T>> {
 | |
|     static constexpr UFixedBigInt<T> min() { return 0; }
 | |
|     static constexpr UFixedBigInt<T> max() { return { NumericLimits<T>::max(), NumericLimits<T>::max() }; }
 | |
|     static constexpr bool is_signed() { return false; }
 | |
| };
 | |
| 
 | |
| template<typename T>
 | |
| requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt {
 | |
| public:
 | |
|     using R = UFixedBigInt<T>;
 | |
| 
 | |
|     constexpr UFixedBigInt() = default;
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr UFixedBigInt(U low)
 | |
|         : m_low(low)
 | |
|         , m_high(0u)
 | |
|     {
 | |
|     }
 | |
|     template<Unsigned U, Unsigned U2>
 | |
|     requires(sizeof(T) >= sizeof(U) && sizeof(T) >= sizeof(U2)) constexpr UFixedBigInt(U low, U2 high)
 | |
|         : m_low(low)
 | |
|         , m_high(high)
 | |
|     {
 | |
|     }
 | |
|     constexpr T& low()
 | |
|     {
 | |
|         return m_low;
 | |
|     }
 | |
|     constexpr const T& low() const
 | |
|     {
 | |
|         return m_low;
 | |
|     }
 | |
|     constexpr T& high()
 | |
|     {
 | |
|         return m_high;
 | |
|     }
 | |
|     constexpr const T& high() const
 | |
|     {
 | |
|         return m_high;
 | |
|     }
 | |
| 
 | |
|     Span<u8> bytes()
 | |
|     {
 | |
|         return Span<u8>(reinterpret_cast<u8*>(this), sizeof(R));
 | |
|     }
 | |
|     Span<const u8> bytes() const
 | |
|     {
 | |
|         return Span<const u8>(reinterpret_cast<const u8*>(this), sizeof(R));
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) explicit operator U() const
 | |
|     {
 | |
|         return static_cast<U>(m_low);
 | |
|     }
 | |
| 
 | |
|     // Utils
 | |
|     constexpr size_t clz() const requires(IsSame<T, u64>)
 | |
|     {
 | |
|         if (m_high)
 | |
|             return count_leading_zeroes(m_high);
 | |
|         else
 | |
|             return sizeof(T) * 8 + count_leading_zeroes(m_low);
 | |
|     }
 | |
|     constexpr size_t clz() const requires(!IsSame<T, u64>)
 | |
|     {
 | |
|         if (m_high)
 | |
|             return m_high.clz();
 | |
|         else
 | |
|             return sizeof(T) * 8 + m_low.clz();
 | |
|     }
 | |
|     constexpr size_t ctz() const requires(IsSame<T, u64>)
 | |
|     {
 | |
|         if (m_low)
 | |
|             return count_trailing_zeroes(m_low);
 | |
|         else
 | |
|             return sizeof(T) * 8 + count_trailing_zeroes(m_high);
 | |
|     }
 | |
|     constexpr size_t ctz() const requires(!IsSame<T, u64>)
 | |
|     {
 | |
|         if (m_low)
 | |
|             return m_low.ctz();
 | |
|         else
 | |
|             return sizeof(T) * 8 + m_high.ctz();
 | |
|     }
 | |
|     constexpr size_t popcnt() const requires(IsSame<T, u64>)
 | |
|     {
 | |
|         return __builtin_popcntll(m_low) + __builtin_popcntll(m_high);
 | |
|     }
 | |
|     constexpr size_t popcnt() const requires(!IsSame<T, u64>)
 | |
|     {
 | |
|         return m_low.popcnt() + m_high.popcnt();
 | |
|     }
 | |
| 
 | |
|     // Comparison Operations
 | |
|     constexpr bool operator!() const
 | |
|     {
 | |
|         return !m_low && !m_high;
 | |
|     }
 | |
|     constexpr explicit operator bool() const
 | |
|     {
 | |
|         return m_low || m_high;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator==(const U& other) const
 | |
|     {
 | |
|         return !m_high && m_low == other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator!=(const U& other) const
 | |
|     {
 | |
|         return m_high || m_low != other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator>(const U& other) const
 | |
|     {
 | |
|         return m_high || m_low > other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator<(const U& other) const
 | |
|     {
 | |
|         return !m_high && m_low < other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator>=(const U& other) const
 | |
|     {
 | |
|         return *this == other || *this > other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr bool operator<=(const U& other) const
 | |
|     {
 | |
|         return *this == other || *this < other;
 | |
|     }
 | |
| 
 | |
|     constexpr bool operator==(const R& other) const
 | |
|     {
 | |
|         return m_low == other.low() && m_high == other.high();
 | |
|     }
 | |
|     constexpr bool operator!=(const R& other) const
 | |
|     {
 | |
|         return m_low != other.low() || m_high != other.high();
 | |
|     }
 | |
|     constexpr bool operator>(const R& other) const
 | |
|     {
 | |
|         return m_high > other.high()
 | |
|             || (m_high == other.high() && m_low > other.low());
 | |
|     }
 | |
|     constexpr bool operator<(const R& other) const
 | |
|     {
 | |
|         return m_high < other.high()
 | |
|             || (m_high == other.high() && m_low < other.low());
 | |
|     }
 | |
|     constexpr bool operator>=(const R& other) const
 | |
|     {
 | |
|         return *this == other || *this > other;
 | |
|     }
 | |
|     constexpr bool operator<=(const R& other) const
 | |
|     {
 | |
|         return *this == other || *this < other;
 | |
|     }
 | |
| 
 | |
|     // Bitwise operations
 | |
|     constexpr R operator~() const
 | |
|     {
 | |
|         return { ~m_low, ~m_high };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr U operator&(const U& other) const
 | |
|     {
 | |
|         return static_cast<const U>(m_low) & other;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R operator|(const U& other) const
 | |
|     {
 | |
|         return { m_low | other, m_high };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R operator^(const U& other) const
 | |
|     {
 | |
|         return { m_low ^ other, m_high };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator<<(const U& shift) const
 | |
|     {
 | |
|         if (shift >= sizeof(R) * 8u)
 | |
|             return 0u;
 | |
|         if (shift >= sizeof(T) * 8u)
 | |
|             return R { 0u, m_low << (shift - sizeof(T) * 8u) };
 | |
|         if (!shift)
 | |
|             return *this;
 | |
| 
 | |
|         T overflow = m_low >> (sizeof(T) * 8u - shift);
 | |
|         return R { m_low << shift, (m_high << shift) | overflow };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator>>(const U& shift) const
 | |
|     {
 | |
|         if (shift >= sizeof(R) * 8u)
 | |
|             return 0u;
 | |
|         if (shift >= sizeof(T) * 8u)
 | |
|             return m_high >> (shift - sizeof(T) * 8u);
 | |
|         if (!shift)
 | |
|             return *this;
 | |
| 
 | |
|         T underflow = m_high << (sizeof(T) * 8u - shift);
 | |
|         return R { (m_low >> shift) | underflow, m_high >> shift };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R rol(const U& shift) const
 | |
|     {
 | |
|         return (*this >> sizeof(T) * 8u - shift) | (*this << shift);
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R ror(const U& shift) const
 | |
|     {
 | |
|         return (*this << sizeof(T) * 8u - shift) | (*this >> shift);
 | |
|     }
 | |
| 
 | |
|     constexpr R operator&(const R& other) const
 | |
|     {
 | |
|         return { m_low & other.low(), m_high & other.high() };
 | |
|     }
 | |
|     constexpr R operator|(const R& other) const
 | |
|     {
 | |
|         return { m_low | other.low(), m_high | other.high() };
 | |
|     }
 | |
|     constexpr R operator^(const R& other) const
 | |
|     {
 | |
|         return { m_low ^ other.low(), m_high ^ other.high() };
 | |
|     }
 | |
| 
 | |
|     // Bitwise assignment
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R& operator&=(const U& other)
 | |
|     {
 | |
|         m_high = 0u;
 | |
|         m_low &= other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R& operator|=(const U& other)
 | |
|     {
 | |
|         m_low |= other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R& operator^=(const U& other)
 | |
|     {
 | |
|         m_low ^= other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator>>=(const U& other)
 | |
|     {
 | |
|         *this = *this >> other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator<<=(const U& other)
 | |
|     {
 | |
|         *this = *this << other;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     constexpr R& operator&=(const R& other)
 | |
|     {
 | |
|         m_high &= other.high();
 | |
|         m_low &= other.low();
 | |
|         return *this;
 | |
|     }
 | |
|     constexpr R& operator|=(const R& other)
 | |
|     {
 | |
|         m_high |= other.high();
 | |
|         m_low |= other.low();
 | |
|         return *this;
 | |
|     }
 | |
|     constexpr R& operator^=(const R& other)
 | |
|     {
 | |
|         m_high ^= other.high();
 | |
|         m_low ^= other.low();
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     static constexpr size_t my_size()
 | |
|     {
 | |
|         return sizeof(R);
 | |
|     }
 | |
| 
 | |
|     // Arithmetic
 | |
| 
 | |
|     // implies size of less than u64, so passing references isn't useful
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U) && IsSame<T, u64>) constexpr R addc(const U other, bool& carry) const
 | |
|     {
 | |
|         bool low_carry = Checked<T>::addition_would_overflow(m_low, other);
 | |
|         low_carry |= Checked<T>::addition_would_overflow(m_low, carry);
 | |
|         bool high_carry = Checked<T>::addition_would_overflow(m_high, low_carry);
 | |
| 
 | |
|         T lower = m_low + other + carry;
 | |
|         T higher = m_high + low_carry;
 | |
| 
 | |
|         carry = high_carry;
 | |
| 
 | |
|         return {
 | |
|             lower,
 | |
|             higher
 | |
|         };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(my_size() > sizeof(U) && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
 | |
|     {
 | |
|         T lower = m_low.addc(other, carry);
 | |
|         T higher = m_high.addc(0u, carry);
 | |
| 
 | |
|         return {
 | |
|             lower,
 | |
|             higher
 | |
|         };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(IsSame<R, U>&& IsSame<T, u64>) constexpr R addc(const U& other, bool& carry) const
 | |
|     {
 | |
|         bool low_carry = Checked<T>::addition_would_overflow(m_low, other.low());
 | |
|         bool high_carry = Checked<T>::addition_would_overflow(m_high, other.high());
 | |
| 
 | |
|         T lower = m_low + other.low();
 | |
|         T higher = m_high + other.high();
 | |
|         low_carry |= Checked<T>::addition_would_overflow(lower, carry);
 | |
|         high_carry |= Checked<T>::addition_would_overflow(higher, low_carry);
 | |
| 
 | |
|         lower += carry;
 | |
|         higher += low_carry;
 | |
|         carry = high_carry;
 | |
| 
 | |
|         return {
 | |
|             lower,
 | |
|             higher
 | |
|         };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(IsSame<R, U> && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
 | |
|     {
 | |
|         T lower = m_low.addc(other.low(), carry);
 | |
|         T higher = m_high.addc(other.high(), carry);
 | |
| 
 | |
|         return {
 | |
|             lower,
 | |
|             higher
 | |
|         };
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(my_size() < sizeof(U)) constexpr U addc(const U& other, bool& carry) const
 | |
|     {
 | |
|         return other.addc(*this, carry);
 | |
|     }
 | |
| 
 | |
|     // FIXME: subc for sizeof(T) < sizeof(U)
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(T) >= sizeof(U)) constexpr R subc(const U& other, bool& carry) const
 | |
|     {
 | |
|         bool low_carry = (!m_low && carry) || (m_low - carry) < other;
 | |
|         bool high_carry = !m_high && low_carry;
 | |
| 
 | |
|         T lower = m_low - other - carry;
 | |
|         T higher = m_high - low_carry;
 | |
|         carry = high_carry;
 | |
| 
 | |
|         return { lower, higher };
 | |
|     }
 | |
|     constexpr R subc(const R& other, bool& carry) const
 | |
|     {
 | |
|         bool low_carry = (!m_low && carry) || (m_low - carry) < other.low();
 | |
|         bool high_carry = (!m_high && low_carry) || (m_high - low_carry) < other.high();
 | |
| 
 | |
|         T lower = m_low - other.low() - carry;
 | |
|         T higher = m_high - other.high() - low_carry;
 | |
|         carry = high_carry;
 | |
| 
 | |
|         return { lower, higher };
 | |
|     }
 | |
| 
 | |
|     constexpr R operator+(const bool& other) const
 | |
|     {
 | |
|         bool carry = false; // unused
 | |
|         return addc((u8)other, carry);
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator+(const U& other) const
 | |
|     {
 | |
|         bool carry = false; // unused
 | |
|         return addc(other, carry);
 | |
|     }
 | |
| 
 | |
|     constexpr R operator-(const bool& other) const
 | |
|     {
 | |
|         bool carry = false; // unused
 | |
|         return subc((u8)other, carry);
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator-(const U& other) const
 | |
|     {
 | |
|         bool carry = false; // unused
 | |
|         return subc(other, carry);
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator+=(const U& other)
 | |
|     {
 | |
|         *this = *this + other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator-=(const U& other)
 | |
|     {
 | |
|         *this = *this - other;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     constexpr R operator++()
 | |
|     {
 | |
|         // x++
 | |
|         auto old = *this;
 | |
|         *this += 1;
 | |
|         return old;
 | |
|     }
 | |
|     constexpr R& operator++(int)
 | |
|     {
 | |
|         // ++x
 | |
|         *this += 1;
 | |
|         return *this;
 | |
|     }
 | |
|     constexpr R operator--()
 | |
|     {
 | |
|         // x--
 | |
|         auto old = *this;
 | |
|         *this -= 1;
 | |
|         return old;
 | |
|     }
 | |
|     constexpr R& operator--(int)
 | |
|     {
 | |
|         // --x
 | |
|         *this -= 1;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     // FIXME: no restraints on this
 | |
|     template<Unsigned U>
 | |
|     requires(my_size() >= sizeof(U)) constexpr R div_mod(const U& divisor, U& remainder) const
 | |
|     {
 | |
|         // FIXME: Is there a better way to raise a division by 0?
 | |
|         //        Maybe as a compiletime warning?
 | |
| #pragma GCC diagnostic push
 | |
| #pragma GCC diagnostic ignored "-Wdiv-by-zero"
 | |
|         if (!divisor) {
 | |
|             volatile int x = 1;
 | |
|             volatile int y = 0;
 | |
|             [[maybe_unused]] volatile int z = x / y;
 | |
|         }
 | |
| #pragma GCC diagnostic pop
 | |
| 
 | |
|         // fastpaths
 | |
|         if (*this < divisor) {
 | |
|             remainder = static_cast<U>(*this);
 | |
|             return 0u;
 | |
|         }
 | |
|         if (*this == divisor) {
 | |
|             remainder = 0u;
 | |
|             return 1u;
 | |
|         }
 | |
|         if (divisor == 1u) {
 | |
|             remainder = 0u;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         remainder = 0u;
 | |
|         R quotient = 0u;
 | |
| 
 | |
|         for (ssize_t i = sizeof(R) * 8 - clz() - 1; i >= 0; --i) {
 | |
|             remainder <<= 1u;
 | |
|             remainder |= (*this >> (size_t)i) & 1u;
 | |
|             if (remainder >= divisor) {
 | |
|                 remainder -= divisor;
 | |
|                 quotient |= R { 1u } << (size_t)i;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return quotient;
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator*(U other) const
 | |
|     {
 | |
|         R res = 0u;
 | |
|         R that = *this;
 | |
|         for (; other != 0u; other >>= 1u) {
 | |
|             if (other & 1u)
 | |
|                 res += that;
 | |
|             that <<= 1u;
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr R operator/(const U& other) const
 | |
|     {
 | |
|         U mod { 0u }; // unused
 | |
|         return div_mod(other, mod);
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr U operator%(const U& other) const
 | |
|     {
 | |
|         R res { 0u };
 | |
|         div_mod(other, res);
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator*=(const U& other)
 | |
|     {
 | |
|         *this = *this * other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator/=(const U& other)
 | |
|     {
 | |
|         *this = *this / other;
 | |
|         return *this;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     constexpr R& operator%=(const U& other)
 | |
|     {
 | |
|         *this = *this % other;
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     constexpr R sqrt() const
 | |
|     {
 | |
|         // Bitwise method: https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations
 | |
|         // the bitwise method seems to be way faster then Newtons:
 | |
|         // https://quick-bench.com/q/eXZwW1DVhZxLE0llumeCXkfOK3Q
 | |
|         if (*this == 1u)
 | |
|             return 1u;
 | |
| 
 | |
|         ssize_t shift = (sizeof(R) * 8 - clz()) & ~1ULL;
 | |
|         // should be equivalent to:
 | |
|         // long shift = 2;
 | |
|         // while ((val >> shift) != 0)
 | |
|         //   shift += 2;
 | |
| 
 | |
|         R res = 0u;
 | |
|         while (shift >= 0) {
 | |
|             res = res << 1u;
 | |
|             R large_cand = (res | 1u);
 | |
|             if (*this >> (size_t)shift >= large_cand * large_cand)
 | |
|                 res = large_cand;
 | |
|             shift -= 2;
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     constexpr R pow(u64 exp)
 | |
|     {
 | |
|         // Montgomery's Ladder Technique
 | |
|         // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
 | |
|         R x1 = *this;
 | |
|         R x2 = *this * *this;
 | |
|         u64 exp_copy = exp;
 | |
|         for (ssize_t i = sizeof(u64) * 8 - count_leading_zeroes(exp) - 2; i >= 0; --i) {
 | |
|             if (exp_copy & 1u) {
 | |
|                 x2 *= x1;
 | |
|                 x1 *= x1;
 | |
|             } else {
 | |
|                 x1 *= x2;
 | |
|                 x2 *= x2;
 | |
|             }
 | |
|             exp_copy >>= 1u;
 | |
|         }
 | |
|         return x1;
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(U) > sizeof(u64)) constexpr R pow(U exp)
 | |
|     {
 | |
|         // Montgomery's Ladder Technique
 | |
|         // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
 | |
|         R x1 = *this;
 | |
|         R x2 = *this * *this;
 | |
|         U exp_copy = exp;
 | |
|         for (ssize_t i = sizeof(U) * 8 - exp().clz() - 2; i >= 0; --i) {
 | |
|             if (exp_copy & 1u) {
 | |
|                 x2 *= x1;
 | |
|                 x1 *= x1;
 | |
|             } else {
 | |
|                 x1 *= x2;
 | |
|                 x2 *= x2;
 | |
|             }
 | |
|             exp_copy >>= 1u;
 | |
|         }
 | |
|         return x1;
 | |
|     }
 | |
| 
 | |
|     template<Unsigned U>
 | |
|     constexpr U pow_mod(u64 exp, U mod)
 | |
|     {
 | |
|         // Left to right binary method:
 | |
|         // https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
 | |
|         // FIXME: this is not sidechanel proof
 | |
|         if (!mod)
 | |
|             return 0u;
 | |
| 
 | |
|         U res = 1;
 | |
|         u64 exp_copy = exp;
 | |
|         for (size_t i = sizeof(u64) - count_leading_zeroes(exp) - 1u; i < exp; ++i) {
 | |
|             res *= res;
 | |
|             res %= mod;
 | |
|             if (exp_copy & 1u) {
 | |
|                 res = (*this * res) % mod;
 | |
|             }
 | |
|             exp_copy >>= 1u;
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
|     template<Unsigned ExpT, Unsigned U>
 | |
|     requires(sizeof(ExpT) > sizeof(u64)) constexpr U pow_mod(ExpT exp, U mod)
 | |
|     {
 | |
|         // Left to right binary method:
 | |
|         // https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
 | |
|         // FIXME: this is not side channel proof
 | |
|         if (!mod)
 | |
|             return 0u;
 | |
| 
 | |
|         U res = 1;
 | |
|         ExpT exp_copy = exp;
 | |
|         for (size_t i = sizeof(ExpT) - exp.clz() - 1u; i < exp; ++i) {
 | |
|             res *= res;
 | |
|             res %= mod;
 | |
|             if (exp_copy & 1u) {
 | |
|                 res = (*this * res) % mod;
 | |
|             }
 | |
|             exp_copy >>= 1u;
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     constexpr size_t log2()
 | |
|     {
 | |
|         // FIXME: proper rounding
 | |
|         return sizeof(R) - clz();
 | |
|     }
 | |
|     constexpr size_t logn(u64 base)
 | |
|     {
 | |
|         // FIXME: proper rounding
 | |
|         return log2() / (sizeof(u64) - count_leading_zeroes(base));
 | |
|     }
 | |
|     template<Unsigned U>
 | |
|     requires(sizeof(U) > sizeof(u64)) constexpr size_t logn(U base)
 | |
|     {
 | |
|         // FIXME: proper rounding
 | |
|         return log2() / base.log2();
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     T m_low;
 | |
|     T m_high;
 | |
| };
 | |
| 
 | |
| // reverse operators
 | |
| template<Unsigned U, Unsigned T>
 | |
| requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<(const U a, const UFixedBigInt<T>& b) { return b >= a; }
 | |
| template<Unsigned U, Unsigned T>
 | |
| requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>(const U a, const UFixedBigInt<T>& b) { return b <= a; }
 | |
| template<Unsigned U, Unsigned T>
 | |
| requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<=(const U a, const UFixedBigInt<T>& b) { return b > a; }
 | |
| template<Unsigned U, Unsigned T>
 | |
| requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>=(const U a, const UFixedBigInt<T>& b) { return b < a; }
 | |
| 
 | |
| template<Unsigned T>
 | |
| struct Formatter<UFixedBigInt<T>> : StandardFormatter {
 | |
|     Formatter() = default;
 | |
|     explicit Formatter(StandardFormatter formatter)
 | |
|         : StandardFormatter(formatter)
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     ErrorOr<void> format(FormatBuilder& builder, UFixedBigInt<T> value)
 | |
|     {
 | |
|         if (m_precision.has_value())
 | |
|             VERIFY_NOT_REACHED();
 | |
| 
 | |
|         if (m_mode == Mode::Pointer) {
 | |
|             // these are way to big for a pointer
 | |
|             VERIFY_NOT_REACHED();
 | |
|         }
 | |
|         if (m_mode == Mode::Default)
 | |
|             m_mode = Mode::Hexadecimal;
 | |
| 
 | |
|         if (!value.high()) {
 | |
|             Formatter<T> formatter { *this };
 | |
|             return formatter.format(builder, value.low());
 | |
|         }
 | |
| 
 | |
|         u8 base = 0;
 | |
|         if (m_mode == Mode::Binary) {
 | |
|             base = 2;
 | |
|         } else if (m_mode == Mode::BinaryUppercase) {
 | |
|             base = 2;
 | |
|         } else if (m_mode == Mode::Octal) {
 | |
|             TODO();
 | |
|         } else if (m_mode == Mode::Decimal) {
 | |
|             TODO();
 | |
|         } else if (m_mode == Mode::Hexadecimal) {
 | |
|             base = 16;
 | |
|         } else if (m_mode == Mode::HexadecimalUppercase) {
 | |
|             base = 16;
 | |
|         } else {
 | |
|             VERIFY_NOT_REACHED();
 | |
|         }
 | |
|         ssize_t width = m_width.value_or(0);
 | |
|         ssize_t lower_length = ceil_div(sizeof(T) * 8, (ssize_t)base);
 | |
|         Formatter<T> formatter { *this };
 | |
|         formatter.m_width = max(width - lower_length, (ssize_t)0);
 | |
|         TRY(formatter.format(builder, value.high()));
 | |
|         TRY(builder.put_literal("'"sv));
 | |
|         formatter.m_zero_pad = true;
 | |
|         formatter.m_alternative_form = false;
 | |
|         formatter.m_width = lower_length;
 | |
|         TRY(formatter.format(builder, value.low()));
 | |
|         return {};
 | |
|     }
 | |
| };
 | |
| }
 | |
| 
 | |
| // Nit: Doing these as custom classes might be faster, especially when writing
 | |
| //      then in SSE, but this would cause a lot of Code duplication and due to
 | |
| //      the nature of constexprs and the intelligence of the compiler they might
 | |
| //      be using SSE/MMX either way
 | |
| 
 | |
| // these sizes should suffice for most usecases
 | |
| using u128 = AK::UFixedBigInt<u64>;
 | |
| using u256 = AK::UFixedBigInt<u128>;
 | |
| using u512 = AK::UFixedBigInt<u256>;
 | |
| using u1024 = AK::UFixedBigInt<u512>;
 | |
| using u2048 = AK::UFixedBigInt<u1024>;
 | |
| using u4096 = AK::UFixedBigInt<u2048>;
 |