mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 02:37:36 +00:00
AK+LibX86: Generalize u128/256 to AK::UFixedBigInt
Doing these as custom classes might be faster, especially when writing them 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
This commit is contained in:
parent
9270fb1e69
commit
5ffe23e4f3
11 changed files with 905 additions and 665 deletions
771
AK/UFixedBigInt.h
Normal file
771
AK/UFixedBigInt.h
Normal file
|
@ -0,0 +1,771 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 __builtin_clzll(m_high);
|
||||
else
|
||||
return sizeof(T) * 8 + __builtin_clzll(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 __builtin_ctzll(m_low);
|
||||
else
|
||||
return sizeof(T) * 8 + __builtin_ctzll(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 T& other) const
|
||||
{
|
||||
return !m_high && m_low == other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator!=(const T& other) const
|
||||
{
|
||||
return m_high || m_low != other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>(const T& other) const
|
||||
{
|
||||
return m_high || m_low > other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<(const T& other) const
|
||||
{
|
||||
return !m_high && m_low < other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>=(const T& other) const
|
||||
{
|
||||
return *this == other || *this > other;
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<=(const T& 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;
|
||||
}
|
||||
|
||||
// Arithmetics
|
||||
|
||||
// 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(sizeof(R) > 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(sizeof(R) < 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(sizeof(R) >= 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 - __builtin_clzll(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) - __builtin_clzll(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: propper rounding
|
||||
return sizeof(R) - clz();
|
||||
}
|
||||
constexpr size_t logn(u64 base)
|
||||
{
|
||||
// FIXME: propper rounding
|
||||
return log2() / (sizeof(u64) - __builtin_clzll(base));
|
||||
}
|
||||
template<Unsigned U>
|
||||
requires(sizeof(U) > sizeof(u64)) constexpr size_t logn(U base)
|
||||
{
|
||||
// FIXME: propper 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)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
formatter.format(builder, value.high());
|
||||
builder.put_literal("'"sv);
|
||||
formatter.m_zero_pad = true;
|
||||
formatter.m_alternative_form = false;
|
||||
formatter.m_width = lower_length;
|
||||
formatter.format(builder, value.low());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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>;
|
Loading…
Add table
Add a link
Reference in a new issue