1
0
mirror of https://github.com/danog/ton.git synced 2024-12-02 17:38:33 +01:00
ton/crypto/common/bigint.hpp

2526 lines
60 KiB
C++
Raw Normal View History

2019-09-07 12:03:22 +02:00
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <ostream>
#include <sstream>
#include <cassert>
#include "common/bitstring.h"
#include "td/utils/bits.h"
#include "td/utils/Span.h"
#include "td/utils/uint128.h"
/**************************************
*
* BIGINT256
*
**************************************
**/
namespace td {
struct BigIntInfo {
typedef long long word_t;
typedef unsigned long long uword_t;
enum { word_bits = 64, word_shift = 52, words_for_256bit = 1 + 256 / word_shift, max_pow10_exp = 18 };
static const word_t Base = (1LL << word_shift);
static const word_t Half = (1LL << (word_shift - 1));
static const word_t MaxDenorm = (1LL << (word_bits - word_shift - 1));
static const word_t max_pow10 = 1000000000000000000LL;
static constexpr double InvBase = 1.0 / (double)Base;
static void set_mul(word_t* hi, word_t* lo, word_t x, word_t y);
static void add_mul(word_t* hi, word_t* lo, word_t x, word_t y);
static void sub_mul(word_t* hi, word_t* lo, word_t x, word_t y);
static void dbl_divmod(word_t* quot, word_t* rem, word_t hi, word_t lo, word_t y);
};
inline void BigIntInfo::set_mul(word_t* hi, word_t* lo, word_t x, word_t y) {
auto z = uint128::from_signed(x).mult_signed(y);
*lo = static_cast<word_t>(z.lo()) & (Base - 1);
*hi = static_cast<word_t>(z.shr(word_shift).lo());
}
inline void BigIntInfo::add_mul(word_t* hi, word_t* lo, word_t x, word_t y) {
auto z = uint128::from_signed(x).mult_signed(y);
*lo += static_cast<word_t>(z.lo()) & (Base - 1);
*hi += static_cast<word_t>(z.shr(word_shift).lo());
}
inline void BigIntInfo::sub_mul(word_t* hi, word_t* lo, word_t x, word_t y) {
auto z = uint128::from_signed(x).mult_signed(y);
*lo -= static_cast<word_t>(z.lo()) & (Base - 1);
*hi -= static_cast<word_t>(z.shr(word_shift).lo());
}
inline void BigIntInfo::dbl_divmod(word_t* quot, word_t* rem, word_t hi, word_t lo, word_t y) {
auto x = uint128::from_signed(hi).shl(word_shift).add(uint128::from_signed(lo));
int64 a, b;
x.divmod_signed(y, &a, &b);
*quot = a;
*rem = b;
}
namespace {
template <typename T>
struct LogOpAnd {
static T op(T x, T y) {
return x & y;
}
static const T neutral = -1;
static const T zero = 0;
static const bool has_zero = true;
};
template <typename T>
struct LogOpOr {
static T op(T x, T y) {
return x | y;
}
static const T neutral = 0;
static const T zero = -1;
static const bool has_zero = true;
};
template <typename T>
struct LogOpXor {
static T op(T x, T y) {
return x ^ y;
}
static const T neutral = 0;
static const T zero = -1;
static const bool has_zero = false;
};
template <typename T>
struct TransformId {
static T eval(T x) {
return x;
}
};
template <typename T>
struct TransformNegate {
static T eval(T x) {
return -x;
}
};
template <typename T, int factor>
struct TransformMul {
static T eval(T x) {
return x * factor;
}
};
} // namespace
struct IntOverflow {};
template <class T>
class PropagateConstSpan {
public:
PropagateConstSpan() = default;
PropagateConstSpan(T* ptr, size_t size) : ptr_(ptr), size_(size) {
}
T& operator[](size_t i) {
DCHECK(i < size_);
return ptr_[i];
}
const T& operator[](size_t i) const {
DCHECK(i < size_);
return ptr_[i];
}
T* data() {
return ptr_;
}
const T* data() const {
return ptr_;
}
size_t size() const {
return size_;
}
private:
T* ptr_{nullptr};
size_t size_{0};
};
template <class Tr = BigIntInfo>
class AnyIntView {
public:
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift };
typedef typename Tr::word_t word_t;
int& n_;
PropagateConstSpan<word_t> digits;
int max_size() const {
return static_cast<int>(digits.size());
}
int size() const {
return n_;
}
void set_size(int new_size) {
n_ = new_size;
}
int inc_size() {
return n_++;
}
void dec_size() {
n_--;
}
bool is_valid() const {
return size() > 0;
}
void enforce_valid() const {
enforce(is_valid());
}
void invalidate() {
set_size(0);
}
bool invalidate_bool() {
set_size(0);
return false;
}
void enforce(bool f) const {
if (!f) {
throw IntOverflow();
}
}
void operator=(word_t y) {
set_size(1);
digits[0] = y;
}
bool normalize_bool_any();
bool set_pow2_any(int exponent);
bool set_any(const AnyIntView& yp);
bool add_pow2_any(int exponent, int factor);
bool add_any(const AnyIntView& yp);
bool sub_any(const AnyIntView& yp);
template <class LogOp>
bool log_op_any(const AnyIntView& yp);
int cmp_any(const AnyIntView& yp) const;
int cmp_any(word_t y) const;
template <class T = TransformId<word_t>>
int cmp_un_any(const AnyIntView& yp) const;
void negate_any();
int sgn_un_any() const;
bool get_bit_any(unsigned bit) const;
bool eq_any(const AnyIntView& yp) const;
bool eq_any(word_t y) const;
void mul_tiny_any(int y);
int divmod_tiny_any(int y);
word_t divmod_short_any(word_t y);
bool mul_add_short_any(word_t y, word_t z);
bool add_mul_any(const AnyIntView& yp, const AnyIntView& zp);
void add_mul_trunc_any(const AnyIntView& yp, const AnyIntView& zp);
bool mod_div_any(const AnyIntView& yp, AnyIntView& quot, int round_mode);
bool mod_pow2_any(int exponent);
bool mod_pow2_any(int exponent, int round_mode);
bool rshift_any(int exponent, int round_mode = -1);
bool lshift_any(int exponent);
bool unsigned_fits_bits_any(int nbits) const;
bool signed_fits_bits_any(int nbits) const;
int bit_size_any(bool sgnd = true) const;
bool export_bytes_any(unsigned char* buff, std::size_t size, bool sgnd = true) const;
bool export_bytes_lsb_any(unsigned char* buff, std::size_t size, bool sgnd = true) const;
bool import_bytes_any(const unsigned char* buff, std::size_t size, bool sgnd = true);
bool import_bytes_lsb_any(const unsigned char* buff, std::size_t size, bool sgnd = true);
bool export_bits_any(unsigned char* buff, int offs, unsigned bits, bool sgnd = true) const;
bool import_bits_any(const unsigned char* buff, int offs, unsigned bits, bool sgnd = true);
word_t top_word() const {
return digits[size() - 1];
}
double top_double() const {
return size() > 1 ? (double)digits[size() - 1] + (double)digits[size() - 2] * (1.0 / Tr::Base)
: (double)digits[size() - 1];
}
word_t to_long_any() const;
int parse_hex_any(const char* str, int str_len, int* frac = nullptr);
int parse_binary_any(const char* str, int str_len, int* frac = nullptr);
std::string to_dec_string_destroy_any();
std::string to_dec_string_slow_destroy_any();
std::string to_hex_string_any(bool upcase = false) const;
std::string to_hex_string_slow_destroy_any();
std::string to_binary_string_any() const;
int sgn() const {
return is_valid() ? (top_word() > 0 ? 1 : (top_word() < 0 ? -1 : 0)) : 0x80000000;
}
};
template <int len, class Tr = BigIntInfo>
class BigIntG {
public:
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift, max_bits = len, word_cnt = len / word_shift + 1 };
typedef typename Tr::word_t word_t;
typedef Tr Traits;
typedef BigIntG<len * 2, Tr> DoubleInt;
AnyIntView<Tr> as_any_int() {
return AnyIntView<Tr>{n, PropagateConstSpan<word_t>(digits, word_cnt)};
}
const AnyIntView<Tr> as_any_int() const {
return AnyIntView<Tr>{const_cast<int&>(n), PropagateConstSpan<word_t>(const_cast<word_t*>(digits), word_cnt)};
}
private:
template <int bits2, class Tr2>
friend class BigIntG;
int n;
word_t digits[word_cnt];
public:
BigIntG() : n(0) {
}
explicit BigIntG(word_t x) : n(1) {
digits[0] = x;
}
BigIntG(const BigIntG& x) : n(x.n) {
std::memcpy(digits, x.digits, n * sizeof(word_t));
///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")";
}
template <int len2>
BigIntG(const BigIntG<len2, Tr>& y) : n(0) {
*this = y;
}
void enforce(bool f) const {
if (!f) {
throw IntOverflow();
}
}
void ignore(bool f) const {
}
bool is_valid() const {
return n > 0;
}
void enforce_valid() const {
enforce(is_valid());
}
BigIntG& invalidate() {
n = 0;
return *this;
}
bool invalidate_bool() {
n = 0;
return false;
}
BigIntG& invalidate_unless(bool f) {
if (!f) {
n = 0;
}
return *this;
}
bool normalize_bool() {
return as_any_int().normalize_bool_any();
}
BigIntG& normalize() {
ignore(normalize_bool());
return *this;
}
BigIntG& denormalize();
int sgn() const {
return is_valid() ? (top_word() > 0 ? 1 : (top_word() < 0 ? -1 : 0)) : 0x80000000;
}
int sgn_un() const {
return as_any_int().sgn_un_any();
}
bool get_bit(unsigned bit) const {
return as_any_int().get_bit_any(bit);
}
BigIntG& negate() {
as_any_int().negate_any();
return *this;
}
BigIntG& logical_not();
BigIntG& operator=(const BigIntG& y) {
n = y.n;
std::memcpy(digits, y.digits, n * sizeof(word_t));
///std::cout << "(BiC=)";
return *this;
}
template <int len2>
BigIntG& operator=(const BigIntG<len2, Tr>& y) {
return invalidate_unless(as_any_int().set_any(y.as_any_int()));
}
template <int len2>
BigIntG& operator+=(const BigIntG<len2, Tr>& y) {
ignore(as_any_int().add_any(y.as_any_int()));
return *this;
}
template <int len2>
BigIntG& operator-=(const BigIntG<len2, Tr>& y) {
ignore(as_any_int().sub_any(y.as_any_int()));
return *this;
}
template <int len2>
BigIntG& operator&=(const BigIntG<len2, Tr>& y) {
ignore(as_any_int().template log_op_any<LogOpAnd<word_t>>(y.as_any_int()));
return *this;
}
template <int len2>
BigIntG& operator|=(const BigIntG<len2, Tr>& y) {
ignore(as_any_int().template log_op_any<LogOpOr<word_t>>(y.as_any_int()));
return *this;
}
template <int len2>
BigIntG& operator^=(const BigIntG<len2, Tr>& y) {
ignore(as_any_int().template log_op_any<LogOpXor<word_t>>(y.as_any_int()));
return *this;
}
BigIntG& operator>>=(int shift) {
as_any_int().rshift_any(shift);
return *this;
}
BigIntG& operator<<=(int shift) {
as_any_int().lshift_any(shift);
return *this;
}
BigIntG& rshift(int shift, int round_mode = -1) {
as_any_int().rshift_any(shift, round_mode);
return *this;
}
template <int len2>
int cmp(const BigIntG<len2, Tr>& y) const {
return as_any_int().cmp_any(y.as_any_int());
}
template <int len2>
int cmp_un(const BigIntG<len2, Tr>& y) const {
return as_any_int().template cmp_un_any<TransformId<word_t>>(y.as_any_int());
}
template <int len2>
bool operator==(const BigIntG<len2, Tr>& y) const {
return as_any_int().eq_any(y.as_any_int());
}
template <int len2>
bool operator!=(const BigIntG<len2, Tr>& y) const {
return !(*this == y);
}
int cmp(word_t y) const {
return as_any_int().cmp_any(y);
}
bool operator==(word_t y) const {
return as_any_int().eq_any(y);
}
bool operator!=(word_t y) const {
return !(*this == y);
}
bool mul_add_short_bool(word_t y, word_t z) {
return as_any_int().mul_add_short_any(y, z);
}
template <int len2, int len3>
bool add_mul_bool(const BigIntG<len2, Tr>& y, const BigIntG<len3, Tr>& z) {
return as_any_int().add_mul_any(y.as_any_int(), z.as_any_int());
}
template <int len2, int len3>
BigIntG& add_mul(const BigIntG<len2, Tr>& y, const BigIntG<len3, Tr>& z) {
ignore(add_mul_bool(y, z));
return *this;
}
template <int len2, int len3>
void add_mul_trunc(const BigIntG<len2, Tr>& y, const BigIntG<len3, Tr>& z) {
as_any_int().add_mul_trunc_any(y.as_any_int(), z.as_any_int());
}
BigIntG& mul_short(word_t y) {
return invalidate_unless(mul_add_short_bool(y, 0));
}
BigIntG& mul_tiny(int y) {
as_any_int().mul_tiny_any(y);
return *this;
}
BigIntG& add_tiny(word_t y) {
digits[0] += y;
return *this;
}
BigIntG& sub_tiny(word_t y) {
digits[0] -= y;
return *this;
}
BigIntG& mul_short_opt(word_t y) {
if (y <= Tr::MaxDenorm && y >= -Tr::MaxDenorm) {
return mul_tiny(static_cast<int>(y));
} else {
return mul_short(y);
}
}
template <int len2, int len3>
bool mod_div_bool(const BigIntG<len2, Tr>& y, BigIntG<len3, Tr>& quot, int round_mode = -1) {
auto q = quot.as_any_int();
return as_any_int().mod_div_any(y.as_any_int(), q, round_mode);
}
template <int len2, int len3>
BigIntG& mod_div(const BigIntG<len2, Tr>& y, BigIntG<len3, Tr>& quot, int round_mode = -1) {
return invalidate_unless(mod_div_bool(y, quot, round_mode));
}
int divmod_tiny(int y) {
return as_any_int().divmod_tiny_any(y);
}
word_t divmod_short(word_t y) {
return as_any_int().divmod_short_any(y);
}
BigIntG& operator=(word_t y) {
n = 1;
digits[0] = y;
return *this;
}
BigIntG& set_zero() {
n = 1;
digits[0] = 0;
return *this;
}
BigIntG& set_pow2(int exponent) {
return invalidate_unless(as_any_int().set_pow2_any(exponent));
}
bool set_pow2_bool(int exponent) {
return as_any_int().set_pow2_any(exponent);
}
BigIntG& mod_pow2(int exponent) {
return invalidate_unless(as_any_int().mod_pow2_any(exponent));
}
BigIntG& mod_pow2(int exponent, int round_mode) {
return invalidate_unless(as_any_int().mod_pow2_any(exponent, round_mode));
}
BigIntG& add_pow2(int exponent) {
return invalidate_unless(as_any_int().add_pow2_any(exponent, 1));
}
BigIntG& sub_pow2(int exponent) {
return invalidate_unless(as_any_int().add_pow2_any(exponent, -1));
}
bool unsigned_fits_bits(int nbits) const {
return as_any_int().unsigned_fits_bits_any(nbits);
}
bool signed_fits_bits(int nbits) const {
return as_any_int().signed_fits_bits_any(nbits);
}
bool fits_bits(int nbits, bool sgnd = true) const {
return sgnd ? signed_fits_bits(nbits) : unsigned_fits_bits(nbits);
}
int bit_size(bool sgnd = true) const {
return as_any_int().bit_size_any(sgnd);
}
bool export_bytes(unsigned char* buff, std::size_t size, bool sgnd = true) const {
return as_any_int().export_bytes_any(buff, size, sgnd);
}
bool export_bytes_lsb(unsigned char* buff, std::size_t size, bool sgnd = true) const {
return as_any_int().export_bytes_lsb_any(buff, size, sgnd);
}
bool import_bytes(const unsigned char* buff, std::size_t size, bool sgnd = true) {
return as_any_int().import_bytes_any(buff, size, sgnd);
}
bool import_bytes_lsb(const unsigned char* buff, std::size_t size, bool sgnd = true) {
return as_any_int().import_bytes_lsb_any(buff, size, sgnd);
}
bool export_bits(unsigned char* buff, int offs, unsigned bits, bool sgnd = true) const {
return as_any_int().export_bits_any(buff, offs, bits, sgnd);
}
bool export_bits(td::BitPtr bp, unsigned bits, bool sgnd = true) const {
return as_any_int().export_bits_any(bp.ptr, bp.offs, bits, sgnd);
}
template <typename T>
bool export_bits(T& bs, bool sgnd = true) const {
return export_bits(bs.bits(), bs.size(), sgnd);
}
bool export_bits(const BitSliceWrite& bs, bool sgnd = true) const {
return export_bits(bs.bits(), bs.size(), sgnd);
}
bool import_bits(const unsigned char* buff, int offs, unsigned bits, bool sgnd = true) {
return as_any_int().import_bits_any(buff, offs, bits, sgnd);
}
bool import_bits(td::ConstBitPtr bp, unsigned bits, bool sgnd = true) {
return as_any_int().import_bits_any(bp.ptr, bp.offs, bits, sgnd);
}
template <typename T>
bool import_bits(const T& bs, bool sgnd = true) {
return import_bits(bs.bits(), bs.size(), sgnd);
}
std::ostream& dump(std::ostream& os, bool nl = true) const;
std::string dump() const;
int parse_dec(const char* str, int str_len, int* frac = nullptr);
int parse_dec(const std::string str, int* frac = nullptr) {
return parse_dec(str.c_str(), (int)str.size(), frac);
}
int parse_dec_slow(const char* str, int str_len);
int parse_dec_slow(const std::string str) {
return parse_dec_slow(str.c_str(), (int)str.size());
}
int parse_hex(const char* str, int str_len, int* frac = nullptr) {
return as_any_int().parse_hex_any(str, str_len, frac);
}
int parse_hex(const std::string str, int* frac = nullptr) {
return parse_hex(str.c_str(), (int)str.size(), frac);
}
int parse_binary(const char* str, int str_len, int* frac = nullptr) {
return as_any_int().parse_binary_any(str, str_len, frac);
}
int parse_binary(const std::string str, int* frac = nullptr) {
return parse_binary(str.c_str(), (int)str.size(), frac);
}
std::string to_dec_string() const;
std::string to_dec_string_destroy();
std::string to_dec_string_slow() const;
std::string to_hex_string_slow() const;
std::string to_hex_string(bool upcase = false) const;
std::string to_binary_string() const;
double to_double() const {
return is_valid() ? ldexp(top_double(), (n - 1) * word_shift) : NAN;
}
word_t to_long() const {
return as_any_int().to_long_any();
}
private:
word_t top_word() const {
return digits[n - 1];
}
double top_double() const {
return n > 1 ? (double)digits[n - 1] + (double)digits[n - 2] * (1.0 / Tr::Base) : (double)digits[n - 1];
}
};
template <class Tr>
bool AnyIntView<Tr>::normalize_bool_any() {
word_t val = 0;
int i;
if (!is_valid()) {
return false;
}
for (i = 0; i < size() && digits[i] < Tr::Half && digits[i] >= -Tr::Half; i++) {
}
for (; i < size(); i++) {
val += digits[i] + Tr::Half;
digits[i] = (val & (Tr::Base - 1)) - Tr::Half;
val >>= word_shift;
}
if (val) {
do {
if (size() == max_size()) {
return invalidate_bool();
}
val += Tr::Half;
digits[inc_size()] = (val & (Tr::Base - 1)) - Tr::Half;
val >>= word_shift;
} while (val);
}
while (size() > 1 && !digits[size() - 1]) {
dec_size();
}
return true;
}
template <class Tr>
bool AnyIntView<Tr>::set_pow2_any(int exponent) {
if (exponent < 0 || exponent >= max_size() * word_shift) {
invalidate();
return false;
}
auto dm = std::div(exponent, word_shift);
int k = dm.quot;
std::memset(digits.data(), 0, k * sizeof(word_t));
if (dm.rem == word_shift - 1 && k + 1 < max_size()) {
digits[k] = -Tr::Half;
digits[k + 1] = 1;
set_size(k + 2);
return true;
}
digits[k] = ((word_t)1 << dm.rem);
set_size(k + 1);
return true;
}
template <class Tr>
bool AnyIntView<Tr>::set_any(const AnyIntView<Tr>& yp) {
if (yp.size() <= max_size()) {
set_size(yp.size());
std::memcpy(digits.data(), yp.digits.data(), size() * sizeof(word_t));
return true;
} else {
set_size(max_size());
std::memcpy(digits.data(), yp.digits.data(), size() * sizeof(word_t));
return false;
}
}
template <class Tr>
bool AnyIntView<Tr>::add_pow2_any(int exponent, int factor) {
if (exponent < 0 || exponent >= max_size() * word_shift) {
invalidate();
return false;
}
if (!is_valid()) {
return false;
}
auto dm = std::div(exponent, word_shift);
int k = dm.quot;
while (size() <= k) {
digits[inc_size()] = 0;
}
digits[k] += (factor << dm.rem);
return true;
}
template <class Tr>
bool AnyIntView<Tr>::add_any(const AnyIntView<Tr>& yp) {
if (yp.size() <= size()) {
if (!yp.is_valid()) {
return invalidate_bool();
}
for (int i = 0; i < yp.size(); i++) {
digits[i] += yp.digits[i];
}
return true;
} else {
if (!is_valid()) {
return false;
}
if (yp.size() > max_size()) {
return invalidate_bool();
}
for (int i = 0; i < size(); i++) {
digits[i] += yp.digits[i];
}
for (int i = size(); i < yp.size(); i++) {
digits[i] = yp.digits[i];
}
set_size(yp.size());
return true;
}
}
template <class Tr>
bool AnyIntView<Tr>::sub_any(const AnyIntView<Tr>& yp) {
if (yp.size() <= size()) {
if (!yp.is_valid()) {
return invalidate_bool();
}
for (int i = 0; i < yp.size(); i++) {
digits[i] -= yp.digits[i];
}
return true;
} else {
if (!is_valid()) {
return false;
}
if (yp.size() > max_size()) {
return invalidate_bool();
}
for (int i = 0; i < size(); i++) {
digits[i] -= yp.digits[i];
}
for (int i = size(); i < yp.size(); i++) {
digits[i] = -yp.digits[i];
}
set_size(yp.size());
return true;
}
}
template <class Tr>
template <class LogOp>
bool AnyIntView<Tr>::log_op_any(const AnyIntView<Tr>& yp) {
word_t cx = 0, cy = 0, cz = 0;
int i = 0;
const int shift = Tr::word_shift;
if (size() == 1) {
if (LogOp::has_zero && digits[0] == LogOp::zero) {
return true;
} else if (digits[0] == LogOp::neutral) {
if (yp.size() <= max_size()) {
set_size(yp.size());
std::memcpy(digits.data(), yp.digits.data(), size() * sizeof(word_t));
return true;
} else {
return invalidate_bool();
}
}
}
if (yp.size() == 1) {
if (LogOp::has_zero && yp.digits[0] == LogOp::zero) {
set_size(1);
digits[0] = LogOp::zero;
return true;
} else if (yp.digits[0] == LogOp::neutral) {
return true;
}
}
if (yp.size() <= size()) {
if (!yp.is_valid()) {
return invalidate_bool();
}
for (; i < yp.size(); i++) {
cx += digits[i];
cy += yp.digits[i];
cz += (LogOp::op(cx, cy) & (Tr::Base - 1)) + Tr::Half;
cx >>= shift;
cy >>= shift;
digits[i] = (cz & (Tr::Base - 1)) - Tr::Half;
cz >>= shift;
}
for (; i < size(); i++) {
cx += digits[i];
cz += (LogOp::op(cx, cy) & (Tr::Base - 1)) + Tr::Half;
cx >>= shift;
cy >>= shift;
digits[i] = (cz & (Tr::Base - 1)) - Tr::Half;
cz >>= shift;
}
cz += LogOp::op(cx, cy);
if (cz) {
if (size() >= max_size()) {
return invalidate_bool();
}
digits[inc_size()] = cz;
} else {
while (size() > 1 && !digits[size() - 1]) {
dec_size();
}
}
return true;
} else {
if (!is_valid()) {
return false;
}
for (; i < size(); i++) {
cx += digits[i];
cy += yp.digits[i];
cz += (LogOp::op(cx, cy) & (Tr::Base - 1)) + Tr::Half;
cx >>= shift;
cy >>= shift;
digits[i] = (cz & (Tr::Base - 1)) - Tr::Half;
cz >>= shift;
}
set_size(std::min(yp.size(), max_size()));
for (; i < size(); i++) {
cy += yp.digits[i];
cz += (LogOp::op(cx, cy) & (Tr::Base - 1)) + Tr::Half;
cx >>= shift;
cy >>= shift;
digits[i] = (cz & (Tr::Base - 1)) - Tr::Half;
cz >>= shift;
}
if (yp.size() > size()) {
for (; i < yp.size(); i++) {
cy += yp.digits[i];
cz += (LogOp::op(cx, cy) & (Tr::Base - 1));
cx >>= shift;
cy >>= shift;
if ((cz & (Tr::Base - 1)) != 0) {
return invalidate_bool();
}
cz >>= shift;
}
}
cz += LogOp::op(cx, cy);
if (cz) {
return invalidate_bool();
}
while (size() > 1 && !digits[size() - 1]) {
dec_size();
}
return true;
}
}
template <class Tr>
bool AnyIntView<Tr>::add_mul_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>& zp) {
int yn = yp.size(), zn = zp.size(), rn = yn + zn;
if (!yp.is_valid() || !zp.is_valid() || !is_valid()) {
return invalidate_bool();
}
if (rn > max_size() + 1) {
return invalidate_bool();
} else if (rn < max_size() + 1) {
while (size() < rn) {
digits[inc_size()] = 0;
}
for (int i = 0; i < yn; i++) {
word_t yv = yp.digits[i];
for (int j = 0; j < zn; j++) {
Tr::add_mul(&digits[i + j + 1], &digits[i + j], yv, zp.digits[j]);
}
}
} else {
while (size() < rn - 1) {
digits[inc_size()] = 0;
}
int i;
for (i = 0; i < yn - 1; i++) {
word_t yv = yp.digits[i];
for (int j = 0; j < zn; j++) {
Tr::add_mul(&digits[i + j + 1], &digits[i + j], yv, zp.digits[j]);
}
}
word_t yv = yp.digits[i];
int j;
for (j = 0; j < zn - 1; j++) {
Tr::add_mul(&digits[i + j + 1], &digits[i + j], yv, zp.digits[j]);
}
word_t hi = 0;
Tr::add_mul(&hi, &digits[i + j], yv, zp.digits[j]);
if (hi && hi != -1) {
return invalidate_bool();
}
digits[size() - 1] += (hi << word_shift);
}
return true;
}
template <class Tr>
void AnyIntView<Tr>::add_mul_trunc_any(const AnyIntView<Tr>& yp, const AnyIntView<Tr>& zp) {
int yn = yp.size(), zn = zp.size();
if (!yp.is_valid() || !zp.is_valid() || !is_valid()) {
invalidate();
return;
}
int xn = std::min(yn + zn, max_size());
while (size() < xn) {
digits[inc_size()] = 0;
}
xn = size();
for (int i = 0; i < yn && i < xn; i++) {
word_t yv = yp.digits[i];
for (int j = 0; j < zn; j++) {
if (i + j + 1 < xn) {
Tr::add_mul(&digits[i + j + 1], &digits[i + j], yv, zp.digits[j]);
} else {
word_t hi = 0;
Tr::add_mul(&hi, &digits[i + j], yv, zp.digits[j]);
break;
}
}
}
}
template <class Tr>
int AnyIntView<Tr>::sgn_un_any() const {
if (!is_valid()) {
return 0;
}
word_t v = digits[size() - 1];
if (size() >= 2) {
if (v >= Tr::MaxDenorm) {
return 1;
} else if (v <= -Tr::MaxDenorm) {
return -1;
}
int i = size() - 2;
do {
v <<= word_shift;
word_t w = digits[i];
if (w >= -v + Tr::MaxDenorm) {
return 1;
} else if (w <= -v - Tr::MaxDenorm) {
return -1;
}
v += w;
} while (--i >= 0);
}
return (v > 0 ? 1 : (v < 0 ? -1 : 0));
}
template <class Tr>
bool AnyIntView<Tr>::get_bit_any(unsigned bit) const {
if (!is_valid()) {
return 0;
}
if (bit >= (unsigned)size() * word_shift) {
return sgn() < 0;
}
if (bit < word_shift) {
return (digits[0] >> bit) & 1;
}
auto q = std::div(bit, word_shift);
int i = q.quot;
word_t x = digits[i];
while (--i >= 0) {
if (digits[i] < 0) {
--x;
break;
} else if (digits[i] > 0) {
break;
}
}
return (x >> q.rem) & 1;
}
template <class Tr>
typename Tr::word_t AnyIntView<Tr>::to_long_any() const {
if (!is_valid()) {
return (~0ULL << 63);
} else if (size() == 1) {
return digits[0];
} else {
word_t v = digits[0] + (digits[1] << word_shift); // approximation mod 2^64
word_t w = (v & (Tr::Base - 1)) - digits[0];
w >>= word_shift;
w += (v >> word_shift); // excess of approximation divided by Tr::Base
int n = size() - 1;
for (int i = 1; i < n; i++) {
w -= digits[i];
if (w & (Tr::Base - 1)) {
return (~0ULL << 63);
}
w >>= word_shift;
}
return w != digits[n] ? (~0ULL << 63) : v;
}
}
template <class Tr>
int AnyIntView<Tr>::cmp_any(const AnyIntView<Tr>& yp) const {
if (yp.size() < size()) {
return top_word() < 0 ? -1 : 1;
} else if (yp.size() > size()) {
return yp.top_word() > 0 ? -1 : 1;
}
for (int i = size() - 1; i >= 0; i--) {
if (digits[i] < yp.digits[i]) {
return -1;
} else if (digits[i] > yp.digits[i]) {
return 1;
}
}
return 0;
}
template <class Tr>
int AnyIntView<Tr>::cmp_any(word_t y) const {
if (size() > 1) {
return top_word() < 0 ? -1 : 1;
} else if (size() == 1) {
return digits[0] < y ? -1 : (digits[0] > y ? 1 : 0);
} else {
return 0x80000000;
}
}
template <class Tr>
template <class T>
int AnyIntView<Tr>::cmp_un_any(const AnyIntView<Tr>& yp) const {
int xn = size(), yn = yp.size();
word_t v;
if (yn < xn) {
v = T::eval(digits[--xn]);
if (v >= Tr::MaxDenorm) {
return 1;
} else if (v <= -Tr::MaxDenorm) {
return -1;
}
while (xn > yn) {
v <<= word_shift;
word_t w = T::eval(digits[--xn]);
if (w >= -v + Tr::MaxDenorm) {
return 1;
} else if (w <= -v + Tr::MaxDenorm) {
return -1;
}
v += w;
}
} else if (yn > xn) {
v = -yp.digits[--yn];
if (v >= Tr::MaxDenorm) {
return 1;
} else if (v <= -Tr::MaxDenorm) {
return -1;
}
while (yn > xn) {
v <<= word_shift;
word_t w = yp.digits[--yn];
if (w <= v - Tr::MaxDenorm) {
return 1;
} else if (w >= v + Tr::MaxDenorm) {
return -1;
}
v -= w;
}
} else {
v = 0;
}
while (--xn >= 0) {
v <<= word_shift;
word_t w = T::eval(digits[xn]) - yp.digits[xn];
if (w >= -v + Tr::MaxDenorm) {
return 1;
} else if (w <= -v - Tr::MaxDenorm) {
return -1;
}
v += w;
}
return (v > 0 ? 1 : (v < 0 ? -1 : 0));
}
template <class Tr>
bool AnyIntView<Tr>::eq_any(const AnyIntView<Tr>& yp) const {
if (yp.size() != size()) {
return false;
}
return !std::memcmp(digits.data(), yp.digits.data(), size() * sizeof(word_t));
}
template <class Tr>
bool AnyIntView<Tr>::eq_any(word_t y) const {
return (size() == 1 && digits[0] == y);
}
template <class Tr>
void AnyIntView<Tr>::negate_any() {
for (int i = 0; i < size(); i++) {
digits[i] = -digits[i];
}
}
template <class Tr>
void AnyIntView<Tr>::mul_tiny_any(int y) {
for (int i = 0; i < size(); i++) {
digits[i] *= y;
}
}
template <class Tr>
int AnyIntView<Tr>::divmod_tiny_any(int y) {
if (!y) {
invalidate();
return 0;
}
int rem = 0;
for (int i = size() - 1; i >= 0; i--) {
auto divmod = std::div(digits[i] + ((word_t)rem << word_shift), (word_t)y);
digits[i] = divmod.quot;
rem = (int)divmod.rem;
if ((rem ^ y) < 0 && rem) {
rem += y;
digits[i]--;
}
}
while (size() > 1 && !digits[size() - 1]) {
dec_size();
}
return rem;
}
template <class Tr>
typename Tr::word_t AnyIntView<Tr>::divmod_short_any(word_t y) {
if (!y || !is_valid()) {
invalidate();
throw IntOverflow{};
}
word_t rem = 0;
int i = size() - 1;
if (!i) {
auto divmod = std::div(digits[0], y);
digits[0] = divmod.quot;
rem = divmod.rem;
if ((rem ^ y) < 0 && rem) {
rem += y;
digits[0]--;
}
return rem;
}
if (std::abs(digits[i]) * 2 < std::abs(y)) {
rem = digits[i--];
dec_size();
}
do {
Tr::dbl_divmod(&digits[i], &rem, rem, digits[i], y);
} while (--i >= 0);
if ((rem ^ y) < 0 && rem) {
rem += y;
digits[0]--;
}
while (size() > 1 && !digits[size() - 1]) {
dec_size();
}
return rem;
}
template <class Tr>
bool AnyIntView<Tr>::mul_add_short_any(word_t y, word_t z) {
if (!is_valid()) {
return false;
}
for (int i = 0; i < size(); i++) {
word_t newc;
Tr::set_mul(&newc, digits.data() + i, y, digits[i]);
digits[i] += z;
z = newc;
}
if (!z) {
return true;
}
if (size() < max_size()) {
digits[inc_size()] = z;
return true;
}
z += (digits[size() - 1] >> word_shift);
digits[size() - 1] &= Tr::Base - 1;
if (!z || z == -1) {
digits[size() - 1] += (z << word_shift);
return true;
} else {
return false;
}
}
template <class Tr>
bool AnyIntView<Tr>::mod_div_any(const AnyIntView<Tr>& yp, AnyIntView<Tr>& quot, int round_mode) {
quot.invalidate();
if (!is_valid()) {
return false;
}
if (yp.size() == 1) {
word_t yv = yp.digits[0];
if (!yv) {
return false;
}
word_t rem = divmod_short_any(yv);
if (!round_mode) {
if ((yv > 0 && rem * 2 >= yv) || (yv < 0 && rem * 2 <= yv)) {
rem -= yv;
digits[0]++;
}
} else if (round_mode > 0 && rem) {
rem -= yv;
digits[0]++;
}
if (!normalize_bool_any()) {
return false;
}
if (size() > quot.max_size()) {
return false;
}
quot.set_size(size());
std::memcpy(quot.digits.data(), digits.data(), size() * sizeof(word_t));
*this = rem;
return true;
}
if (!yp.is_valid()) {
return invalidate_bool();
}
double y_top = yp.top_double();
if (y_top == 0) {
// division by zero
return invalidate_bool();
}
double y_inv = (double)Tr::Base / y_top;
int k = size() - yp.size();
if (k >= 0) {
if (std::abs(top_word()) * 2 <= std::abs(yp.top_word())) {
if (k > quot.max_size()) {
return invalidate_bool();
}
quot.set_size(k);
} else {
if (k >= quot.max_size()) {
return invalidate_bool();
}
quot.set_size(k + 1);
double x_top = top_double();
word_t q = std::llrint(x_top * y_inv * Tr::InvBase);
quot.digits[k] = q;
int i = yp.size() - 1;
word_t hi = 0;
Tr::sub_mul(&hi, &digits[k + i], q, yp.digits[i]);
while (--i >= 0) {
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
}
digits[size() - 1] += (hi << word_shift);
}
} else {
quot.set_size(1);
quot.digits[0] = 0;
}
while (--k >= 0) {
double x_top = top_double();
word_t q = std::llrint(x_top * y_inv);
quot.digits[k] = q;
for (int i = yp.size() - 1; i >= 0; --i) {
Tr::sub_mul(&digits[k + i + 1], &digits[k + i], q, yp.digits[i]);
}
dec_size();
digits[size() - 1] += (digits[size()] << word_shift);
}
if (size() >= yp.size()) {
assert(size() == yp.size());
double x_top = top_double();
double t = x_top * y_inv * Tr::InvBase;
if (round_mode >= 0) {
t += (round_mode ? 1 : 0.5);
}
word_t q = std::llrint(std::floor(t));
if (q) {
for (int i = 0; i < size(); i++) {
digits[i] -= q * yp.digits[i];
}
quot.digits[0] += q;
}
}
int q_adj = 0, sy = (y_inv > 0 ? 1 : -1);
if (round_mode < 0) {
// floor: must have 0 <= rem < y or 0 >= rem > y
int sr = sgn_un_any();
if (sr * sy < 0) {
q_adj = -1;
} else {
sr = cmp_un_any<TransformId<word_t>>(yp);
if (sr * sy >= 0) {
q_adj = 1;
}
}
} else if (round_mode > 0) {
// ceil: must have -y < rem <= 0 or -y > rem >= 0
int sr = sgn_un_any();
if (sr * sy > 0) {
q_adj = 1;
} else {
sr = cmp_un_any<TransformNegate<word_t>>(yp); // -rem ?? y
if (sr * sy >= 0) {
q_adj = -1;
}
}
} else {
// round: must have -y <= 2*rem < y or -y >= 2*rem > y
int sr = sgn_un_any();
if (sr * sy > 0) {
// y and rem same sign, check 2*rem < y or 2*rem > y
sr = cmp_un_any<TransformMul<word_t, 2>>(yp);
if (sr * sy >= 0) {
q_adj = 1;
}
} else {
// y and rem different sign, check 2*rem >= -y or 2*rem <= -y
sr = cmp_un_any<TransformMul<word_t, -2>>(yp);
if (sr * sy > 0) {
q_adj = -1;
}
}
}
if (q_adj) {
quot.digits[0] += q_adj;
if (q_adj < 0 ? !add_any(yp) : !sub_any(yp)) {
return invalidate_bool();
}
}
return normalize_bool_any();
}
template <class Tr>
bool AnyIntView<Tr>::mod_pow2_any(int exponent) {
if (!is_valid()) {
return false;
}
if (exponent <= 0) {
*this = 0;
return true;
}
int q = exponent - (size() - 1) * word_shift;
if (q >= word_bits) {
if (sgn() >= 0) {
return true;
}
if (exponent >= max_size() * word_shift) {
return invalidate_bool();
}
while (q >= word_shift) {
digits[inc_size()] = 0;
q -= word_shift;
}
if (q == word_shift - 1 && size() < max_size()) {
digits[size() - 1] = -Tr::Half;
digits[inc_size()] = 1;
} else {
digits[size() - 1] = ((word_t)1 << q);
}
return true;
}
while (q < 0) {
dec_size();
q += word_shift;
}
word_t pow = ((word_t)1 << q);
word_t v = digits[size() - 1] & (pow - 1);
if (!v) {
int k = size() - 1;
while (k > 0 && !digits[k - 1]) {
--k;
}
if (!k) {
*this = 0;
return true;
}
if (digits[k - 1] > 0) {
set_size(k);
return true;
}
if (exponent >= max_size() * word_shift) {
return invalidate_bool();
}
if (q - word_shift >= 0) {
digits[size() - 1] = 0;
digits[inc_size()] = ((word_t)1 << (q - word_shift));
}
if (q - word_shift == -1 && size() < max_size() - 1) {
digits[size() - 1] = -Tr::Half;
digits[inc_size()] = 1;
} else {
digits[size() - 1] = pow;
}
return true;
} else if (v >= Tr::Half) {
if (size() == max_size() - 1) {
return invalidate_bool();
} else {
digits[size() - 1] = v | -Tr::Half;
digits[inc_size()] = ((word_t)1 << (q - word_shift));
return true;
}
} else {
digits[size() - 1] = v;
return true;
}
}
template <class Tr>
bool AnyIntView<Tr>::mod_pow2_any(int exponent, int round_mode) {
if (round_mode < 0) {
return mod_pow2_any(exponent);
}
if (!is_valid()) {
return false;
}
if (exponent <= 0) {
*this = 0;
return true;
}
if (round_mode > 0) {
negate_any();
bool res = mod_pow2_any(exponent);
negate_any();
return res;
}
if (signed_fits_bits_any(exponent)) {
return true;
}
if (!mod_pow2_any(exponent)) {
return false;
}
if (!unsigned_fits_bits_any(exponent - 1)) {
return add_pow2_any(exponent, -1);
}
return true;
}
template <class Tr>
bool AnyIntView<Tr>::rshift_any(int exponent, int round_mode) {
if (exponent < 0) {
return invalidate_bool();
}
if (!exponent) {
return true;
}
if (exponent > size() * word_shift + word_bits - word_shift) {
if (!round_mode) {
*this = 0;
} else if (round_mode < 0) {
*this = (sgn() < 0 ? -1 : 0);
} else {
*this = (sgn() > 0 ? 1 : 0);
}
return true;
}
int q = exponent / word_shift, r = exponent % word_shift;
assert(q <= size());
if (!round_mode && !r) {
digits[q - 1] += Tr::Half;
round_mode = -1;
}
word_t v = (round_mode > 0 ? -1 : 0);
for (int i = 0; i < q; i++) {
v += digits[i];
v >>= word_shift;
}
set_size(size() - q);
if (!size()) {
if (!round_mode) {
*this = (((v >> (r - 1)) + 1) >> 1);
} else {
*this = (v >> r) + (round_mode > 0);
}
return true;
}
if (!r) {
std::memmove(digits.data(), digits.data() + q, size() * sizeof(word_t));
digits[0] += v + (round_mode > 0);
return true;
}
v += digits[q];
if (!round_mode) {
v = (((v >> (r - 1)) + 1) >> 1);
} else {
v >>= r;
v += (round_mode > 0);
}
word_t mask = ((word_t)1 << r) - 1;
for (int i = 1; i < size(); i++) {
word_t w = digits[q + i];
v += ((w & mask) << (word_shift - r));
digits[i - 1] = v;
v = (w >> r);
}
digits[size() - 1] = v;
return true;
}
template <class Tr>
bool AnyIntView<Tr>::lshift_any(int exponent) {
if (exponent < 0) {
return invalidate_bool();
}
if (!exponent) {
return true;
}
int q = exponent / word_shift, r = exponent % word_shift;
if (size() + q > max_size()) {
return invalidate_bool();
}
if (!r) {
std::memmove(digits.data() + q, digits.data(), size() * sizeof(word_t));
std::memset(digits.data(), 0, q * sizeof(word_t));
set_size(size() + q);
return true;
}
word_t v = 0, mask = (Tr::Base >> r) - 1;
for (int i = 0; i < size(); i++) {
word_t w = digits[i];
v += ((w & mask) << r);
digits[i] = v;
v = (w >> (word_shift - r));
}
if (v) {
if (size() + q < max_size()) {
digits[inc_size()] = v;
} else if (v != -1) {
return invalidate_bool();
} else {
digits[size() - 1] += (v << word_shift);
}
}
if (q) {
std::memmove(digits.data() + q, digits.data(), size() * sizeof(word_t));
std::memset(digits.data(), 0, q * sizeof(word_t));
set_size(size() + q);
}
return true;
}
template <class Tr>
bool AnyIntView<Tr>::unsigned_fits_bits_any(int nbits) const {
if (!is_valid()) {
return false;
}
if (sgn() < 0) {
return false;
}
if (!sgn()) {
return true;
}
if (nbits >= size() * word_shift) {
return true;
}
if (nbits < 0) {
return false;
}
auto dm = std::div(nbits, word_shift);
int k = dm.quot;
if (size() >= k + 2) {
if (!(size() == k + 2 && dm.rem == word_shift - 1)) {
return false;
}
if (digits[k + 1] != 1) {
return false;
}
if (digits[k] > -Tr::Half) {
return false;
} else if (digits[k] < -Tr::Half) {
return true;
}
} else {
if (size() <= k) {
return true;
}
word_t pow = ((word_t)1 << dm.rem);
if (digits[k] > pow) {
return false;
} else if (digits[k] < pow) {
return true;
}
}
while (--k >= 0) {
if (digits[k] < 0) {
return true;
} else if (digits[k] > 0) {
return false;
}
}
return false;
}
template <class Tr>
bool AnyIntView<Tr>::signed_fits_bits_any(int nbits) const {
if (!is_valid()) {
return false;
}
if (nbits > size() * word_shift) {
return true;
}
int s = sgn();
if (!s) {
return true;
}
if (nbits <= 0) {
return false;
}
auto dm = std::div(nbits - 1, word_shift);
int k = dm.quot;
if (size() <= k) {
return true;
}
if (size() >= k + 2) {
if (!(size() == k + 2 && dm.rem == word_shift - 1)) {
return false;
}
if (digits[k + 1] != s) {
return false;
}
word_t val = (s > 0 ? digits[k] : -digits[k]);
if (val > -Tr::Half) {
return false;
} else if (val < -Tr::Half) {
return true;
}
} else {
word_t val = (s > 0 ? digits[k] : -digits[k]);
word_t pow = ((word_t)1 << dm.rem);
if (val > pow) {
return false;
} else if (val < pow) {
return true;
}
}
while (--k >= 0) {
if (digits[k] < 0) {
return s > 0;
} else if (digits[k] > 0) {
return s < 0;
}
}
return s < 0;
}
template <class Tr>
int AnyIntView<Tr>::bit_size_any(bool sgnd) const {
if (!is_valid()) {
return 0x7fffffff;
}
int sg = sgn();
if (!sg) {
return 0;
} else if (sg >= 0) {
int k = size() - 1;
word_t q = digits[k];
if (k > 0 && q < Tr::MaxDenorm / 2) {
q <<= word_shift;
q += digits[--k];
}
if (!k) {
int s = 64 - td::count_leading_zeroes64(q);
return s + sgnd;
}
int s = 64 - td::count_leading_zeroes64(q - Tr::MaxDenorm / 4);
q -= (word_t)1 << s;
s += k * word_shift + sgnd;
while (k > 0) {
if (q >= Tr::MaxDenorm / 2) {
return s + 1;
} else if (q <= -Tr::MaxDenorm / 2) {
return s;
}
q <<= word_shift;
q += digits[--k];
}
return q >= 0 ? s + 1 : s;
} else if (sgnd) {
int k = size() - 1;
word_t q = digits[k];
if (k > 0 && q > -Tr::MaxDenorm / 2) {
q <<= word_shift;
q += digits[--k];
}
if (!k) {
int s = 64 - td::count_leading_zeroes64(~q);
return s + 1;
}
int s = 64 - td::count_leading_zeroes64(-q - Tr::MaxDenorm / 4);
q += (word_t)1 << s;
s += k * word_shift + 1;
while (k > 0) {
if (q >= Tr::MaxDenorm / 2) {
return s;
} else if (q <= -Tr::MaxDenorm / 2) {
return s + 1;
}
q <<= word_shift;
q += digits[--k];
}
return q >= 0 ? s : s + 1;
} else {
return 0x7fffffff;
}
}
template <class Tr>
bool AnyIntView<Tr>::export_bytes_any(unsigned char* buff, std::size_t buff_size, bool sgnd) const {
if (!is_valid()) {
return false;
}
if (!buff_size) {
return sgn_un_any() == 0;
}
int k = 0;
word_t v = 0;
unsigned char* ptr = buff + buff_size;
unsigned char s = (sgn_un_any() < 0 ? 0xff : 0);
if (s && !sgnd) {
return false;
}
for (int i = 0; i < size(); i++) {
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
if (ptr > buff) {
*--ptr = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
k += word_shift;
}
while (k >= 8) {
if (ptr > buff) {
*--ptr = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
k -= 8;
}
}
while (ptr > buff) {
*--ptr = (unsigned char)(v & 0xff);
v >>= 8;
}
if (v != -(s & 1)) {
return false;
}
return !sgnd ? true : !((*ptr ^ s) & 0x80);
}
template <class Tr>
bool AnyIntView<Tr>::export_bytes_lsb_any(unsigned char* buff, std::size_t buff_size, bool sgnd) const {
if (!is_valid()) {
return false;
}
if (!buff_size) {
return sgn_un_any() == 0;
}
int k = 0;
word_t v = 0;
unsigned char* end = buff + buff_size;
unsigned char s = (sgn_un_any() < 0 ? 0xff : 0);
if (s && !sgnd) {
return false;
}
for (int i = 0; i < size(); i++) {
if ((word_shift & 7) && word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
if (buff < end) {
*buff++ = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
k += word_shift;
}
while (k >= 8) {
if (buff < end) {
*buff++ = (unsigned char)(v & 0xff);
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
k -= 8;
}
}
while (buff < end) {
*buff++ = (unsigned char)(v & 0xff);
v >>= 8;
}
if (v != -(s & 1)) {
return false;
}
return !sgnd ? true : !((buff[-1] ^ s) & 0x80);
}
template <class Tr>
bool AnyIntView<Tr>::export_bits_any(unsigned char* buff, int offs, unsigned bits, bool sgnd) const {
if (!is_valid()) {
return false;
}
if (!bits) {
return sgn_un_any() == 0;
}
if (size() == 1 || bits < 64) {
word_t v = to_long_any();
if (bits < 64) {
if (!sgnd) {
if (v < 0 || (unsigned long long)v >= (1ULL << bits)) {
return false;
}
} else {
word_t pw = (1LL << (bits - 1));
if (v < -pw || v >= pw) {
return false;
}
}
td::bitstring::bits_store_long_top(buff, offs, v << (64 - bits), bits);
} else {
if (!sgnd && v < 0) {
return false;
}
td::bitstring::bits_memset(buff, offs, v < 0, bits - 64);
td::bitstring::bits_store_long_top(buff, offs + bits - 64, v, 64);
}
return true;
}
buff += (offs >> 3);
offs &= 7;
unsigned char s = (sgn_un_any() < 0 ? 0xff : 0);
if (s && !sgnd) {
return false;
}
unsigned end_offs = offs + bits;
unsigned char* ptr = buff + (end_offs >> 3);
int k = td::bits_negate32(end_offs) & 7;
word_t v = k ? (*ptr++ & ((1 << k) - 1)) : 0;
for (int i = 0; i < size(); i++) {
if (word_shift + 8 >= word_bits && k >= word_bits - word_shift - 1) {
int k1 = 8 - k;
v += (digits[i] << k) & 0xff;
if (ptr > buff) {
if (--ptr > buff) {
*ptr = (unsigned char)(v & 0xff);
} else {
int mask = (-0x100 >> offs) & 0xff;
if (((unsigned char)v ^ s) & mask) {
return false;
}
*ptr = (unsigned char)((*ptr & mask) | ((int)v & ~mask));
}
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
v += (digits[i] >> k1);
k += word_shift - 8;
} else {
v += (digits[i] << k);
k += word_shift;
}
while (k >= 8) {
if (ptr > buff) {
if (--ptr > buff) {
*ptr = (unsigned char)(v & 0xff);
} else {
int mask = (-0x100 >> offs) & 0xff;
if (((unsigned char)v ^ s) & mask) {
return false;
}
*ptr = (unsigned char)((*ptr & mask) | ((int)v & ~mask));
}
} else if ((unsigned char)(v & 0xff) != s) {
return false;
}
v >>= 8;
k -= 8;
}
}
if (ptr > buff) {
while (--ptr > buff) {
*ptr = (unsigned char)(v & 0xff);
v >>= 8;
}
int mask = (-0x100 >> offs) & 0xff;
if (((unsigned char)v ^ s) & mask) {
return false;
}
*ptr = (unsigned char)((*ptr & mask) | ((int)v & ~mask));
v >>= 8;
}
if (v != -(s & 1)) {
return false;
}
return !sgnd ? true : !((*ptr ^ s) & (0x80 >> offs));
}
template <class Tr>
bool AnyIntView<Tr>::import_bytes_any(const unsigned char* buff, std::size_t buff_size, bool sgnd) {
if (!buff_size) {
*this = 0;
return true;
}
unsigned char s = (sgnd && (buff[0] & 0x80)) ? 0xff : 0;
const unsigned char* ptr = buff + buff_size;
while (buff < ptr && *buff == s) {
buff++;
}
int k = 0;
word_t v = 0;
set_size(1);
assert(word_bits - word_shift >= 8);
while (ptr > buff) {
if (k >= word_shift) {
if (size() < max_size()) {
digits[size() - 1] = v;
inc_size();
v = 0;
k -= word_shift;
} else if (k >= word_bits - 8) {
return invalidate_bool();
}
}
v |= (((word_t) * --ptr) << k);
k += 8;
}
if (s) {
v -= ((word_t)1 << k);
}
digits[size() - 1] = v;
return normalize_bool_any();
}
template <class Tr>
bool AnyIntView<Tr>::import_bits_any(const unsigned char* buff, int offs, unsigned bits, bool sgnd) {
if (bits < word_shift) {
set_size(1);
unsigned long long val = td::bitstring::bits_load_long_top(buff, offs, bits);
if (sgnd) {
digits[0] = ((long long)val >> (64 - bits));
} else {
digits[0] = (val >> (64 - bits));
}
return true;
}
buff += (offs >> 3);
offs &= 7;
unsigned char s = (sgnd && (buff[0] & (0x80 >> offs))) ? 0xff : 0;
unsigned end_offs = (unsigned)offs + bits;
const unsigned char* ptr = buff + (end_offs >> 3);
if (buff < ptr && !((*buff ^ s) & (0xff >> offs))) {
buff++;
offs = 0;
while (buff < ptr && *buff == s) {
buff++;
}
}
int k = end_offs & 7;
word_t v = k ? (*ptr >> (8 - k)) : 0;
set_size(1);
assert(word_bits - word_shift >= 8);
while (ptr > buff) {
if (k >= word_shift) {
if (size() < max_size()) {
digits[size() - 1] = v;
inc_size();
v = 0;
k -= word_shift;
} else if (k >= word_bits - 8) {
return invalidate_bool();
}
}
v |= (((word_t) * --ptr) << k);
k += 8;
}
k -= offs;
word_t pw = ((word_t)1 << k);
v &= pw - 1;
if (s) {
v -= pw;
}
digits[size() - 1] = v;
return normalize_bool_any();
}
template <class Tr>
bool AnyIntView<Tr>::import_bytes_lsb_any(const unsigned char* buff, std::size_t buff_size, bool sgnd) {
if (!buff_size) {
*this = 0;
return true;
}
const unsigned char* end = buff + buff_size;
unsigned char s = (sgnd && (end[-1] & 0x80)) ? 0xff : 0;
while (end > buff && end[-1] == s) {
--end;
}
int k = 0;
word_t v = 0;
set_size(1);
assert(word_bits - word_shift >= 8);
while (buff < end) {
if (k >= word_shift) {
if (size() < max_size()) {
digits[size() - 1] = v;
inc_size();
v = 0;
k -= word_shift;
} else if (k >= word_bits - 8) {
return invalidate_bool();
}
}
v |= (((word_t)*buff++) << k);
k += 8;
}
if (s) {
v -= ((word_t)1 << k);
}
digits[size() - 1] = v;
return normalize_bool_any();
}
template <class Tr>
int AnyIntView<Tr>::parse_hex_any(const char* str, int str_len, int* frac) {
invalidate();
bool sgn = (str[0] == '-');
int i = sgn, j;
int p = (frac ? -1 : 0);
while (i < str_len && str[i] == '0') {
i++;
}
for (j = i; j < str_len; j++) {
int c = str[j];
if (c == '.' && p < 0) {
p = j + 1;
continue;
}
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
break;
}
}
if (j == sgn + (p > 0)) {
return 0;
}
if ((j - i - (p > 0)) * 4 > (max_size() - 1) * word_shift + word_bits - 2) {
return 0;
}
int j_len = j, k = 0;
word_t v = 0;
set_size(0);
while (j > i) {
if (j == p) {
--j;
continue;
}
if (k >= word_shift && size() < max_size() - 1) {
digits[inc_size()] = (sgn ? -v : v);
v = 0;
k -= word_shift;
}
int c = str[--j];
if (c <= '9') {
c -= '0';
} else {
c = (c | 0x20) - ('a' - 10);
}
v += ((word_t)c << k);
k += 4;
enforce(k < word_bits - 1);
}
digits[inc_size()] = (sgn ? -v : v);
if (!normalize_bool_any()) {
invalidate();
return 0;
}
if (p) {
*frac = (p > 0 ? j_len - p : -1);
}
return j_len;
}
template <class Tr>
int AnyIntView<Tr>::parse_binary_any(const char* str, int str_len, int* frac) {
invalidate();
bool sgn = (str[0] == '-');
int i = sgn, j, p = (frac ? -1 : 0);
while (i < str_len && str[i] == '0') {
i++;
}
for (j = i; j < str_len; j++) {
int c = str[j];
if (c != '0' && c != '1') {
if (c == '.' && p < 0) {
p = j + 1;
continue;
}
break;
}
}
if (j == sgn + (p > 0)) {
return 0;
}
if (j - i - (p > 0) > (max_size() - 1) * word_shift + word_bits - 2) {
return 0;
}
int j_len = j, k = 0;
word_t v = 0;
set_size(0);
while (j > i) {
if (j == p) {
--j;
continue;
}
if (k >= word_shift && size() < max_size() - 1) {
digits[inc_size()] = (sgn ? -v : v);
v = 0;
k -= word_shift;
}
v += ((word_t)(str[--j] & 1) << k);
k++;
enforce(k < word_bits - 1);
}
digits[inc_size()] = (sgn ? -v : v);
if (!normalize_bool_any()) {
invalidate();
return 0;
}
if (p) {
*frac = (p > 0 ? j_len - p : -1);
}
return j_len;
}
template <class Tr>
std::string AnyIntView<Tr>::to_dec_string_slow_destroy_any() {
if (!is_valid()) {
return "NaN";
}
std::string x;
x.reserve((size() * word_shift + word_bits) * 97879 / 325147 + 2);
int s = sgn();
if (s < 0) {
negate_any();
}
do {
x += (char)('0' + divmod_short_any(10));
} while (sgn());
if (s < 0) {
x += '-';
}
std::reverse(x.begin(), x.end());
return x;
}
template <class Tr>
std::string AnyIntView<Tr>::to_dec_string_destroy_any() {
if (!is_valid()) {
return "NaN";
}
std::string s;
std::vector<word_t> stack;
int l10 = (size() * word_shift + word_bits) * 97879 / 325147;
s.reserve(l10 + 2);
stack.reserve(l10 / Tr::max_pow10_exp + 1);
if (sgn() < 0) {
negate_any();
s += '-';
}
do {
stack.push_back(divmod_short_any(Tr::max_pow10));
} while (sgn());
char slice[word_bits * 97879 / 325147 + 2];
std::sprintf(slice, "%lld", stack.back());
s += slice;
stack.pop_back();
while (stack.size()) {
std::sprintf(slice, "%018lld", stack.back());
s += slice;
stack.pop_back();
}
return s;
}
static const char hex_digits[] = "0123456789abcdef";
static const char HEX_digits[] = "0123456789ABCDEF";
template <class Tr>
std::string AnyIntView<Tr>::to_hex_string_slow_destroy_any() {
if (!is_valid()) {
return "NaN";
}
std::string x;
x.reserve(((size() * word_shift + word_bits) >> 2) + 2);
int s = sgn();
if (s < 0) {
negate_any();
}
do {
x += hex_digits[divmod_short_any(16)];
} while (sgn());
if (s < 0) {
x += '-';
}
std::reverse(x.begin(), x.end());
return x;
}
template <class Tr>
std::string AnyIntView<Tr>::to_hex_string_any(bool upcase) const {
if (!is_valid()) {
return "NaN";
}
int s = sgn(), k = 0;
if (!s) {
return "0";
}
std::string x;
x.reserve(((size() * word_shift + word_bits) >> 2) + 2);
assert(word_shift < word_bits - 4);
const char* hex_digs = (upcase ? HEX_digits : hex_digits);
word_t v = 0;
for (int i = 0; i < size(); i++) {
v += ((s >= 0 ? digits[i] : -digits[i]) << k);
k += word_shift;
while (k >= 4 && (v || i < size() - 1)) {
x += hex_digs[v & 15];
v >>= 4;
k -= 4;
}
}
assert(v >= 0);
while (v > 0) {
x += hex_digs[v & 15];
v >>= 4;
}
if (s < 0) {
x += '-';
}
std::reverse(x.begin(), x.end());
return x;
}
template <class Tr>
std::string AnyIntView<Tr>::to_binary_string_any() const {
if (!is_valid()) {
return "NaN";
}
int s = sgn();
if (!s) {
return "0";
}
std::string x;
x.reserve(size() * word_shift + word_bits + 2);
assert(word_shift < word_bits - 1);
word_t v = 0;
for (int i = 0; i < size(); i++) {
v += (s >= 0 ? digits[i] : -digits[i]);
int k = word_shift;
while (--k >= 0 && (v || i < size() - 1)) {
x += (v & 1 ? '1' : '0');
v >>= 1;
}
}
assert(v >= 0);
while (v > 0) {
x += (v & 1 ? '1' : '0');
v >>= 1;
}
if (s < 0) {
x += '-';
}
std::reverse(x.begin(), x.end());
return x;
}
template <int len, class Tr>
BigIntG<len, Tr>& BigIntG<len, Tr>::denormalize() {
word_t val = 0;
for (int i = 0; i < n; i++) {
val += digits[i];
digits[i] = (val & (Tr::Base - 1));
val >>= word_shift;
}
while (n < word_cnt) {
digits[n++] = (val & (Tr::Base - 1));
val >>= word_shift;
}
return *this;
}
template <int len, class Tr>
BigIntG<len, Tr>& BigIntG<len, Tr>::logical_not() {
digits[0] = ~digits[0];
for (int i = 1; i < n; i++) {
digits[i] = -digits[i];
}
return *this;
}
template <int len, class Tr>
std::ostream& BigIntG<len, Tr>::dump(std::ostream& os, bool nl) const {
os << "{";
//auto f = os.flags();
//os.flags(std::ios::hex | std::ios::showbase);
//os.width(16);
for (int i = n - 1; i >= 0; i--) {
os << digits[i] << (i ? ' ' : '}');
}
if (!n) {
os << "nan}";
}
if (nl) {
os << std::endl;
}
//os.flags(f);
return os;
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::dump() const {
std::ostringstream os;
dump(os);
return os.str();
}
template <int len, class Tr>
int BigIntG<len, Tr>::parse_dec_slow(const char* str, int str_len) {
*this = 0;
int i;
bool sgn = (str[0] == '-');
if (str_len <= static_cast<int>(sgn)) {
return 0;
}
for (i = sgn; i < str_len; i++) {
if (str[i] < '0' || str[i] > '9') {
return i;
}
mul_tiny(10);
add_tiny(sgn ? '0' - str[i] : str[i] - '0');
if (!normalize_bool()) {
*this = 0;
return 0;
}
}
return i;
}
template <int len, class Tr>
int BigIntG<len, Tr>::parse_dec(const char* str, int str_len, int* frac) {
*this = 0;
int i;
int p = frac ? -1 : 0;
bool sgn = (str[0] == '-'), ok = false;
word_t q = 1, a = 0;
for (i = sgn; i < str_len; i++) {
if (str[i] == '.') {
if (p >= 0) {
break;
}
p = i + 1;
continue;
}
int digit = (int)str[i] - '0';
if ((unsigned)digit >= 10) {
break;
}
ok = true;
if (q >= Tr::Half / 10) {
if (!mul_add_short_bool(q, a)) {
return 0;
}
q = 1;
a = 0;
}
q *= 10;
a *= 10;
a += (sgn ? -digit : digit);
}
if (!ok || !mul_add_short_bool(q, a) || !normalize_bool()) {
return 0;
}
if (frac) {
*frac = (p > 0 ? i - p : -1);
}
return i;
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_dec_string_slow() const {
BigIntG<len, Tr> copy(*this);
copy.normalize_bool();
return copy.as_any_int().to_dec_string_slow_destroy_any();
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_dec_string() const {
BigIntG<len, Tr> copy(*this);
copy.normalize_bool();
//std::cout << "(tds " << (const void*)this << "->" << (void*)&copy << ")";
return copy.as_any_int().to_dec_string_destroy_any();
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_dec_string_destroy() {
normalize_bool();
return as_any_int().to_dec_string_destroy_any();
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_hex_string_slow() const {
BigIntG<len, Tr> copy(*this);
copy.normalize_bool();
return copy.as_any_int().to_hex_string_slow_destroy_any();
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_hex_string(bool upcase) const {
return as_any_int().to_hex_string_any(upcase);
}
template <int len, class Tr>
std::string BigIntG<len, Tr>::to_binary_string() const {
return as_any_int().to_binary_string_any();
}
template <int len, class Tr>
std::ostream& operator<<(std::ostream& os, const BigIntG<len, Tr>& x) {
return os << x.to_dec_string();
}
template <int len, class Tr>
std::ostream& operator<<(std::ostream& os, BigIntG<len, Tr>&& x) {
return os << x.to_dec_string_destroy();
}
extern template class AnyIntView<BigIntInfo>;
extern template class BigIntG<257, BigIntInfo>;
typedef BigIntG<257, BigIntInfo> BigInt256;
namespace literals {
extern BigInt256 operator""_i256(const char* str, std::size_t str_len);
extern BigInt256 operator""_x256(const char* str, std::size_t str_len);
} // namespace literals
} // namespace td