Chris@102: /* boost random/detail/polynomial.hpp header file Chris@102: * Chris@102: * Copyright Steven Watanabe 2014 Chris@102: * Distributed under the Boost Software License, Version 1.0. (See Chris@102: * accompanying file LICENSE_1_0.txt or copy at Chris@102: * http://www.boost.org/LICENSE_1_0.txt) Chris@102: * Chris@102: * See http://www.boost.org for most recent version including documentation. Chris@102: * Chris@102: * $Id$ Chris@102: */ Chris@102: Chris@102: #ifndef BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP Chris@102: #define BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP Chris@102: Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: Chris@102: namespace boost { Chris@102: namespace random { Chris@102: namespace detail { Chris@102: Chris@102: class polynomial_ops { Chris@102: public: Chris@102: typedef unsigned long digit_t; Chris@102: Chris@102: static void add(std::size_t size, const digit_t * lhs, Chris@102: const digit_t * rhs, digit_t * output) Chris@102: { Chris@102: for(std::size_t i = 0; i < size; ++i) { Chris@102: output[i] = lhs[i] ^ rhs[i]; Chris@102: } Chris@102: } Chris@102: Chris@102: static void add_shifted_inplace(std::size_t size, const digit_t * lhs, Chris@102: digit_t * output, std::size_t shift) Chris@102: { Chris@102: if(shift == 0) { Chris@102: add(size, lhs, output, output); Chris@102: return; Chris@102: } Chris@102: std::size_t bits = std::numeric_limits::digits; Chris@102: digit_t prev = 0; Chris@102: for(std::size_t i = 0; i < size; ++i) { Chris@102: digit_t tmp = lhs[i]; Chris@102: output[i] ^= (tmp << shift) | (prev >> (bits-shift)); Chris@102: prev = tmp; Chris@102: } Chris@102: output[size] ^= (prev >> (bits-shift)); Chris@102: } Chris@102: Chris@102: static void multiply_simple(std::size_t size, const digit_t * lhs, Chris@102: const digit_t * rhs, digit_t * output) Chris@102: { Chris@102: std::size_t bits = std::numeric_limits::digits; Chris@102: for(std::size_t i = 0; i < 2*size; ++i) { Chris@102: output[i] = 0; Chris@102: } Chris@102: for(std::size_t i = 0; i < size; ++i) { Chris@102: for(std::size_t j = 0; j < bits; ++j) { Chris@102: if((lhs[i] & (digit_t(1) << j)) != 0) { Chris@102: add_shifted_inplace(size, rhs, output + i, j); Chris@102: } Chris@102: } Chris@102: } Chris@102: } Chris@102: Chris@102: // memory requirements: (size - cutoff) * 4 + next_smaller Chris@102: static void multiply_karatsuba(std::size_t size, Chris@102: const digit_t * lhs, const digit_t * rhs, Chris@102: digit_t * output) Chris@102: { Chris@102: if(size < 64) { Chris@102: multiply_simple(size, lhs, rhs, output); Chris@102: return; Chris@102: } Chris@102: // split in half Chris@102: std::size_t cutoff = size/2; Chris@102: multiply_karatsuba(cutoff, lhs, rhs, output); Chris@102: multiply_karatsuba(size - cutoff, lhs + cutoff, rhs + cutoff, Chris@102: output + cutoff*2); Chris@102: std::vector local1(size - cutoff); Chris@102: std::vector local2(size - cutoff); Chris@102: // combine the digits for the inner multiply Chris@102: add(cutoff, lhs, lhs + cutoff, &local1[0]); Chris@102: if(size & 1) local1[cutoff] = lhs[size - 1]; Chris@102: add(cutoff, rhs + cutoff, rhs, &local2[0]); Chris@102: if(size & 1) local2[cutoff] = rhs[size - 1]; Chris@102: std::vector local3((size - cutoff) * 2); Chris@102: multiply_karatsuba(size - cutoff, &local1[0], &local2[0], &local3[0]); Chris@102: add(cutoff * 2, output, &local3[0], &local3[0]); Chris@102: add((size - cutoff) * 2, output + cutoff*2, &local3[0], &local3[0]); Chris@102: // Finally, add the inner result Chris@102: add((size - cutoff) * 2, output + cutoff, &local3[0], output + cutoff); Chris@102: } Chris@102: Chris@102: static void multiply_add_karatsuba(std::size_t size, Chris@102: const digit_t * lhs, const digit_t * rhs, Chris@102: digit_t * output) Chris@102: { Chris@102: std::vector buf(size * 2); Chris@102: multiply_karatsuba(size, lhs, rhs, &buf[0]); Chris@102: add(size * 2, &buf[0], output, output); Chris@102: } Chris@102: Chris@102: static void multiply(const digit_t * lhs, std::size_t lhs_size, Chris@102: const digit_t * rhs, std::size_t rhs_size, Chris@102: digit_t * output) Chris@102: { Chris@102: std::fill_n(output, lhs_size + rhs_size, digit_t(0)); Chris@102: multiply_add(lhs, lhs_size, rhs, rhs_size, output); Chris@102: } Chris@102: Chris@102: static void multiply_add(const digit_t * lhs, std::size_t lhs_size, Chris@102: const digit_t * rhs, std::size_t rhs_size, Chris@102: digit_t * output) Chris@102: { Chris@102: // split into pieces that can be passed to Chris@102: // karatsuba multiply. Chris@102: while(lhs_size != 0) { Chris@102: if(lhs_size < rhs_size) { Chris@102: std::swap(lhs, rhs); Chris@102: std::swap(lhs_size, rhs_size); Chris@102: } Chris@102: Chris@102: multiply_add_karatsuba(rhs_size, lhs, rhs, output); Chris@102: Chris@102: lhs += rhs_size; Chris@102: lhs_size -= rhs_size; Chris@102: output += rhs_size; Chris@102: } Chris@102: } Chris@102: Chris@102: static void copy_bits(const digit_t * x, std::size_t low, std::size_t high, Chris@102: digit_t * out) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: std::size_t offset = low/bits; Chris@102: x += offset; Chris@102: low -= offset*bits; Chris@102: high -= offset*bits; Chris@102: std::size_t n = (high-low)/bits; Chris@102: if(low == 0) { Chris@102: for(std::size_t i = 0; i < n; ++i) { Chris@102: out[i] = x[i]; Chris@102: } Chris@102: } else { Chris@102: for(std::size_t i = 0; i < n; ++i) { Chris@102: out[i] = (x[i] >> low) | (x[i+1] << (bits-low)); Chris@102: } Chris@102: } Chris@102: if((high-low)%bits) { Chris@102: digit_t low_mask = (digit_t(1) << ((high-low)%bits)) - 1; Chris@102: digit_t result = (x[n] >> low); Chris@102: if(low != 0 && (n+1)*bits < high) { Chris@102: result |= (x[n+1] << (bits-low)); Chris@102: } Chris@102: out[n] = (result & low_mask); Chris@102: } Chris@102: } Chris@102: Chris@102: static void shift_left(digit_t * val, std::size_t size, std::size_t shift) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: BOOST_ASSERT(shift > 0); Chris@102: BOOST_ASSERT(shift < bits); Chris@102: digit_t prev = 0; Chris@102: for(std::size_t i = 0; i < size; ++i) { Chris@102: digit_t tmp = val[i]; Chris@102: val[i] = (prev >> (bits - shift)) | (val[i] << shift); Chris@102: prev = tmp; Chris@102: } Chris@102: } Chris@102: Chris@102: static digit_t sqr(digit_t val) { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: digit_t mask = (digit_t(1) << bits/2) - 1; Chris@102: for(std::size_t i = bits; i > 1; i /= 2) { Chris@102: val = ((val & ~mask) << i/2) | (val & mask); Chris@102: mask = mask & (mask >> i/4); Chris@102: mask = mask | (mask << i/2); Chris@102: } Chris@102: return val; Chris@102: } Chris@102: Chris@102: static void sqr(digit_t * val, std::size_t size) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: digit_t mask = (digit_t(1) << bits/2) - 1; Chris@102: for(std::size_t i = 0; i < size; ++i) { Chris@102: digit_t x = val[size - i - 1]; Chris@102: val[(size - i - 1) * 2] = sqr(x & mask); Chris@102: val[(size - i - 1) * 2 + 1] = sqr(x >> bits/2); Chris@102: } Chris@102: } Chris@102: Chris@102: // optimized for the case when the modulus has few bits set. Chris@102: struct sparse_mod { Chris@102: sparse_mod(const digit_t * divisor, std::size_t divisor_bits) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: _remainder_bits = divisor_bits - 1; Chris@102: for(std::size_t i = 0; i < divisor_bits; ++i) { Chris@102: if(divisor[i/bits] & (digit_t(1) << i%bits)) { Chris@102: _bit_indices.push_back(i); Chris@102: } Chris@102: } Chris@102: BOOST_ASSERT(_bit_indices.back() == divisor_bits - 1); Chris@102: _bit_indices.pop_back(); Chris@102: if(_bit_indices.empty()) { Chris@102: _block_bits = divisor_bits; Chris@102: _lower_bits = 0; Chris@102: } else { Chris@102: _block_bits = divisor_bits - _bit_indices.back() - 1; Chris@102: _lower_bits = _bit_indices.back() + 1; Chris@102: } Chris@102: Chris@102: _partial_quotient.resize((_block_bits + bits - 1)/bits); Chris@102: } Chris@102: void operator()(digit_t * dividend, std::size_t dividend_bits) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: while(dividend_bits > _remainder_bits) { Chris@102: std::size_t block_start = (std::max)(dividend_bits - _block_bits, _remainder_bits); Chris@102: std::size_t block_size = (dividend_bits - block_start + bits - 1) / bits; Chris@102: copy_bits(dividend, block_start, dividend_bits, &_partial_quotient[0]); Chris@102: for(std::size_t i = 0; i < _bit_indices.size(); ++i) { Chris@102: std::size_t pos = _bit_indices[i] + block_start - _remainder_bits; Chris@102: add_shifted_inplace(block_size, &_partial_quotient[0], dividend + pos/bits, pos%bits); Chris@102: } Chris@102: add_shifted_inplace(block_size, &_partial_quotient[0], dividend + block_start/bits, block_start%bits); Chris@102: dividend_bits = block_start; Chris@102: } Chris@102: } Chris@102: std::vector _partial_quotient; Chris@102: std::size_t _remainder_bits; Chris@102: std::size_t _block_bits; Chris@102: std::size_t _lower_bits; Chris@102: std::vector _bit_indices; Chris@102: }; Chris@102: Chris@102: // base should have the same number of bits as mod Chris@102: // base, and mod should both be able to hold a power Chris@102: // of 2 >= mod_bits. out needs to be twice as large. Chris@102: static void mod_pow_x(boost::uintmax_t exponent, const digit_t * mod, std::size_t mod_bits, digit_t * out) Chris@102: { Chris@102: const std::size_t bits = std::numeric_limits::digits; Chris@102: const std::size_t n = (mod_bits + bits - 1) / bits; Chris@102: const std::size_t highbit = mod_bits - 1; Chris@102: if(exponent == 0) { Chris@102: out[0] = 1; Chris@102: std::fill_n(out + 1, n - 1, digit_t(0)); Chris@102: return; Chris@102: } Chris@102: boost::uintmax_t i = std::numeric_limits::digits - 1; Chris@102: while(((boost::uintmax_t(1) << i) & exponent) == 0) { Chris@102: --i; Chris@102: } Chris@102: out[0] = 2; Chris@102: std::fill_n(out + 1, n - 1, digit_t(0)); Chris@102: sparse_mod m(mod, mod_bits); Chris@102: while(i--) { Chris@102: sqr(out, n); Chris@102: m(out, 2 * mod_bits - 1); Chris@102: if((boost::uintmax_t(1) << i) & exponent) { Chris@102: shift_left(out, n, 1); Chris@102: if(out[highbit / bits] & (digit_t(1) << highbit%bits)) Chris@102: add(n, out, mod, out); Chris@102: } Chris@102: } Chris@102: } Chris@102: }; Chris@102: Chris@102: class polynomial Chris@102: { Chris@102: typedef polynomial_ops::digit_t digit_t; Chris@102: public: Chris@102: polynomial() : _size(0) {} Chris@102: class reference { Chris@102: public: Chris@102: reference(digit_t &value, int idx) Chris@102: : _value(value), _idx(idx) {} Chris@102: operator bool() const { return (_value & (digit_t(1) << _idx)) != 0; } Chris@102: reference& operator=(bool b) Chris@102: { Chris@102: if(b) { Chris@102: _value |= (digit_t(1) << _idx); Chris@102: } else { Chris@102: _value &= ~(digit_t(1) << _idx); Chris@102: } Chris@102: return *this; Chris@102: } Chris@102: reference &operator^=(bool b) Chris@102: { Chris@102: _value ^= (digit_t(b) << _idx); Chris@102: return *this; Chris@102: } Chris@102: Chris@102: reference &operator=(const reference &other) Chris@102: { Chris@102: return *this = static_cast(other); Chris@102: } Chris@102: private: Chris@102: digit_t &_value; Chris@102: int _idx; Chris@102: }; Chris@102: reference operator[](std::size_t i) Chris@102: { Chris@102: static const std::size_t bits = std::numeric_limits::digits; Chris@102: ensure_bit(i); Chris@102: return reference(_storage[i/bits], i%bits); Chris@102: } Chris@102: bool operator[](std::size_t i) const Chris@102: { Chris@102: static const std::size_t bits = std::numeric_limits::digits; Chris@102: if(i < size()) Chris@102: return (_storage[i/bits] & (digit_t(1) << (i%bits))) != 0; Chris@102: else Chris@102: return false; Chris@102: } Chris@102: std::size_t size() const Chris@102: { Chris@102: return _size; Chris@102: } Chris@102: void resize(std::size_t n) Chris@102: { Chris@102: static const std::size_t bits = std::numeric_limits::digits; Chris@102: _storage.resize((n + bits - 1)/bits); Chris@102: // clear the high order bits in case we're shrinking. Chris@102: if(n%bits) { Chris@102: _storage.back() &= ((digit_t(1) << (n%bits)) - 1); Chris@102: } Chris@102: _size = n; Chris@102: } Chris@102: friend polynomial operator*(const polynomial &lhs, const polynomial &rhs); Chris@102: friend polynomial mod_pow_x(boost::uintmax_t exponent, polynomial mod); Chris@102: private: Chris@102: std::vector _storage; Chris@102: std::size_t _size; Chris@102: void ensure_bit(std::size_t i) Chris@102: { Chris@102: if(i >= size()) { Chris@102: resize(i + 1); Chris@102: } Chris@102: } Chris@102: void normalize() Chris@102: { Chris@102: while(size() && (*this)[size() - 1] == 0) Chris@102: resize(size() - 1); Chris@102: } Chris@102: }; Chris@102: Chris@102: inline polynomial operator*(const polynomial &lhs, const polynomial &rhs) Chris@102: { Chris@102: polynomial result; Chris@102: result._storage.resize(lhs._storage.size() + rhs._storage.size()); Chris@102: polynomial_ops::multiply(&lhs._storage[0], lhs._storage.size(), Chris@102: &rhs._storage[0], rhs._storage.size(), Chris@102: &result._storage[0]); Chris@102: result._size = lhs._size + rhs._size; Chris@102: return result; Chris@102: } Chris@102: Chris@102: inline polynomial mod_pow_x(boost::uintmax_t exponent, polynomial mod) Chris@102: { Chris@102: polynomial result; Chris@102: mod.normalize(); Chris@102: std::size_t mod_size = mod.size(); Chris@102: result._storage.resize(mod._storage.size() * 2); Chris@102: result._size = mod.size() * 2; Chris@102: polynomial_ops::mod_pow_x(exponent, &mod._storage[0], mod_size, &result._storage[0]); Chris@102: result.resize(mod.size() - 1); Chris@102: return result; Chris@102: } Chris@102: Chris@102: } Chris@102: } Chris@102: } Chris@102: Chris@102: #endif // BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP