annotate DEPENDENCIES/generic/include/boost/random/detail/polynomial.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents f46d142149f5
children
rev   line source
Chris@102 1 /* boost random/detail/polynomial.hpp header file
Chris@102 2 *
Chris@102 3 * Copyright Steven Watanabe 2014
Chris@102 4 * Distributed under the Boost Software License, Version 1.0. (See
Chris@102 5 * accompanying file LICENSE_1_0.txt or copy at
Chris@102 6 * http://www.boost.org/LICENSE_1_0.txt)
Chris@102 7 *
Chris@102 8 * See http://www.boost.org for most recent version including documentation.
Chris@102 9 *
Chris@102 10 * $Id$
Chris@102 11 */
Chris@102 12
Chris@102 13 #ifndef BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP
Chris@102 14 #define BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP
Chris@102 15
Chris@102 16 #include <cstddef>
Chris@102 17 #include <limits>
Chris@102 18 #include <vector>
Chris@102 19 #include <algorithm>
Chris@102 20 #include <boost/assert.hpp>
Chris@102 21 #include <boost/cstdint.hpp>
Chris@102 22
Chris@102 23 namespace boost {
Chris@102 24 namespace random {
Chris@102 25 namespace detail {
Chris@102 26
Chris@102 27 class polynomial_ops {
Chris@102 28 public:
Chris@102 29 typedef unsigned long digit_t;
Chris@102 30
Chris@102 31 static void add(std::size_t size, const digit_t * lhs,
Chris@102 32 const digit_t * rhs, digit_t * output)
Chris@102 33 {
Chris@102 34 for(std::size_t i = 0; i < size; ++i) {
Chris@102 35 output[i] = lhs[i] ^ rhs[i];
Chris@102 36 }
Chris@102 37 }
Chris@102 38
Chris@102 39 static void add_shifted_inplace(std::size_t size, const digit_t * lhs,
Chris@102 40 digit_t * output, std::size_t shift)
Chris@102 41 {
Chris@102 42 if(shift == 0) {
Chris@102 43 add(size, lhs, output, output);
Chris@102 44 return;
Chris@102 45 }
Chris@102 46 std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 47 digit_t prev = 0;
Chris@102 48 for(std::size_t i = 0; i < size; ++i) {
Chris@102 49 digit_t tmp = lhs[i];
Chris@102 50 output[i] ^= (tmp << shift) | (prev >> (bits-shift));
Chris@102 51 prev = tmp;
Chris@102 52 }
Chris@102 53 output[size] ^= (prev >> (bits-shift));
Chris@102 54 }
Chris@102 55
Chris@102 56 static void multiply_simple(std::size_t size, const digit_t * lhs,
Chris@102 57 const digit_t * rhs, digit_t * output)
Chris@102 58 {
Chris@102 59 std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 60 for(std::size_t i = 0; i < 2*size; ++i) {
Chris@102 61 output[i] = 0;
Chris@102 62 }
Chris@102 63 for(std::size_t i = 0; i < size; ++i) {
Chris@102 64 for(std::size_t j = 0; j < bits; ++j) {
Chris@102 65 if((lhs[i] & (digit_t(1) << j)) != 0) {
Chris@102 66 add_shifted_inplace(size, rhs, output + i, j);
Chris@102 67 }
Chris@102 68 }
Chris@102 69 }
Chris@102 70 }
Chris@102 71
Chris@102 72 // memory requirements: (size - cutoff) * 4 + next_smaller
Chris@102 73 static void multiply_karatsuba(std::size_t size,
Chris@102 74 const digit_t * lhs, const digit_t * rhs,
Chris@102 75 digit_t * output)
Chris@102 76 {
Chris@102 77 if(size < 64) {
Chris@102 78 multiply_simple(size, lhs, rhs, output);
Chris@102 79 return;
Chris@102 80 }
Chris@102 81 // split in half
Chris@102 82 std::size_t cutoff = size/2;
Chris@102 83 multiply_karatsuba(cutoff, lhs, rhs, output);
Chris@102 84 multiply_karatsuba(size - cutoff, lhs + cutoff, rhs + cutoff,
Chris@102 85 output + cutoff*2);
Chris@102 86 std::vector<digit_t> local1(size - cutoff);
Chris@102 87 std::vector<digit_t> local2(size - cutoff);
Chris@102 88 // combine the digits for the inner multiply
Chris@102 89 add(cutoff, lhs, lhs + cutoff, &local1[0]);
Chris@102 90 if(size & 1) local1[cutoff] = lhs[size - 1];
Chris@102 91 add(cutoff, rhs + cutoff, rhs, &local2[0]);
Chris@102 92 if(size & 1) local2[cutoff] = rhs[size - 1];
Chris@102 93 std::vector<digit_t> local3((size - cutoff) * 2);
Chris@102 94 multiply_karatsuba(size - cutoff, &local1[0], &local2[0], &local3[0]);
Chris@102 95 add(cutoff * 2, output, &local3[0], &local3[0]);
Chris@102 96 add((size - cutoff) * 2, output + cutoff*2, &local3[0], &local3[0]);
Chris@102 97 // Finally, add the inner result
Chris@102 98 add((size - cutoff) * 2, output + cutoff, &local3[0], output + cutoff);
Chris@102 99 }
Chris@102 100
Chris@102 101 static void multiply_add_karatsuba(std::size_t size,
Chris@102 102 const digit_t * lhs, const digit_t * rhs,
Chris@102 103 digit_t * output)
Chris@102 104 {
Chris@102 105 std::vector<digit_t> buf(size * 2);
Chris@102 106 multiply_karatsuba(size, lhs, rhs, &buf[0]);
Chris@102 107 add(size * 2, &buf[0], output, output);
Chris@102 108 }
Chris@102 109
Chris@102 110 static void multiply(const digit_t * lhs, std::size_t lhs_size,
Chris@102 111 const digit_t * rhs, std::size_t rhs_size,
Chris@102 112 digit_t * output)
Chris@102 113 {
Chris@102 114 std::fill_n(output, lhs_size + rhs_size, digit_t(0));
Chris@102 115 multiply_add(lhs, lhs_size, rhs, rhs_size, output);
Chris@102 116 }
Chris@102 117
Chris@102 118 static void multiply_add(const digit_t * lhs, std::size_t lhs_size,
Chris@102 119 const digit_t * rhs, std::size_t rhs_size,
Chris@102 120 digit_t * output)
Chris@102 121 {
Chris@102 122 // split into pieces that can be passed to
Chris@102 123 // karatsuba multiply.
Chris@102 124 while(lhs_size != 0) {
Chris@102 125 if(lhs_size < rhs_size) {
Chris@102 126 std::swap(lhs, rhs);
Chris@102 127 std::swap(lhs_size, rhs_size);
Chris@102 128 }
Chris@102 129
Chris@102 130 multiply_add_karatsuba(rhs_size, lhs, rhs, output);
Chris@102 131
Chris@102 132 lhs += rhs_size;
Chris@102 133 lhs_size -= rhs_size;
Chris@102 134 output += rhs_size;
Chris@102 135 }
Chris@102 136 }
Chris@102 137
Chris@102 138 static void copy_bits(const digit_t * x, std::size_t low, std::size_t high,
Chris@102 139 digit_t * out)
Chris@102 140 {
Chris@102 141 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 142 std::size_t offset = low/bits;
Chris@102 143 x += offset;
Chris@102 144 low -= offset*bits;
Chris@102 145 high -= offset*bits;
Chris@102 146 std::size_t n = (high-low)/bits;
Chris@102 147 if(low == 0) {
Chris@102 148 for(std::size_t i = 0; i < n; ++i) {
Chris@102 149 out[i] = x[i];
Chris@102 150 }
Chris@102 151 } else {
Chris@102 152 for(std::size_t i = 0; i < n; ++i) {
Chris@102 153 out[i] = (x[i] >> low) | (x[i+1] << (bits-low));
Chris@102 154 }
Chris@102 155 }
Chris@102 156 if((high-low)%bits) {
Chris@102 157 digit_t low_mask = (digit_t(1) << ((high-low)%bits)) - 1;
Chris@102 158 digit_t result = (x[n] >> low);
Chris@102 159 if(low != 0 && (n+1)*bits < high) {
Chris@102 160 result |= (x[n+1] << (bits-low));
Chris@102 161 }
Chris@102 162 out[n] = (result & low_mask);
Chris@102 163 }
Chris@102 164 }
Chris@102 165
Chris@102 166 static void shift_left(digit_t * val, std::size_t size, std::size_t shift)
Chris@102 167 {
Chris@102 168 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 169 BOOST_ASSERT(shift > 0);
Chris@102 170 BOOST_ASSERT(shift < bits);
Chris@102 171 digit_t prev = 0;
Chris@102 172 for(std::size_t i = 0; i < size; ++i) {
Chris@102 173 digit_t tmp = val[i];
Chris@102 174 val[i] = (prev >> (bits - shift)) | (val[i] << shift);
Chris@102 175 prev = tmp;
Chris@102 176 }
Chris@102 177 }
Chris@102 178
Chris@102 179 static digit_t sqr(digit_t val) {
Chris@102 180 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 181 digit_t mask = (digit_t(1) << bits/2) - 1;
Chris@102 182 for(std::size_t i = bits; i > 1; i /= 2) {
Chris@102 183 val = ((val & ~mask) << i/2) | (val & mask);
Chris@102 184 mask = mask & (mask >> i/4);
Chris@102 185 mask = mask | (mask << i/2);
Chris@102 186 }
Chris@102 187 return val;
Chris@102 188 }
Chris@102 189
Chris@102 190 static void sqr(digit_t * val, std::size_t size)
Chris@102 191 {
Chris@102 192 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 193 digit_t mask = (digit_t(1) << bits/2) - 1;
Chris@102 194 for(std::size_t i = 0; i < size; ++i) {
Chris@102 195 digit_t x = val[size - i - 1];
Chris@102 196 val[(size - i - 1) * 2] = sqr(x & mask);
Chris@102 197 val[(size - i - 1) * 2 + 1] = sqr(x >> bits/2);
Chris@102 198 }
Chris@102 199 }
Chris@102 200
Chris@102 201 // optimized for the case when the modulus has few bits set.
Chris@102 202 struct sparse_mod {
Chris@102 203 sparse_mod(const digit_t * divisor, std::size_t divisor_bits)
Chris@102 204 {
Chris@102 205 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 206 _remainder_bits = divisor_bits - 1;
Chris@102 207 for(std::size_t i = 0; i < divisor_bits; ++i) {
Chris@102 208 if(divisor[i/bits] & (digit_t(1) << i%bits)) {
Chris@102 209 _bit_indices.push_back(i);
Chris@102 210 }
Chris@102 211 }
Chris@102 212 BOOST_ASSERT(_bit_indices.back() == divisor_bits - 1);
Chris@102 213 _bit_indices.pop_back();
Chris@102 214 if(_bit_indices.empty()) {
Chris@102 215 _block_bits = divisor_bits;
Chris@102 216 _lower_bits = 0;
Chris@102 217 } else {
Chris@102 218 _block_bits = divisor_bits - _bit_indices.back() - 1;
Chris@102 219 _lower_bits = _bit_indices.back() + 1;
Chris@102 220 }
Chris@102 221
Chris@102 222 _partial_quotient.resize((_block_bits + bits - 1)/bits);
Chris@102 223 }
Chris@102 224 void operator()(digit_t * dividend, std::size_t dividend_bits)
Chris@102 225 {
Chris@102 226 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 227 while(dividend_bits > _remainder_bits) {
Chris@102 228 std::size_t block_start = (std::max)(dividend_bits - _block_bits, _remainder_bits);
Chris@102 229 std::size_t block_size = (dividend_bits - block_start + bits - 1) / bits;
Chris@102 230 copy_bits(dividend, block_start, dividend_bits, &_partial_quotient[0]);
Chris@102 231 for(std::size_t i = 0; i < _bit_indices.size(); ++i) {
Chris@102 232 std::size_t pos = _bit_indices[i] + block_start - _remainder_bits;
Chris@102 233 add_shifted_inplace(block_size, &_partial_quotient[0], dividend + pos/bits, pos%bits);
Chris@102 234 }
Chris@102 235 add_shifted_inplace(block_size, &_partial_quotient[0], dividend + block_start/bits, block_start%bits);
Chris@102 236 dividend_bits = block_start;
Chris@102 237 }
Chris@102 238 }
Chris@102 239 std::vector<digit_t> _partial_quotient;
Chris@102 240 std::size_t _remainder_bits;
Chris@102 241 std::size_t _block_bits;
Chris@102 242 std::size_t _lower_bits;
Chris@102 243 std::vector<std::size_t> _bit_indices;
Chris@102 244 };
Chris@102 245
Chris@102 246 // base should have the same number of bits as mod
Chris@102 247 // base, and mod should both be able to hold a power
Chris@102 248 // of 2 >= mod_bits. out needs to be twice as large.
Chris@102 249 static void mod_pow_x(boost::uintmax_t exponent, const digit_t * mod, std::size_t mod_bits, digit_t * out)
Chris@102 250 {
Chris@102 251 const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 252 const std::size_t n = (mod_bits + bits - 1) / bits;
Chris@102 253 const std::size_t highbit = mod_bits - 1;
Chris@102 254 if(exponent == 0) {
Chris@102 255 out[0] = 1;
Chris@102 256 std::fill_n(out + 1, n - 1, digit_t(0));
Chris@102 257 return;
Chris@102 258 }
Chris@102 259 boost::uintmax_t i = std::numeric_limits<boost::uintmax_t>::digits - 1;
Chris@102 260 while(((boost::uintmax_t(1) << i) & exponent) == 0) {
Chris@102 261 --i;
Chris@102 262 }
Chris@102 263 out[0] = 2;
Chris@102 264 std::fill_n(out + 1, n - 1, digit_t(0));
Chris@102 265 sparse_mod m(mod, mod_bits);
Chris@102 266 while(i--) {
Chris@102 267 sqr(out, n);
Chris@102 268 m(out, 2 * mod_bits - 1);
Chris@102 269 if((boost::uintmax_t(1) << i) & exponent) {
Chris@102 270 shift_left(out, n, 1);
Chris@102 271 if(out[highbit / bits] & (digit_t(1) << highbit%bits))
Chris@102 272 add(n, out, mod, out);
Chris@102 273 }
Chris@102 274 }
Chris@102 275 }
Chris@102 276 };
Chris@102 277
Chris@102 278 class polynomial
Chris@102 279 {
Chris@102 280 typedef polynomial_ops::digit_t digit_t;
Chris@102 281 public:
Chris@102 282 polynomial() : _size(0) {}
Chris@102 283 class reference {
Chris@102 284 public:
Chris@102 285 reference(digit_t &value, int idx)
Chris@102 286 : _value(value), _idx(idx) {}
Chris@102 287 operator bool() const { return (_value & (digit_t(1) << _idx)) != 0; }
Chris@102 288 reference& operator=(bool b)
Chris@102 289 {
Chris@102 290 if(b) {
Chris@102 291 _value |= (digit_t(1) << _idx);
Chris@102 292 } else {
Chris@102 293 _value &= ~(digit_t(1) << _idx);
Chris@102 294 }
Chris@102 295 return *this;
Chris@102 296 }
Chris@102 297 reference &operator^=(bool b)
Chris@102 298 {
Chris@102 299 _value ^= (digit_t(b) << _idx);
Chris@102 300 return *this;
Chris@102 301 }
Chris@102 302
Chris@102 303 reference &operator=(const reference &other)
Chris@102 304 {
Chris@102 305 return *this = static_cast<bool>(other);
Chris@102 306 }
Chris@102 307 private:
Chris@102 308 digit_t &_value;
Chris@102 309 int _idx;
Chris@102 310 };
Chris@102 311 reference operator[](std::size_t i)
Chris@102 312 {
Chris@102 313 static const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 314 ensure_bit(i);
Chris@102 315 return reference(_storage[i/bits], i%bits);
Chris@102 316 }
Chris@102 317 bool operator[](std::size_t i) const
Chris@102 318 {
Chris@102 319 static const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 320 if(i < size())
Chris@102 321 return (_storage[i/bits] & (digit_t(1) << (i%bits))) != 0;
Chris@102 322 else
Chris@102 323 return false;
Chris@102 324 }
Chris@102 325 std::size_t size() const
Chris@102 326 {
Chris@102 327 return _size;
Chris@102 328 }
Chris@102 329 void resize(std::size_t n)
Chris@102 330 {
Chris@102 331 static const std::size_t bits = std::numeric_limits<digit_t>::digits;
Chris@102 332 _storage.resize((n + bits - 1)/bits);
Chris@102 333 // clear the high order bits in case we're shrinking.
Chris@102 334 if(n%bits) {
Chris@102 335 _storage.back() &= ((digit_t(1) << (n%bits)) - 1);
Chris@102 336 }
Chris@102 337 _size = n;
Chris@102 338 }
Chris@102 339 friend polynomial operator*(const polynomial &lhs, const polynomial &rhs);
Chris@102 340 friend polynomial mod_pow_x(boost::uintmax_t exponent, polynomial mod);
Chris@102 341 private:
Chris@102 342 std::vector<polynomial_ops::digit_t> _storage;
Chris@102 343 std::size_t _size;
Chris@102 344 void ensure_bit(std::size_t i)
Chris@102 345 {
Chris@102 346 if(i >= size()) {
Chris@102 347 resize(i + 1);
Chris@102 348 }
Chris@102 349 }
Chris@102 350 void normalize()
Chris@102 351 {
Chris@102 352 while(size() && (*this)[size() - 1] == 0)
Chris@102 353 resize(size() - 1);
Chris@102 354 }
Chris@102 355 };
Chris@102 356
Chris@102 357 inline polynomial operator*(const polynomial &lhs, const polynomial &rhs)
Chris@102 358 {
Chris@102 359 polynomial result;
Chris@102 360 result._storage.resize(lhs._storage.size() + rhs._storage.size());
Chris@102 361 polynomial_ops::multiply(&lhs._storage[0], lhs._storage.size(),
Chris@102 362 &rhs._storage[0], rhs._storage.size(),
Chris@102 363 &result._storage[0]);
Chris@102 364 result._size = lhs._size + rhs._size;
Chris@102 365 return result;
Chris@102 366 }
Chris@102 367
Chris@102 368 inline polynomial mod_pow_x(boost::uintmax_t exponent, polynomial mod)
Chris@102 369 {
Chris@102 370 polynomial result;
Chris@102 371 mod.normalize();
Chris@102 372 std::size_t mod_size = mod.size();
Chris@102 373 result._storage.resize(mod._storage.size() * 2);
Chris@102 374 result._size = mod.size() * 2;
Chris@102 375 polynomial_ops::mod_pow_x(exponent, &mod._storage[0], mod_size, &result._storage[0]);
Chris@102 376 result.resize(mod.size() - 1);
Chris@102 377 return result;
Chris@102 378 }
Chris@102 379
Chris@102 380 }
Chris@102 381 }
Chris@102 382 }
Chris@102 383
Chris@102 384 #endif // BOOST_RANDOM_DETAIL_POLYNOMIAL_HPP