integer.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. ///////////////////////////////////////////////////////////////
  2. // Copyright 2012-21 John Maddock.
  3. // Copyright 2021 Iskandarov Lev. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
  6. #ifndef BOOST_MP_INTEGER_HPP
  7. #define BOOST_MP_INTEGER_HPP
  8. #include <type_traits>
  9. #include <boost/multiprecision/cpp_int.hpp>
  10. #include <boost/multiprecision/detail/bitscan.hpp>
  11. #include <boost/multiprecision/detail/no_exceptions_support.hpp>
  12. #include <boost/multiprecision/detail/standalone_config.hpp>
  13. namespace boost {
  14. namespace multiprecision {
  15. template <class Integer, class I2>
  16. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value && boost::multiprecision::detail::is_integral<I2>::value, Integer&>::type
  17. multiply(Integer& result, const I2& a, const I2& b)
  18. {
  19. return result = static_cast<Integer>(a) * static_cast<Integer>(b);
  20. }
  21. template <class Integer, class I2>
  22. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value && boost::multiprecision::detail::is_integral<I2>::value, Integer&>::type
  23. add(Integer& result, const I2& a, const I2& b)
  24. {
  25. return result = static_cast<Integer>(a) + static_cast<Integer>(b);
  26. }
  27. template <class Integer, class I2>
  28. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value && boost::multiprecision::detail::is_integral<I2>::value, Integer&>::type
  29. subtract(Integer& result, const I2& a, const I2& b)
  30. {
  31. return result = static_cast<Integer>(a) - static_cast<Integer>(b);
  32. }
  33. template <class Integer>
  34. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value>::type divide_qr(const Integer& x, const Integer& y, Integer& q, Integer& r)
  35. {
  36. q = x / y;
  37. r = x % y;
  38. }
  39. template <class I1, class I2>
  40. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<I1>::value && boost::multiprecision::detail::is_integral<I2>::value, I2>::type integer_modulus(const I1& x, I2 val)
  41. {
  42. return static_cast<I2>(x % val);
  43. }
  44. namespace detail {
  45. //
  46. // Figure out the kind of integer that has twice as many bits as some builtin
  47. // integer type I. Use a native type if we can (including types which may not
  48. // be recognised by boost::int_t because they're larger than long long),
  49. // otherwise synthesize a cpp_int to do the job.
  50. //
  51. template <class I>
  52. struct double_integer
  53. {
  54. static constexpr const unsigned int_t_digits =
  55. 2 * sizeof(I) <= sizeof(long long) ? std::numeric_limits<I>::digits * 2 : 1;
  56. using type = typename std::conditional<
  57. 2 * sizeof(I) <= sizeof(long long),
  58. typename std::conditional<
  59. boost::multiprecision::detail::is_signed<I>::value && boost::multiprecision::detail::is_integral<I>::value,
  60. typename boost::multiprecision::detail::int_t<int_t_digits>::least,
  61. typename boost::multiprecision::detail::uint_t<int_t_digits>::least>::type,
  62. typename std::conditional<
  63. 2 * sizeof(I) <= sizeof(double_limb_type),
  64. typename std::conditional<
  65. boost::multiprecision::detail::is_signed<I>::value && boost::multiprecision::detail::is_integral<I>::value,
  66. signed_double_limb_type,
  67. double_limb_type>::type,
  68. number<cpp_int_backend<sizeof(I) * CHAR_BIT * 2, sizeof(I) * CHAR_BIT * 2, (boost::multiprecision::detail::is_signed<I>::value ? signed_magnitude : unsigned_magnitude), unchecked, void> > >::type>::type;
  69. };
  70. } // namespace detail
  71. template <class I1, class I2, class I3>
  72. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<I1>::value && boost::multiprecision::detail::is_unsigned<I2>::value && boost::multiprecision::detail::is_integral<I3>::value, I1>::type
  73. powm(const I1& a, I2 b, I3 c)
  74. {
  75. using double_type = typename detail::double_integer<I1>::type;
  76. I1 x(1), y(a);
  77. double_type result(0);
  78. while (b > 0)
  79. {
  80. if (b & 1)
  81. {
  82. multiply(result, x, y);
  83. x = integer_modulus(result, c);
  84. }
  85. multiply(result, y, y);
  86. y = integer_modulus(result, c);
  87. b >>= 1;
  88. }
  89. return x % c;
  90. }
  91. template <class I1, class I2, class I3>
  92. inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<I1>::value && boost::multiprecision::detail::is_signed<I2>::value && boost::multiprecision::detail::is_integral<I2>::value && boost::multiprecision::detail::is_integral<I3>::value, I1>::type
  93. powm(const I1& a, I2 b, I3 c)
  94. {
  95. if (b < 0)
  96. {
  97. BOOST_MP_THROW_EXCEPTION(std::runtime_error("powm requires a positive exponent."));
  98. }
  99. return powm(a, static_cast<typename boost::multiprecision::detail::make_unsigned<I2>::type>(b), c);
  100. }
  101. template <class Integer>
  102. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, std::size_t>::type lsb(const Integer& val)
  103. {
  104. if (val <= 0)
  105. {
  106. if (val == 0)
  107. {
  108. BOOST_MP_THROW_EXCEPTION(std::domain_error("No bits were set in the operand."));
  109. }
  110. else
  111. {
  112. BOOST_MP_THROW_EXCEPTION(std::domain_error("Testing individual bits in negative values is not supported - results are undefined."));
  113. }
  114. }
  115. return detail::find_lsb(val);
  116. }
  117. template <class Integer>
  118. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, std::size_t>::type msb(Integer val)
  119. {
  120. if (val <= 0)
  121. {
  122. if (val == 0)
  123. {
  124. BOOST_MP_THROW_EXCEPTION(std::domain_error("No bits were set in the operand."));
  125. }
  126. else
  127. {
  128. BOOST_MP_THROW_EXCEPTION(std::domain_error("Testing individual bits in negative values is not supported - results are undefined."));
  129. }
  130. }
  131. return detail::find_msb(val);
  132. }
  133. template <class Integer>
  134. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, bool>::type bit_test(const Integer& val, std::size_t index)
  135. {
  136. Integer mask = 1;
  137. if (index >= sizeof(Integer) * CHAR_BIT)
  138. return 0;
  139. if (index)
  140. mask <<= index;
  141. return val & mask ? true : false;
  142. }
  143. template <class Integer>
  144. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, Integer&>::type bit_set(Integer& val, std::size_t index)
  145. {
  146. Integer mask = 1;
  147. if (index >= sizeof(Integer) * CHAR_BIT)
  148. return val;
  149. if (index)
  150. mask <<= index;
  151. val |= mask;
  152. return val;
  153. }
  154. template <class Integer>
  155. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, Integer&>::type bit_unset(Integer& val, std::size_t index)
  156. {
  157. Integer mask = 1;
  158. if (index >= sizeof(Integer) * CHAR_BIT)
  159. return val;
  160. if (index)
  161. mask <<= index;
  162. val &= ~mask;
  163. return val;
  164. }
  165. template <class Integer>
  166. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, Integer&>::type bit_flip(Integer& val, std::size_t index)
  167. {
  168. Integer mask = 1;
  169. if (index >= sizeof(Integer) * CHAR_BIT)
  170. return val;
  171. if (index)
  172. mask <<= index;
  173. val ^= mask;
  174. return val;
  175. }
  176. namespace detail {
  177. template <class Integer>
  178. BOOST_MP_CXX14_CONSTEXPR Integer karatsuba_sqrt(const Integer& x, Integer& r, size_t bits)
  179. {
  180. //
  181. // Define the floating point type used for std::sqrt, in our tests, sqrt(double) and sqrt(long double) take
  182. // about the same amount of time as long as long double is not an emulated 128-bit type (ie the same type
  183. // as __float128 from libquadmath). So only use long double if it's an 80-bit type:
  184. //
  185. #ifndef __clang__
  186. typedef typename std::conditional<(std::numeric_limits<long double>::digits == 64), long double, double>::type real_cast_type;
  187. #else
  188. // clang has buggy __int128 -> long double conversion:
  189. typedef double real_cast_type;
  190. #endif
  191. //
  192. // As per the Karatsuba sqrt algorithm, the low order bits/4 bits pay no part in the result, only in the remainder,
  193. // so define the number of bits our argument must have before passing to std::sqrt is safe, even if doing so
  194. // looses a few bits:
  195. //
  196. constexpr std::size_t cutoff = (std::numeric_limits<real_cast_type>::digits * 4) / 3;
  197. //
  198. // Type which can hold at least "cutoff" bits:
  199. //
  200. #ifdef BOOST_HAS_INT128
  201. using cutoff_t = typename std::conditional<(cutoff > 64), uint128_type, std::uint64_t>::type;
  202. #else
  203. using cutoff_t = std::uint64_t;
  204. #endif
  205. //
  206. // See if we can take the fast path:
  207. //
  208. if (bits <= cutoff)
  209. {
  210. constexpr cutoff_t half_bits = (cutoff_t(1u) << ((sizeof(cutoff_t) * CHAR_BIT) / 2)) - 1;
  211. cutoff_t val = static_cast<cutoff_t>(x);
  212. real_cast_type real_val = static_cast<real_cast_type>(val);
  213. cutoff_t s64 = static_cast<cutoff_t>(std::sqrt(real_val));
  214. // converting to long double can loose some precision, and `sqrt` can give eps error, so we'll fix this
  215. // this is needed
  216. while ((s64 > half_bits) || (s64 * s64 > val))
  217. s64--;
  218. // in my tests this never fired, but theoretically this might be needed
  219. while ((s64 < half_bits) && ((s64 + 1) * (s64 + 1) <= val))
  220. s64++;
  221. r = static_cast<Integer>(val - s64 * s64);
  222. return static_cast<Integer>(s64);
  223. }
  224. // https://hal.inria.fr/file/index/docid/72854/filename/RR-3805.pdf
  225. std::size_t b = bits / 4;
  226. Integer q = x;
  227. q >>= b * 2;
  228. Integer s = karatsuba_sqrt(q, r, bits - b * 2);
  229. Integer t = 0u;
  230. bit_set(t, static_cast<unsigned>(b * 2));
  231. r <<= b;
  232. t--;
  233. t &= x;
  234. t >>= b;
  235. t += r;
  236. s <<= 1;
  237. divide_qr(t, s, q, r);
  238. r <<= b;
  239. t = 0u;
  240. bit_set(t, static_cast<unsigned>(b));
  241. t--;
  242. t &= x;
  243. r += t;
  244. s <<= (b - 1); // we already <<1 it before
  245. s += q;
  246. q *= q;
  247. // we substract after, so it works for unsigned integers too
  248. if (r < q)
  249. {
  250. t = s;
  251. t <<= 1;
  252. t--;
  253. r += t;
  254. s--;
  255. }
  256. r -= q;
  257. return s;
  258. }
  259. template <class Integer>
  260. BOOST_MP_CXX14_CONSTEXPR Integer bitwise_sqrt(const Integer& x, Integer& r)
  261. {
  262. //
  263. // This is slow bit-by-bit integer square root, see for example
  264. // http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
  265. // There are better methods such as http://hal.inria.fr/docs/00/07/28/54/PDF/RR-3805.pdf
  266. // and http://hal.inria.fr/docs/00/07/21/13/PDF/RR-4475.pdf which should be implemented
  267. // at some point.
  268. //
  269. Integer s = 0;
  270. switch (x)
  271. {
  272. case 0:
  273. r = 0;
  274. return s;
  275. case 1:
  276. r = 0;
  277. return 1;
  278. case 2:
  279. r = 1;
  280. return 1;
  281. case 3:
  282. r = 2;
  283. return 1;
  284. default:
  285. break;
  286. // fall through:
  287. }
  288. std::ptrdiff_t g = msb(x);
  289. Integer t = 0;
  290. r = x;
  291. g /= 2;
  292. bit_set(s, g);
  293. bit_set(t, 2 * g);
  294. r = x - t;
  295. --g;
  296. do
  297. {
  298. t = s;
  299. t <<= g + 1;
  300. bit_set(t, 2 * g);
  301. if (t <= r)
  302. {
  303. bit_set(s, g);
  304. r -= t;
  305. }
  306. --g;
  307. } while (g >= 0);
  308. return s;
  309. }
  310. } // namespace detail
  311. template <class Integer>
  312. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, Integer>::type sqrt(const Integer& x, Integer& r)
  313. {
  314. #ifndef BOOST_MP_NO_CONSTEXPR_DETECTION
  315. // recursive Karatsuba sqrt can cause issues in constexpr context:
  316. if (BOOST_MP_IS_CONST_EVALUATED(x))
  317. {
  318. return detail::bitwise_sqrt(x, r);
  319. }
  320. #endif
  321. if (x == 0u) {
  322. r = 0u;
  323. return 0u;
  324. }
  325. return detail::karatsuba_sqrt(x, r, msb(x) + 1);
  326. }
  327. template <class Integer>
  328. BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<boost::multiprecision::detail::is_integral<Integer>::value, Integer>::type sqrt(const Integer& x)
  329. {
  330. Integer r(0);
  331. return sqrt(x, r);
  332. }
  333. }} // namespace boost::multiprecision
  334. #endif