1040 lines
26 KiB
C
1040 lines
26 KiB
C
|
#ifndef DQN_U128_H
|
||
|
#define DQN_U128_H
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// NOTE: Dqn_U128
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// Provides some helper funtions ontop of intx that currently use heap
|
||
|
// allocation but don't actually need heap allocation.
|
||
|
//
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// NOTE: Configuration
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// #define DQN_U128_IMPLEMENTATION
|
||
|
// Define this in one and only one C++ file to enable the implementation
|
||
|
// code of the header file.
|
||
|
|
||
|
// intx: extended precision integer library.
|
||
|
// Copyright 2019-2020 Pawel Bylica.
|
||
|
// Licensed under the Apache License, Version 2.0.
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cassert>
|
||
|
#include <climits>
|
||
|
#include <cstdint>
|
||
|
#include <limits>
|
||
|
#include <stdexcept>
|
||
|
#include <string>
|
||
|
#include <tuple>
|
||
|
#include <type_traits>
|
||
|
|
||
|
#ifndef __has_builtin
|
||
|
#define __has_builtin(NAME) 0
|
||
|
#endif
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#include <intrin.h>
|
||
|
#endif
|
||
|
|
||
|
#if !defined(__has_builtin)
|
||
|
#define __has_builtin(NAME) 0
|
||
|
#endif
|
||
|
|
||
|
#if !defined(__has_feature)
|
||
|
#define __has_feature(NAME) 0
|
||
|
#endif
|
||
|
|
||
|
#if !defined(NDEBUG)
|
||
|
#define INTX_UNREACHABLE() assert(false)
|
||
|
#elif __has_builtin(__builtin_unreachable)
|
||
|
#define INTX_UNREACHABLE() __builtin_unreachable()
|
||
|
#elif defined(_MSC_VER)
|
||
|
#define INTX_UNREACHABLE() __assume(0)
|
||
|
#else
|
||
|
#define INTX_UNREACHABLE() (void)0
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if __has_builtin(__builtin_expect)
|
||
|
#define INTX_UNLIKELY(EXPR) __builtin_expect(bool{EXPR}, false)
|
||
|
#else
|
||
|
#define INTX_UNLIKELY(EXPR) (bool{EXPR})
|
||
|
#endif
|
||
|
|
||
|
#if !defined(NDEBUG)
|
||
|
#define INTX_REQUIRE assert
|
||
|
#else
|
||
|
#define INTX_REQUIRE(X) (X) ? (void)0 : INTX_UNREACHABLE()
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// Detect compiler support for 128-bit integer __int128
|
||
|
#if defined(__SIZEOF_INT128__)
|
||
|
#define INTX_HAS_BUILTIN_INT128 1
|
||
|
#else
|
||
|
#define INTX_HAS_BUILTIN_INT128 0
|
||
|
#endif
|
||
|
|
||
|
|
||
|
namespace intx
|
||
|
{
|
||
|
#if INTX_HAS_BUILTIN_INT128
|
||
|
#pragma GCC diagnostic push
|
||
|
#pragma GCC diagnostic ignored "-Wpedantic" // Usage of __int128 triggers a pedantic warning.
|
||
|
|
||
|
/// Alias for the compiler supported unsigned __int128 type.
|
||
|
using builtin_uint128 = unsigned __int128;
|
||
|
|
||
|
#pragma GCC diagnostic pop
|
||
|
#endif
|
||
|
|
||
|
template <unsigned N>
|
||
|
struct uint;
|
||
|
|
||
|
/// The 128-bit unsigned integer.
|
||
|
///
|
||
|
/// This type is defined as a specialization of uint<> to easier integration with full intx package,
|
||
|
/// however, uint128 may be used independently.
|
||
|
template <>
|
||
|
struct uint<128>
|
||
|
{
|
||
|
using word_type = uint64_t;
|
||
|
static constexpr auto word_num_bits = sizeof(word_type) * 8;
|
||
|
static constexpr unsigned num_bits = 128;
|
||
|
static constexpr auto num_words = num_bits / word_num_bits;
|
||
|
|
||
|
private:
|
||
|
uint64_t words_[2]{};
|
||
|
|
||
|
public:
|
||
|
constexpr uint() noexcept = default;
|
||
|
|
||
|
constexpr uint(uint64_t low, uint64_t high) noexcept : words_{low, high} {}
|
||
|
|
||
|
template <typename T,
|
||
|
typename = typename std::enable_if_t<std::is_convertible<T, uint64_t>::value>>
|
||
|
constexpr uint(T x) noexcept : words_{static_cast<uint64_t>(x), 0} // NOLINT
|
||
|
{}
|
||
|
|
||
|
#if INTX_HAS_BUILTIN_INT128
|
||
|
constexpr uint(builtin_uint128 x) noexcept // NOLINT
|
||
|
: words_{uint64_t(x), uint64_t(x >> 64)}
|
||
|
{}
|
||
|
|
||
|
constexpr explicit operator builtin_uint128() const noexcept
|
||
|
{
|
||
|
return (builtin_uint128{words_[1]} << 64) | words_[0];
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
constexpr uint64_t& operator[](size_t i) noexcept { return words_[i]; }
|
||
|
constexpr const uint64_t& operator[](size_t i) const noexcept { return words_[i]; }
|
||
|
|
||
|
constexpr explicit operator bool() const noexcept { return (words_[0] | words_[1]) != 0; }
|
||
|
|
||
|
/// Explicit converting operator for all builtin integral types.
|
||
|
template <typename Int, typename = typename std::enable_if<std::is_integral<Int>::value>::type>
|
||
|
constexpr explicit operator Int() const noexcept
|
||
|
{
|
||
|
return static_cast<Int>(words_[0]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
using uint128 = uint<128>;
|
||
|
|
||
|
|
||
|
inline constexpr bool is_constant_evaluated() noexcept
|
||
|
{
|
||
|
#if __has_builtin(__builtin_is_constant_evaluated) || (defined(_MSC_VER) && _MSC_VER >= 1925)
|
||
|
return __builtin_is_constant_evaluated();
|
||
|
#else
|
||
|
return true;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Contains result of add/sub/etc with a carry flag.
|
||
|
template <typename T>
|
||
|
struct result_with_carry
|
||
|
{
|
||
|
T value;
|
||
|
bool carry;
|
||
|
|
||
|
/// Conversion to tuple of references, to allow usage with std::tie().
|
||
|
constexpr operator std::tuple<T&, bool&>() noexcept { return {value, carry}; }
|
||
|
};
|
||
|
|
||
|
|
||
|
/// Linear arithmetic operators.
|
||
|
/// @{
|
||
|
|
||
|
inline constexpr result_with_carry<uint64_t> add_with_carry(
|
||
|
uint64_t x, uint64_t y, bool carry = false) noexcept
|
||
|
{
|
||
|
const auto s = x + y;
|
||
|
const auto carry1 = s < x;
|
||
|
const auto t = s + carry;
|
||
|
const auto carry2 = t < s;
|
||
|
return {t, carry1 || carry2};
|
||
|
}
|
||
|
|
||
|
template <unsigned N>
|
||
|
inline constexpr result_with_carry<uint<N>> add_with_carry(
|
||
|
const uint<N>& x, const uint<N>& y, bool carry = false) noexcept
|
||
|
{
|
||
|
uint<N> s;
|
||
|
bool k = carry;
|
||
|
for (size_t i = 0; i < uint<N>::num_words; ++i)
|
||
|
{
|
||
|
s[i] = x[i] + y[i];
|
||
|
const auto k1 = s[i] < x[i];
|
||
|
s[i] += k;
|
||
|
k = (s[i] < uint64_t{k}) || k1;
|
||
|
}
|
||
|
return {s, k};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator+(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return add_with_carry(x, y).value;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator+(uint128 x) noexcept
|
||
|
{
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
inline constexpr result_with_carry<uint64_t> sub_with_carry(
|
||
|
uint64_t x, uint64_t y, bool carry = false) noexcept
|
||
|
{
|
||
|
const auto d = x - y;
|
||
|
const auto carry1 = x < y;
|
||
|
const auto e = d - carry;
|
||
|
const auto carry2 = d < uint64_t{carry};
|
||
|
return {e, carry1 || carry2};
|
||
|
}
|
||
|
|
||
|
/// Performs subtraction of two unsigned numbers and returns the difference
|
||
|
/// and the carry bit (aka borrow, overflow).
|
||
|
template <unsigned N>
|
||
|
inline constexpr result_with_carry<uint<N>> sub_with_carry(
|
||
|
const uint<N>& x, const uint<N>& y, bool carry = false) noexcept
|
||
|
{
|
||
|
uint<N> z;
|
||
|
bool k = carry;
|
||
|
for (size_t i = 0; i < uint<N>::num_words; ++i)
|
||
|
{
|
||
|
z[i] = x[i] - y[i];
|
||
|
const auto k1 = x[i] < y[i];
|
||
|
const auto k2 = z[i] < uint64_t{k};
|
||
|
z[i] -= k;
|
||
|
k = k1 || k2;
|
||
|
}
|
||
|
return {z, k};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator-(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return sub_with_carry(x, y).value;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator-(uint128 x) noexcept
|
||
|
{
|
||
|
// Implementing as subtraction is better than ~x + 1.
|
||
|
// Clang9: Perfect.
|
||
|
// GCC8: Does something weird.
|
||
|
return 0 - x;
|
||
|
}
|
||
|
|
||
|
inline uint128& operator++(uint128& x) noexcept
|
||
|
{
|
||
|
return x = x + 1;
|
||
|
}
|
||
|
|
||
|
inline uint128& operator--(uint128& x) noexcept
|
||
|
{
|
||
|
return x = x - 1;
|
||
|
}
|
||
|
|
||
|
inline uint128 operator++(uint128& x, int) noexcept
|
||
|
{
|
||
|
auto ret = x;
|
||
|
++x;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
inline uint128 operator--(uint128& x, int) noexcept
|
||
|
{
|
||
|
auto ret = x;
|
||
|
--x;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/// Optimized addition.
|
||
|
///
|
||
|
/// This keeps the multiprecision addition until CodeGen so the pattern is not
|
||
|
/// broken during other optimizations.
|
||
|
inline constexpr uint128 fast_add(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
#if INTX_HAS_BUILTIN_INT128
|
||
|
return builtin_uint128{x} + builtin_uint128{y};
|
||
|
#else
|
||
|
return x + y; // Fallback to generic addition.
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/// Comparison operators.
|
||
|
///
|
||
|
/// In all implementations bitwise operators are used instead of logical ones
|
||
|
/// to avoid branching.
|
||
|
///
|
||
|
/// @{
|
||
|
|
||
|
inline constexpr bool operator==(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
// Clang7: generates perfect xor based code,
|
||
|
// much better than __int128 where it uses vector instructions.
|
||
|
// GCC8: generates a bit worse cmp based code
|
||
|
// although it generates the xor based one for __int128.
|
||
|
return (x[0] == y[0]) & (x[1] == y[1]);
|
||
|
}
|
||
|
|
||
|
inline constexpr bool operator!=(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
// Analogous to ==, but == not used directly, because that confuses GCC 8-9.
|
||
|
return (x[0] != y[0]) | (x[1] != y[1]);
|
||
|
}
|
||
|
|
||
|
inline constexpr bool operator<(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
// OPT: This should be implemented by checking the borrow of x - y,
|
||
|
// but compilers (GCC8, Clang7)
|
||
|
// have problem with properly optimizing subtraction.
|
||
|
#if INTX_HAS_BUILTIN_INT128
|
||
|
return builtin_uint128{x} < builtin_uint128{y};
|
||
|
#else
|
||
|
return (x[1] < y[1]) | ((x[1] == y[1]) & (x[0] < y[0]));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline constexpr bool operator<=(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return !(y < x);
|
||
|
}
|
||
|
|
||
|
inline constexpr bool operator>(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return y < x;
|
||
|
}
|
||
|
|
||
|
inline constexpr bool operator>=(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return !(x < y);
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/// Bitwise operators.
|
||
|
/// @{
|
||
|
|
||
|
inline constexpr uint128 operator~(uint128 x) noexcept
|
||
|
{
|
||
|
return {~x[0], ~x[1]};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator|(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
// Clang7: perfect.
|
||
|
// GCC8: stupidly uses a vector instruction in all bitwise operators.
|
||
|
return {x[0] | y[0], x[1] | y[1]};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator&(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return {x[0] & y[0], x[1] & y[1]};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator^(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return {x[0] ^ y[0], x[1] ^ y[1]};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator<<(uint128 x, uint64_t shift) noexcept
|
||
|
{
|
||
|
return (shift < 64) ?
|
||
|
// Find the part moved from lo to hi.
|
||
|
// For shift == 0 right shift by (64 - shift) is invalid so
|
||
|
// split it into 2 shifts by 1 and (63 - shift).
|
||
|
uint128{x[0] << shift, (x[1] << shift) | ((x[0] >> 1) >> (63 - shift))} :
|
||
|
|
||
|
// Guarantee "defined" behavior for shifts larger than 128.
|
||
|
(shift < 128) ? uint128{0, x[0] << (shift - 64)} : 0;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator<<(uint128 x, uint128 shift) noexcept
|
||
|
{
|
||
|
if (INTX_UNLIKELY(shift[1] != 0))
|
||
|
return 0;
|
||
|
|
||
|
return x << shift[0];
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator>>(uint128 x, uint64_t shift) noexcept
|
||
|
{
|
||
|
return (shift < 64) ?
|
||
|
// Find the part moved from lo to hi.
|
||
|
// For shift == 0 left shift by (64 - shift) is invalid so
|
||
|
// split it into 2 shifts by 1 and (63 - shift).
|
||
|
uint128{(x[0] >> shift) | ((x[1] << 1) << (63 - shift)), x[1] >> shift} :
|
||
|
|
||
|
// Guarantee "defined" behavior for shifts larger than 128.
|
||
|
(shift < 128) ? uint128{x[1] >> (shift - 64)} : 0;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator>>(uint128 x, uint128 shift) noexcept
|
||
|
{
|
||
|
if (INTX_UNLIKELY(shift[1] != 0))
|
||
|
return 0;
|
||
|
|
||
|
return x >> static_cast<uint64_t>(shift);
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/// Multiplication
|
||
|
/// @{
|
||
|
|
||
|
/// Full unsigned multiplication 64 x 64 -> 128.
|
||
|
inline constexpr uint128 umul(uint64_t x, uint64_t y) noexcept
|
||
|
{
|
||
|
#if INTX_HAS_BUILTIN_INT128
|
||
|
return builtin_uint128{x} * builtin_uint128{y};
|
||
|
#elif defined(_MSC_VER) && _MSC_VER >= 1925
|
||
|
if (!is_constant_evaluated())
|
||
|
{
|
||
|
unsigned __int64 hi = 0;
|
||
|
const auto lo = _umul128(x, y, &hi);
|
||
|
return {lo, hi};
|
||
|
}
|
||
|
// For constexpr fallback to portable variant.
|
||
|
#endif
|
||
|
|
||
|
// Portable full unsigned multiplication 64 x 64 -> 128.
|
||
|
uint64_t xl = x & 0xffffffff;
|
||
|
uint64_t xh = x >> 32;
|
||
|
uint64_t yl = y & 0xffffffff;
|
||
|
uint64_t yh = y >> 32;
|
||
|
|
||
|
uint64_t t0 = xl * yl;
|
||
|
uint64_t t1 = xh * yl;
|
||
|
uint64_t t2 = xl * yh;
|
||
|
uint64_t t3 = xh * yh;
|
||
|
|
||
|
uint64_t u1 = t1 + (t0 >> 32);
|
||
|
uint64_t u2 = t2 + (u1 & 0xffffffff);
|
||
|
|
||
|
uint64_t lo = (u2 << 32) | (t0 & 0xffffffff);
|
||
|
uint64_t hi = t3 + (u2 >> 32) + (u1 >> 32);
|
||
|
return {lo, hi};
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator*(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
auto p = umul(x[0], y[0]);
|
||
|
p[1] += (x[0] * y[1]) + (x[1] * y[0]);
|
||
|
return {p[0], p[1]};
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/// Assignment operators.
|
||
|
/// @{
|
||
|
|
||
|
inline constexpr uint128& operator+=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x + y;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator-=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x - y;
|
||
|
}
|
||
|
|
||
|
inline uint128& operator*=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x * y;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator|=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x | y;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator&=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x & y;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator^=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x ^ y;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator<<=(uint128& x, uint64_t shift) noexcept
|
||
|
{
|
||
|
return x = x << shift;
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128& operator>>=(uint128& x, uint64_t shift) noexcept
|
||
|
{
|
||
|
return x = x >> shift;
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
inline constexpr unsigned clz_generic(uint32_t x) noexcept
|
||
|
{
|
||
|
unsigned n = 32;
|
||
|
for (int i = 4; i >= 0; --i)
|
||
|
{
|
||
|
const auto s = unsigned{1} << i;
|
||
|
const auto hi = x >> s;
|
||
|
if (hi != 0)
|
||
|
{
|
||
|
n -= s;
|
||
|
x = hi;
|
||
|
}
|
||
|
}
|
||
|
return n - x;
|
||
|
}
|
||
|
|
||
|
inline constexpr unsigned clz_generic(uint64_t x) noexcept
|
||
|
{
|
||
|
unsigned n = 64;
|
||
|
for (int i = 5; i >= 0; --i)
|
||
|
{
|
||
|
const auto s = unsigned{1} << i;
|
||
|
const auto hi = x >> s;
|
||
|
if (hi != 0)
|
||
|
{
|
||
|
n -= s;
|
||
|
x = hi;
|
||
|
}
|
||
|
}
|
||
|
return n - static_cast<unsigned>(x);
|
||
|
}
|
||
|
|
||
|
inline constexpr unsigned clz(uint32_t x) noexcept
|
||
|
{
|
||
|
#ifdef _MSC_VER
|
||
|
return clz_generic(x);
|
||
|
#else
|
||
|
return x != 0 ? unsigned(__builtin_clz(x)) : 32;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline constexpr unsigned clz(uint64_t x) noexcept
|
||
|
{
|
||
|
#ifdef _MSC_VER
|
||
|
return clz_generic(x);
|
||
|
#else
|
||
|
return x != 0 ? unsigned(__builtin_clzll(x)) : 64;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline constexpr unsigned clz(uint128 x) noexcept
|
||
|
{
|
||
|
// In this order `h == 0` we get less instructions than in case of `h != 0`.
|
||
|
return x[1] == 0 ? clz(x[0]) + 64 : clz(x[1]);
|
||
|
}
|
||
|
|
||
|
|
||
|
inline constexpr uint64_t bswap(uint64_t x) noexcept
|
||
|
{
|
||
|
#if __has_builtin(__builtin_bswap64)
|
||
|
return __builtin_bswap64(x);
|
||
|
#else
|
||
|
#ifdef _MSC_VER
|
||
|
if (!is_constant_evaluated())
|
||
|
return _byteswap_uint64(x);
|
||
|
#endif
|
||
|
const auto a = ((x << 8) & 0xFF00FF00FF00FF00) | ((x >> 8) & 0x00FF00FF00FF00FF);
|
||
|
const auto b = ((a << 16) & 0xFFFF0000FFFF0000) | ((a >> 16) & 0x0000FFFF0000FFFF);
|
||
|
return (b << 32) | (b >> 32);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 bswap(uint128 x) noexcept
|
||
|
{
|
||
|
return {bswap(x[1]), bswap(x[0])};
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Division.
|
||
|
/// @{
|
||
|
|
||
|
template <typename QuotT, typename RemT = QuotT>
|
||
|
struct div_result
|
||
|
{
|
||
|
QuotT quot;
|
||
|
RemT rem;
|
||
|
|
||
|
/// Conversion to tuple of references, to allow usage with std::tie().
|
||
|
constexpr operator std::tuple<QuotT&, RemT&>() noexcept { return {quot, rem}; }
|
||
|
};
|
||
|
|
||
|
namespace internal
|
||
|
{
|
||
|
inline constexpr uint16_t reciprocal_table_item(uint8_t d9) noexcept
|
||
|
{
|
||
|
return uint16_t(0x7fd00 / (0x100 | d9));
|
||
|
}
|
||
|
|
||
|
#define REPEAT4(x) \
|
||
|
reciprocal_table_item((x) + 0), reciprocal_table_item((x) + 1), \
|
||
|
reciprocal_table_item((x) + 2), reciprocal_table_item((x) + 3)
|
||
|
|
||
|
#define REPEAT32(x) \
|
||
|
REPEAT4((x) + 4 * 0), REPEAT4((x) + 4 * 1), REPEAT4((x) + 4 * 2), REPEAT4((x) + 4 * 3), \
|
||
|
REPEAT4((x) + 4 * 4), REPEAT4((x) + 4 * 5), REPEAT4((x) + 4 * 6), REPEAT4((x) + 4 * 7)
|
||
|
|
||
|
#define REPEAT256() \
|
||
|
REPEAT32(32 * 0), REPEAT32(32 * 1), REPEAT32(32 * 2), REPEAT32(32 * 3), REPEAT32(32 * 4), \
|
||
|
REPEAT32(32 * 5), REPEAT32(32 * 6), REPEAT32(32 * 7)
|
||
|
|
||
|
/// Reciprocal lookup table.
|
||
|
constexpr uint16_t reciprocal_table[] = {REPEAT256()};
|
||
|
|
||
|
#undef REPEAT4
|
||
|
#undef REPEAT32
|
||
|
#undef REPEAT256
|
||
|
} // namespace internal
|
||
|
|
||
|
/// Computes the reciprocal (2^128 - 1) / d - 2^64 for normalized d.
|
||
|
///
|
||
|
/// Based on Algorithm 2 from "Improved division by invariant integers".
|
||
|
inline uint64_t reciprocal_2by1(uint64_t d) noexcept
|
||
|
{
|
||
|
INTX_REQUIRE(d & 0x8000000000000000); // Must be normalized.
|
||
|
|
||
|
const uint64_t d9 = d >> 55;
|
||
|
const uint32_t v0 = internal::reciprocal_table[d9 - 256];
|
||
|
|
||
|
const uint64_t d40 = (d >> 24) + 1;
|
||
|
const uint64_t v1 = (v0 << 11) - uint32_t(v0 * v0 * d40 >> 40) - 1;
|
||
|
|
||
|
const uint64_t v2 = (v1 << 13) + (v1 * (0x1000000000000000 - v1 * d40) >> 47);
|
||
|
|
||
|
const uint64_t d0 = d & 1;
|
||
|
const uint64_t d63 = (d >> 1) + d0; // ceil(d/2)
|
||
|
const uint64_t e = ((v2 >> 1) & (0 - d0)) - v2 * d63;
|
||
|
const uint64_t v3 = (umul(v2, e)[1] >> 1) + (v2 << 31);
|
||
|
|
||
|
const uint64_t v4 = v3 - (umul(v3, d) + d)[1] - d;
|
||
|
return v4;
|
||
|
}
|
||
|
|
||
|
inline uint64_t reciprocal_3by2(uint128 d) noexcept
|
||
|
{
|
||
|
auto v = reciprocal_2by1(d[1]);
|
||
|
auto p = d[1] * v;
|
||
|
p += d[0];
|
||
|
if (p < d[0])
|
||
|
{
|
||
|
--v;
|
||
|
if (p >= d[1])
|
||
|
{
|
||
|
--v;
|
||
|
p -= d[1];
|
||
|
}
|
||
|
p -= d[1];
|
||
|
}
|
||
|
|
||
|
const auto t = umul(v, d[0]);
|
||
|
|
||
|
p += t[1];
|
||
|
if (p < t[1])
|
||
|
{
|
||
|
--v;
|
||
|
if (p >= d[1])
|
||
|
{
|
||
|
if (p > d[1] || t[0] >= d[0])
|
||
|
--v;
|
||
|
}
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
inline div_result<uint64_t> udivrem_2by1(uint128 u, uint64_t d, uint64_t v) noexcept
|
||
|
{
|
||
|
auto q = umul(v, u[1]);
|
||
|
q = fast_add(q, u);
|
||
|
|
||
|
++q[1];
|
||
|
|
||
|
auto r = u[0] - q[1] * d;
|
||
|
|
||
|
if (r > q[0])
|
||
|
{
|
||
|
--q[1];
|
||
|
r += d;
|
||
|
}
|
||
|
|
||
|
if (r >= d)
|
||
|
{
|
||
|
++q[1];
|
||
|
r -= d;
|
||
|
}
|
||
|
|
||
|
return {q[1], r};
|
||
|
}
|
||
|
|
||
|
inline div_result<uint64_t, uint128> udivrem_3by2(
|
||
|
uint64_t u2, uint64_t u1, uint64_t u0, uint128 d, uint64_t v) noexcept
|
||
|
{
|
||
|
auto q = umul(v, u2);
|
||
|
q = fast_add(q, {u1, u2});
|
||
|
|
||
|
auto r1 = u1 - q[1] * d[1];
|
||
|
|
||
|
auto t = umul(d[0], q[1]);
|
||
|
|
||
|
auto r = uint128{u0, r1} - t - d;
|
||
|
r1 = r[1];
|
||
|
|
||
|
++q[1];
|
||
|
|
||
|
if (r1 >= q[0])
|
||
|
{
|
||
|
--q[1];
|
||
|
r += d;
|
||
|
}
|
||
|
|
||
|
if (r >= d)
|
||
|
{
|
||
|
++q[1];
|
||
|
r -= d;
|
||
|
}
|
||
|
|
||
|
return {q[1], r};
|
||
|
}
|
||
|
|
||
|
inline div_result<uint128> udivrem(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
if (y[1] == 0)
|
||
|
{
|
||
|
INTX_REQUIRE(y[0] != 0); // Division by 0.
|
||
|
|
||
|
const auto lsh = clz(y[0]);
|
||
|
const auto rsh = (64 - lsh) % 64;
|
||
|
const auto rsh_mask = uint64_t{lsh == 0} - 1;
|
||
|
|
||
|
const auto yn = y[0] << lsh;
|
||
|
const auto xn_lo = x[0] << lsh;
|
||
|
const auto xn_hi = (x[1] << lsh) | ((x[0] >> rsh) & rsh_mask);
|
||
|
const auto xn_ex = (x[1] >> rsh) & rsh_mask;
|
||
|
|
||
|
const auto v = reciprocal_2by1(yn);
|
||
|
const auto res1 = udivrem_2by1({xn_hi, xn_ex}, yn, v);
|
||
|
const auto res2 = udivrem_2by1({xn_lo, res1.rem}, yn, v);
|
||
|
return {{res2.quot, res1.quot}, res2.rem >> lsh};
|
||
|
}
|
||
|
|
||
|
if (y[1] > x[1])
|
||
|
return {0, x};
|
||
|
|
||
|
const auto lsh = clz(y[1]);
|
||
|
if (lsh == 0)
|
||
|
{
|
||
|
const auto q = unsigned{y[1] < x[1]} | unsigned{y[0] <= x[0]};
|
||
|
return {q, x - (q ? y : 0)};
|
||
|
}
|
||
|
|
||
|
const auto rsh = 64 - lsh;
|
||
|
|
||
|
const auto yn_lo = y[0] << lsh;
|
||
|
const auto yn_hi = (y[1] << lsh) | (y[0] >> rsh);
|
||
|
const auto xn_lo = x[0] << lsh;
|
||
|
const auto xn_hi = (x[1] << lsh) | (x[0] >> rsh);
|
||
|
const auto xn_ex = x[1] >> rsh;
|
||
|
|
||
|
const auto v = reciprocal_3by2({yn_lo, yn_hi});
|
||
|
const auto res = udivrem_3by2(xn_ex, xn_hi, xn_lo, {yn_lo, yn_hi}, v);
|
||
|
|
||
|
return {res.quot, res.rem >> lsh};
|
||
|
}
|
||
|
|
||
|
inline div_result<uint128> sdivrem(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
constexpr auto sign_mask = uint128{1} << 127;
|
||
|
const auto x_is_neg = (x & sign_mask) != 0;
|
||
|
const auto y_is_neg = (y & sign_mask) != 0;
|
||
|
|
||
|
const auto x_abs = x_is_neg ? -x : x;
|
||
|
const auto y_abs = y_is_neg ? -y : y;
|
||
|
|
||
|
const auto q_is_neg = x_is_neg ^ y_is_neg;
|
||
|
|
||
|
const auto res = udivrem(x_abs, y_abs);
|
||
|
|
||
|
return {q_is_neg ? -res.quot : res.quot, x_is_neg ? -res.rem : res.rem};
|
||
|
}
|
||
|
|
||
|
inline uint128 operator/(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return udivrem(x, y).quot;
|
||
|
}
|
||
|
|
||
|
inline uint128 operator%(uint128 x, uint128 y) noexcept
|
||
|
{
|
||
|
return udivrem(x, y).rem;
|
||
|
}
|
||
|
|
||
|
inline uint128& operator/=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x / y;
|
||
|
}
|
||
|
|
||
|
inline uint128& operator%=(uint128& x, uint128 y) noexcept
|
||
|
{
|
||
|
return x = x % y;
|
||
|
}
|
||
|
|
||
|
/// @}
|
||
|
|
||
|
} // namespace intx
|
||
|
|
||
|
|
||
|
namespace std
|
||
|
{
|
||
|
template <unsigned N>
|
||
|
struct numeric_limits<intx::uint<N>>
|
||
|
{
|
||
|
using type = intx::uint<N>;
|
||
|
|
||
|
static constexpr bool is_specialized = true;
|
||
|
static constexpr bool is_integer = true;
|
||
|
static constexpr bool is_signed = false;
|
||
|
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 = false;
|
||
|
static constexpr float_denorm_style has_denorm = denorm_absent;
|
||
|
static constexpr bool has_denorm_loss = false;
|
||
|
static constexpr float_round_style round_style = 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 = CHAR_BIT * sizeof(type);
|
||
|
static constexpr int digits10 = int(0.3010299956639812 * digits);
|
||
|
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 = std::numeric_limits<unsigned>::traps;
|
||
|
static constexpr bool tinyness_before = false;
|
||
|
|
||
|
static constexpr type min() noexcept { return 0; }
|
||
|
static constexpr type lowest() noexcept { return min(); }
|
||
|
static constexpr type max() noexcept { return ~type{0}; }
|
||
|
static constexpr type epsilon() noexcept { return 0; }
|
||
|
static constexpr type round_error() noexcept { return 0; }
|
||
|
static constexpr type infinity() noexcept { return 0; }
|
||
|
static constexpr type quiet_NaN() noexcept { return 0; }
|
||
|
static constexpr type signaling_NaN() noexcept { return 0; }
|
||
|
static constexpr type denorm_min() noexcept { return 0; }
|
||
|
};
|
||
|
} // namespace std
|
||
|
|
||
|
namespace intx
|
||
|
{
|
||
|
template <typename T>
|
||
|
[[noreturn]] inline void throw_(const char* what)
|
||
|
{
|
||
|
#if __cpp_exceptions
|
||
|
throw T{what};
|
||
|
#else
|
||
|
std::fputs(what, stderr);
|
||
|
std::abort();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline constexpr int from_dec_digit(char c)
|
||
|
{
|
||
|
if (c < '0' || c > '9')
|
||
|
throw_<std::invalid_argument>("invalid digit");
|
||
|
return c - '0';
|
||
|
}
|
||
|
|
||
|
inline constexpr int from_hex_digit(char c)
|
||
|
{
|
||
|
if (c >= 'a' && c <= 'f')
|
||
|
return c - ('a' - 10);
|
||
|
if (c >= 'A' && c <= 'F')
|
||
|
return c - ('A' - 10);
|
||
|
return from_dec_digit(c);
|
||
|
}
|
||
|
|
||
|
template <typename Int>
|
||
|
inline constexpr Int from_string(const char* str)
|
||
|
{
|
||
|
auto s = str;
|
||
|
auto x = Int{};
|
||
|
int num_digits = 0;
|
||
|
|
||
|
if (s[0] == '0' && s[1] == 'x')
|
||
|
{
|
||
|
s += 2;
|
||
|
while (const auto c = *s++)
|
||
|
{
|
||
|
if (++num_digits > int{sizeof(x) * 2})
|
||
|
throw_<std::out_of_range>(str);
|
||
|
x = (x << uint64_t{4}) | from_hex_digit(c);
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
while (const auto c = *s++)
|
||
|
{
|
||
|
if (num_digits++ > std::numeric_limits<Int>::digits10)
|
||
|
throw_<std::out_of_range>(str);
|
||
|
|
||
|
const auto d = from_dec_digit(c);
|
||
|
x = x * Int{10} + d;
|
||
|
if (x < d)
|
||
|
throw_<std::out_of_range>(str);
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
template <typename Int>
|
||
|
inline constexpr Int from_string(const std::string& s)
|
||
|
{
|
||
|
return from_string<Int>(s.c_str());
|
||
|
}
|
||
|
|
||
|
inline constexpr uint128 operator""_u128(const char* s)
|
||
|
{
|
||
|
return from_string<uint128>(s);
|
||
|
}
|
||
|
|
||
|
template <unsigned N>
|
||
|
inline std::string to_string(uint<N> x, int base = 10)
|
||
|
{
|
||
|
if (base < 2 || base > 36)
|
||
|
throw_<std::invalid_argument>("invalid base");
|
||
|
|
||
|
if (x == 0)
|
||
|
return "0";
|
||
|
|
||
|
auto s = std::string{};
|
||
|
while (x != 0)
|
||
|
{
|
||
|
// TODO: Use constexpr udivrem_1?
|
||
|
const auto res = udivrem(x, uint<N>{base});
|
||
|
const auto d = int(res.rem);
|
||
|
const auto c = d < 10 ? '0' + d : 'a' + d - 10;
|
||
|
s.push_back(char(c));
|
||
|
x = res.quot;
|
||
|
}
|
||
|
std::reverse(s.begin(), s.end());
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
template <unsigned N>
|
||
|
inline std::string hex(uint<N> x)
|
||
|
{
|
||
|
return to_string(x, 16);
|
||
|
}
|
||
|
} // namespace intx
|
||
|
|
||
|
struct Dqn_U128String
|
||
|
{
|
||
|
// NOTE: Max value 340,282,366,920,938,463,463,374,607,431,768,211,455
|
||
|
char str[51 + 1];
|
||
|
int size;
|
||
|
};
|
||
|
|
||
|
Dqn_U128String Dqn_U128_String (intx::uint128 x, bool separate, char separate_ch = ',');
|
||
|
intx::uint128 Dqn_U128_FromString(const char *str, int size);
|
||
|
#endif // DQN_U128_H
|
||
|
|
||
|
#if defined(DQN_U128_IMPLEMENTATION)
|
||
|
Dqn_U128String Dqn_U128_String(intx::uint128 x, bool separate, char separate_ch)
|
||
|
{
|
||
|
Dqn_U128String val = {};
|
||
|
if (x == 0)
|
||
|
{
|
||
|
val.str[val.size++] = '0';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int insert_count = 0;
|
||
|
while (x != 0)
|
||
|
{
|
||
|
// TODO: Use constexpr udivrem_1?
|
||
|
const auto res = udivrem(x, intx::uint128{10 /*base*/});
|
||
|
const auto d = int(res.rem);
|
||
|
const auto c = d < 10 ? '0' + d : 'a' + d - 10;
|
||
|
val.str[val.size++] = char(c);
|
||
|
x = res.quot;
|
||
|
|
||
|
if (separate && x != 0)
|
||
|
{
|
||
|
insert_count++;
|
||
|
if (insert_count % 3 == 0)
|
||
|
val.str[val.size++] = separate_ch;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Dqn_U128String result = {};
|
||
|
for (int i = val.size - 1; i >= 0; i--)
|
||
|
result.str[result.size++] = val.str[i];
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
intx::uint128 Dqn_U128_FromString(const char *str, int size)
|
||
|
{
|
||
|
// TODO(dqn): Don't let this throw
|
||
|
auto x = intx::uint128{};
|
||
|
int num_digits = 0;
|
||
|
|
||
|
if (size > 2 && (str[0] == '0' && str[1] == 'x'))
|
||
|
{
|
||
|
for (int str_index = 2; str_index < size; str_index++)
|
||
|
{
|
||
|
char c = str[str_index];
|
||
|
if (++num_digits > int{sizeof(x) * 2})
|
||
|
intx::throw_<std::out_of_range>(str);
|
||
|
x = (x << uint64_t{4}) | intx::from_hex_digit(c);
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
for (int str_index = 0; str_index < size; str_index++)
|
||
|
{
|
||
|
char c = str[str_index];
|
||
|
if (num_digits++ > std::numeric_limits<intx::uint128>::digits10)
|
||
|
intx::throw_<std::out_of_range>(str);
|
||
|
|
||
|
const auto d = intx::from_dec_digit(c);
|
||
|
x = x * intx::uint128{10} + d;
|
||
|
if (x < d)
|
||
|
intx::throw_<std::out_of_range>(str);
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
#endif // DQN_U128_IMPLEMENTATION
|