// Copyright 2018 Daniel Parker // Distributed under the Boost license, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // See https://github.com/danielaparker/jsoncons for latest version #ifndef JSONCONS_BIGNUM_HPP #define JSONCONS_BIGNUM_HPP #include #include // std::vector #include #include #include // assert #include // std::numeric_limits #include // std::max, std::min, std::reverse #include // std::string #include // std::memcpy #include // std::fmod #include // std::allocator #include // std::initializer_list #include // std::enable_if namespace jsoncons { /* This implementation is based on Chapter 2 and Appendix A of Ammeraal, L. (1996) Algorithms and Data Structures in C++, Chichester: John Wiley. */ template class basic_bignum_base { public: typedef Allocator allocator_type; typedef typename std::allocator_traits:: template rebind_alloc basic_type_allocator_type; private: basic_type_allocator_type allocator_; public: basic_bignum_base() : allocator_() { } explicit basic_bignum_base(const allocator_type& allocator) : allocator_(basic_type_allocator_type(allocator)) { } basic_type_allocator_type allocator() const { return allocator_; } }; template > class basic_bignum : protected basic_bignum_base { private: using basic_bignum_base::allocator; static constexpr uint64_t max_basic_type = (std::numeric_limits::max)(); static constexpr uint64_t basic_type_bits = sizeof(uint64_t) * 8; // Number of bits static constexpr uint64_t basic_type_halfBits = basic_type_bits/2; static constexpr uint16_t word_length = 4; // Use multiples of word_length words static constexpr uint64_t r_mask = (uint64_t(1) << basic_type_halfBits) - 1; static constexpr uint64_t l_mask = max_basic_type - r_mask; static constexpr uint64_t l_bit = max_basic_type - (max_basic_type >> 1); union { size_t capacity_; uint64_t values_[2]; }; uint64_t* data_; bool neg_; bool dynamic_; size_t length_; public: // Constructors and Destructor basic_bignum() : values_{0,0}, data_(values_), neg_(false), dynamic_(false), length_(0) { } explicit basic_bignum(const Allocator& allocator) : basic_bignum_base(allocator), values_{0,0}, data_(values_), neg_(false), dynamic_(false), length_(0) { } basic_bignum(const basic_bignum& n) : basic_bignum_base(n.allocator()), neg_(n.neg_), length_(n.length_) { if (!n.dynamic_) { values_[0] = n.values_[0]; values_[1] = n.values_[1]; data_ = values_; dynamic_ = false; } else { capacity_ = n.capacity_; data_ = allocator().allocate(capacity_); dynamic_ = true; std::memcpy( data_, n.data_, n.length_*sizeof(uint64_t) ); } } basic_bignum(basic_bignum&& other) : basic_bignum_base(other.allocator()), neg_(other.neg_), dynamic_(false), length_(other.length_) { initialize(std::move(other)); } template explicit basic_bignum(const CharT* str) : values_{0,0} { initialize(str, strlen(str)); } template basic_bignum(const std::basic_string& s) : values_{0,0} { initialize(s.data(), s.length()); } template basic_bignum(const CharT* data, size_t length) : values_{0,0} { initialize(data, length); } template basic_bignum(const std::basic_string& s, uint8_t base) : values_{0,0} { initialize(s.data(), s.length(), base); } template basic_bignum(const CharT* data, size_t length, uint8_t base) : values_{0,0} { initialize(data, length, base); } basic_bignum(int signum, std::initializer_list l) : values_{0,0} { if (l.size() > 0) { basic_bignum v = 0; for (auto c: l) { v = (v * 256) + (uint64_t)(c); } if (signum == -1) { v = -1 - v; } initialize(v); } else { neg_ = false; initialize_from_integer(0u); } } basic_bignum(int signum, const uint8_t* str, size_t n) : values_{0,0} { if (n > 0) { basic_bignum v = 0; for (size_t i = 0; i < n; i++) { v = (v * 256) + (uint64_t)(str[i]); } if (signum == -1) { v = -1 - v; } initialize(v); } else { neg_ = false; initialize_from_integer(0u); } } basic_bignum(short i) : values_{0,0} { neg_ = i < 0; uint64_t u = neg_ ? -i : i; initialize_from_integer( u ); } basic_bignum(unsigned short u) : values_{0,0} { neg_ = false; initialize_from_integer( u ); } basic_bignum(int i) : values_{0,0} { neg_ = i < 0; uint64_t u = neg_ ? -i : i; initialize_from_integer( u ); } basic_bignum(unsigned int u) : values_{0,0} { neg_ = false; initialize_from_integer( u ); } basic_bignum(long i) : values_{0,0} { neg_ = i < 0; uint64_t u = neg_ ? -i : i; initialize_from_integer( u ); } basic_bignum(unsigned long u) : values_{0,0} { neg_ = false; initialize_from_integer( u ); } basic_bignum(long long i) : values_{0,0} { neg_ = i < 0; uint64_t u = neg_ ? -i : i; initialize_from_integer( u ); } basic_bignum(unsigned long long u) : values_{0,0} { neg_ = false; initialize_from_integer( u ); } explicit basic_bignum( double x ) : values_{0,0} { bool neg = false; if ( x < 0 ) { neg = true; x = -x; } basic_bignum v = 0; double values = (double)max_basic_type + 1.0; basic_bignum factor = 1; while ( x >= 1 ) { uint64_t u = (uint64_t) std::fmod( x, values ); v = v + factor* basic_bignum(u); x /= values; factor = factor*(basic_bignum( max_basic_type ) + basic_bignum(1)); } if ( neg ) { v.neg_ = true; } initialize( v ); } explicit basic_bignum(long double x) : values_{0,0} { bool neg = false; if ( x < 0 ) { neg = true; x = -x; } basic_bignum v = 0; long double values = (long double)max_basic_type + 1.0; basic_bignum factor = 1; while ( x >= 1.0 ) { uint64_t u = (uint64_t) std::fmod( x, values ); v = v + factor* basic_bignum(u); x /= values; factor = factor*(basic_bignum( max_basic_type ) + basic_bignum(1)); } if ( neg ) { v.neg_ = true; } initialize( v ); } ~basic_bignum() { if ( dynamic_ ) { allocator().deallocate(data_, capacity_); } } size_t capacity() const { return dynamic_ ? capacity_ : 2; } // Operators bool operator!() const { return length() == 0 ? true : false; } basic_bignum operator-() const { basic_bignum v = *this; v.neg_ = !v.neg_; return v; } basic_bignum& operator=( const basic_bignum& y ) { if ( this != &y ) { set_length( y.length() ); neg_ = y.neg_; if ( y.length() > 0 ) { std::memcpy( data_, y.data_, y.length()*sizeof(uint64_t) ); } } return *this; } basic_bignum& operator+=( const basic_bignum& y ) { if ( neg_ != y.neg_ ) return *this -= -y; uint64_t d; uint64_t carry = 0; incr_length( (std::max)(y.length(), length()) + 1 ); for (size_t i = 0; i < length(); i++ ) { if ( i >= y.length() && carry == 0 ) break; d = data_[i] + carry; carry = d < carry; if ( i < y.length() ) { data_[i] = d + y.data_[i]; if ( data_[i] < d ) carry = 1; } else data_[i] = d; } reduce(); return *this; } basic_bignum& operator-=( const basic_bignum& y ) { if ( neg_ != y.neg_ ) return *this += -y; if ( (!neg_ && y > *this) || (neg_ && y < *this) ) return *this = -(y - *this); uint64_t borrow = 0; uint64_t d; for (size_t i = 0; i < length(); i++ ) { if ( i >= y.length() && borrow == 0 ) break; d = data_[i] - borrow; borrow = d > data_[i]; if ( i < y.length()) { data_[i] = d - y.data_[i]; if ( data_[i] > d ) borrow = 1; } else data_[i] = d; } reduce(); return *this; } basic_bignum& operator*=( int64_t y ) { *this *= uint64_t(y < 0 ? -y : y); if ( y < 0 ) neg_ = !neg_; return *this; } basic_bignum& operator*=( uint64_t y ) { size_t len0 = length(); uint64_t hi; uint64_t lo; uint64_t dig = data_[0]; uint64_t carry = 0; incr_length( length() + 1 ); size_t i = 0; for (i = 0; i < len0; i++ ) { DDproduct( dig, y, hi, lo ); data_[i] = lo + carry; dig = data_[i+1]; carry = hi + (data_[i] < lo); } data_[i] = carry; reduce(); return *this; } basic_bignum& operator*=( basic_bignum y ) { if ( length() == 0 || y.length() == 0 ) return *this = 0; bool difSigns = neg_ != y.neg_; if ( length() + y.length() == 2 ) // length() = y.length() = 1 { uint64_t a = data_[0], b = y.data_[0]; data_[0] = a * b; if ( data_[0] / a != b ) { set_length( 2 ); DDproduct( a, b, data_[1], data_[0] ); } neg_ = difSigns; return *this; } if ( length() == 1 ) // && y.length() > 1 { uint64_t digit = data_[0]; *this = y; *this *= digit; } else { if ( y.length() == 1 ) *this *= y.data_[0]; else { size_t lenProd = length() + y.length(), jA, jB; uint64_t sumHi = 0, sumLo, hi, lo, sumLo_old, sumHi_old, carry=0; basic_bignum x = *this; set_length( lenProd ); // Give *this length lenProd for (size_t i = 0; i < lenProd; i++ ) { sumLo = sumHi; sumHi = carry; carry = 0; for ( jA=0; jA < x.length(); jA++ ) { jB = i - jA; if ( jB >= 0 && jB < y.length() ) { DDproduct( x.data_[jA], y.data_[jB], hi, lo ); sumLo_old = sumLo; sumHi_old = sumHi; sumLo += lo; if ( sumLo < sumLo_old ) sumHi++; sumHi += hi; carry += (sumHi < sumHi_old); } } data_[i] = sumLo; } } } reduce(); neg_ = difSigns; return *this; } basic_bignum& operator/=( const basic_bignum& divisor ) { basic_bignum r; divide( divisor, *this, r, false ); return *this; } basic_bignum& operator%=( const basic_bignum& divisor ) { basic_bignum q; divide( divisor, q, *this, true ); return *this; } basic_bignum& operator<<=( uint64_t k ) { size_t q = (size_t)(k / basic_type_bits); if ( q ) // Increase length_ by q: { incr_length(length() + q); for (size_t i = length(); i-- > 0; ) data_[i] = ( i < q ? 0 : data_[i - q]); k %= basic_type_bits; } if ( k ) // 0 < k < basic_type_bits: { uint64_t k1 = basic_type_bits - k; uint64_t mask = (1 << k) - 1; incr_length( length() + 1 ); for (size_t i = length(); i-- > 0; ) { data_[i] <<= k; if ( i > 0 ) data_[i] |= (data_[i-1] >> k1) & mask; } } reduce(); return *this; } basic_bignum& operator>>=(uint64_t k) { size_t q = (size_t)(k / basic_type_bits); if ( q >= length() ) { set_length( 0 ); return *this; } if (q > 0) { memmove( data_, data_+q, (size_t)((length() - q)*sizeof(uint64_t)) ); set_length( length() - q ); k %= basic_type_bits; if ( k == 0 ) { reduce(); return *this; } } size_t n = (size_t)(length() - 1); int64_t k1 = basic_type_bits - k; uint64_t mask = (1 << k) - 1; for (size_t i = 0; i <= n; i++) { data_[i] >>= k; if ( i < n ) data_[i] |= ((data_[i+1] & mask) << k1); } reduce(); return *this; } basic_bignum& operator++() { *this += 1; return *this; } basic_bignum operator++(int) { basic_bignum old = *this; ++(*this); return old; } basic_bignum& operator--() { *this -= 1; return *this; } basic_bignum operator--(int) { basic_bignum old = *this; --(*this); return old; } basic_bignum& operator|=( const basic_bignum& a ) { if ( length() < a.length() ) { incr_length( a.length() ); } const uint64_t* qBegin = a.begin(); const uint64_t* q = a.end() - 1; uint64_t* p = begin() + a.length() - 1; while ( q >= qBegin ) { *p-- |= *q--; } reduce(); return *this; } basic_bignum& operator^=( const basic_bignum& a ) { if ( length() < a.length() ) { incr_length( a.length() ); } const uint64_t* qBegin = a.begin(); const uint64_t* q = a.end() - 1; uint64_t* p = begin() + a.length() - 1; while ( q >= qBegin ) { *p-- ^= *q--; } reduce(); return *this; } basic_bignum& operator&=( const basic_bignum& a ) { size_t old_length = length(); set_length( (std::min)( length(), a.length() ) ); const uint64_t* pBegin = begin(); uint64_t* p = end() - 1; const uint64_t* q = a.begin() + length() - 1; while ( p >= pBegin ) { *p-- &= *q--; } if ( old_length > length() ) { memset( data_ + length(), 0, old_length - length() ); } reduce(); return *this; } explicit operator bool() const { return length() != 0 ? true : false; } explicit operator int64_t() const { int64_t x = 0; if ( length() > 0 ) { x = data_ [0]; } return neg_ ? -x : x; } explicit operator uint64_t() const { uint64_t u = 0; if ( length() > 0 ) { u = data_ [0]; } return u; } explicit operator double() const { double x = 0.0; double factor = 1.0; double values = (double)max_basic_type + 1.0; const uint64_t* p = begin(); const uint64_t* pEnd = end(); while ( p < pEnd ) { x += *p*factor; factor *= values; ++p; } return neg_ ? -x : x; } explicit operator long double() const { long double x = 0.0; long double factor = 1.0; long double values = (long double)max_basic_type + 1.0; const uint64_t* p = begin(); const uint64_t* pEnd = end(); while ( p < pEnd ) { x += *p*factor; factor *= values; ++p; } return neg_ ? -x : x; } template void dump(int& signum, std::vector& data) const { basic_bignum n(*this); if (neg_) { signum = -1; n = - n -1; } else { signum = 1; } basic_bignum divisor(256); while (n >= 256) { basic_bignum q; basic_bignum r; n.divide(divisor, q, r, true); n = q; data.push_back((uint8_t)(uint64_t)r); } if (n >= 0) { data.push_back((uint8_t)(uint64_t)n); } std::reverse(data.begin(),data.end()); } template void dump(std::basic_string& data) const { basic_bignum v(*this); int len = int(uint32_t(v.length()) * basic_bignum::basic_type_bits / 3) + 2; data.resize(len); int n = len; int i = 0; // 1/3 > ln(2)/ln(10) static uint64_t p10 = 1; static uint64_t ip10 = 0; if ( v.length() == 0 ) { data[0] = '0'; i = 1; } else { uint64_t r; if ( p10 == 1 ) { while ( p10 <= (std::numeric_limits::max)()/10 ) { p10 *= 10; ip10++; } } // p10 is max unsigned power of 10 basic_bignum R; basic_bignum LP10 = p10; // LP10 = p10 = ::pow(10, ip10) if ( v.neg_ ) { data[0] = '-'; i = 1; } do { v.divide( LP10, v, R, true ); r = (R.length() ? R.data_[0] : 0); for ( size_t j=0; j < ip10; j++ ) { data[--n] = char(r % 10 + '0'); r /= 10; if ( r + v.length() == 0 ) break; } } while ( v.length() ); while ( n < len ) data[i++] = data[n++]; } data.resize(i); } template void dump_hex_string(std::basic_string& data) const { basic_bignum v(*this); int len = int(uint32_t(v.length()) * basic_bignum::basic_type_bits / 3) + 2; data.resize(len); int n = len; int i = 0; // 1/3 > ln(2)/ln(10) static uint64_t p10 = 1; static uint64_t ip10 = 0; if ( v.length() == 0 ) { data[0] = '0'; i = 1; } else { uint64_t r; if ( p10 == 1 ) { while ( p10 <= (std::numeric_limits::max)()/16 ) { p10 *= 16; ip10++; } } // p10 is max unsigned power of 16 basic_bignum R; basic_bignum LP10 = p10; // LP10 = p10 = ::pow(16, ip10) if ( v.neg_ ) { data[0] = '-'; i = 1; } do { v.divide( LP10, v, R, true ); r = (R.length() ? R.data_[0] : 0); for ( size_t j=0; j < ip10; j++ ) { uint8_t c = r % 16; data[--n] = (c < 10) ? ('0' + c) : ('A' - 10 + c); r /= 16; if ( r + v.length() == 0 ) break; } } while ( v.length() ); while ( n < len ) data[i++] = data[n++]; } data.resize(i); } // Global Operators friend bool operator==( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) == 0 ? true : false; } friend bool operator==( const basic_bignum& x, int y ) { return x.compare(y) == 0 ? true : false; } friend bool operator!=( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) != 0 ? true : false; } friend bool operator!=( const basic_bignum& x, int y ) { return x.compare(basic_bignum(y)) != 0 ? true : false; } friend bool operator<( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) < 0 ? true : false; } friend bool operator<( const basic_bignum& x, int64_t y ) { return x.compare(y) < 0 ? true : false; } friend bool operator>( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) > 0 ? true : false; } friend bool operator>( const basic_bignum& x, int y ) { return x.compare(basic_bignum(y)) > 0 ? true : false; } friend bool operator<=( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) <= 0 ? true : false; } friend bool operator<=( const basic_bignum& x, int y ) { return x.compare(y) <= 0 ? true : false; } friend bool operator>=( const basic_bignum& x, const basic_bignum& y ) { return x.compare(y) >= 0 ? true : false; } friend bool operator>=( const basic_bignum& x, int y ) { return x.compare(y) >= 0 ? true : false; } friend basic_bignum operator+( basic_bignum x, const basic_bignum& y ) { return x += y; } friend basic_bignum operator+( basic_bignum x, int64_t y ) { return x += y; } friend basic_bignum operator-( basic_bignum x, const basic_bignum& y ) { return x -= y; } friend basic_bignum operator-( basic_bignum x, int64_t y ) { return x -= y; } friend basic_bignum operator*( int64_t x, const basic_bignum& y ) { return basic_bignum(y) *= x; } friend basic_bignum operator*( basic_bignum x, const basic_bignum& y ) { return x *= y; } friend basic_bignum operator*( basic_bignum x, int64_t y ) { return x *= y; } friend basic_bignum operator/( basic_bignum x, const basic_bignum& y ) { return x /= y; } friend basic_bignum operator/( basic_bignum x, int y ) { return x /= y; } friend basic_bignum operator%( basic_bignum x, const basic_bignum& y ) { return x %= y; } friend basic_bignum operator<<( basic_bignum u, unsigned k ) { return u <<= k; } friend basic_bignum operator<<( basic_bignum u, int k ) { return u <<= k; } friend basic_bignum operator>>( basic_bignum u, unsigned k ) { return u >>= k; } friend basic_bignum operator>>( basic_bignum u, int k ) { return u >>= k; } friend basic_bignum operator|( basic_bignum x, const basic_bignum& y ) { return x |= y; } friend basic_bignum operator|( basic_bignum x, int y ) { return x |= y; } friend basic_bignum operator|( basic_bignum x, unsigned y ) { return x |= y; } friend basic_bignum operator^( basic_bignum x, const basic_bignum& y ) { return x ^= y; } friend basic_bignum operator^( basic_bignum x, int y ) { return x ^= y; } friend basic_bignum operator^( basic_bignum x, unsigned y ) { return x ^= y; } friend basic_bignum operator&( basic_bignum x, const basic_bignum& y ) { return x &= y; } friend basic_bignum operator&( basic_bignum x, int y ) { return x &= y; } friend basic_bignum operator&( basic_bignum x, unsigned y ) { return x &= y; } friend basic_bignum abs( const basic_bignum& a ) { if ( a.neg_ ) { return -a; } return a; } friend basic_bignum power( basic_bignum x, unsigned n ) { basic_bignum y = 1; while ( n ) { if ( n & 1 ) { y *= x; } x *= x; n >>= 1; } return y; } friend basic_bignum sqrt( const basic_bignum& a ) { basic_bignum x = a; basic_bignum b = a; basic_bignum q; b <<= 1; while ( b >>= 2, b > 0 ) { x >>= 1; } while ( x > (q = a/x) + 1 || x < q - 1 ) { x += q; x >>= 1; } return x < q ? x : q; } template friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_bignum& v) { std::basic_string s; v.dump(s); os << s; return os; } int compare( const basic_bignum& y ) const { if ( neg_ != y.neg_ ) return y.neg_ - neg_; int code = 0; if ( length() == 0 && y.length() == 0 ) code = 0; else if ( length() < y.length() ) code = -1; else if ( length() > y.length() ) code = +1; else { for (size_t i = length(); i-- > 0; ) { if (data_[i] > y.data_[i]) { code = 1; break; } else if (data_[i] < y.data_[i]) { code = -1; break; } } } return neg_ ? -code : code; } private: void DDproduct( uint64_t A, uint64_t B, uint64_t& hi, uint64_t& lo ) const // Multiplying two digits: (hi, lo) = A * B { uint64_t hiA = A >> basic_type_halfBits, loA = A & r_mask, hiB = B >> basic_type_halfBits, loB = B & r_mask, mid1, mid2, old; lo = loA * loB; hi = hiA * hiB; mid1 = loA * hiB; mid2 = hiA * loB; old = lo; lo += mid1 << basic_type_halfBits; hi += (lo < old) + (mid1 >> basic_type_halfBits); old = lo; lo += mid2 << basic_type_halfBits; hi += (lo < old) + (mid2 >> basic_type_halfBits); } uint64_t DDquotient( uint64_t A, uint64_t B, uint64_t d ) const // Divide double word (A, B) by d. Quotient = (qHi, qLo) { uint64_t left, middle, right, qHi, qLo, x, dLo1, dHi = d >> basic_type_halfBits, dLo = d & r_mask; qHi = A/(dHi + 1); // This initial guess of qHi may be too small. middle = qHi * dLo; left = qHi * dHi; x = B - (middle << basic_type_halfBits); A -= (middle >> basic_type_halfBits) + left + (x > B); B = x; dLo1 = dLo << basic_type_halfBits; // Increase qHi if necessary: while ( A > dHi || (A == dHi && B >= dLo1) ) { x = B - dLo1; A -= dHi + (x > B); B = x; qHi++; } qLo = ((A << basic_type_halfBits) | (B >> basic_type_halfBits))/(dHi + 1); // This initial guess of qLo may be too small. right = qLo * dLo; middle = qLo * dHi; x = B - right; A -= (x > B); B = x; x = B - (middle << basic_type_halfBits); A -= (middle >> basic_type_halfBits) + (x > B); B = x; // Increase qLo if necessary: while ( A || B >= d ) { x = B - d; A -= (x > B); B = x; qLo++; } return (qHi << basic_type_halfBits) + qLo; } void subtractmul( uint64_t* a, uint64_t* b, size_t n, uint64_t& q ) const // a -= q * b: b in n positions; correct q if necessary { uint64_t hi, lo, d, carry = 0; size_t i; for ( i = 0; i < n; i++ ) { DDproduct( b[i], q, hi, lo ); d = a[i]; a[i] -= lo; if ( a[i] > d ) carry++; d = a[i + 1]; a[i + 1] -= hi + carry; carry = a[i + 1] > d; } if ( carry ) // q was too large { q--; carry = 0; for ( i = 0; i < n; i++ ) { d = a[i] + carry; carry = d < carry; a[i] = d + b[i]; if ( a[i] < d ) carry = 1; } a[n] = 0; } } int normalize( basic_bignum& denom, basic_bignum& num, int& x ) const { size_t r = denom.length() - 1; uint64_t y = denom.data_[r]; x = 0; while ( (y & l_bit) == 0 ) { y <<= 1; x++; } denom <<= x; num <<= x; if ( r > 0 && denom.data_[r] < denom.data_[r-1] ) { denom *= max_basic_type; num *= max_basic_type; return 1; } return 0; } void unnormalize( basic_bignum& rem, int x, int secondDone ) const { if ( secondDone ) { rem /= max_basic_type; } if ( x > 0 ) { rem >>= x; } else { rem.reduce(); } } void divide( basic_bignum denom, basic_bignum& quot, basic_bignum& rem, bool remDesired ) const { if ( denom.length() == 0 ) { throw std::runtime_error( "Zero divide." ); } bool quot_neg = neg_ ^ denom.neg_; bool rem_neg = neg_; int x = 0; basic_bignum num = *this; num.neg_ = denom.neg_ = false; if ( num < denom ) { quot = uint64_t(0); rem = num; rem.neg_ = rem_neg; return; } if ( denom.length() == 1 && num.length() == 1 ) { quot = uint64_t( num.data_[0]/denom.data_[0] ); rem = uint64_t( num.data_[0]%denom.data_[0] ); quot.neg_ = quot_neg; rem.neg_ = rem_neg; return; } else if (denom.length() == 1 && (denom.data_[0] & l_mask) == 0 ) { // Denominator fits into a half word uint64_t divisor = denom.data_[0], dHi = 0, q1, r, q2, dividend; quot.set_length(length()); for (size_t i=length(); i-- > 0; ) { dividend = (dHi << basic_type_halfBits) | (data_[i] >> basic_type_halfBits); q1 = dividend/divisor; r = dividend % divisor; dividend = (r << basic_type_halfBits) | (data_[i] & r_mask); q2 = dividend/divisor; dHi = dividend % divisor; quot.data_[i] = (q1 << basic_type_halfBits) | q2; } quot.reduce(); rem = dHi; quot.neg_ = quot_neg; rem.neg_ = rem_neg; return; } basic_bignum num0 = num, denom0 = denom; int second_done = normalize(denom, num, x); size_t l = denom.length() - 1; size_t n = num.length() - 1; quot.set_length(n - l); for (size_t i=quot.length(); i-- > 0; ) quot.data_[i] = 0; rem = num; if ( rem.data_[n] >= denom.data_[l] ) { rem.incr_length(rem.length() + 1); n++; quot.incr_length(quot.length() + 1); } uint64_t d = denom.data_[l]; for ( size_t k = n; k > l; k-- ) { uint64_t q = DDquotient(rem.data_[k], rem.data_[k-1], d); subtractmul( rem.data_ + k - l - 1, denom.data_, l + 1, q ); quot.data_[k - l - 1] = q; } quot.reduce(); quot.neg_ = quot_neg; if ( remDesired ) { unnormalize(rem, x, second_done); rem.neg_ = rem_neg; } } size_t length() const { return length_; } uint64_t* begin() { return data_; } const uint64_t* begin() const { return data_; } uint64_t* end() { return data_ + length_; } const uint64_t* end() const { return data_ + length_; } void set_length(size_t n) { length_ = n; if ( length_ > capacity() ) { if ( dynamic_ ) { delete[] data_; } capacity_ = round_up(length_); data_ = allocator().allocate(capacity_); dynamic_ = true; } } size_t round_up(size_t i) const // Find suitable new block size { return (i/word_length + 1) * word_length; } template typename std::enable_if::value && !std::is_signed::value && sizeof(T) <= sizeof(int64_t),void>::type initialize_from_integer(T u) { data_ = values_; dynamic_ = false; length_ = u != 0 ? 1 : 0; data_ [0] = u; } template typename std::enable_if::value && !std::is_signed::value && sizeof(int64_t) < sizeof(T) && sizeof(T) <= sizeof(int64_t)*2, void>::type initialize_from_integer(T u) { data_ = values_; dynamic_ = false; length_ = u != 0 ? 2 : 0; data_[0] = uint64_t(u & max_basic_type); u >>= basic_type_bits; data_[1] = uint64_t(u & max_basic_type); } void initialize( const basic_bignum& n ) { neg_ = n.neg_; length_ = n.length_; if ( length_ <= 2 ) { data_ = values_; dynamic_ = false; values_ [0] = n.data_ [0]; values_ [1] = n.data_ [1]; } else { capacity_ = round_up( length_ ); data_ = allocator().allocate(capacity_); dynamic_ = true; if ( length_ > 0 ) { std::memcpy( data_, n.data_, length_*sizeof(uint64_t) ); } reduce(); } } void initialize(basic_bignum&& other) { neg_ = other.neg_; length_ = other.length_; dynamic_ = other.dynamic_; if (other.dynamic_) { capacity_ = other.capacity_; data_ = other.data_; other.data_ = other.values_; other.dynamic_ = false; other.length_ = 0; other.neg_ = false; } else { values_[0] = other.data_[0]; values_[1] = other.data_[1]; data_ = values_; } } void reduce() { uint64_t* p = end() - 1; uint64_t* pBegin = begin(); while ( p >= pBegin ) { if ( *p ) { break; } --length_; --p; } if ( length_ == 0 ) { neg_ = false; } } void incr_length( size_t len_new ) { size_t len_old = length_; length_ = len_new; // length_ > len_old if ( length_ > capacity() ) { size_t capacity_new = round_up( length_ ); uint64_t* data_old = data_; data_ = allocator().allocate(capacity_new); if ( len_old > 0 ) { std::memcpy( data_, data_old, len_old*sizeof(uint64_t) ); } if ( dynamic_ ) { allocator().deallocate(data_old,capacity_); } capacity_ = capacity_new; dynamic_ = true; } if ( length_ > len_old ) { memset( data_+len_old, 0, (length_ - len_old)*sizeof(uint64_t) ); } } template void initialize(const CharT* data, size_t length) { bool neg; if (*data == '-') { neg = true; data++; --length; } else { neg = false; } basic_bignum v = 0; for (size_t i = 0; i < length; i++) { CharT c = data[i]; switch (c) { case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': v = (v * 10) + (uint64_t)(c - '0'); break; default: throw std::runtime_error(std::string("Invalid digit ") + "\'" + (char)c + "\'"); } } if ( neg ) { v.neg_ = true; } initialize(std::move(v)); } template void initialize(const CharT* data, size_t length, uint8_t base) { if (!(base >= 2 && base <= 16)) { throw std::runtime_error("Unsupported base"); } bool neg; if (*data == '-') { neg = true; data++; --length; } else { neg = false; } basic_bignum v = 0; for (size_t i = 0; i < length; i++) { CharT c = data[i]; uint64_t d; switch (c) { case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': d = (uint64_t)(c - '0'); break; case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': d = (uint64_t)(c - ('a' - 10)); break; case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': d = (uint64_t)(c - ('A' - 10)); break; default: throw std::runtime_error(std::string("Invalid digit in base ") + std::to_string(base) + ": \'" + (char)c + "\'"); } if (d >= base) { throw std::runtime_error(std::string("Invalid digit in base ") + std::to_string(base) + ": \'" + (char)c + "\'"); } v = (v * base) + d; } if ( neg ) { v.neg_ = true; } initialize(std::move(v)); } }; typedef basic_bignum> bignum; } #endif