sql_utils/common/multiprecision_int.h (1,284 lines of code) (raw):
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_COMMON_MULTIPRECISION_INT_H_
#define THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_COMMON_MULTIPRECISION_INT_H_
#include <math.h>
#include <stddef.h>
#include <string.h>
#include <sys/types.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <iterator>
#include <limits>
#include <string>
#include <type_traits>
#include <vector>
#include "sql_utils/base/logging.h"
#include "sql_utils/common/multiprecision_int_impl.h"
#include "absl/base/attributes.h"
#include "absl/base/optimization.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "sql_utils/base/endian.h"
namespace bigquery_ml_utils {
// Don't use this class directly. Use VarUintRef, ConstVarUintRef, VarIntRef,
// or ConstVarIntRef instead. Note that the methods of theses classes are not
// optimized for performance. Prefer FixedInt or FixedUint.
template <bool is_signed, typename UnsignedWord>
class VarIntBase {
public:
template <typename Containder>
explicit VarIntBase(Containder& src) : number_(src.data(), src.size()) {}
absl::Span<UnsignedWord> number() const { return number_; }
UnsignedWord* data() const { return number_.data(); }
size_t size() const { return number_.size(); }
void SerializeToBytes(std::string* bytes) const;
bool DeserializeFromBytes(absl::string_view bytes);
void AppendToString(std::string* result) const;
std::string ToString() const {
std::string result;
AppendToString(&result);
return result;
}
protected:
absl::Span<UnsignedWord> number_;
};
template <int kNumBitsPerWord>
using ConstVarUintRef =
VarIntBase<false, const multiprecision_int_impl::Uint<kNumBitsPerWord>>;
template <int kNumBitsPerWord>
using ConstVarIntRef =
VarIntBase<true, const multiprecision_int_impl::Uint<kNumBitsPerWord>>;
template <int kNumBitsPerWord>
class VarUintRef
: public VarIntBase<false, multiprecision_int_impl::Uint<kNumBitsPerWord>> {
public:
using VarIntBase<false,
multiprecision_int_impl::Uint<kNumBitsPerWord>>::VarIntBase;
using UnsignedWord = multiprecision_int_impl::Uint<kNumBitsPerWord>;
using UnsignedHalfWord =
typename multiprecision_int_impl::IntTraits<kNumBitsPerWord>::HalfUint;
// Defined only for kNumBitsPerWord == 64.
// Computes *this /= divisor and returns the remainder. This number must be
// non-empty.
template <UnsignedWord divisor>
UnsignedWord DivMod(std::integral_constant<UnsignedWord, divisor> x);
template <UnsignedHalfWord divisor>
UnsignedHalfWord DivMod(std::integral_constant<UnsignedHalfWord, divisor> x);
uint32_t DivMod(uint32_t divisor);
// Defined only for kNumBitsPerWord == 64. Scales down by up to 10**19.
// Do not pass a negative or > 19 scale_down_digits. Returns divisor.
uint64_t ScaleDown(int scale_down_digits, uint64_t& remainder);
};
namespace multiprecision_int_impl {
// Array of function pointers where function i divides the VarUintRef by 10**i.
// Defined for range [1, 19].
extern uint64_t (*kVarUintDivModPow10[])(VarUintRef<64>&, uint64_t&);
} // namespace multiprecision_int_impl
template <int kNumBitsPerWord>
class VarIntRef
: public VarIntBase<true, multiprecision_int_impl::Uint<kNumBitsPerWord>> {
public:
using VarIntBase<true,
multiprecision_int_impl::Uint<kNumBitsPerWord>>::VarIntBase;
void Negate();
};
template <int kNumBitsPerWord, int kNumWords>
class FixedInt;
template <int kNumBitsPerWord, int kNumWords>
class FixedUint final {
public:
using Word = multiprecision_int_impl::Uint<kNumBitsPerWord>;
using UnsignedWord = Word;
static constexpr int kNumBits = kNumBitsPerWord * kNumWords;
// The max bits an integer can have to be casted to double when all the bits
// in the integer are set. Also limited by the kNumBitsPerWord.
static constexpr int kMaxBitsToDouble = 992;
static constexpr FixedUint min() {
return FixedUint(multiprecision_int_impl::LeftPad<Word, kNumWords>(0));
}
static constexpr FixedUint max() {
return FixedUint(
multiprecision_int_impl::LeftPad<Word, kNumWords>(~Word{0}));
}
constexpr FixedUint()
: number_(multiprecision_int_impl::RightPad<Word, kNumWords>(0)) {}
constexpr explicit FixedUint(uint32_t x)
: number_(multiprecision_int_impl::RightPad<Word, kNumWords>(0, x)) {}
constexpr explicit FixedUint(uint64_t x)
: number_(multiprecision_int_impl::UintToArray<64, kNumBitsPerWord,
kNumWords>(x, 0)) {
static_assert(kNumBits >= 64, "Size too small");
}
constexpr explicit FixedUint(unsigned __int128 x)
: number_(multiprecision_int_impl::UintToArray<128, kNumBitsPerWord,
kNumWords>(x, Word{0})) {
static_assert(kNumBits >= 128, "Size too small");
}
FixedUint(uint64_t hi, unsigned __int128 low) {
static_assert(kNumBits >= 192, "Size too small");
number_.fill(0);
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(low,
number_.data());
multiprecision_int_impl::UintToArray<64, kNumBitsPerWord>(
hi, &number_[128 / kNumBitsPerWord]);
}
FixedUint(unsigned __int128 hi, unsigned __int128 low) {
static_assert(kNumBits >= 256, "Size too small");
number_.fill(0);
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(low,
number_.data());
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(
hi, &number_[128 / kNumBitsPerWord]);
}
// If k * n > kNumBits, then the k * n - kNumBits most significant bits are
// dropped.
template <int k, int n>
explicit FixedUint(const FixedUint<k, n>& src)
: number_(
multiprecision_int_impl::Convert<kNumBitsPerWord, kNumWords, k, n>(
src.number(), false)) {}
explicit constexpr FixedUint(
const std::array<Word, kNumWords>& little_endian_number)
: number_(little_endian_number) {}
explicit operator unsigned __int128() const {
return multiprecision_int_impl::ArrayToUint<128, kNumBitsPerWord>(
number_.data());
}
// Cast FixedUint<kNumBitsPerWord, kNumWords> into double. When there is a
// loss of significance during conversion, we will use the rounding rule that
// rounds half to even. It is the same rule being used in the built-in cast
// functions for integer to double numbers. For example, 0xfffffffffffff400
// (2^64 - 3 * 2^10) should be rounded down to 0xfffffffffffff000 (2^64 - 4 *
// 2^10) while 0xfffffffffffffc00 (2^64 - 2^10) should be rounded up to
// 0x10000000000000000 (2^64) since the values rounded to have even mantissas
// in double form while 0xfffffffffffff800 (2^64 - 2 * 2^10) has an odd
// mantissa.
explicit operator double() const;
// Shifts the number left by the given number of bits.
FixedUint& operator<<=(uint bits) {
if (ABSL_PREDICT_TRUE(bits != 0)) {
if (ABSL_PREDICT_TRUE(bits < kNumBitsPerWord)) {
multiprecision_int_impl::ShiftLeftFast(number_.data(), kNumWords, bits);
return *this;
}
multiprecision_int_impl::ShiftLeft(number_.data(), kNumWords, bits);
}
return *this;
}
// Shifts the number right by the given number of bits.
FixedUint& operator>>=(uint bits) {
if (ABSL_PREDICT_TRUE(bits != 0)) {
if (ABSL_PREDICT_TRUE(bits < kNumBitsPerWord)) {
multiprecision_int_impl::ShiftRightFast<Word>(number_.data(), kNumWords,
bits);
return *this;
}
multiprecision_int_impl::ShiftRight(Word{0}, number_.data(), kNumWords,
bits);
}
return *this;
}
// Returns true iff the result overflows.
bool AddOverflow(Word x) { return AddOverflow(FixedUint(x)); }
bool AddOverflow(const FixedUint& rh) {
return multiprecision_int_impl::Add<kNumWords>(number_, rh.number_) != 0;
}
FixedUint& operator+=(Word x) {
AddOverflow(x);
return *this;
}
FixedUint& operator+=(const FixedUint& rh) {
multiprecision_int_impl::Add<kNumWords>(number_, rh.number_);
return *this;
}
bool SubtractOverflow(Word x) {
uint8_t carry =
multiprecision_int_impl::SubtractWithBorrow(&number_[0], x, 0);
for (int i = 1; i < kNumWords; ++i) {
carry = multiprecision_int_impl::SubtractWithBorrow(&number_[i], Word{0},
carry);
}
return carry != 0;
}
bool SubtractOverflow(const FixedUint& rh) {
return multiprecision_int_impl::Subtract<kNumWords>(number_, rh.number_) !=
0;
}
FixedUint& operator-=(Word x) {
SubtractOverflow(x);
return *this;
}
FixedUint& operator-=(const FixedUint& rh) {
multiprecision_int_impl::Subtract<kNumWords>(number_, rh.number_);
return *this;
}
FixedUint& operator*=(Word x) {
multiprecision_int_impl::MulWord(number_.data(), kNumWords, x);
return *this;
}
FixedUint& operator*=(const FixedUint& rh) {
FixedUint res;
PartialMultiplyOverflow(rh, &res);
return *this = res;
}
bool MultiplyOverflow(Word x) {
return multiprecision_int_impl::MulWord(number_.data(), kNumWords, x) != 0;
}
bool MultiplyOverflow(const FixedUint& rh) {
FixedUint res;
bool overflow = PartialMultiplyOverflow(rh, &res) ||
NonZeroLength() + rh.NonZeroLength() > kNumWords + 1;
*this = res;
return overflow;
}
template <uint32_t divisor>
void DivMod(std::integral_constant<uint32_t, divisor> x, FixedUint* quotient,
uint32_t* remainder) const {
uint32_t r = multiprecision_int_impl::ShortDivModConstant<kNumWords>(
number_, x, quotient != nullptr ? "ient->number_ : nullptr);
if (remainder != nullptr) {
*remainder = r;
}
}
// Only valid for 64bit words.
template <uint64_t divisor>
void DivMod(std::integral_constant<uint64_t, divisor> x, FixedUint* quotient,
uint64_t* remainder) const {
uint64_t r;
if (quotient != nullptr) {
r = static_cast<uint64_t>(
multiprecision_int_impl::ShortDivModConstant<kNumWords, divisor,
/*need_quotient=*/true>(
number_, std::integral_constant<uint64_t, divisor>(),
"ient->number_));
} else {
r = static_cast<uint64_t>(
multiprecision_int_impl::ShortDivModConstant<kNumWords, divisor,
/*need_quotient=*/false>(
number_, std::integral_constant<uint64_t, divisor>(),
/*quotient=*/nullptr));
}
if (remainder != nullptr) {
*remainder = r;
}
}
void DivMod(Word x, FixedUint* quotient, Word* remainder) const {
Word r = multiprecision_int_impl::ShortDivMod<Word, kNumWords>(
number_, x, quotient != nullptr ? "ient->number_ : nullptr);
if (remainder != nullptr) {
*remainder = r;
}
}
// Computes *quotient = *this / divisor, and *remainder = *this % divisor.
// quotient and remainder can be null, this, or point to other instances.
// If quotient and remainder are the same and are not null, the instance will
// receive the remainder value.
void DivMod(const FixedUint& divisor, FixedUint* quotient,
FixedUint* remainder) const {
multiprecision_int_impl::DivMod<kNumWords>(
number_, divisor.number_,
quotient != nullptr ? "ient->number_ : nullptr,
remainder != nullptr ? &remainder->number_ : nullptr);
}
// The caller is responsible for ensuring that the divisor is not zero.
template <uint32_t divisor>
FixedUint& operator/=(std::integral_constant<uint32_t, divisor> x) {
multiprecision_int_impl::ShortDivModConstant<kNumWords>(number_, x,
&number_);
return *this;
}
// The caller is responsible for ensuring that the divisor is not zero.
template <uint64_t divisor>
FixedUint& operator/=(std::integral_constant<uint64_t, divisor> x) {
multiprecision_int_impl::ShortDivModConstant<kNumWords>(number_, x,
&number_);
return *this;
}
FixedUint& operator/=(const FixedUint& x) {
multiprecision_int_impl::DivMod<kNumWords>(number_, x.number_, &number_,
nullptr);
return *this;
}
FixedUint& operator/=(Word divisor) {
if (kNumBitsPerWord == 32) {
multiprecision_int_impl::ShortDivMod<Word, kNumWords>(number_, divisor,
&number_);
return *this;
}
FixedUint tmp;
tmp.number_[0] = divisor;
return *this /= tmp;
}
template <uint32_t divisor>
FixedUint& DivAndRoundAwayFromZero(
std::integral_constant<uint32_t, divisor> x) {
if (ABSL_PREDICT_TRUE(!AddOverflow(divisor >> 1))) {
return *this /= x;
}
*this -= x;
*this /= x;
return *this += Word{1};
}
FixedUint& DivAndRoundAwayFromZero(Word x) {
if (ABSL_PREDICT_TRUE(!AddOverflow(x >> 1))) {
return *this /= x;
}
*this -= x;
*this /= x;
return *this += Word{1};
}
FixedUint& DivAndRoundAwayFromZero(const FixedUint& x) {
FixedUint half_x = x;
half_x >>= 1;
uint8_t carry =
multiprecision_int_impl::Add<kNumWords>(number_, half_x.number_);
if (ABSL_PREDICT_TRUE(carry == 0)) {
return *this /= x;
}
*this -= x;
*this /= x;
return *this += Word{1};
}
template <uint32_t divisor>
FixedUint& operator%=(std::integral_constant<uint32_t, divisor> x) {
number_[0] = multiprecision_int_impl::ShortDivModConstant<kNumWords>(
number_, x, nullptr);
std::fill(number_.begin() + 1, number_.end(), 0);
return *this;
}
template <uint64_t divisor>
FixedUint& operator%=(std::integral_constant<uint64_t, divisor> x) {
number_[0] =
multiprecision_int_impl::ShortDivModConstant<kNumWords, divisor,
/*need_quotient=*/false>(
number_, x, /*quotient=*/nullptr);
std::fill(number_.begin() + 1, number_.end(), 0);
return *this;
}
FixedUint& operator%=(const FixedUint& x) {
DivMod(x, nullptr, this);
return *this;
}
FixedUint& operator%=(Word x) {
if (kNumBitsPerWord == 32) {
number_[0] = multiprecision_int_impl::ShortDivMod<Word, kNumWords>(
number_, x, nullptr);
std::fill(number_.begin() + 1, number_.end(), 0);
return *this;
}
FixedUint tmp;
tmp.number_[0] = x;
return *this %= tmp;
}
bool is_zero() const { return NonZeroLength() == 0; }
// Returns the number of words excluding leading zero words.
int NonZeroLength() const {
return multiprecision_int_impl::NonZeroLength<Word, kNumWords>(
number_.data());
}
// Returns the first set most significant bit index, 0 based. If this number
// is 0 then this function will return 0;
int FindMSBSetNonZero() const;
constexpr const std::array<Word, kNumWords>& number() const {
return number_;
}
// Serializes to minimum number of bytes needed to represent the number.
// The result is appended to *out.
void SerializeToBytes(std::string* out) const {
ConstVarUintRef<kNumBitsPerWord>(number_).SerializeToBytes(out);
}
// Deserializes the output of Serialize() from a FixedUint (not FixedInt) with
// the same template arguments. If the input is valid, false is returned and
// this instance is unchanged.
ABSL_MUST_USE_RESULT bool DeserializeFromBytes(absl::string_view bytes) {
return VarUintRef<kNumBitsPerWord>(number_).DeserializeFromBytes(bytes);
}
// Convert the FixedUint to a readable string form.
std::string ToString() const {
std::string result;
AppendToString(&result);
return result;
}
void AppendToString(std::string* result) const;
// Parse digit-only string representation of an unsigned decimal integer and
// write the number into the FixedUint. Returns true iff str is valid.
// If false is returned, the state of *this is undefined.
bool ParseFromStringStrict(absl::string_view str) {
return !str.empty() && ParseOrAppendDigits(str, false);
}
// Equivalent to ParseFromStringStrict(absl::StrCat(<all segments>)),
// except that no temporary string is created, and first_segment cannot be
// empty (extra_segments and its elements can be empty).
bool ParseFromStringSegments(
absl::string_view first_segment,
absl::Span<const absl::string_view> extra_segments) {
if (ABSL_PREDICT_FALSE(!ParseFromStringStrict(first_segment))) {
return false;
}
for (absl::string_view segment : extra_segments) {
if (ABSL_PREDICT_FALSE(!segment.empty() &&
!ParseOrAppendDigits(segment, true))) {
return false;
}
}
return true;
}
// Returns pow(10, exponent).
static const FixedUint& PowerOf10(uint exponent) {
static constexpr auto kPowersOf10 = GetPowersOf10();
SQL_DCHECK_LT(exponent, kPowersOf10.size());
return kPowersOf10[exponent];
}
// Equivalent to ToString().size(), but is much faster.
// Note, this method returns 1 when *this = 0.
uint CountDecimalDigits() const {
static constexpr auto kPowersOf10 = GetPowersOf10();
constexpr uint kStartingValue = 1; // returns 1 even when *this = 0
static constexpr auto kMSBInfos =
GetMSBInfoArray(kPowersOf10, FixedUint(Word{1}), kStartingValue);
int msb_set = FindMSBSetNonZero();
const MSBInfo& t = kMSBInfos[msb_set];
uint num_digits = t.min_num_digits;
if (t.power_of_10_in_bucket != nullptr &&
*this >= *t.power_of_10_in_bucket) {
++num_digits;
}
return num_digits;
}
template <typename H>
friend H AbslHashValue(H h,
const FixedUint<kNumBitsPerWord, kNumWords>& value) {
return H::combine(std::move(h), value.number_);
}
private:
friend class FixedInt<kNumBitsPerWord, kNumWords>;
// Computes *this * rh using only the products of the input words that fit
// into <result>, and returns whether these products result in an overflow.
// For example, in the case kNumWords = 2, then only
// number_[0] * rh.number_[0], number_[0] * rh.number_[1] and
// number_[1] * rh.number_[0] are considered.
bool PartialMultiplyOverflow(const FixedUint& rh, FixedUint* result) const;
// Either parse or append decimal digits. In append mode, for example,
// if *this = 123 and str = "456", then *this will become 123456.
// The caller must ensure str is not empty.
bool ParseOrAppendDigits(absl::string_view str, bool append);
static constexpr auto GetPowersOf10() {
// Convert number of bits to max number of decimal digits.
constexpr double kLog10_2 = 0.3010299956639812;
constexpr size_t kMaxDigits = static_cast<size_t>(kNumBits * kLog10_2) + 1;
return PowersAsc<kMaxDigits>(FixedUint(Word{1}), 10);
}
// PowersAsc<size>(v, multipler) returns an array of arrays
// representing {v, v * multiplier, ..., v * pow(multiplier, size - 1)}.
template <size_t size, typename... T>
static constexpr std::array<FixedUint, size> PowersAsc(
const FixedUint& last_value, Word multiplier, const T&... v) {
if constexpr (sizeof...(T) < size) {
const FixedUint new_value(multiprecision_int_impl::MulWord(
last_value.number(), multiplier, Word{0}));
return PowersAsc<size>(new_value, multiplier, v..., last_value);
} else {
return std::array<FixedUint, size>{v...};
}
}
// Decimal info of an MSB (most-significant-bit) index.
// For example, in an array of MSBInfo, the first element means
// the decimal info for the values whose MSB index is 0 (i.e., 0 and 1),
// the second element means the decimal info for the values whose MSB index is
// 1 (i.e., values 2 and 3), the third element is for the values whose MSB
// index is 2 (i.e., values 4, 5, 6, 7), and so on. This array is designed for
// efficient lookup of number of decimal digits by value. The number of
// decimal digits of the value is either t.min_num_digits or t.min_num_digits
// + 1 where t is the corresponding MSBInfo.
struct MSBInfo {
uint min_num_digits;
// If not null, it means the value range covers a power of 10. If a value in
// the range is greater than or equal to this power of 10, then it has
// min_num_digits + 1 decimal digits; otherwise it has min_num_digits
// decimal digits.
const FixedUint* power_of_10_in_bucket;
};
template <size_t max_num_digits, typename... T>
static constexpr std::array<MSBInfo, kNumBits> GetMSBInfoArray(
const std::array<FixedUint, max_num_digits>& powers_of_10,
const FixedUint& power_of_2, uint current_num_digits, T... v) {
if constexpr (sizeof...(T) < kNumBits) {
const FixedUint next_power_of_2(
multiprecision_int_impl::MulWord<Word>(power_of_2.number(), 2, 0));
const bool threshold_in_current_range =
current_num_digits < max_num_digits &&
multiprecision_int_impl::Less(
powers_of_10[current_num_digits].number(),
next_power_of_2.number());
const uint next_num_digits =
current_num_digits + threshold_in_current_range;
const MSBInfo current_elem = {current_num_digits,
threshold_in_current_range
? &powers_of_10[current_num_digits]
: nullptr};
return GetMSBInfoArray(powers_of_10, next_power_of_2, next_num_digits,
v..., current_elem);
} else {
return std::array<MSBInfo, kNumBits>{v...};
}
}
// The number is stored in the little-endian order with the least significant
// word being at the index 0.
std::array<Word, kNumWords> number_;
};
template <int kNumBitsPerWord, int kNumWords>
FixedUint<kNumBitsPerWord, kNumWords>::operator double() const {
static_assert(kNumBits <= kMaxBitsToDouble,
"Size too big to convert to double.");
static_assert(kNumBits > 0, "The number has less than one bit.");
// DOUBLE can have 53 bits in the significand, which is less than 64. We will
// keep 55 bits at most for rounding. We take at most 54 bits from the
// FixedUint, and decide the 55th by the trailing bits if needed. Keeping
// two more bits for rounding halves ties to the nearest even number.
uint64_t significand = 0;
int word_idx = NonZeroLength() - 1;
if (word_idx == -1) {
return 0.0;
}
int bit_idx = multiprecision_int_impl::FindMSBSetNonZero(number_[word_idx]);
int significant_bits = 0;
while (true) {
if (significant_bits + bit_idx >= 54) {
significand <<= (54 - significant_bits);
bit_idx += (significant_bits - 54);
significand |= (number_[word_idx] >> bit_idx);
// Set the 55th bit for rounding. Set it to 1 if any non-zero in trailing
// digits.
significand <<= 1;
int exp = word_idx * kNumBitsPerWord + bit_idx - 1;
Word remainder = number_[word_idx] & ~(~Word{0} << bit_idx);
while (remainder == 0) {
if (--word_idx < 0) {
return std::ldexp(significand, exp);
}
remainder = number_[word_idx];
}
return std::ldexp(significand | 1, exp);
}
significand = (significand << bit_idx) | number_[word_idx];
significant_bits += bit_idx;
bit_idx = kNumBitsPerWord;
if (--word_idx < 0) {
SQL_DCHECK_LT(significant_bits, 54);
return static_cast<double>(significand);
}
}
}
template <int kNumBitsPerWord, int kNumWords>
inline int FixedUint<kNumBitsPerWord, kNumWords>::FindMSBSetNonZero() const {
const int nzlen = NonZeroLength();
if (nzlen == 0) {
return 0;
}
return multiprecision_int_impl::FindMSBSetNonZero(number_[nzlen - 1]) +
(nzlen - 1) * kNumBitsPerWord;
}
template <int kNumBitsPerWord, int kNumWords>
inline bool FixedUint<kNumBitsPerWord, kNumWords>::PartialMultiplyOverflow(
const FixedUint& rh, FixedUint* result) const {
using DWord = multiprecision_int_impl::Uint<kNumBitsPerWord * 2>;
Word overflow_carry = 0;
for (int j = 0; j < kNumWords; ++j) {
Word carry = 0;
for (int i = 0; i < kNumWords - j; ++i) {
DWord tmp = static_cast<DWord>(number_[i]) * rh.number_[j] +
result->number_[i + j] + carry;
result->number_[i + j] = static_cast<Word>(tmp);
carry = static_cast<Word>(tmp >> kNumBitsPerWord);
}
overflow_carry |= carry;
}
return overflow_carry != 0;
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator==(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return lh.number() == rh.number();
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator!=(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return lh.number() != rh.number();
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator<(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return multiprecision_int_impl::Less<kNumBitsPerWord, kNumWords>(
lh.number().data(), rh.number().data());
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator>(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return rh < lh;
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator<=(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return !(rh < lh);
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator>=(const FixedUint<kNumBitsPerWord, kNumWords>& lh,
const FixedUint<kNumBitsPerWord, kNumWords>& rh) {
return !(lh < rh);
}
template <int kNumBitsPerWord, int kNumWords>
void FixedUint<kNumBitsPerWord, kNumWords>::AppendToString(
std::string* result) const {
static_assert(kNumBitsPerWord == 64 || kNumBitsPerWord == 32);
FixedUint<kNumBitsPerWord, kNumWords> quotient(*this);
using Uint = multiprecision_int_impl::Uint<kNumBitsPerWord>;
std::integral_constant<
Uint, multiprecision_int_impl::IntTraits<kNumBitsPerWord>::kMaxPowerOf10>
divisor;
// For 32bit words we will group into segments 10**9 and for 10**19 for
// 64bit words.
// The number of segments needed = ceil(num_bits / log(SegmentSize, 2))
// = ceil(num_bits / log(SegmentSize, 2)) <=
// ceil(num_bits / floor(log(SegmentSize, 2))).
constexpr int kFloorLog2MaxPowerOf10 = multiprecision_int_impl::IntTraits<
kNumBitsPerWord>::kFloorLog2MaxPowerOf10;
std::vector<Uint> segments(
(kNumBitsPerWord * kNumWords + kFloorLog2MaxPowerOf10 - 1) /
kFloorLog2MaxPowerOf10);
int num_segments = 0;
while (!quotient.is_zero()) {
quotient.DivMod(divisor, "ient, &segments[num_segments]);
++num_segments;
}
multiprecision_int_impl::AppendSegmentsToString(segments.data(), num_segments,
result);
}
template <int kNumBitsPerWord, int kNumWords>
bool FixedUint<kNumBitsPerWord, kNumWords>::ParseOrAppendDigits(
absl::string_view str, bool append) {
SQL_DCHECK(!str.empty());
Word radix =
multiprecision_int_impl::IntTraits<kNumBitsPerWord>::kMaxPowerOf10;
constexpr size_t kMaxDigitsPerSegment = multiprecision_int_impl::IntTraits<
kNumBitsPerWord>::kMaxWholeDecimalDigits;
size_t first_segment_length = (str.size() - 1) % kMaxDigitsPerSegment + 1;
const char* ptr = str.data();
const char* end = ptr + str.size();
Word segment_val;
// Handle the first segment of string
if (ABSL_PREDICT_FALSE(
!multiprecision_int_impl::ParseFromBase10UnsignedString(
str.substr(0, first_segment_length), &segment_val))) {
return false;
}
if (append) {
static constexpr std::array<Word, kMaxDigitsPerSegment> kPowersOf10 =
multiprecision_int_impl::PowersAsc<Word, 10, 10,
kMaxDigitsPerSegment>();
if (ABSL_PREDICT_FALSE(
MultiplyOverflow(kPowersOf10[first_segment_length - 1]) ||
AddOverflow(segment_val))) {
return false;
}
} else {
*this = FixedUint(segment_val);
}
for (ptr += first_segment_length; ptr < end; ptr += kMaxDigitsPerSegment) {
if (ABSL_PREDICT_FALSE(MultiplyOverflow(radix)) ||
ABSL_PREDICT_FALSE(
!multiprecision_int_impl::ParseFromBase10UnsignedString(
absl::string_view(ptr, kMaxDigitsPerSegment), &segment_val)) ||
ABSL_PREDICT_FALSE(AddOverflow(segment_val))) {
return false;
}
}
return true;
}
template <int kNumBitsPerWord, int kNumWords>
class FixedInt final {
public:
using Word = multiprecision_int_impl::Int<kNumBitsPerWord>;
using UnsignedWord = multiprecision_int_impl::Uint<kNumBitsPerWord>;
static constexpr int kNumBits = kNumBitsPerWord * kNumWords;
static constexpr int kMaxBitsToDouble = 992;
static constexpr FixedInt min() {
return FixedInt(multiprecision_int_impl::LeftPad<UnsignedWord, kNumWords>(
0, static_cast<UnsignedWord>(std::numeric_limits<Word>::min())));
}
static constexpr FixedInt max() {
constexpr UnsignedWord kMaxUnsigned =
std::numeric_limits<UnsignedWord>::max();
constexpr Word kMaxSigned = std::numeric_limits<Word>::max();
return FixedInt(multiprecision_int_impl::LeftPad<UnsignedWord, kNumWords>(
kMaxUnsigned, static_cast<UnsignedWord>(kMaxSigned)));
}
constexpr FixedInt() {}
constexpr explicit FixedInt(int32_t x)
: rep_(multiprecision_int_impl::RightPad<UnsignedWord, kNumWords>(
x >= 0 ? 0 : ~UnsignedWord{0},
static_cast<UnsignedWord>(static_cast<Word>(x)))) {}
constexpr explicit FixedInt(int64_t x)
: rep_(multiprecision_int_impl::UintToArray<64, kNumBitsPerWord,
kNumWords>(
x, x >= 0 ? 0 : ~UnsignedWord{0})) {
static_assert(kNumBits >= 64, "Size too small");
}
constexpr explicit FixedInt(__int128 x)
: rep_(multiprecision_int_impl::UintToArray<128, kNumBitsPerWord,
kNumWords>(
x, x >= 0 ? 0 : ~UnsignedWord{0})) {
static_assert(kNumBits >= 128, "Size too small");
}
FixedInt(int64_t hi, unsigned __int128 low) {
static_assert(kNumBits >= 192, "Size too small");
rep_.number_.fill(hi >= 0 ? 0 : ~UnsignedWord{0});
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(
low, rep_.number_.data());
multiprecision_int_impl::UintToArray<64, kNumBitsPerWord>(
static_cast<uint64_t>(hi), &rep_.number_[128 / kNumBitsPerWord]);
}
FixedInt(__int128 hi, unsigned __int128 low) {
static_assert(kNumBits >= 256, "Size too small");
rep_.number_.fill(hi >= 0 ? 0 : ~UnsignedWord{0});
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(
low, rep_.number_.data());
multiprecision_int_impl::UintToArray<128, kNumBitsPerWord>(
static_cast<unsigned __int128>(hi),
&rep_.number_[128 / kNumBitsPerWord]);
}
template <int k, int n>
explicit FixedInt(const FixedInt<k, n>& src)
: rep_(multiprecision_int_impl::Convert<kNumBitsPerWord, kNumWords, k, n>(
src.number(), src.is_negative())) {}
template <int k, int n>
explicit FixedInt(const FixedUint<k, n>& src) : rep_(src) {}
explicit constexpr FixedInt(
const std::array<UnsignedWord, kNumWords>& little_endian_number)
: rep_(little_endian_number) {}
explicit operator __int128() const {
return static_cast<unsigned __int128>(rep_);
}
explicit operator double() const {
static_assert(kNumBits <= kMaxBitsToDouble,
"Size too big to convert to double.");
static_assert(kNumBits > 0, "The number has less than one bit.");
double abs_result = static_cast<double>(abs());
return ABSL_PREDICT_FALSE(is_negative()) ? -abs_result : abs_result;
}
FixedInt& operator<<=(uint bits) {
rep_ <<= bits;
return *this;
}
FixedInt& operator>>=(uint bits) {
if (ABSL_PREDICT_TRUE(bits != 0)) {
if (ABSL_PREDICT_TRUE(bits < kNumBitsPerWord)) {
multiprecision_int_impl::ShiftRightFast<Word>(rep_.number_.data(),
kNumWords, bits);
return *this;
}
Word filler = -Word{is_negative()};
multiprecision_int_impl::ShiftRight(filler, rep_.number_.data(),
kNumWords, bits);
}
return *this;
}
bool AddOverflow(UnsignedWord x) {
UnsignedWord old_val = rep_.number_[kNumWords - 1];
rep_ += x;
UnsignedWord new_val = rep_.number_[kNumWords - 1];
return static_cast<Word>(~old_val & new_val) < 0;
}
bool AddOverflow(Word x) { return AddOverflow(FixedInt(x)); }
bool AddOverflow(const FixedInt& rh) {
UnsignedWord old_val = rep_.number_[kNumWords - 1];
UnsignedWord y = rh.rep_.number_[kNumWords - 1];
rep_ += rh.rep_;
UnsignedWord new_val = rep_.number_[kNumWords - 1];
return static_cast<Word>((old_val ^ new_val) & (new_val ^ y)) < 0;
}
FixedInt& operator+=(UnsignedWord x) {
rep_ += x;
return *this;
}
FixedInt& operator+=(Word x) { return *this += FixedInt(x); }
FixedInt& operator+=(const FixedInt& rh) {
rep_ += rh.rep_;
return *this;
}
bool SubtractOverflow(UnsignedWord x) {
UnsignedWord old_val = rep_.number_[kNumWords - 1];
rep_ -= x;
UnsignedWord new_val = rep_.number_[kNumWords - 1];
return static_cast<Word>(~new_val & old_val) < 0;
}
bool SubtractOverflow(Word x) { return SubtractOverflow(FixedInt(x)); }
bool SubtractOverflow(const FixedInt& rh) {
UnsignedWord old_val = rep_.number_[kNumWords - 1];
UnsignedWord y = rh.rep_.number_[kNumWords - 1];
rep_ -= rh.rep_;
UnsignedWord new_val = rep_.number_[kNumWords - 1];
return static_cast<Word>((new_val ^ old_val) & (old_val ^ y)) < 0;
}
FixedInt& operator-=(UnsignedWord x) {
rep_ -= x;
return *this;
}
FixedInt& operator-=(Word x) { return *this -= FixedInt(x); }
FixedInt& operator-=(const FixedInt& rh) {
rep_ -= rh.rep_;
return *this;
}
FixedInt& operator*=(UnsignedWord x) {
rep_ *= x;
return *this;
}
FixedInt& operator*=(Word x) {
if (x >= 0) {
rep_ *= x;
return *this;
}
rep_ *= -static_cast<UnsignedWord>(x);
*this = -(*this);
return *this;
}
FixedInt& operator*=(const FixedInt& x) {
rep_ *= x.rep_;
return *this;
}
bool MultiplyOverflow(UnsignedWord x) {
bool was_negative = is_negative();
UnsignedWord carry =
multiprecision_int_impl::MulWord(rep_.number_.data(), kNumWords, x);
// See comment at ExtendAndMultiply(FixedInt) for why we subtract x from
// carry.
carry -= was_negative ? x : 0;
return carry != (is_negative() ? ~UnsignedWord{0} : 0);
}
bool MultiplyOverflow(Word x) {
FixedInt<kNumBitsPerWord, kNumWords + 1> result =
ExtendAndMultiply(*this, FixedInt<kNumBitsPerWord, 1>(x));
*this = FixedInt(result);
return result.number()[kNumWords] != (is_negative() ? ~UnsignedWord{0} : 0);
}
// MultiplyOverflow(const FixedInt&) is much less efficient than
// operator*=(const FixedInt&) and MultiplyOverflow(Word).
bool MultiplyOverflow(const FixedInt& rh) {
bool result_non_positive = is_negative() != rh.is_negative();
if (ABSL_PREDICT_FALSE(is_negative())) {
*this = -(*this);
}
// use | instead of ||, to call SetSignAndAbs even when MultiplyOverflow
// returns true.
return rep_.MultiplyOverflow(SafeAbs(rh)) |
!SetSignAndAbs(result_non_positive, rep_);
}
template <int32_t divisor>
void DivMod(std::integral_constant<int32_t, divisor> x, FixedInt* quotient,
int32_t* remainder) const {
bool neg = is_negative();
bool divisor_negative = divisor < 0;
const FixedInt& dividend_abs = ABSL_PREDICT_TRUE(!neg) ? *this : -(*this);
uint32_t r = multiprecision_int_impl::ShortDivModConstant<kNumWords>(
dividend_abs.rep_.number_, SafeAbs(x),
quotient != nullptr ? "ient->rep_.number_ : nullptr);
if (ABSL_PREDICT_FALSE(neg != divisor_negative) && quotient != nullptr) {
*quotient = -(*quotient);
}
if (remainder != nullptr) {
*remainder = ABSL_PREDICT_FALSE(neg) ? -r : r;
}
}
void DivMod(Word x, FixedInt* quotient, Word* remainder) const {
bool neg = is_negative();
bool divisor_negative = x < 0;
const FixedInt& dividend_abs = ABSL_PREDICT_TRUE(!neg) ? *this : -(*this);
UnsignedWord r =
multiprecision_int_impl::ShortDivMod<UnsignedWord, kNumWords>(
dividend_abs.rep_.number_, SafeAbs(x),
quotient != nullptr ? "ient->rep_.number_ : nullptr);
if (ABSL_PREDICT_FALSE(neg != divisor_negative) && quotient != nullptr) {
*quotient = -(*quotient);
}
if (remainder != nullptr) {
*remainder = ABSL_PREDICT_FALSE(neg) ? -r : r;
}
}
void DivMod(const FixedInt& divisor, FixedInt* quotient,
FixedInt* remainder) const {
bool neg = is_negative();
bool divisor_negative = divisor.is_negative();
const FixedInt& dividend_abs = ABSL_PREDICT_TRUE(!neg) ? *this : -(*this);
const FixedInt& divisor_abs =
ABSL_PREDICT_TRUE(!divisor_negative) ? divisor : -divisor;
multiprecision_int_impl::DivMod<kNumWords>(
dividend_abs.rep_.number_, divisor_abs.rep_.number_,
quotient != nullptr ? "ient->rep_.number_ : nullptr,
remainder != nullptr ? &remainder->rep_.number_ : nullptr);
if (ABSL_PREDICT_FALSE(neg != divisor_negative) && quotient != nullptr &&
quotient != remainder) {
*quotient = -(*quotient);
}
if (ABSL_PREDICT_FALSE(neg) && remainder != nullptr) {
*remainder = -(*remainder);
}
}
template <uint32_t divisor>
FixedInt& operator/=(std::integral_constant<uint32_t, divisor> x) {
return InternalDivMod<DivOp, true>(x);
}
template <int32_t divisor>
FixedInt& operator/=(std::integral_constant<int32_t, divisor> x) {
return InternalDivMod<DivOp, true>(x);
}
FixedInt& operator/=(UnsignedWord x) {
return InternalDivMod<DivOp, true>(x);
}
FixedInt& operator/=(Word x) { return InternalDivMod<DivOp, true>(x); }
FixedInt& operator/=(const FixedInt& x) {
return InternalDivMod<DivOp, true>(x);
}
template <uint32_t divisor>
FixedInt& DivAndRoundAwayFromZero(
std::integral_constant<uint32_t, divisor> x) {
return InternalDivMod<DivRoundOp, true>(x);
}
template <int32_t divisor>
FixedInt& DivAndRoundAwayFromZero(
std::integral_constant<int32_t, divisor> x) {
return InternalDivMod<DivRoundOp, true>(x);
}
FixedInt& DivAndRoundAwayFromZero(UnsignedWord x) {
return InternalDivMod<DivRoundOp, true>(x);
}
FixedInt& DivAndRoundAwayFromZero(Word x) {
return InternalDivMod<DivRoundOp, true>(x);
}
FixedInt& DivAndRoundAwayFromZero(const FixedInt& x) {
return InternalDivMod<DivRoundOp, true>(x);
}
template <int32_t divisor>
FixedInt& operator%=(std::integral_constant<int32_t, divisor> x) {
return InternalDivMod<ModOp, false>(x);
}
template <uint32_t divisor>
FixedInt& operator%=(std::integral_constant<uint32_t, divisor> x) {
return InternalDivMod<ModOp, false>(x);
}
FixedInt& operator%=(UnsignedWord x) {
return InternalDivMod<ModOp, false>(x);
}
FixedInt& operator%=(Word x) { return InternalDivMod<ModOp, false>(x); }
FixedInt& operator%=(const FixedInt& x) {
return InternalDivMod<ModOp, false>(x);
}
bool is_zero() const { return rep_.is_zero(); }
bool is_negative() const {
return static_cast<Word>(rep_.number().back()) < 0;
}
FixedInt operator-() const { return FixedInt() -= *this; }
bool NegateOverflow() {
bool was_negative = is_negative();
FixedUint<kNumBitsPerWord, kNumWords> result;
bool is_nonzero = result.SubtractOverflow(rep_);
rep_ = result;
return is_nonzero && was_negative == is_negative();
}
constexpr const std::array<UnsignedWord, kNumWords>& number() const {
return rep_.number_;
}
FixedUint<kNumBitsPerWord, kNumWords> abs() const {
return ABSL_PREDICT_FALSE(is_negative()) ? (-(*this)).rep_ : rep_;
}
// Serializes to minimum number of bytes needed to represent the number,
// using two's complement. The result is appended to *out.
void SerializeToBytes(std::string* out) const {
ConstVarIntRef<kNumBitsPerWord>(rep_.number_).SerializeToBytes(out);
}
// Deserializes the output of Serialize() from a FixedInt (not FixedUint) with
// the same template arguments. If the input is valid, false is returned and
// this instance is unchanged.
ABSL_MUST_USE_RESULT bool DeserializeFromBytes(absl::string_view bytes) {
return VarIntRef<kNumBitsPerWord>(rep_.number_).DeserializeFromBytes(bytes);
}
// Convert the FixedInt to a readable string form.
std::string ToString() const {
std::string result;
AppendToString(&result);
return result;
}
void AppendToString(std::string* result) const {
if (ABSL_PREDICT_TRUE(!is_negative())) {
rep_.AppendToString(result);
return;
}
result->push_back('-');
(-(*this)).rep_.AppendToString(result);
}
// Parse string representation of a signed decimal integer with digits only
// except the plus/minus sign at the front, and write the number into the
// FixedInt.
bool ParseFromStringStrict(absl::string_view str) {
if (ABSL_PREDICT_FALSE(str.empty())) {
return false;
}
bool negate = str.at(0) == '-';
str.remove_prefix(str.at(0) == '-' || str.at(0) == '+');
return rep_.ParseFromStringStrict(str) && SetSignAndAbs(negate, rep_);
}
bool ParseFromStringSegments(
absl::string_view first_segment,
absl::Span<const absl::string_view> extra_segments) {
if (ABSL_PREDICT_FALSE(first_segment.empty())) {
return false;
}
bool negate = first_segment.at(0) == '-';
first_segment.remove_prefix(first_segment.at(0) == '-' ||
first_segment.at(0) == '+');
return rep_.ParseFromStringSegments(first_segment, extra_segments) &&
SetSignAndAbs(negate, rep_);
}
static FixedInt PowerOf10(uint exponent) {
FixedInt result(FixedUint<kNumBitsPerWord, kNumWords>::PowerOf10(exponent));
SQL_DCHECK(!result.is_negative());
return result;
}
uint CountDecimalDigits() const { return abs().CountDecimalDigits(); }
// Sets sign and absolute value. Returns false in case of overflow.
bool SetSignAndAbs(bool negative,
const FixedUint<kNumBitsPerWord, kNumWords>& abs) {
if (!negative) {
rep_ = abs;
return !is_negative();
}
FixedUint<kNumBitsPerWord, kNumWords> abs_copy = abs;
rep_ = FixedUint<kNumBitsPerWord, kNumWords>();
// SubtractOverflow returns false iff abs == 0.
return !rep_.SubtractOverflow(abs_copy) || is_negative();
}
template <typename H>
friend H AbslHashValue(H h,
const FixedInt<kNumBitsPerWord, kNumWords>& value) {
return H::combine(std::move(h), value.rep_);
}
private:
struct DivOp {
template <typename T>
void operator()(FixedInt& dividend, const T& divisor) const {
dividend.rep_ /= divisor;
}
};
struct DivRoundOp {
template <typename T>
void operator()(FixedInt& dividend, const T& divisor) const {
// Highest value of rep_ is 0x8000...; the addition never overflows.
dividend.rep_ += (divisor >> 1);
dividend.rep_ /= divisor;
}
void operator()(
FixedInt& dividend,
const FixedUint<kNumBitsPerWord, kNumWords>& divisor) const {
FixedUint<kNumBitsPerWord, kNumWords> tmp = divisor;
tmp >>= 1;
// Highest value of rep_ is 0x8000...; the addition never overflows.
dividend.rep_ += tmp;
dividend.rep_ /= divisor;
}
};
struct ModOp {
template <typename T>
void operator()(FixedInt& dividend, const T& divisor) const {
dividend.rep_ %= divisor;
}
};
template <typename Op, bool use_divisor_sign, typename T>
// GCC seems to produce bad code here on -02
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize("O0")))
#endif
inline FixedInt& InternalDivMod(const T& divisor) {
bool neg = is_negative();
bool should_negate_again =
neg != (use_divisor_sign && internal_is_negative(divisor));
auto abs_divisor = SafeAbs(divisor);
if (ABSL_PREDICT_FALSE(neg)) {
*this = -(*this);
}
Op()(*this, abs_divisor);
if (ABSL_PREDICT_FALSE(should_negate_again)) {
*this = -(*this);
}
return *this;
}
template <typename V>
static bool internal_is_negative(V x) {
return x < 0;
}
static bool internal_is_negative(const FixedInt& x) {
return x.is_negative();
}
static FixedUint<kNumBitsPerWord, kNumWords> SafeAbs(const FixedInt& x) {
return x.abs();
}
static UnsignedWord SafeAbs(Word x) {
return multiprecision_int_impl::SafeAbs<Word>(x);
}
static UnsignedWord SafeAbs(UnsignedWord x) { return x; }
template <int32_t v>
static std::integral_constant<uint32_t, multiprecision_int_impl::SafeAbs(v)>
SafeAbs(std::integral_constant<int32_t, v> x) {
return std::integral_constant<uint32_t,
multiprecision_int_impl::SafeAbs(v)>();
}
template <uint32_t v>
static std::integral_constant<uint32_t, v> SafeAbs(
std::integral_constant<uint32_t, v> x) {
return x;
}
FixedUint<kNumBitsPerWord, kNumWords> rep_;
};
template <int kNumBitsPerWord, int kNumWords>
inline bool operator==(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
return lh.number() == rh.number();
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator!=(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
return lh.number() != rh.number();
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator<(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
if (kNumWords == 1) {
using SignedWord = multiprecision_int_impl::Int<kNumBitsPerWord>;
return static_cast<SignedWord>(lh.number()[0]) <
static_cast<SignedWord>(rh.number()[0]);
}
using SignedDword = multiprecision_int_impl::Int<kNumBitsPerWord * 2>;
auto lh_hi = static_cast<SignedDword>(
multiprecision_int_impl::MakeDword<kNumBitsPerWord>(lh.number().data() +
kNumWords - 2));
auto rh_hi = static_cast<SignedDword>(
multiprecision_int_impl::MakeDword<kNumBitsPerWord>(rh.number().data() +
kNumWords - 2));
if (lh_hi != rh_hi) {
return lh_hi < rh_hi;
}
return multiprecision_int_impl::Less<kNumBitsPerWord, kNumWords - 2>(
lh.number().data(), rh.number().data());
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator>(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
return rh < lh;
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator<=(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
return !(rh < lh);
}
template <int kNumBitsPerWord, int kNumWords>
inline bool operator>=(const FixedInt<kNumBitsPerWord, kNumWords>& lh,
const FixedInt<kNumBitsPerWord, kNumWords>& rh) {
return !(lh < rh);
}
// Equivalent to FixedUint<k, n1 + n2>(x) *= FixedUint<k, n1 + n2>(y)
// except that this method is typically 50-60% faster than the above code.
// This method never overflows.
template <int k, int n1, int n2>
inline FixedUint<k, n1 + n2> ExtendAndMultiply(const FixedUint<k, n1>& lh,
const FixedUint<k, n2>& rh) {
return FixedUint<k, n1 + n2>(
multiprecision_int_impl::ExtendAndMultiply<k, n1, n2>(lh.number(),
rh.number()));
}
// Equivalent to FixedInt<k, n1 + n2>(x) *= FixedInt<k, n1 + n2>(y)
// except that this method is typically 50-60% faster than the above code.
// This method never overflows.
template <int k, int n1, int n2>
inline FixedInt<k, n1 + n2> ExtendAndMultiply(const FixedInt<k, n1>& lh,
const FixedInt<k, n2>& rh) {
auto result = multiprecision_int_impl::ExtendAndMultiply<k, n1, n2>(
lh.number(), rh.number());
// Let b = 2 ^ k, L = lh.number() (treated as an unsigned integer) and
// R = rh.number() (treated as an unsigned integer).
// 1) If lh < 0 and rh > 0, then lh = L - b ^ n1 and rh = R;
// lh * rh = L * R - R * b ^ n1 = result - R * b ^ n1;
// we should subtract R * b ^ n1 from <result>.
// 2) Similarly, if lh > 0 and rh < 0, then we should subtract L * b ^ n2 from
// <result>.
// 3) If lh < 0 and rh < 0, then
// lh * rh = (L - b ^ n1) * (R - b ^ n2)
// = L * R - R * b ^ n1 - L * b ^ n2 + b ^ (n1 + n2);
// b ^ (n1 + n2) can be ignored because result has only n1 + n2 words.
// We should subtract both R * b ^ n1 and L * b ^ n2.
if (ABSL_PREDICT_FALSE(lh.is_negative())) {
multiprecision_int_impl::SubtractWithVariableSize(result.data() + n1,
rh.number().data(), n2);
}
if (ABSL_PREDICT_FALSE(rh.is_negative())) {
multiprecision_int_impl::SubtractWithVariableSize(result.data() + n2,
lh.number().data(), n1);
}
return FixedInt<k, n1 + n2>(result);
}
template <bool is_signed, typename UnsignedWord>
void VarIntBase<is_signed, UnsignedWord>::SerializeToBytes(
std::string* bytes) const {
#ifdef ABSL_IS_LITTLE_ENDIAN
SQL_DCHECK(!number_.empty());
const char extension =
is_signed &&
static_cast<std::make_signed_t<UnsignedWord>>(number_.back()) < 0
? '\xff'
: '\x0';
const char* src_begin = reinterpret_cast<const char*>(number_.data());
const char* src_end = src_begin + sizeof(UnsignedWord) * number_.size();
const char* last_byte = src_end - 1;
// Skip last extension characters if they are redundant.
while (last_byte > src_begin && *last_byte == extension) {
--last_byte;
}
// If signed and the last byte has a different sign bit than the extension,
// add one extension byte to preserve the sign bit.
// "last_byte < src_end" is not necessary but makes the compiler generate
// more efficient code, for unknown reasons.
if (is_signed && last_byte < src_end &&
((*last_byte ^ extension) & '\x80') != 0) {
++last_byte;
}
bytes->append(src_begin, last_byte - src_begin + 1);
#else
multiprecision_int_impl::SerializeNoOptimization<is_signed>(number_, bytes);
#endif
}
// Deserialize from minimum number of bytes needed to represent the number.
// Similarly to Serialize, if is_signed is true then a high 0x80 bit
// will result in padding with 0xff rather than 0x00
template <bool is_signed, typename UnsignedWord>
bool VarIntBase<is_signed, UnsignedWord>::DeserializeFromBytes(
absl::string_view bytes) {
// We allow one extra byte in case of the 0x80 high byte case, see
// comments on Serialize for details
if (ABSL_PREDICT_FALSE(bytes.empty() || bytes.size() > sizeof(UnsignedWord) *
number_.size())) {
return false;
}
// Most callers already initialize *out to zero and the compiler is likely
// to optimize fill(0) away.
UnsignedWord* output_begin = number_.data();
UnsignedWord* output_end = output_begin + number_.size();
std::fill(output_begin, output_end, UnsignedWord{0});
if (is_signed && (bytes.back() & '\x80')) {
std::fill(output_begin, output_end, ~UnsignedWord{0});
}
memcpy(output_begin, bytes.data(), bytes.size());
// The compiler is expected to remove this loop if the host is little endian.
for (UnsignedWord* word = output_begin; word != output_end; ++word) {
static_assert(sizeof(UnsignedWord) == sizeof(uint32_t) ||
sizeof(UnsignedWord) == sizeof(uint64_t));
if (sizeof(UnsignedWord) == sizeof(uint32_t)) {
*word = bigquery_ml_utils_base::LittleEndian::ToHost32(*word);
} else {
*word = bigquery_ml_utils_base::LittleEndian::ToHost64(*word);
}
}
return true;
}
template <int kNumBitsPerWord>
void VarIntRef<kNumBitsPerWord>::Negate() {
uint8_t carry = 1;
using UnsignedWord = multiprecision_int_impl::Uint<kNumBitsPerWord>;
for (UnsignedWord& word : this->number_) {
word = ~word;
carry =
multiprecision_int_impl::AddWithCarry(&word, UnsignedWord{0}, carry);
}
}
template <int kNumBitsPerWord>
uint64_t VarUintRef<kNumBitsPerWord>::ScaleDown(int scale_down_digits,
uint64_t& remainder) {
static_assert(kNumBitsPerWord == 64);
return multiprecision_int_impl::kVarUintDivModPow10[scale_down_digits](
*this, remainder);
}
template <>
template <uint32_t divisor>
uint32_t VarUintRef<32>::DivMod(std::integral_constant<uint32_t, divisor> x) {
uint32_t remainder = 0;
for (auto itr = this->number_.rbegin(); itr != this->number_.rend(); ++itr) {
multiprecision_int_impl::DivModWord(remainder, *itr, divisor, &*itr,
&remainder);
}
return remainder;
}
template <>
template <uint64_t divisor>
uint64_t VarUintRef<64>::DivMod(std::integral_constant<uint64_t, divisor> x) {
uint64_t remainder = 0;
uint64_t throwaway;
// Since the divisor is shifted we also must shift the dividend. We do this
// inline.
constexpr uint64_t kShiftAmount =
multiprecision_int_impl::NormalizedDivisorShiftAmount(divisor);
if constexpr (kShiftAmount != 0) {
// The first division's quotient is always zero, so we throw it away.
multiprecision_int_impl::DivModWordNormalizedConstant<divisor>(
remainder,
multiprecision_int_impl::ShiftLeftAndGetHighWord(
0, this->number_.back(), kShiftAmount),
&throwaway, &remainder);
}
for (auto itr = this->number_.rbegin(); itr != (this->number_.rend() - 1);
++itr) {
multiprecision_int_impl::DivModWordNormalizedConstant<divisor>(
remainder,
multiprecision_int_impl::ShiftLeftAndGetHighWord(*itr, *(itr + 1),
kShiftAmount),
&*itr, &remainder);
}
multiprecision_int_impl::DivModWordNormalizedConstant<divisor>(
remainder,
multiprecision_int_impl::ShiftLeftAndGetHighWord(this->number_.front(), 0,
kShiftAmount),
&this->number_.front(), &remainder);
return remainder >> kShiftAmount;
}
template <>
template <uint32_t divisor>
uint32_t VarUintRef<64>::DivMod(std::integral_constant<uint32_t, divisor> x) {
return static_cast<uint32_t>(
this->DivMod(std::integral_constant<uint64_t, divisor>()));
}
template <typename T>
void SwapPairs(T& container) {
for (int i = 0; i < container.size(); i += 2) {
std::swap(container[i], container[i + 1]);
}
}
template <int kNumBitsPerWord>
uint32_t VarUintRef<kNumBitsPerWord>::DivMod(uint32_t divisor) {
uint32_t remainder = 0;
// Fallback to 32bit division in this case.
absl::Span<uint32_t> number_as_32bit(
reinterpret_cast<uint32_t*>(this->number_.data()),
kNumBitsPerWord / 32 * this->number_.size());
#ifdef ABSL_IS_BIG_ENDIAN
if constexpr (kNumBitsPerWord == 64) {
SwapPairs(number_as_32bit);
}
#endif
for (auto itr = number_as_32bit.rbegin(); itr != number_as_32bit.rend();
++itr) {
multiprecision_int_impl::RawDivModWord(remainder, *itr, divisor, &*itr,
&remainder);
}
#ifdef ABSL_IS_BIG_ENDIAN
if constexpr (kNumBitsPerWord == 64) {
SwapPairs(number_as_32bit);
}
#endif
return remainder;
}
template <bool is_signed, typename UnsignedWord>
void VarIntBase<is_signed, UnsignedWord>::AppendToString(
std::string* result) const {
SQL_DCHECK(result != nullptr);
if (number_.empty()) {
result->push_back('0');
return;
}
constexpr UnsignedWord kBitWidth =
static_cast<UnsignedWord>(sizeof(UnsignedWord) * 8);
// In the case of ConstVarIntRef, UnsignedWord has const modifier.
using UnsignedWordBase = std::remove_const_t<UnsignedWord>;
std::vector<UnsignedWordBase> dividend(data(), data() + size());
if (is_signed && (dividend.back() & (1ULL << (kBitWidth - 1))) != 0) {
VarIntRef<kBitWidth>(dividend).Negate();
result->push_back('-');
}
while (dividend.back() == 0) {
dividend.pop_back();
if (dividend.empty()) {
result->push_back('0');
return;
}
}
size_t num_bits = kBitWidth * dividend.size();
// For 32bit words we will group into segments 10**9 and for 10**19 for
// 64bit words.
// The number of segments needed = ceil(num_bits / log(SegmentSize, 2))
// = ceil(num_bits / log(SegmentSize, 2)) <=
// ceil(num_bits / floor(log(SegmentSize, 2))).
constexpr int kFloorLog2MaxPowerOf10 =
multiprecision_int_impl::IntTraits<kBitWidth>::kFloorLog2MaxPowerOf10;
std::vector<UnsignedWordBase> segments(
(num_bits + kFloorLog2MaxPowerOf10 - 1) / kFloorLog2MaxPowerOf10);
size_t num_segments = 0;
do {
VarUintRef<kBitWidth> ref(dividend);
segments[num_segments] = static_cast<UnsignedWordBase>(ref.DivMod(
std::integral_constant<
UnsignedWordBase,
multiprecision_int_impl::IntTraits<kBitWidth>::kMaxPowerOf10>()));
++num_segments;
if (dividend.back() == 0) {
dividend.pop_back();
}
} while (!dividend.empty());
multiprecision_int_impl::AppendSegmentsToString(segments.data(), num_segments,
result);
}
} // namespace bigquery_ml_utils
#endif // THIRD_PARTY_PY_BIGQUERY_ML_UTILS_SQL_UTILS_COMMON_MULTIPRECISION_INT_H_