1 // Copyright 2016-2021 Francesco Biscani (bluescarni@gmail.com)
2 //
3 // This file is part of the mp++ library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
9 #ifndef MPPP_INTEGER_HPP
10 #define MPPP_INTEGER_HPP
11
12 #include <mp++/config.hpp>
13
14 // NOTE: cinttypes comes from the std::abs() include mess:
15 // http://en.cppreference.com/w/cpp/numeric/math/abs
16
17 #include <algorithm>
18 #include <array>
19 #include <cassert>
20 #include <cinttypes>
21 #include <cmath>
22 #include <complex>
23 #include <cstddef>
24 #include <cstdint>
25 #include <cstdlib>
26 #include <functional>
27 #include <initializer_list>
28 #include <ios>
29 #include <iostream>
30 #include <limits>
31 #include <new>
32 #include <stdexcept>
33 #include <string>
34 #include <tuple>
35 #include <type_traits>
36 #include <utility>
37 #include <vector>
38
39 #if defined(MPPP_HAVE_STRING_VIEW)
40
41 #include <string_view>
42
43 #endif
44
45 #if defined(MPPP_WITH_BOOST_S11N)
46
47 #include <boost/archive/binary_iarchive.hpp>
48 #include <boost/archive/binary_oarchive.hpp>
49 #include <boost/serialization/access.hpp>
50 #include <boost/serialization/binary_object.hpp>
51 #include <boost/serialization/split_member.hpp>
52 #include <boost/serialization/string.hpp>
53 #include <boost/serialization/tracking.hpp>
54
55 #endif
56
57 #include <mp++/concepts.hpp>
58 #include <mp++/detail/gmp.hpp>
59 #include <mp++/detail/type_traits.hpp>
60 #include <mp++/detail/utils.hpp>
61 #include <mp++/detail/visibility.hpp>
62 #include <mp++/exceptions.hpp>
63 #include <mp++/fwd.hpp>
64 #include <mp++/type_name.hpp>
65
66 #if defined(MPPP_WITH_MPFR)
67 #include <mp++/detail/mpfr.hpp>
68 #endif
69
70 // Compiler configuration.
71 // NOTE: check for MSVC first, as clang-cl does define both __clang__ and _MSC_VER,
72 // and we want to configure it as MSVC.
73 #if defined(_MSC_VER)
74
75 // Disable clang-format here, as the include order seems to matter.
76 // clang-format off
77 #include <windows.h>
78 #include <Winnt.h>
79 // clang-format on
80
81 // We use the BitScanReverse(64) intrinsic in the implementation of limb_size_nbits(), but
82 // only if we are *not* on clang-cl: there, we can use GCC-style intrinsics.
83 // https://msdn.microsoft.com/en-us/library/fbxyd7zd.aspx
84 #if !defined(__clang__)
85 #if _WIN64
86 #pragma intrinsic(_BitScanReverse64)
87 #else
88 #include <intrin.h>
89 #pragma intrinsic(_BitScanReverse)
90 #endif
91 #endif
92
93 // Disable some warnings for MSVC.
94 #pragma warning(push)
95 #pragma warning(disable : 4127)
96 #pragma warning(disable : 4146)
97 #pragma warning(disable : 4804)
98
99 // Checked iterators functionality.
100 #include <iterator>
101
102 #endif
103
104 namespace mppp
105 {
106
107 // Strongly typed enum to represent a bit count in the constructor
108 // of integer from number of bits.
109 enum class integer_bitcnt_t : ::mp_bitcnt_t {};
110
111 namespace detail
112 {
113
114 // Small helper to get the size in limbs from an mpz_t. Will return zero if n is zero.
get_mpz_size(const::mpz_t n)115 inline std::size_t get_mpz_size(const ::mpz_t n)
116 {
117 return (n->_mp_size >= 0) ? static_cast<std::size_t>(n->_mp_size) : static_cast<std::size_t>(nint_abs(n->_mp_size));
118 }
119
120 #if defined(_MSC_VER) && defined(__clang__)
121
122 // NOTE: clang-cl gives a deprecation warning
123 // here due to the fact that the dllexported
124 // cache class is missing a copy assignment
125 // operator. Not sure what to make of it.
126 #pragma clang diagnostic push
127 #pragma clang diagnostic ignored "-Wdeprecated"
128
129 #endif
130
131 // Structure for caching allocated arrays of limbs.
132 // NOTE: needs to be public for testing purposes.
133 struct MPPP_DLL_PUBLIC mpz_alloc_cache {
134 // Arrays up to this size will be cached.
135 static constexpr std::size_t max_size = 10;
136 // Max number of arrays to cache for each size.
137 static constexpr std::size_t max_entries = 100;
138 // The actual cache.
139 std::array<std::array<::mp_limb_t *, max_entries>, max_size> caches;
140 // The number of arrays actually stored in each cache entry.
141 std::array<std::size_t, max_size> sizes;
142 // NOTE: use round brackets init for the usual GCC 4.8 workaround.
143 // NOTE: this will zero initialise recursively both members: we will
144 // have all nullptrs in the caches, and all cache sizes will be zeroes.
mpz_alloc_cachemppp::detail::mpz_alloc_cache145 constexpr mpz_alloc_cache() noexcept : caches(), sizes() {}
146 mpz_alloc_cache(const mpz_alloc_cache &) = delete;
147 mpz_alloc_cache(mpz_alloc_cache &&) = delete;
148 mpz_alloc_cache &operator=(const mpz_alloc_cache &) = delete;
149 mpz_alloc_cache &operator=(mpz_alloc_cache &&) = delete;
150 // Clear the cache, deallocating all the data in the arrays.
151 void clear() noexcept;
~mpz_alloc_cachemppp::detail::mpz_alloc_cache152 ~mpz_alloc_cache()
153 {
154 clear();
155 }
156 };
157
158 #if defined(_MSC_VER) && defined(__clang__)
159
160 #pragma clang diagnostic pop
161
162 #endif
163
164 #if defined(MPPP_HAVE_THREAD_LOCAL)
165
166 // Get a reference to the thread-local mpz allocation
167 // cache. Used only for debugging.
168 MPPP_DLL_PUBLIC mpz_alloc_cache &get_thread_local_mpz_cache();
169
170 #endif
171
172 // Helper function to init an mpz to zero with nlimbs preallocated limbs.
173 MPPP_DLL_PUBLIC void mpz_init_nlimbs(mpz_struct_t &, std::size_t);
174
175 // Small helper to determine how many GMP limbs we need to fit nbits bits.
nbits_to_nlimbs(::mp_bitcnt_t nbits)176 constexpr ::mp_bitcnt_t nbits_to_nlimbs(::mp_bitcnt_t nbits)
177 {
178 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
179 return static_cast<::mp_bitcnt_t>(nbits / unsigned(GMP_NUMB_BITS) + ((nbits % unsigned(GMP_NUMB_BITS)) != 0u));
180 }
181
182 // Helper function to init an mpz to zero with enough space for nbits bits. The
183 // nlimbs parameter must be consistent with the nbits parameter (it will be computed
184 // outside this function).
185 MPPP_DLL_PUBLIC void mpz_init_nbits(mpz_struct_t &, ::mp_bitcnt_t, std::size_t);
186
187 // Thin wrapper around mpz_clear(): will add entry to cache if possible instead of clearing.
188 MPPP_DLL_PUBLIC void mpz_clear_wrap(mpz_struct_t &);
189
190 // Combined init+set.
mpz_init_set_nlimbs(mpz_struct_t & m0,const mpz_struct_t & m1)191 inline void mpz_init_set_nlimbs(mpz_struct_t &m0, const mpz_struct_t &m1)
192 {
193 mpz_init_nlimbs(m0, get_mpz_size(&m1));
194 mpz_set(&m0, &m1);
195 }
196
197 // Convert an mpz to a string in a specific base, to be written into out.
198 MPPP_DLL_PUBLIC void mpz_to_str(std::vector<char> &, const mpz_struct_t *, int = 10);
199
200 // Convenience overload for the above.
mpz_to_str(const mpz_struct_t * mpz,int base=10)201 inline std::string mpz_to_str(const mpz_struct_t *mpz, int base = 10)
202 {
203 MPPP_MAYBE_TLS std::vector<char> tmp;
204 mpz_to_str(tmp, mpz, base);
205 return tmp.data();
206 }
207
208 // Small wrapper to copy limbs.
copy_limbs(const::mp_limb_t * begin,const::mp_limb_t * end,::mp_limb_t * out)209 inline void copy_limbs(const ::mp_limb_t *begin, const ::mp_limb_t *end, ::mp_limb_t *out)
210 {
211 for (; begin != end; ++begin, ++out) {
212 *out = *begin;
213 }
214 }
215
216 // The version with non-overlapping ranges.
copy_limbs_no(const::mp_limb_t * begin,const::mp_limb_t * end,::mp_limb_t * MPPP_RESTRICT out)217 inline void copy_limbs_no(const ::mp_limb_t *begin, const ::mp_limb_t *end, ::mp_limb_t *MPPP_RESTRICT out)
218 {
219 assert(begin != out);
220 for (; begin != end; ++begin, ++out) {
221 *out = *begin;
222 }
223 }
224
225 // Add a and b, store the result in res, and return 1 if there's unsigned overflow, 0 otherwise.
226 // NOTE: recent GCC versions have builtins for this, but they don't seem to make much of a difference:
227 // https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
limb_add_overflow(::mp_limb_t a,::mp_limb_t b,::mp_limb_t * res)228 inline ::mp_limb_t limb_add_overflow(::mp_limb_t a, ::mp_limb_t b, ::mp_limb_t *res)
229 {
230 *res = a + b;
231 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
232 return *res < a;
233 }
234
235 // Implementation of the function to count the number of leading zeroes
236 // in an unsigned integral value for GCC/clang. The clz builtin is available
237 // in all supported GCC/clang versions.
238 #if defined(__clang__) || defined(__GNUC__)
239
240 // Dispatch based on the integer type.
builtin_clz_impl(unsigned n)241 inline int builtin_clz_impl(unsigned n)
242 {
243 return __builtin_clz(n);
244 }
245
builtin_clz_impl(unsigned long n)246 inline int builtin_clz_impl(unsigned long n)
247 {
248 return __builtin_clzl(n);
249 }
250
builtin_clz_impl(unsigned long long n)251 inline int builtin_clz_impl(unsigned long long n)
252 {
253 return __builtin_clzll(n);
254 }
255
256 // NOTE: we checked earlier that mp_limb_t is one of the 3 types supported by the
257 // clz builtin. No need to constrain the template.
258 template <typename T>
builtin_clz(T n)259 inline unsigned builtin_clz(T n)
260 {
261 assert(n != 0u);
262 return static_cast<unsigned>(builtin_clz_impl(n));
263 }
264
265 #endif
266
267 // Determine the size in (numeric) bits of limb l.
268 // l does not need to be masked (we will mask inside this function),
269 // and, after masking, it cannot be zero.
limb_size_nbits(::mp_limb_t l)270 inline unsigned limb_size_nbits(::mp_limb_t l)
271 {
272 assert((l & GMP_NUMB_MASK) != 0u);
273 #if defined(__clang__) || defined(__GNUC__)
274 // Implementation using GCC/clang builtins.
275 // NOTE: here we need nl_digits<::mp_limb_t>() (instead of GMP_NUMB_BITS) because
276 // builtin_clz() counts zeroes also in the nail bits.
277 return (unsigned(nl_digits<::mp_limb_t>()) - builtin_clz(l & GMP_NUMB_MASK));
278 #elif defined(_MSC_VER)
279 // Implementation using MSVC's intrinsics.
280 unsigned long index;
281 #if _WIN64
282 _BitScanReverse64
283 #else
284 _BitScanReverse
285 #endif
286 (&index, l & GMP_NUMB_MASK);
287 return static_cast<unsigned>(index) + 1u;
288 #else
289 // Implementation via GMP.
290 // NOTE: assuming GMP knows how to deal with nails, so not masking.
291 return static_cast<unsigned>(mpn_sizeinbase(&l, 1, 2));
292 #endif
293 }
294
295 // Machinery for the conversion of a large uint to a limb array.
296
297 // Definition of the limb array type.
298 template <typename T>
299 struct limb_array_t_ {
300 // We want only unsigned ints.
301 static_assert(is_integral<T>::value && is_unsigned<T>::value, "Type error.");
302 // Overflow check.
303 static_assert(unsigned(nl_digits<T>()) <= nl_max<::mp_bitcnt_t>(), "Overflow error.");
304 // The max number of GMP limbs needed to represent an instance of T.
305 constexpr static std::size_t size = nbits_to_nlimbs(static_cast<::mp_bitcnt_t>(nl_digits<T>()));
306 // Overflow check.
307 static_assert(size <= nl_max<std::size_t>(), "Overflow error.");
308 // The type definition.
309 using type = std::array<::mp_limb_t, static_cast<std::size_t>(size)>;
310 };
311
312 // Handy alias.
313 template <typename T>
314 using limb_array_t = typename limb_array_t_<T>::type;
315
316 // This is a small utility function to shift down the unsigned integer n by GMP_NUMB_BITS.
317 // If GMP_NUMB_BITS is not smaller than the bit size of T, then an assertion will fire. We need this
318 // little helper in order to avoid compiler warnings.
319 template <typename T>
u_checked_rshift(T & n,const std::true_type &)320 inline void u_checked_rshift(T &n, const std::true_type &)
321 {
322 static_assert(is_integral<T>::value && is_unsigned<T>::value, "Invalid type.");
323 n >>= GMP_NUMB_BITS;
324 }
325
326 template <typename T>
u_checked_rshift(T &,const std::false_type &)327 inline void u_checked_rshift(T &, const std::false_type &)
328 {
329 static_assert(is_integral<T>::value && is_unsigned<T>::value, "Invalid type.");
330 assert(false);
331 }
332
333 // Convert a large unsigned integer into a limb array, and return the effective size.
334 // n must be > GMP_NUMB_MAX.
335 template <typename T>
uint_to_limb_array(limb_array_t<T> & rop,T n)336 inline std::size_t uint_to_limb_array(limb_array_t<T> &rop, T n)
337 {
338 static_assert(is_integral<T>::value && is_unsigned<T>::value, "Invalid type.");
339 assert(n > GMP_NUMB_MAX);
340 // We can assign the first two limbs directly, as we know n > GMP_NUMB_MAX.
341 rop[0] = static_cast<::mp_limb_t>(n & GMP_NUMB_MASK);
342 constexpr auto dispatcher = std::integral_constant<bool, (GMP_NUMB_BITS < nl_constants<T>::digits)>{};
343 u_checked_rshift(n, dispatcher);
344 assert(n);
345 rop[1] = static_cast<::mp_limb_t>(n & GMP_NUMB_MASK);
346 u_checked_rshift(n, dispatcher);
347 std::size_t size = 2;
348 // NOTE: currently this code is hit only on 32-bit archs with 64-bit integers,
349 // and we have no nail builds: we cannot go past 2 limbs size.
350 // LCOV_EXCL_START
351 for (; n; ++size, u_checked_rshift(n, dispatcher)) {
352 rop[size] = static_cast<::mp_limb_t>(n & GMP_NUMB_MASK);
353 }
354 // LCOV_EXCL_STOP
355 assert(size <= rop.size());
356 return size;
357 }
358
359 // Small utility to check that no nail bits are set.
check_no_nails(const::mp_limb_t & l)360 inline bool check_no_nails(const ::mp_limb_t &l)
361 {
362 return l <= GMP_NUMB_MAX;
363 }
364
365 // Small utility to compute the absolute value of the size of a 2-limbs number from its lo and hi limbs.
366 // Requires no nail bits in hi or lo.
size_from_lohi(const::mp_limb_t & lo,const::mp_limb_t & hi)367 inline mpz_size_t size_from_lohi(const ::mp_limb_t &lo, const ::mp_limb_t &hi)
368 {
369 assert(check_no_nails(lo) && check_no_nails(hi));
370 const auto lonz = static_cast<unsigned>(lo != 0u), hinz = static_cast<unsigned>(hi != 0u);
371 // NOTE: this contraption ensures the correct result:
372 // hi | lo | asize
373 // -----------------------------
374 // 1 | 1 | 1 * 2 + (0 & 1) = 2
375 // 1 | 0 | 1 * 2 + (0 & 0) = 2
376 // 0 | 1 | 0 * 2 + (1 & 1) = 1
377 // 0 | 0 | 0 * 2 + (1 & 0) = 0
378 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
379 return static_cast<mpz_size_t>(hinz * 2u + (static_cast<unsigned>(!hinz) & lonz));
380 }
381
382 // Branchless sign function for C++ integrals:
383 // https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c
384 template <typename T>
integral_sign(T n)385 constexpr int integral_sign(T n)
386 {
387 static_assert(is_integral<T>::value && is_signed<T>::value,
388 "Invalid type: this function requires signed integrals in input.");
389 return (T(0) < n) - (n < T(0));
390 }
391
392 // Small utility to selectively disable checked iterators warnings
393 // in MSVC. See:
394 // https://msdn.microsoft.com/en-us/library/dn217887.aspx
395 // On compilers other than MSVC, this just returns the input value.
396 template <typename T>
make_uai(T * ptr)397 inline auto make_uai(T *ptr) ->
398 #if defined(_MSC_VER)
399 decltype(stdext::make_unchecked_array_iterator(ptr))
400 #else
401 decltype(ptr)
402 #endif
403 {
404 return
405 #if defined(_MSC_VER)
406 stdext::make_unchecked_array_iterator(ptr);
407 #else
408 ptr;
409 #endif
410 }
411
412 // The static integer class.
413 template <std::size_t SSize>
414 struct static_int {
415 // Let's put a hard cap and sanity check on the static size.
416 static_assert(SSize > 0u && SSize <= 64u, "Invalid static size for integer.");
417 using limbs_type = std::array<::mp_limb_t, SSize>;
418 // Cast it to mpz_size_t for convenience.
419 static const mpz_size_t s_size = SSize;
420 // Special alloc value to signal static storage in the union.
421 static const mpz_alloc_t s_alloc = -1;
422 // The largest number of limbs for which special optimisations are activated.
423 // This is important because the arithmetic optimisations rely on the unused limbs
424 // being zero, thus whenever we use mpn functions on a static int we need to
425 // take care of ensuring that this invariant is respected (see dtor_checks() and
426 // zero_unused_limbs(), for instance).
427 static const std::size_t opt_size = 2;
428 // Zero the limbs from index idx up to the end of the limbs array, but only
429 // if the static size is a target for special optimisations.
zero_upper_limbsmppp::detail::static_int430 void zero_upper_limbs(std::size_t idx)
431 {
432 if (SSize <= opt_size) {
433 std::fill(m_limbs.begin() + idx, m_limbs.end(), ::mp_limb_t(0));
434 }
435 }
436 // Zero the limbs that are not used for representing the value, but only
437 // if the static size is a target for special optimisations.
438 // This is normally not needed, but it is useful when using the GMP mpn api on a static int:
439 // the GMP api does not clear unused limbs, but we rely on unused limbs being zero when optimizing operations
440 // for few static limbs.
zero_unused_limbsmppp::detail::static_int441 void zero_unused_limbs()
442 {
443 zero_upper_limbs(static_cast<std::size_t>(abs_size()));
444 }
445 // Default constructor, inits to zero.
static_intmppp::detail::static_int446 static_int() : _mp_size(0)
447 {
448 // Zero the limbs, if needed.
449 zero_upper_limbs(0);
450 }
451 // Let's avoid copying the _mp_alloc member, as it is never written to and it must always
452 // have the same value.
static_intmppp::detail::static_int453 static_int(const static_int &other) : _mp_size(other._mp_size)
454 {
455 if (SSize <= opt_size) {
456 // In this case, we know that other's upper limbs are already
457 // properly zeroed.
458 m_limbs = other.m_limbs;
459 } else {
460 // Otherwise, we cannot read potentially uninited limbs.
461 const auto asize = other.abs_size();
462 // Copy over the limbs. This is safe, as other is a distinct object from this.
463 copy_limbs_no(other.m_limbs.data(), other.m_limbs.data() + asize, m_limbs.data());
464 // No need to zero, we are in non-optimised static size.
465 }
466 }
467 // Same as copy constructor.
468 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
static_intmppp::detail::static_int469 static_int(static_int &&other) noexcept : static_int(other) {}
470 // These 2 constructors are used in the generic constructor of integer_union.
471 //
472 // Constructor from a size and a single limb (will be the least significant limb).
static_intmppp::detail::static_int473 explicit static_int(mpz_size_t size, ::mp_limb_t l) : _mp_size(size)
474 {
475 // Input sanity checks.
476 assert(size <= s_size && size >= -s_size);
477 // l and size must both be zero or both nonzero.
478 assert((l && size) || (!l && !size));
479 // The limb is coming in from the decomposition of an uintegral value,
480 // it should not have any nail bit set.
481 assert(l <= GMP_NUMB_MAX);
482 // NOTE: this is ok if l is zero: after init, the size will be zero
483 // and either all limbs have been set to zero (for SSize <= opt_size),
484 // or only the first limb has been set to zero (for SSize > opt_size).
485 m_limbs[0] = l;
486 // Zero fill the remaining limbs, if needed.
487 zero_upper_limbs(1);
488 }
489 // Constructor from a (signed) size and a limb range. The limbs in the range will be
490 // copied as the least significant limbs.
static_intmppp::detail::static_int491 explicit static_int(mpz_size_t size, const ::mp_limb_t *begin, std::size_t asize) : _mp_size(size)
492 {
493 // Input sanity checks.
494 assert(asize <= SSize);
495 assert(size <= s_size && size >= -s_size);
496 assert(size == static_cast<mpz_size_t>(asize) || size == -static_cast<mpz_size_t>(asize));
497 // Copy the input limbs.
498 // NOTE: here we are constructing a *new* object, thus I don't think it's possible that begin
499 // overlaps with m_limbs.data() (unless some UBish stuff is being attempted).
500 copy_limbs_no(begin, begin + asize, m_limbs.data());
501 // Zero fill the remaining limbs, if needed.
502 zero_upper_limbs(asize);
503 }
504 // NOLINTNEXTLINE(cert-oop54-cpp)
operator =mppp::detail::static_int505 static_int &operator=(const static_int &other)
506 {
507 _mp_size = other._mp_size;
508 if (SSize <= opt_size) {
509 // In this case, we know other's upper limbs are properly zeroed out.
510 // NOTE: self assignment of std::array should be fine.
511 m_limbs = other.m_limbs;
512 } else {
513 // Otherwise, we must avoid reading from uninited limbs.
514 // Copy over the limbs. There's potential overlap here.
515 const auto asize = other.abs_size();
516 copy_limbs(other.m_limbs.data(), other.m_limbs.data() + asize, m_limbs.data());
517 // No need to zero, we are in non-optimised static size.
518 }
519 return *this;
520 }
operator =mppp::detail::static_int521 static_int &operator=(static_int &&other) noexcept
522 {
523 // Just forward to the copy assignment.
524 // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
525 return operator=(other);
526 }
527 // Swap primitive.
swapmppp::detail::static_int528 void swap(static_int &other) noexcept
529 {
530 if (SSize <= opt_size) {
531 // In this case, we know other's upper limbs are properly zeroed out.
532 // NOTE: self swap of std::array should be fine.
533 std::swap(_mp_size, other._mp_size);
534 std::swap(m_limbs, other.m_limbs);
535 } else {
536 // Otherwise, we must avoid reading from uninited limbs.
537 // Copy over the limbs of this to temp storage.
538 limbs_type tmp;
539 const auto asize1 = abs_size();
540 copy_limbs_no(m_limbs.data(), m_limbs.data() + asize1, tmp.data());
541
542 // Copy over the limbs of other to this.
543 // NOTE: potential overlap here.
544 copy_limbs(other.m_limbs.data(), other.m_limbs.data() + other.abs_size(), m_limbs.data());
545
546 // Copy over the limbs in temp storage to other.
547 copy_limbs_no(tmp.data(), tmp.data() + asize1, other.m_limbs.data());
548
549 // Swap the sizes.
550 std::swap(_mp_size, other._mp_size);
551 }
552 }
553 #ifdef __MINGW32__
554
555 #pragma GCC diagnostic push
556 #pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
557
558 #endif
dtor_checksmppp::detail::static_int559 MPPP_NODISCARD bool dtor_checks() const
560 {
561 // LCOV_EXCL_START
562 const auto asize = abs_size();
563 // Check the value of the alloc member.
564 if (_mp_alloc != s_alloc) {
565 return false;
566 }
567 // Check the asize is not too large.
568 if (asize > s_size) {
569 return false;
570 }
571 // Check that all limbs which do not participate in the value are zero, iff the SSize is small enough.
572 // For small SSize, we might be using optimisations that require unused limbs to be zero.
573 if (SSize <= opt_size) {
574 for (auto i = static_cast<std::size_t>(asize); i < SSize; ++i) {
575 if (m_limbs[i]) {
576 return false;
577 }
578 }
579 }
580 // Check that the highest limb is not zero.
581 if (asize > 0 && (m_limbs[static_cast<std::size_t>(asize - 1)] & GMP_NUMB_MASK) == 0u) {
582 return false;
583 }
584 return true;
585 // LCOV_EXCL_STOP
586 }
587 #ifdef __MINGW32__
588
589 #pragma GCC diagnostic pop
590
591 #endif
~static_intmppp::detail::static_int592 ~static_int()
593 {
594 assert(dtor_checks());
595 }
596 // Size in limbs (absolute value of the _mp_size member).
abs_sizemppp::detail::static_int597 MPPP_NODISCARD mpz_size_t abs_size() const
598 {
599 return std::abs(_mp_size);
600 }
601 // NOTE: the retval here can be used only in read-only mode, otherwise
602 // we will have UB due to the const_cast use.
get_mpz_viewmppp::detail::static_int603 MPPP_NODISCARD mpz_struct_t get_mpz_view() const
604 {
605 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
606 return mpz_struct_t{_mp_alloc, _mp_size, const_cast<::mp_limb_t *>(m_limbs.data())};
607 }
608 mpz_alloc_t _mp_alloc = s_alloc;
609 // NOLINTNEXTLINE(modernize-use-default-member-init)
610 mpz_size_t _mp_size;
611 limbs_type m_limbs;
612 };
613
614 // {static_int,mpz} union.
615 template <std::size_t SSize>
616 union integer_union {
617 using s_storage = static_int<SSize>;
618 using d_storage = mpz_struct_t;
619 // Def ctor, will init to static.
integer_union()620 integer_union() : m_st() {}
621 // Copy constructor, does a deep copy maintaining the storage class of other.
622 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const integer_union & other)623 integer_union(const integer_union &other)
624 {
625 if (other.is_static()) {
626 ::new (static_cast<void *>(&m_st)) s_storage(other.g_st());
627 } else {
628 ::new (static_cast<void *>(&m_dy)) d_storage;
629 mpz_init_set_nlimbs(m_dy, other.g_dy());
630 assert(m_dy._mp_alloc >= 0);
631 }
632 }
633 // Move constructor. Will downgrade other to a static zero integer if other is dynamic.
634 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(integer_union && other)635 integer_union(integer_union &&other) noexcept
636 {
637 if (other.is_static()) {
638 // Activate the static member with a copy.
639 ::new (static_cast<void *>(&m_st)) s_storage(other.g_st());
640 } else {
641 // Activate dynamic member and shallow copy it from other.
642 ::new (static_cast<void *>(&m_dy)) d_storage(other.g_dy());
643 // Downgrade the other to an empty static.
644 other.g_dy().~d_storage();
645 ::new (static_cast<void *>(&other.m_st)) s_storage();
646 }
647 }
648 // Special casing for bool.
dispatch_generic_ctor(bool b)649 void dispatch_generic_ctor(bool b)
650 {
651 // Construct the static. No need for masking in the only limb.
652 ::new (static_cast<void *>(&m_st)) s_storage{static_cast<mpz_size_t>(b), static_cast<::mp_limb_t>(b)};
653 }
654 // Construction from unsigned ints. The Neg flag will negate the integer after construction, it is for use in
655 // the constructor from signed ints.
656 template <typename T, bool Neg = false, enable_if_t<conjunction<is_integral<T>, is_unsigned<T>>::value, int> = 0>
dispatch_generic_ctor(T n)657 void dispatch_generic_ctor(T n)
658 {
659 if (n <= GMP_NUMB_MAX) {
660 // Special codepath if n fits directly in a single limb.
661 // No need for the mask as we are sure that n <= GMP_NUMB_MAX.
662 ::new (static_cast<void *>(&m_st)) s_storage{Neg ? -(n != 0u) : (n != 0u), static_cast<::mp_limb_t>(n)};
663 return;
664 }
665 // Convert n into an array of limbs.
666 limb_array_t<T> tmp;
667 const auto size = uint_to_limb_array(tmp, n);
668 construct_from_limb_array<false>(tmp.data(), size);
669 // Negate if requested.
670 if (Neg) {
671 neg();
672 }
673 }
674 // Construction from signed ints.
675 template <typename T, enable_if_t<conjunction<is_integral<T>, is_signed<T>>::value, int> = 0>
dispatch_generic_ctor(T n)676 void dispatch_generic_ctor(T n)
677 {
678 if (n >= T(0)) {
679 // Positive value, just cast to unsigned.
680 dispatch_generic_ctor(make_unsigned(n));
681 } else {
682 // Negative value, use its abs.
683 dispatch_generic_ctor<make_unsigned_t<T>, true>(nint_abs(n));
684 }
685 }
686 // Construction from float/double.
687 template <typename T, enable_if_t<disjunction<std::is_same<T, float>, std::is_same<T, double>>::value, int> = 0>
dispatch_generic_ctor(T x)688 void dispatch_generic_ctor(T x)
689 {
690 if (mppp_unlikely(!std::isfinite(x))) {
691 throw std::domain_error("Cannot construct an integer from the non-finite floating-point value "
692 + to_string(x));
693 }
694 MPPP_MAYBE_TLS mpz_raii tmp;
695 mpz_set_d(&tmp.m_mpz, static_cast<double>(x));
696 dispatch_mpz_ctor(&tmp.m_mpz);
697 }
698 #if defined(MPPP_WITH_MPFR)
699 // Construction from long double, requires MPFR.
dispatch_generic_ctor(long double x)700 void dispatch_generic_ctor(long double x)
701 {
702 if (mppp_unlikely(!std::isfinite(x))) {
703 throw std::domain_error("Cannot construct an integer from the non-finite floating-point value "
704 + to_string(x));
705 }
706 // NOTE: static checks for overflows and for the precision value are done in mpfr.hpp.
707 constexpr int d2 = std::numeric_limits<long double>::max_digits10 * 4;
708 MPPP_MAYBE_TLS mpfr_raii mpfr(static_cast<::mpfr_prec_t>(d2));
709 MPPP_MAYBE_TLS mpz_raii tmp;
710 ::mpfr_set_ld(&mpfr.m_mpfr, x, MPFR_RNDN);
711 ::mpfr_get_z(&tmp.m_mpz, &mpfr.m_mpfr, MPFR_RNDZ);
712 dispatch_mpz_ctor(&tmp.m_mpz);
713 }
714 #endif
715 // The generic constructor.
716 template <typename T>
717 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const T & x)718 explicit integer_union(const T &x)
719 {
720 dispatch_generic_ctor(x);
721 }
722 // Implementation of the constructor from string. Abstracted into separate function because it is re-used.
dispatch_c_string_ctor(const char * s,int base)723 void dispatch_c_string_ctor(const char *s, int base)
724 {
725 if (mppp_unlikely(base != 0 && (base < 2 || base > 62))) {
726 throw std::invalid_argument(
727 "In the constructor of integer from string, a base of " + to_string(base)
728 + " was specified, but the only valid values are 0 and any value in the [2,62] range");
729 }
730 MPPP_MAYBE_TLS mpz_raii mpz;
731 if (mppp_unlikely(mpz_set_str(&mpz.m_mpz, s, base))) {
732 if (base != 0) {
733 throw std::invalid_argument(std::string("The string '") + s + "' is not a valid integer in base "
734 + to_string(base));
735 } else {
736 throw std::invalid_argument(std::string("The string '") + s
737 + "' is not a valid integer in any supported base");
738 }
739 }
740 dispatch_mpz_ctor(&mpz.m_mpz);
741 }
742 // Constructor from C string and base.
743 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const char * s,int base)744 explicit integer_union(const char *s, int base)
745 {
746 dispatch_c_string_ctor(s, base);
747 }
748 // Constructor from string range and base.
749 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const char * begin,const char * end,int base)750 explicit integer_union(const char *begin, const char *end, int base)
751 {
752 // Copy the range into a local buffer.
753 MPPP_MAYBE_TLS std::vector<char> buffer;
754 buffer.assign(begin, end);
755 buffer.emplace_back('\0');
756 dispatch_c_string_ctor(buffer.data(), base);
757 }
758 // Constructor from mpz_t. Abstracted into separate function because it is re-used.
dispatch_mpz_ctor(const::mpz_t n)759 void dispatch_mpz_ctor(const ::mpz_t n)
760 {
761 const auto asize = get_mpz_size(n);
762 if (asize > SSize) {
763 // Too big, need to use dynamic.
764 ::new (static_cast<void *>(&m_dy)) d_storage;
765 mpz_init_set_nlimbs(m_dy, *n);
766 } else {
767 ::new (static_cast<void *>(&m_st)) s_storage{n->_mp_size, n->_mp_d, asize};
768 }
769 }
770 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const::mpz_t n)771 explicit integer_union(const ::mpz_t n)
772 {
773 dispatch_mpz_ctor(n);
774 }
775 #if !defined(_MSC_VER)
776 // Move ctor from mpz_t.
777 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(::mpz_t && n)778 explicit integer_union(::mpz_t &&n)
779 {
780 const auto asize = get_mpz_size(n);
781 if (asize > SSize) {
782 // Too big, make shallow copy into dynamic. this will now
783 // own the resources of n.
784 ::new (static_cast<void *>(&m_dy)) d_storage(*n);
785 } else {
786 // Fits into small.
787 ::new (static_cast<void *>(&m_st)) s_storage{n->_mp_size, n->_mp_d, asize};
788 // Clear n: its resources have been copied into this, and we must
789 // ensure uniform behaviour with the case in which we shallow-copied it
790 // into dynamic storage.
791 mpz_clear_wrap(*n);
792 }
793 }
794 #endif
795 // Implementation of the ctor from an array of limbs. CheckArray establishes
796 // if p is checked for sanity.
797 template <bool CheckArray>
construct_from_limb_array(const::mp_limb_t * p,std::size_t size)798 void construct_from_limb_array(const ::mp_limb_t *p, std::size_t size)
799 {
800 if (CheckArray) {
801 // If size is not zero, then the most significant limb must contain something.
802 if (mppp_unlikely(size && !p[size - 1u])) {
803 throw std::invalid_argument(
804 "When initialising an integer from an array of limbs, the last element of the "
805 "limbs array must be nonzero");
806 }
807 // NOTE: no builds in the CI have nail bits, cannot test this failure.
808 // LCOV_EXCL_START
809 // All elements of p must be <= GMP_NUMB_MAX.
810 if (mppp_unlikely(std::any_of(p, p + size, [](::mp_limb_t l) { return l > GMP_NUMB_MAX; }))) {
811 throw std::invalid_argument("When initialising an integer from an array of limbs, every element of the "
812 "limbs array must not be greater than GMP_NUMB_MAX");
813 }
814 // LCOV_EXCL_STOP
815 } else {
816 assert(!size || p[size - 1u]);
817 assert(std::all_of(p, p + size, [](::mp_limb_t l) { return l <= GMP_NUMB_MAX; }));
818 }
819 if (size <= SSize) {
820 // Fits into small. This constructor will take care
821 // of zeroing out the top limbs as well.
822 ::new (static_cast<void *>(&m_st)) s_storage{static_cast<mpz_size_t>(size), p, size};
823 } else {
824 // Convert size to mpz_size_t before anything else, for exception safety.
825 const auto s = safe_cast<mpz_size_t>(size);
826 // Init the dynamic storage struct.
827 ::new (static_cast<void *>(&m_dy)) d_storage;
828 // Init to zero, with size precallocated limbs.
829 // NOTE: currently this does not throw, it will just abort() in case of overflow,
830 // allocation errors, etc. If this ever becomes throwing, we probably need to move
831 // the init of d_storage below this line, so that if an exception is thrown we have
832 // not constructed the union member yet.
833 mpz_init_nlimbs(m_dy, size);
834 // Copy the input limbs.
835 // NOTE: here we are constructing a *new* object, thus I don't think it's possible that p
836 // overlaps with m_dy._mp_d (unless some UBish stuff is being attempted).
837 copy_limbs_no(p, p + size, m_dy._mp_d);
838 // Assign the size.
839 m_dy._mp_size = s;
840 }
841 }
842 // Constructor from array of limbs.
843 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(const::mp_limb_t * p,std::size_t size)844 explicit integer_union(const ::mp_limb_t *p, std::size_t size)
845 {
846 construct_from_limb_array<true>(p, size);
847 }
848 // Constructor from number of bits.
849 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
integer_union(integer_bitcnt_t nbits_)850 explicit integer_union(integer_bitcnt_t nbits_)
851 {
852 const auto nbits = static_cast<::mp_bitcnt_t>(nbits_);
853 const auto nlimbs = safe_cast<std::size_t>(nbits_to_nlimbs(nbits));
854 if (nlimbs <= SSize) {
855 // Static storage is enough for the requested number of bits. Def init.
856 ::new (static_cast<void *>(&m_st)) s_storage();
857 } else {
858 // Init the dynamic storage struct.
859 ::new (static_cast<void *>(&m_dy)) d_storage;
860 // Init to zero with the desired number of bits.
861 mpz_init_nbits(m_dy, nbits, nlimbs);
862 }
863 }
864 // Copy assignment operator, performs a deep copy maintaining the storage class.
865 // NOLINTNEXTLINE(cert-oop54-cpp)
operator =(const integer_union & other)866 integer_union &operator=(const integer_union &other)
867 {
868 const bool s1 = is_static(), s2 = other.is_static();
869 if (s1 && s2) {
870 // Self assignment is fine, handled in the static.
871 g_st() = other.g_st();
872 } else if (s1 && !s2) {
873 // Destroy static.
874 g_st().~s_storage();
875 // Construct the dynamic struct.
876 ::new (static_cast<void *>(&m_dy)) d_storage;
877 // Init + assign the mpz.
878 mpz_init_set_nlimbs(m_dy, other.g_dy());
879 assert(m_dy._mp_alloc >= 0);
880 } else if (!s1 && s2) {
881 // Destroy the dynamic this.
882 destroy_dynamic();
883 // Init-copy the static from other.
884 ::new (static_cast<void *>(&m_st)) s_storage(other.g_st());
885 } else {
886 // Self assignment is fine, mpz_set() can have aliasing arguments.
887 mpz_set(&g_dy(), &other.g_dy());
888 }
889 return *this;
890 }
891 // Move assignment, same as above plus possibly steals resources. If this is static
892 // and other is dynamic, other is downgraded to a zero static.
operator =(integer_union && other)893 integer_union &operator=(integer_union &&other) noexcept
894 {
895 const bool s1 = is_static(), s2 = other.is_static();
896 if (s1 && s2) {
897 // Self assignment is fine, handled in the static.
898 g_st() = other.g_st();
899 } else if (s1 && !s2) {
900 // Destroy static.
901 g_st().~s_storage();
902 // Construct the dynamic struct, shallow-copying from other.
903 ::new (static_cast<void *>(&m_dy)) d_storage(other.g_dy());
904 // Downgrade the other to an empty static.
905 other.g_dy().~d_storage();
906 ::new (static_cast<void *>(&other.m_st)) s_storage();
907 } else if (!s1 && s2) {
908 // Same as copy assignment: destroy and copy-construct.
909 destroy_dynamic();
910 ::new (static_cast<void *>(&m_st)) s_storage(other.g_st());
911 } else {
912 // Swap with other. Self-assignment is fine, mpz_swap() can have
913 // aliasing arguments.
914 mpz_swap(&g_dy(), &other.g_dy());
915 }
916 return *this;
917 }
~integer_union()918 ~integer_union()
919 {
920 if (is_static()) {
921 g_st().~s_storage();
922 } else {
923 destroy_dynamic();
924 }
925 }
destroy_dynamic()926 void destroy_dynamic()
927 {
928 assert(!is_static());
929 assert(g_dy()._mp_alloc >= 0);
930 assert(g_dy()._mp_d != nullptr);
931 mpz_clear_wrap(g_dy());
932 g_dy().~d_storage();
933 }
934 // Check storage type.
is_static() const935 MPPP_NODISCARD bool is_static() const
936 {
937 return m_st._mp_alloc == s_storage::s_alloc;
938 }
is_dynamic() const939 MPPP_NODISCARD bool is_dynamic() const
940 {
941 return m_st._mp_alloc != s_storage::s_alloc;
942 }
943 // Getters for st and dy.
g_st() const944 MPPP_NODISCARD const s_storage &g_st() const
945 {
946 assert(is_static());
947 return m_st;
948 }
g_st()949 s_storage &g_st()
950 {
951 assert(is_static());
952 return m_st;
953 }
g_dy() const954 MPPP_NODISCARD const d_storage &g_dy() const
955 {
956 assert(is_dynamic());
957 return m_dy;
958 }
g_dy()959 d_storage &g_dy()
960 {
961 assert(is_dynamic());
962 return m_dy;
963 }
964 // Promotion from static to dynamic. If nlimbs != 0u, allocate nlimbs limbs, otherwise
965 // allocate exactly the nlimbs necessary to represent this.
promote(std::size_t nlimbs=0u)966 void promote(std::size_t nlimbs = 0u)
967 {
968 assert(is_static());
969 mpz_struct_t tmp_mpz;
970 // Get a static_view.
971 const auto v = g_st().get_mpz_view();
972 if (nlimbs == 0u) {
973 // If nlimbs is zero, we will allocate exactly the needed
974 // number of limbs to represent this.
975 mpz_init_set_nlimbs(tmp_mpz, v);
976 } else {
977 // Otherwise, we preallocate nlimbs and then set tmp_mpz
978 // to the value of this.
979 mpz_init_nlimbs(tmp_mpz, nlimbs);
980 mpz_set(&tmp_mpz, &v);
981 }
982 // Destroy static.
983 g_st().~s_storage();
984 // Construct the dynamic struct.
985 ::new (static_cast<void *>(&m_dy)) d_storage;
986 m_dy = tmp_mpz;
987 }
988 // Demotion from dynamic to static.
demote()989 bool demote()
990 {
991 assert(is_dynamic());
992 const auto dyn_size = get_mpz_size(&g_dy());
993 // If the dynamic size is greater than the static size, we cannot demote.
994 if (dyn_size > SSize) {
995 return false;
996 }
997 // Copy over the limbs to temporary storage.
998 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
999 std::array<::mp_limb_t, SSize> tmp;
1000 copy_limbs_no(g_dy()._mp_d, g_dy()._mp_d + dyn_size, tmp.data());
1001 const auto signed_size = g_dy()._mp_size;
1002 // Destroy the dynamic storage.
1003 destroy_dynamic();
1004 // Init the static storage with the saved data. The unused limbs will be zeroed
1005 // by the invoked static_int ctor.
1006 ::new (static_cast<void *>(&m_st)) s_storage{signed_size, tmp.data(), dyn_size};
1007 return true;
1008 }
1009 // Negation.
neg()1010 void neg()
1011 {
1012 if (is_static()) {
1013 g_st()._mp_size = -g_st()._mp_size;
1014 } else {
1015 mpz_neg(&g_dy(), &g_dy());
1016 }
1017 }
1018 // NOTE: keep these public as we need them below.
1019 s_storage m_st;
1020 d_storage m_dy;
1021 };
1022 } // namespace detail
1023
1024 // Fwd declarations.
1025 template <std::size_t SSize>
1026 integer<SSize> &sqrt(integer<SSize> &, const integer<SSize> &);
1027
1028 template <std::size_t SSize>
1029 integer<SSize> &sqr(integer<SSize> &, const integer<SSize> &);
1030
1031 namespace detail
1032 {
1033
1034 template <std::size_t SSize>
1035 void nextprime_impl(integer<SSize> &, const integer<SSize> &);
1036
1037 } // namespace detail
1038
1039 // Detect C++ arithmetic types compatible with integer.
1040 template <typename T>
1041 using is_integer_cpp_arithmetic = detail::conjunction<is_cpp_arithmetic<T>
1042 #if !defined(MPPP_WITH_MPFR)
1043 ,
1044 detail::negation<std::is_same<T, long double>>
1045 #endif
1046 >;
1047
1048 #if defined(MPPP_HAVE_CONCEPTS)
1049
1050 template <typename T>
1051 MPPP_CONCEPT_DECL integer_cpp_arithmetic = is_integer_cpp_arithmetic<T>::value;
1052
1053 #endif
1054
1055 // Detect C++ complex types compatible with integer.
1056 template <typename T>
1057 using is_integer_cpp_complex = detail::conjunction<is_cpp_complex<T>
1058 #if !defined(MPPP_WITH_MPFR)
1059 ,
1060 detail::negation<std::is_same<T, std::complex<long double>>>
1061 #endif
1062 >;
1063
1064 #if defined(MPPP_HAVE_CONCEPTS)
1065
1066 template <typename T>
1067 MPPP_CONCEPT_DECL integer_cpp_complex = is_integer_cpp_complex<T>::value;
1068
1069 #endif
1070
1071 namespace detail
1072 {
1073
1074 // For private use only.
1075 template <typename T>
1076 using is_integer_cpp_floating_point = detail::conjunction<is_cpp_floating_point<T>
1077 #if !defined(MPPP_WITH_MPFR)
1078 ,
1079 detail::negation<std::is_same<T, long double>>
1080 #endif
1081 >;
1082
1083 } // namespace detail
1084
1085 // NOTE: a few misc future directions:
1086 // - re-visit at one point the issue of the estimators when we need to promote from static to dynamic
1087 // in arithmetic ops. Currently they are not 100% optimal since they rely on the information coming out
1088 // of the static implementation rather than computing the estimated size of rop themselves, for performance
1089 // reasons (see comments);
1090 // - maybe the lshift/rshift optimisation for 2 limbs could use dlimb types if available?
1091 // - it seems like it might be possible to re-implement a bare-bone mpz api on top of the mpn primitives,
1092 // with the goal of providing some form of error recovery in case of memory errors (e.g., throw instead
1093 // of aborting). This is probably not an immediate concern though.
1094 // - pow() can probably benefit for some specialised static implementation, especially in conjunction with
1095 // mpn_sqr().
1096 // - functions still to be de-branched: all the mpn implementations, if worth it.
1097 // Probably better to wait for benchmarks before moving.
1098 // - for s11n, we could consider implementing binary_save/load overloads based on iterators. These could return
1099 // the iterator at the end of the serialised representation, and maybe the original iterator if errors
1100 // occurred? Probably some code could be refactored/shared with the char * interface, taking advantage
1101 // of the fact that std::copy() actually returns something. Longer term, we probably should add optional
1102 // support for boost.serialization, cereal, etc.
1103 // - for the comparison operators, we should consider using lower level primitives for comparisons with
1104 // both C++ integrals and FP, instead of going through type conversions (same as done for add_ui() etc.).
1105 // - perhaps we should consider adding new overloads to functions which return more than one value
1106 // (e.g., tdiv_qr(), sqrtrem(), etc.). At this time we have only the GMP-style overload, perhaps
1107 // we could have an overload that returns the two values as tuple/pair/array.
1108 // - the lt/gt static implementations could be specialised for 2-limbs integers. But we need to have
1109 // benchmarks before doing it.
1110 // - Regarding complex interoperability (for integer but rational as well): it seems like for
1111 // mixed-mode binary operations there might be benefits in converting the integer argument not
1112 // to complex<T> (as we are doing now), but rather T, because like this we end up using
1113 // real vs complex rather than complex vs complex primitives. It's not however 100% clear
1114 // to me that proceeding like this is always equivalent to doing the complex promotion
1115 // (which is what the usual type coercion rules would dictate),
1116 // and in any case the performance of integer vs complex arithmetics is not a high
1117 // priority at this time. Perhaps revisit this topic in the future.
1118 // - We can probably remove the public dependency on the GMP library by writing
1119 // thin wrappers for all the invoked GMP functions, implemented in the mp++ library.
1120 // Same for MPFR.
1121 // - Implement a binary version of fac_ui(). Note that it will not be able to be called
1122 // via ADL because the argument is a primitive type.
1123
1124 // NOTE: about the nails:
1125 // - whenever we need to read the *numerical value* of a limb (e.g., in our optimised primitives),
1126 // we don't make any assumption about nail bits. That is, we assume that anything could be in that bit,
1127 // so we always mask the limb read with GMP_NUMB_MASK (which, today, is a no-op on virtually all setups).
1128 // This ensures that we are actually working with a subsection of a multiprecision value, uncontaminated
1129 // by extraneous bits. If, on the other hand, we are just doing a copy of the limb we don't care about
1130 // the nail bit as we are not using the numerical value of the limb, and any extraneous bit has no
1131 // consequence on any computation.
1132 // - If we are using the mpz/mpn API, we don't care about nail bits because we assume that is handled by
1133 // the GMP library itself. Not our problem.
1134 // - When we produce manually (i.e., without using the mpz/mpn API) some limb to be written out to
1135 // an integer, we take care of ensuring that no nail bits are set. This is actually currently required
1136 // by the GMP API:
1137 //
1138 // "All the mpn functions accepting limb data will expect the nail bits to be zero on entry, and will
1139 // return data with the nails similarly all zero."
1140 //
1141 // Again, this masking before writing will be a no-op in most cases. And in many cases we don't have
1142 // to do anything explicitly as many of our primitives require no nails or they just don't end up
1143 // writing into nail bits (see the shift operators for an exception to this).
1144 // - This whole topic of nails is at this time largely academic and unlikely to have any real impact,
1145 // as it seems like nobody builds GMP with nails support nowadays. If we ever want to get rid of
1146 // all the masking, see commit 30c23c8984d2955d19c35af84e7845dba88d94c0 for a starting point.
1147
1148 // NOTE: about the zeroing of the upper limbs in static_int:
1149 // - the idea here is that, to implement optimised basic primitives for small static sizes, it is convenient
1150 // to rely on the fact that unused limbs are zeroed out. This can reduce branching and it generally
1151 // simplifies the code.
1152 // - Because of this, whenever we write into a "small" static int, we must take care of zeroing out the upper limbs
1153 // that are unused in the representation of the current value. This holds both when using the mpn/mpz functions
1154 // and when implementing our own specialised primitives (note however that if we can prove that we are calling mpn
1155 // or mpz on a non-small integer, then we can omit the call to zero_upper_limbs() - we do this occasionally).
1156 // - If the static size is small, we can copy/assign limb arrays around without caring for the effective size, as
1157 // all limbs are always initialised to some value. With large static size, we cannot do that as the upper
1158 // limbs are not necessarily inited.
1159 // - Contrary to the nail business, ensuring that the upper limbs are zeroed is essential, and it is something
1160 // we check in the unit tests.
1161
1162 // Multiprecision integer class.
1163 template <std::size_t SSize>
1164 class integer
1165 {
1166 #if defined(MPPP_WITH_BOOST_S11N)
1167 // Boost serialization support.
1168 friend class boost::serialization::access;
1169
1170 template <typename Archive>
save(Archive & ar,unsigned) const1171 void save(Archive &ar, unsigned) const
1172 {
1173 ar << to_string();
1174 }
1175
1176 template <typename Archive>
load(Archive & ar,unsigned)1177 void load(Archive &ar, unsigned)
1178 {
1179 std::string tmp;
1180 ar >> tmp;
1181
1182 *this = integer{tmp};
1183 }
1184
1185 // Overloads for binary archives.
save(boost::archive::binary_oarchive & ar,unsigned) const1186 void save(boost::archive::binary_oarchive &ar, unsigned) const
1187 {
1188 MPPP_MAYBE_TLS std::vector<char> buffer;
1189 binary_save(buffer);
1190
1191 // Record the size and the raw data.
1192 ar << buffer.size();
1193 ar << boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
1194 }
1195
load(boost::archive::binary_iarchive & ar,unsigned)1196 void load(boost::archive::binary_iarchive &ar, unsigned)
1197 {
1198 MPPP_MAYBE_TLS std::vector<char> buffer;
1199
1200 // Recover the size.
1201 decltype(buffer.size()) s;
1202 ar >> s;
1203 buffer.resize(s);
1204
1205 ar >> boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
1206
1207 binary_load(buffer);
1208 }
1209
1210 BOOST_SERIALIZATION_SPLIT_MEMBER()
1211 #endif
1212
1213 // Typedefs for ease of use.
1214 using s_storage = detail::static_int<SSize>;
1215 using d_storage = detail::mpz_struct_t;
1216 // The underlying static int.
1217 using s_int = s_storage;
1218 // mpz view class.
1219 // NOTE: this class looks more complicated than it seemingly should be - it looks like
1220 // it should be enough to shallow-copy around an mpz_struct_t with the limbs pointing
1221 // either to a static integer or a dynamic one. This option, however, has a problem:
1222 // in case of a dynamic integer, one can get 2 different mpz_struct_t instances, one
1223 // via m_int.g_dy(), the other via the view class, which internally point to the same
1224 // limbs vector. Having 2 *distinct* mpz_t point to the same limbs vector is not
1225 // something which is supported by the GMP API, at least when one mpz_t is being written into.
1226 // This happens for instance when using arithmetic functions
1227 // in which the return object is the same as one of the operands (GMP supports aliasing
1228 // if multiple mpz_t are the same object, but in this case they are distinct objects.)
1229 //
1230 // In the current implementation, mixing dynamic views with mpz_t objects in the GMP
1231 // API is fine, as dynamic views point to unique mpz_t objects within the integers.
1232 // We still however have potential for aliasing when using the static views: these
1233 // do not point to "real" mpz_t objects, they are mpz_t objects internally pointing
1234 // to the limbs of static integers. So, for instance, in an add() operation with
1235 // rop dynamic, and op1 and op2 statics and the same object, we create two separate
1236 // static views pointing to the same limbs internally, and feed them to the GMP API.
1237 //
1238 // This seems to work however: the data in the static views is strictly read-only and
1239 // it cannot be modified by a modification to a rop, as this needs to be a real mpz_t
1240 // in order to be used in the GMP API (views are convertible to const mpz_t only,
1241 // not mutable mpz_t). That is, static views and rops can only point to distinct
1242 // limbs vectors.
1243 //
1244 // The bottom line is that, in practice, the GMP API does not seem to be bothered by the fact
1245 // that const arguments might overlap behind its back. If this ever becomes a problem,
1246 // we can consider a "true" static view which does not simply point to an existing
1247 // static integer but actually copies it as a data member.
1248 struct mpz_view {
mpz_viewmppp::integer::mpz_view1249 explicit mpz_view(const integer &n)
1250 // NOTE: explicitly initialize mpz_struct_t() in order to avoid reading from
1251 // uninited memory in the move constructor, in case of a dynamic view.
1252 : m_static_view(n.is_static() ? n.m_int.g_st().get_mpz_view() : detail::mpz_struct_t()),
1253 m_ptr(n.is_static() ? &m_static_view : &(n.m_int.g_dy()))
1254 {
1255 }
1256 mpz_view(const mpz_view &) = delete;
mpz_viewmppp::integer::mpz_view1257 mpz_view(mpz_view &&other) noexcept
1258 : m_static_view(other.m_static_view),
1259 // NOTE: we need to re-init ptr here if other is a static view, because in that case
1260 // other.m_ptr will be pointing to an mpz_struct_t in other and we want it to point to
1261 // the struct in this now.
1262 m_ptr((other.m_ptr == &other.m_static_view) ? &m_static_view : other.m_ptr)
1263 {
1264 }
1265 ~mpz_view() = default;
1266 mpz_view &operator=(const mpz_view &) = delete;
1267 mpz_view &operator=(mpz_view &&) = delete;
1268 // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
operator const detail::mpz_struct_t*mppp::integer::mpz_view1269 operator const detail::mpz_struct_t *() const
1270 {
1271 return get();
1272 }
getmppp::integer::mpz_view1273 MPPP_NODISCARD const detail::mpz_struct_t *get() const
1274 {
1275 return m_ptr;
1276 }
1277 detail::mpz_struct_t m_static_view;
1278 const detail::mpz_struct_t *m_ptr;
1279 };
1280 // Make friends with rational.
1281 template <std::size_t>
1282 friend class rational;
1283
1284 public:
1285 // Alias for the template parameter SSize.
1286 static constexpr std::size_t ssize = SSize;
1287 // Default constructor.
1288 integer() = default;
1289 // Copy constructor.
1290 integer(const integer &) = default;
1291 // Move constructor.
1292 // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor)
1293 integer(integer &&other) = default;
1294 // Constructor from an array of limbs.
integer(const::mp_limb_t * p,std::size_t size)1295 explicit integer(const ::mp_limb_t *p, std::size_t size) : m_int(p, size) {}
1296 // Constructor from number of bits.
integer(integer_bitcnt_t nbits)1297 explicit integer(integer_bitcnt_t nbits) : m_int(nbits) {}
1298 // Generic constructor.
1299 #if defined(MPPP_HAVE_CONCEPTS)
1300 template <typename T>
1301 requires integer_cpp_arithmetic<T> &&(!cpp_integral<T>)
1302 #else
1303 template <
1304 typename T,
1305 detail::enable_if_t<
1306 detail::conjunction<is_integer_cpp_arithmetic<T>, detail::negation<is_cpp_integral<T>>>::value, int> = 0>
1307 #endif
integer(const T & x)1308 explicit integer(const T &x)
1309 : m_int(x)
1310 {
1311 }
1312 #if defined(MPPP_HAVE_CONCEPTS)
1313 template <typename T>
1314 requires integer_cpp_arithmetic<T> && cpp_integral<T>
1315 #else
1316 template <typename T, detail::enable_if_t<
1317 detail::conjunction<is_integer_cpp_arithmetic<T>, is_cpp_integral<T>>::value, int> = 0>
1318 #endif
integer(const T & x)1319 integer(const T &x) : m_int(x)
1320 {
1321 }
1322 // Generic constructor from a C++ complex type.
1323 #if defined(MPPP_HAVE_CONCEPTS)
1324 template <integer_cpp_complex T>
1325 #else
1326 template <typename T, detail::enable_if_t<is_integer_cpp_complex<T>::value, int> = 0>
1327 #endif
integer(const T & c)1328 explicit integer(const T &c)
1329 : integer(c.imag() == 0
1330 ? c.real()
1331 : throw std::domain_error(
1332 "Cannot construct an integer from a complex C++ value with a non-zero imaginary part of "
1333 + detail::to_string(c.imag())))
1334 {
1335 }
1336
1337 private:
1338 // A tag to call private ctors.
1339 struct ptag {
1340 };
integer(const ptag &,const char * s,int base)1341 explicit integer(const ptag &, const char *s, int base) : m_int(s, base) {}
integer(const ptag &,const std::string & s,int base)1342 explicit integer(const ptag &, const std::string &s, int base) : integer(s.c_str(), base) {}
1343 #if defined(MPPP_HAVE_STRING_VIEW)
integer(const ptag &,const std::string_view & s,int base)1344 explicit integer(const ptag &, const std::string_view &s, int base) : integer(s.data(), s.data() + s.size(), base)
1345 {
1346 }
1347 #endif
1348
1349 public:
1350 // Constructor from string.
1351 #if defined(MPPP_HAVE_CONCEPTS)
1352 template <string_type T>
1353 #else
1354 template <typename T, detail::enable_if_t<is_string_type<T>::value, int> = 0>
1355 #endif
integer(const T & s,int base=10)1356 explicit integer(const T &s, int base = 10) : integer(ptag{}, s, base)
1357 {
1358 }
1359 // Constructor from range of characters.
integer(const char * begin,const char * end,int base=10)1360 explicit integer(const char *begin, const char *end, int base = 10) : m_int(begin, end, base) {}
1361 // Copy constructor from mpz_t.
integer(const::mpz_t n)1362 explicit integer(const ::mpz_t n) : m_int(n) {}
1363 #if !defined(_MSC_VER)
1364 // Move constructor from mpz_t.
1365 // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
integer(::mpz_t && n)1366 explicit integer(::mpz_t &&n) : m_int(std::move(n)) {}
1367 #endif
1368 ~integer() = default;
1369 // Copy assignment operator.
1370 integer &operator=(const integer &) = default;
1371 // Move assignment operator.
1372 // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor)
1373 integer &operator=(integer &&) = default;
1374
1375 private:
1376 // Implementation of the assignment from unsigned C++ integral.
1377 template <typename T, bool Neg = false,
1378 detail::enable_if_t<detail::conjunction<detail::is_integral<T>, detail::is_unsigned<T>>::value, int> = 0>
dispatch_assignment(T n)1379 void dispatch_assignment(T n)
1380 {
1381 const auto s = is_static();
1382 if (n <= GMP_NUMB_MAX) {
1383 // Optimise the case in which n fits in a single limb.
1384 const auto size = static_cast<detail::mpz_size_t>(n != 0);
1385 if (s) {
1386 // Just write the limb into static storage.
1387 m_int.g_st()._mp_size = Neg ? -size : size;
1388 m_int.g_st().m_limbs[0] = static_cast<::mp_limb_t>(n);
1389 // Zero fill the remaining limbs.
1390 m_int.g_st().zero_upper_limbs(1);
1391 } else {
1392 // Destroy the dynamic structure, re-init an appropriate static.
1393 m_int.destroy_dynamic();
1394 // The constructor will take care of zeroing the upper limbs.
1395 ::new (static_cast<void *>(&m_int.m_st)) s_storage(Neg ? -size : size, static_cast<::mp_limb_t>(n));
1396 }
1397 return;
1398 }
1399 // Convert n into an array of limbs.
1400 detail::limb_array_t<T> tmp;
1401 const auto size = detail::uint_to_limb_array(tmp, n);
1402 if (s && size <= SSize) {
1403 // this is static, and n also fits in static. Overwrite the existing value.
1404 // NOTE: we know size is small, casting is fine.
1405 m_int.g_st()._mp_size = static_cast<detail::mpz_size_t>(size);
1406 detail::copy_limbs_no(tmp.data(), tmp.data() + size, m_int.g_st().m_limbs.data());
1407 // Zero fill the remaining limbs.
1408 m_int.g_st().zero_upper_limbs(size);
1409 } else if (!s && size > SSize) {
1410 // this is dynamic and n requires dynamic storage.
1411 // Convert the size to detail::mpz_size_t, do it before anything else for exception safety.
1412 const auto new_mpz_size = detail::safe_cast<detail::mpz_size_t>(size);
1413 if (m_int.g_dy()._mp_alloc < new_mpz_size) {
1414 // There's not enough space for the new integer. We'll clear the existing
1415 // mpz_t (but not destory it), and re-init with the necessary number of limbs.
1416 detail::mpz_clear_wrap(m_int.g_dy());
1417 // NOTE: do not use g_dy() here, as in principle mpz_clear() could touch
1418 // the _mp_alloc member in unpredictable ways, and then g_dy() would assert
1419 // out in debug builds.
1420 detail::mpz_init_nlimbs(m_int.m_dy, size);
1421 }
1422 // Assign the new size.
1423 m_int.g_dy()._mp_size = new_mpz_size;
1424 // Copy over.
1425 detail::copy_limbs_no(tmp.data(), tmp.data() + size, m_int.g_dy()._mp_d);
1426 } else if (s && size > SSize) {
1427 // this is static and n requires dynamic storage.
1428 const auto new_mpz_size = detail::safe_cast<detail::mpz_size_t>(size);
1429 // Destroy static.
1430 m_int.g_st().~s_storage();
1431 // Init the dynamic struct.
1432 ::new (static_cast<void *>(&m_int.m_dy)) d_storage;
1433 // Init to zero, with the necessary amount of allocated limbs.
1434 // NOTE: need to use m_dy instead of g_dy() here as usual: the alloc
1435 // tag has not been set yet.
1436 detail::mpz_init_nlimbs(m_int.m_dy, size);
1437 // Assign the new size.
1438 m_int.g_dy()._mp_size = new_mpz_size;
1439 // Copy over.
1440 detail::copy_limbs_no(tmp.data(), tmp.data() + size, m_int.g_dy()._mp_d);
1441 } else {
1442 // This is dynamic and n fits into static.
1443 assert(!s && size <= SSize);
1444 // Destroy the dynamic storage.
1445 m_int.destroy_dynamic();
1446 // Init a static with the content from tmp. The constructor
1447 // will zero the upper limbs.
1448 ::new (static_cast<void *>(&m_int.m_st)) s_storage{static_cast<detail::mpz_size_t>(size), tmp.data(), size};
1449 }
1450 // Negate if requested.
1451 if (Neg) {
1452 neg();
1453 }
1454 }
1455 // Assignment from signed integral: take its abs() and negate if necessary, as usual.
1456 template <typename T,
1457 detail::enable_if_t<detail::conjunction<detail::is_integral<T>, detail::is_signed<T>>::value, int> = 0>
dispatch_assignment(T n)1458 void dispatch_assignment(T n)
1459 {
1460 if (n >= T(0)) {
1461 // Positive value, just cast to unsigned.
1462 dispatch_assignment(detail::make_unsigned(n));
1463 } else {
1464 // Negative value, use its abs.
1465 dispatch_assignment<detail::make_unsigned_t<T>, true>(detail::nint_abs(n));
1466 }
1467 }
1468 // Special casing for bool.
dispatch_assignment(bool n)1469 void dispatch_assignment(bool n)
1470 {
1471 if (is_static()) {
1472 m_int.g_st()._mp_size = static_cast<detail::mpz_size_t>(n);
1473 m_int.g_st().m_limbs[0] = static_cast<::mp_limb_t>(n);
1474 // Zero out the upper limbs.
1475 m_int.g_st().zero_upper_limbs(1);
1476 } else {
1477 m_int.destroy_dynamic();
1478 // Construct from size and single limb. This will zero the upper limbs.
1479 ::new (static_cast<void *>(&m_int.m_st))
1480 s_storage{static_cast<detail::mpz_size_t>(n), static_cast<::mp_limb_t>(n)};
1481 }
1482 }
1483 // Assignment from float/double. Uses the mpz_set_d() function.
1484 template <typename T,
1485 detail::enable_if_t<detail::disjunction<std::is_same<T, float>, std::is_same<T, double>>::value, int> = 0>
dispatch_assignment(T x)1486 void dispatch_assignment(T x)
1487 {
1488 if (mppp_unlikely(!std::isfinite(x))) {
1489 throw std::domain_error("Cannot assign the non-finite floating-point value " + detail::to_string(x)
1490 + " to an integer");
1491 }
1492 MPPP_MAYBE_TLS detail::mpz_raii tmp;
1493 mpz_set_d(&tmp.m_mpz, static_cast<double>(x));
1494 *this = &tmp.m_mpz;
1495 }
1496 #if defined(MPPP_WITH_MPFR)
1497 // Assignment from long double, requires MPFR.
dispatch_assignment(long double x)1498 void dispatch_assignment(long double x)
1499 {
1500 if (mppp_unlikely(!std::isfinite(x))) {
1501 throw std::domain_error("Cannot assign the non-finite floating-point value " + detail::to_string(x)
1502 + " to an integer");
1503 }
1504 // NOTE: static checks for overflows and for the precision value are done in mpfr.hpp.
1505 constexpr int d2 = std::numeric_limits<long double>::max_digits10 * 4;
1506 MPPP_MAYBE_TLS detail::mpfr_raii mpfr(static_cast<::mpfr_prec_t>(d2));
1507 MPPP_MAYBE_TLS detail::mpz_raii tmp;
1508 ::mpfr_set_ld(&mpfr.m_mpfr, x, MPFR_RNDN);
1509 ::mpfr_get_z(&tmp.m_mpz, &mpfr.m_mpfr, MPFR_RNDZ);
1510 *this = &tmp.m_mpz;
1511 }
1512 #endif
1513
1514 public:
1515 // Generic assignment operator from a fundamental C++ type.
1516 #if defined(MPPP_HAVE_CONCEPTS)
1517 template <integer_cpp_arithmetic T>
1518 #else
1519 template <typename T, detail::enable_if_t<is_integer_cpp_arithmetic<T>::value, int> = 0>
1520 #endif
operator =(const T & x)1521 integer &operator=(const T &x)
1522 {
1523 dispatch_assignment(x);
1524 return *this;
1525 }
1526 // Generic assignment operator from a complex C++ type.
1527 #if defined(MPPP_HAVE_CONCEPTS)
1528 template <integer_cpp_complex T>
1529 #else
1530 template <typename T, detail::enable_if_t<is_integer_cpp_complex<T>::value, int> = 0>
1531 #endif
operator =(const T & c)1532 integer &operator=(const T &c)
1533 {
1534 if (mppp_unlikely(c.imag() != 0)) {
1535 throw std::domain_error("Cannot assign a complex C++ value with a non-zero imaginary part of "
1536 + detail::to_string(c.imag()) + " to an integer");
1537 }
1538 // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
1539 return *this = c.real();
1540 }
1541
1542 // Declaration of the assignments from
1543 // other mp++ classes.
1544 integer &operator=(const rational<SSize> &);
1545 #if defined(MPPP_WITH_QUADMATH)
1546 integer &operator=(const real128 &);
1547 integer &operator=(const complex128 &);
1548 #endif
1549 #if defined(MPPP_WITH_MPFR)
1550 integer &operator=(const real &);
1551 #endif
1552 #if defined(MPPP_WITH_MPC)
1553 integer &operator=(const complex &);
1554 #endif
1555
1556 // Assignment from string.
1557 #if defined(MPPP_HAVE_CONCEPTS)
1558 template <string_type T>
1559 #else
1560 template <typename T, detail::enable_if_t<is_string_type<T>::value, int> = 0>
1561 #endif
operator =(const T & s)1562 integer &operator=(const T &s)
1563 {
1564 // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
1565 return *this = integer{s};
1566 }
1567 // Copy assignment from mpz_t.
operator =(const::mpz_t n)1568 integer &operator=(const ::mpz_t n)
1569 {
1570 const auto asize = detail::get_mpz_size(n);
1571 const auto s = is_static();
1572 if (s && asize <= SSize) {
1573 // this is static, n fits into static. Copy over.
1574 m_int.g_st()._mp_size = n->_mp_size;
1575 detail::copy_limbs_no(n->_mp_d, n->_mp_d + asize, m_int.g_st().m_limbs.data());
1576 // Zero the non-copied limbs, if necessary.
1577 m_int.g_st().zero_upper_limbs(asize);
1578 } else if (!s && asize > SSize) {
1579 // Dynamic to dynamic.
1580 mpz_set(&m_int.m_dy, n);
1581 } else if (s && asize > SSize) {
1582 // this is static, n is too big. Promote and assign.
1583 // Destroy static.
1584 m_int.g_st().~s_storage();
1585 // Init dynamic.
1586 ::new (static_cast<void *>(&m_int.m_dy)) d_storage;
1587 detail::mpz_init_set_nlimbs(m_int.m_dy, *n);
1588 } else {
1589 // This is dynamic and n fits into static.
1590 assert(!s && asize <= SSize);
1591 // Destroy the dynamic storage.
1592 m_int.destroy_dynamic();
1593 // Init a static with the content from n. This will zero the upper limbs.
1594 ::new (static_cast<void *>(&m_int.m_st)) s_storage{n->_mp_size, n->_mp_d, asize};
1595 }
1596 return *this;
1597 }
1598 #if !defined(_MSC_VER)
1599 // Move assignment from mpz_t.
operator =(::mpz_t && n)1600 integer &operator=(::mpz_t &&n)
1601 {
1602 const auto asize = detail::get_mpz_size(n);
1603 const auto s = is_static();
1604 if (s && asize <= SSize) {
1605 // this is static, n fits into static. Copy over.
1606 m_int.g_st()._mp_size = n->_mp_size;
1607 detail::copy_limbs_no(n->_mp_d, n->_mp_d + asize, m_int.g_st().m_limbs.data());
1608 // Zero the non-copied limbs, if necessary.
1609 m_int.g_st().zero_upper_limbs(asize);
1610 // Clear out n.
1611 detail::mpz_clear_wrap(*n);
1612 } else if (!s && asize > SSize) {
1613 // Dynamic to dynamic: clear this, shallow copy n.
1614 detail::mpz_clear_wrap(m_int.m_dy);
1615 m_int.m_dy = *n;
1616 } else if (s && asize > SSize) {
1617 // this is static, n is too big. Promote and assign.
1618 // Destroy static.
1619 m_int.g_st().~s_storage();
1620 // Init dynamic with a shallow copy.
1621 ::new (static_cast<void *>(&m_int.m_dy)) d_storage(*n);
1622 } else {
1623 // This is dynamic and n fits into static.
1624 assert(!s && asize <= SSize);
1625 // Destroy the dynamic storage.
1626 m_int.destroy_dynamic();
1627 // Init a static with the content from n. This will zero the upper limbs.
1628 ::new (static_cast<void *>(&m_int.m_st)) s_storage{n->_mp_size, n->_mp_d, asize};
1629 // Clear out n.
1630 detail::mpz_clear_wrap(*n);
1631 }
1632 return *this;
1633 }
1634 #endif
1635 // Set to zero.
set_zero()1636 integer &set_zero()
1637 {
1638 if (is_static()) {
1639 m_int.g_st()._mp_size = 0;
1640 // Zero out the whole limbs array, if needed.
1641 m_int.g_st().zero_upper_limbs(0);
1642 } else {
1643 m_int.destroy_dynamic();
1644 // Def construction of static results in zero.
1645 ::new (static_cast<void *>(&m_int.m_st)) s_storage();
1646 }
1647 return *this;
1648 }
1649
1650 private:
1651 template <bool PlusOrMinus>
set_one_impl()1652 integer &set_one_impl()
1653 {
1654 if (is_static()) {
1655 m_int.g_st()._mp_size = PlusOrMinus ? 1 : -1;
1656 m_int.g_st().m_limbs[0] = 1;
1657 // Zero the unused limbs, if needed.
1658 m_int.g_st().zero_upper_limbs(1);
1659 } else {
1660 m_int.destroy_dynamic();
1661 // Construct from a single limb the static. This will zero any unused limb.
1662 ::new (static_cast<void *>(&m_int.m_st)) s_storage{PlusOrMinus ? 1 : -1, 1u};
1663 }
1664 return *this;
1665 }
1666
1667 public:
1668 // Set to one.
set_one()1669 integer &set_one()
1670 {
1671 return set_one_impl<true>();
1672 }
1673 // Set to minus one.
set_negative_one()1674 integer &set_negative_one()
1675 {
1676 return set_one_impl<false>();
1677 }
1678 // Test for static storage.
is_static() const1679 MPPP_NODISCARD bool is_static() const
1680 {
1681 return m_int.is_static();
1682 }
1683 // Test for dynamic storage.
is_dynamic() const1684 MPPP_NODISCARD bool is_dynamic() const
1685 {
1686 return m_int.is_dynamic();
1687 }
1688 // Conversion to string.
to_string(int base=10) const1689 MPPP_NODISCARD std::string to_string(int base = 10) const
1690 {
1691 if (mppp_unlikely(base < 2 || base > 62)) {
1692 throw std::invalid_argument("Invalid base for string conversion: the base must be between "
1693 "2 and 62, but a value of "
1694 + detail::to_string(base) + " was provided instead");
1695 }
1696 return detail::mpz_to_str(get_mpz_view(), base);
1697 }
1698 // NOTE: maybe provide a member function to access the lower-level str conversion that writes to
1699 // std::vector<char>?
1700
1701 private:
1702 // Conversion to bool.
1703 template <typename T, detail::enable_if_t<std::is_same<bool, T>::value, int> = 0>
dispatch_conversion() const1704 MPPP_NODISCARD std::pair<bool, T> dispatch_conversion() const
1705 {
1706 return std::make_pair(true, m_int.m_st._mp_size != 0);
1707 }
1708 // Implementation of the conversion to unsigned types which fit in a limb.
1709 template <typename T, bool Sign, detail::enable_if_t<(detail::nl_constants<T>::digits <= GMP_NUMB_BITS), int> = 0>
convert_to_unsigned() const1710 MPPP_NODISCARD std::pair<bool, T> convert_to_unsigned() const
1711 {
1712 static_assert(detail::is_integral<T>::value && detail::is_unsigned<T>::value, "Invalid type.");
1713 assert((Sign && m_int.m_st._mp_size > 0) || (!Sign && m_int.m_st._mp_size < 0));
1714 if ((Sign && m_int.m_st._mp_size != 1) || (!Sign && m_int.m_st._mp_size != -1)) {
1715 // If the asize is not 1, the conversion will fail.
1716 return std::make_pair(false, T(0));
1717 }
1718 // Get the pointer to the limbs.
1719 const ::mp_limb_t *ptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
1720 if ((ptr[0] & GMP_NUMB_MASK) > detail::nl_max<T>()) {
1721 // The only limb has a value which exceeds the limit of T.
1722 return std::make_pair(false, T(0));
1723 }
1724 // There's a single limb and the result fits.
1725 return std::make_pair(true, static_cast<T>(ptr[0] & GMP_NUMB_MASK));
1726 }
1727 // Implementation of the conversion to unsigned types which do not fit in a limb.
1728 template <typename T, bool Sign, detail::enable_if_t<(detail::nl_constants<T>::digits > GMP_NUMB_BITS), int> = 0>
convert_to_unsigned() const1729 MPPP_NODISCARD std::pair<bool, T> convert_to_unsigned() const
1730 {
1731 static_assert(detail::is_integral<T>::value && detail::is_unsigned<T>::value, "Invalid type.");
1732 assert((Sign && m_int.m_st._mp_size > 0) || (!Sign && m_int.m_st._mp_size < 0));
1733 const auto asize = Sign ? static_cast<std::size_t>(m_int.m_st._mp_size)
1734 : static_cast<std::size_t>(detail::nint_abs(m_int.m_st._mp_size));
1735 // Get the pointer to the limbs.
1736 const ::mp_limb_t *ptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
1737 // Init the retval with the first limb. This is safe as T has more bits than the limb type.
1738 auto retval = static_cast<T>(ptr[0] & GMP_NUMB_MASK);
1739 // Add the other limbs, if any.
1740 constexpr unsigned u_bits = detail::nl_digits<T>();
1741 unsigned shift(GMP_NUMB_BITS);
1742 for (std::size_t i = 1u; i < asize; ++i, shift += unsigned(GMP_NUMB_BITS)) {
1743 if (shift >= u_bits) {
1744 // We need to shift left the current limb. If the shift is too large, we run into UB
1745 // and it also means that the value does not fit in T.
1746 return std::make_pair(false, T(0));
1747 }
1748 // Get the current limb. Safe as T has more bits than the limb type.
1749 const auto l = static_cast<T>(ptr[i] & GMP_NUMB_MASK);
1750 // LCOV_EXCL_START
1751 if (l >> (u_bits - shift)) {
1752 // Left-shifting the current limb is well-defined from the point of view of the language, but the result
1753 // overflows: the value does not fit in T.
1754 // NOTE: I suspect this branch can be triggered on common architectures only with nail builds.
1755 return std::make_pair(false, T(0));
1756 }
1757 // LCOV_EXCL_STOP
1758 // This will not overflow, as there is no carry from retval and l << shift is fine.
1759 retval = static_cast<T>(retval + (l << shift));
1760 }
1761 return std::make_pair(true, retval);
1762 }
1763 // Conversion to unsigned ints, excluding bool.
1764 template <typename T, detail::enable_if_t<detail::conjunction<detail::is_integral<T>, detail::is_unsigned<T>,
1765 detail::negation<std::is_same<bool, T>>>::value,
1766 int> = 0>
dispatch_conversion() const1767 MPPP_NODISCARD std::pair<bool, T> dispatch_conversion() const
1768 {
1769 // Handle zero.
1770 if (!m_int.m_st._mp_size) {
1771 return std::make_pair(true, T(0));
1772 }
1773 // Handle negative value.
1774 if (m_int.m_st._mp_size < 0) {
1775 return std::make_pair(false, T(0));
1776 }
1777 return convert_to_unsigned<T, true>();
1778 }
1779 // Conversion to signed ints.
1780 //
1781 // NOTE: the implementation of conversion to signed at the moment is split into 2 branches:
1782 // a specialised implementation for T not larger than the limb type, and a slower implementation
1783 // for T larger than the limb type. The slower implementation is based on the conversion
1784 // to the unsigned counterpart of T, and it can probably be improved performance-wise.
1785 //
1786 // Helper type trait to detect conversion to small signed integers (i.e., the absolute values of T fit
1787 // into a limb). We need this instead of just typedeffing an std::integral_constant because MSVC
1788 // chokes on constexpr functions in a SFINAE context.
1789 template <typename T>
1790 struct sconv_is_small {
1791 static const bool value
1792 = detail::c_max(detail::make_unsigned(detail::nl_max<T>()), detail::nint_abs(detail::nl_min<T>()))
1793 <= GMP_NUMB_MAX;
1794 };
1795 // Overload if the all the absolute values of T fit into a limb.
1796 template <typename T, detail::enable_if_t<sconv_is_small<T>::value, int> = 0>
convert_to_signed() const1797 MPPP_NODISCARD std::pair<bool, T> convert_to_signed() const
1798 {
1799 static_assert(detail::is_integral<T>::value && detail::is_signed<T>::value, "Invalid type.");
1800 assert(size());
1801 // Cache for convenience.
1802 constexpr auto Tmax = detail::make_unsigned(detail::nl_max<T>());
1803 if (m_int.m_st._mp_size != 1 && m_int.m_st._mp_size != -1) {
1804 // this consists of more than 1 limb, the conversion is not possible.
1805 return std::make_pair(false, T(0));
1806 }
1807 // Get the pointer to the limbs.
1808 const ::mp_limb_t *ptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
1809 // The candidate output value.
1810 const ::mp_limb_t candidate = ptr[0] & GMP_NUMB_MASK;
1811 // Branch out on the sign.
1812 if (m_int.m_st._mp_size > 0) {
1813 // This is positive, it needs to fit within max().
1814 if (candidate <= Tmax) {
1815 return std::make_pair(true, static_cast<T>(candidate));
1816 }
1817 return std::make_pair(false, T(0));
1818 } else {
1819 return detail::unsigned_to_nsigned<T>(candidate);
1820 }
1821 }
1822 // Overload if not all the absolute values of T fit into a limb.
1823 template <typename T, detail::enable_if_t<!sconv_is_small<T>::value, int> = 0>
convert_to_signed() const1824 MPPP_NODISCARD std::pair<bool, T> convert_to_signed() const
1825 {
1826 // Cache for convenience.
1827 constexpr auto Tmax = detail::make_unsigned(detail::nl_max<T>());
1828 // Branch out depending on the sign of this.
1829 if (m_int.m_st._mp_size > 0) {
1830 // Attempt conversion to the unsigned counterpart.
1831 const auto candidate = convert_to_unsigned<detail::make_unsigned_t<T>, true>();
1832 if (candidate.first && candidate.second <= Tmax) {
1833 // The conversion to unsigned was successful, and the result fits in
1834 // the positive range of T. Return the result.
1835 return std::make_pair(true, static_cast<T>(candidate.second));
1836 }
1837 // The conversion to unsigned failed, or the result does not fit.
1838 return std::make_pair(false, T(0));
1839 } else {
1840 // Attempt conversion to the unsigned counterpart.
1841 const auto candidate = convert_to_unsigned<detail::make_unsigned_t<T>, false>();
1842 if (candidate.first) {
1843 // The converstion to unsigned was successful, try to negate now.
1844 return detail::unsigned_to_nsigned<T>(candidate.second);
1845 }
1846 // The conversion to unsigned failed.
1847 return std::make_pair(false, T(0));
1848 }
1849 }
1850 template <typename T,
1851 detail::enable_if_t<detail::conjunction<detail::is_integral<T>, detail::is_signed<T>>::value, int> = 0>
dispatch_conversion() const1852 MPPP_NODISCARD std::pair<bool, T> dispatch_conversion() const
1853 {
1854 // Handle zero.
1855 if (!m_int.m_st._mp_size) {
1856 return std::make_pair(true, T(0));
1857 }
1858 return convert_to_signed<T>();
1859 }
1860 // Implementation of the conversion to floating-point through GMP/MPFR routines.
1861 template <typename T,
1862 detail::enable_if_t<detail::disjunction<std::is_same<T, float>, std::is_same<T, double>>::value, int> = 0>
mpz_float_conversion(const detail::mpz_struct_t & m)1863 static std::pair<bool, T> mpz_float_conversion(const detail::mpz_struct_t &m)
1864 {
1865 return std::make_pair(true, static_cast<T>(mpz_get_d(&m)));
1866 }
1867 #if defined(MPPP_WITH_MPFR)
1868 template <typename T, detail::enable_if_t<std::is_same<T, long double>::value, int> = 0>
mpz_float_conversion(const detail::mpz_struct_t & m)1869 static std::pair<bool, T> mpz_float_conversion(const detail::mpz_struct_t &m)
1870 {
1871 constexpr int d2 = std::numeric_limits<long double>::max_digits10 * 4;
1872 MPPP_MAYBE_TLS detail::mpfr_raii mpfr(static_cast<::mpfr_prec_t>(d2));
1873 ::mpfr_set_z(&mpfr.m_mpfr, &m, MPFR_RNDN);
1874 return std::make_pair(true, ::mpfr_get_ld(&mpfr.m_mpfr, MPFR_RNDN));
1875 }
1876 #endif
1877 // Conversion to floating-point.
1878 template <typename T, detail::enable_if_t<std::is_floating_point<T>::value, int> = 0>
dispatch_conversion() const1879 MPPP_NODISCARD std::pair<bool, T> dispatch_conversion() const
1880 {
1881 // Handle zero.
1882 if (!m_int.m_st._mp_size) {
1883 return std::make_pair(true, T(0));
1884 }
1885 if (std::numeric_limits<T>::is_iec559) {
1886 // Optimization for single-limb integers.
1887 //
1888 // NOTE: the reasoning here is as follows. If the floating-point type has infinity,
1889 // then its "range" is the whole real line. The C++ standard guarantees that:
1890 // """
1891 // A prvalue of an integer type or of an unscoped enumeration type can be converted to a prvalue of a
1892 // floating point type. The result is exact if possible. If the value being converted is in the range
1893 // of values that can be represented but the value cannot be represented exactly,
1894 // it is an implementation-defined choice of either the next lower or higher representable value. If the
1895 // value being converted is outside the range of values that can be represented, the behavior is undefined.
1896 // """
1897 // This seems to indicate that if the limb value "overflows" the finite range of the floating-point type, we
1898 // will get either the max/min finite value or +-inf. Additionally, the IEEE standard seems to indicate
1899 // that an overflowing conversion will produce infinity:
1900 // http://stackoverflow.com/questions/40694384/integer-to-float-conversions-with-ieee-fp
1901 //
1902 // Get the pointer to the limbs.
1903 const ::mp_limb_t *ptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
1904 if (m_int.m_st._mp_size == 1) {
1905 return std::make_pair(true, static_cast<T>(ptr[0] & GMP_NUMB_MASK));
1906 }
1907 if (m_int.m_st._mp_size == -1) {
1908 return std::make_pair(true, -static_cast<T>(ptr[0] & GMP_NUMB_MASK));
1909 }
1910 }
1911 // For all the other cases, just delegate to the GMP/MPFR routines.
1912 return mpz_float_conversion<T>(*static_cast<const detail::mpz_struct_t *>(get_mpz_view()));
1913 }
1914
1915 public:
1916 // NOTE: in C++17 and later it is possible to implement implicit conversion
1917 // towards C++ types higher in the hierarchy (e.g., double). This works in C++17
1918 // because of mandatory copy elision, which allows to skip a step in the conversion
1919 // sequence which, in C++14, makes things ambiguous. See the discussion here:
1920 // https://cpplang.slack.com/archives/C21PKDHSL/p1624893578046400
1921 // And the code snippet here:
1922 // https://godbolt.org/z/snK19f5Wf
1923 // Unfortunately, for some reason clang goes ballistic if we make the conversion
1924 // operator conditionally explicit, taking huge amounts of time/memory
1925 // to compile the tests. We can probably revisit this behaviour in the future,
1926 // perhaps even using C++20's conditionally-explicit syntax rather than
1927 // SFINAE dispatching.
1928
1929 // Generic conversion operator to a C++ fundamental type.
1930 #if defined(MPPP_HAVE_CONCEPTS)
1931 template <integer_cpp_arithmetic T>
1932 #else
1933 template <typename T, detail::enable_if_t<is_integer_cpp_arithmetic<T>::value, int> = 0>
1934 #endif
operator T() const1935 explicit operator T() const
1936 {
1937 auto retval = dispatch_conversion<T>();
1938 if (mppp_unlikely(!retval.first)) {
1939 throw std::overflow_error("The conversion of the integer " + to_string() + " to the type '" + type_name<T>()
1940 + "' results in overflow");
1941 }
1942 return std::move(retval.second);
1943 }
1944 // Generic conversion operator to a C++ complex type.
1945 #if defined(MPPP_HAVE_CONCEPTS)
1946 template <integer_cpp_complex T>
1947 #else
1948 template <typename T, detail::enable_if_t<is_integer_cpp_complex<T>::value, int> = 0>
1949 #endif
operator T() const1950 explicit operator T() const
1951 {
1952 return T(static_cast<typename T::value_type>(*this));
1953 }
1954 // Generic conversion member function to a C++ fundamental type.
1955 #if defined(MPPP_HAVE_CONCEPTS)
1956 template <integer_cpp_arithmetic T>
1957 #else
1958 template <typename T, detail::enable_if_t<is_integer_cpp_arithmetic<T>::value, int> = 0>
1959 #endif
get(T & rop) const1960 bool get(T &rop) const
1961 {
1962 auto retval = dispatch_conversion<T>();
1963 if (retval.first) {
1964 rop = std::move(retval.second);
1965 return true;
1966 }
1967 return false;
1968 }
1969 // Generic conversion member function to a C++ complex type.
1970 #if defined(MPPP_HAVE_CONCEPTS)
1971 template <integer_cpp_complex T>
1972 #else
1973 template <typename T, detail::enable_if_t<is_integer_cpp_complex<T>::value, int> = 0>
1974 #endif
get(T & rop) const1975 bool get(T &rop) const
1976 {
1977 rop = static_cast<T>(*this);
1978 return true;
1979 }
1980 // Promote to dynamic storage.
promote()1981 bool promote()
1982 {
1983 if (is_static()) {
1984 m_int.promote();
1985 return true;
1986 }
1987 return false;
1988 }
1989 // Demote to static storage.
demote()1990 bool demote()
1991 {
1992 if (is_dynamic()) {
1993 return m_int.demote();
1994 }
1995 return false;
1996 }
1997 // Size in bits.
nbits() const1998 MPPP_NODISCARD std::size_t nbits() const
1999 {
2000 const std::size_t ls = size();
2001 if (ls == 0u) {
2002 return 0;
2003 }
2004 const ::mp_limb_t *lptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
2005 // LCOV_EXCL_START
2006 if (mppp_unlikely(ls > detail::nl_max<std::size_t>() / unsigned(GMP_NUMB_BITS))) {
2007 throw std::overflow_error("Overflow in the computation of the number of bits required to represent an "
2008 "integer - the limb size is "
2009 + detail::to_string(ls));
2010 }
2011 // LCOV_EXCL_STOP
2012 // Index of the most significant limb.
2013 const std::size_t idx = ls - 1u;
2014 return static_cast<std::size_t>(idx * unsigned(GMP_NUMB_BITS) + detail::limb_size_nbits(lptr[idx]));
2015 }
2016 // Size in limbs.
size() const2017 MPPP_NODISCARD std::size_t size() const
2018 {
2019 // NOTE: the idea here is that, regardless of what mpz_size_t is exactly, the
2020 // asize of an integer represents ultimately the size of a limb array, and as such
2021 // it has to be representable by std::size_t.
2022 return (m_int.m_st._mp_size) >= 0 ? static_cast<std::size_t>(m_int.m_st._mp_size)
2023 : static_cast<std::size_t>(detail::nint_abs(m_int.m_st._mp_size));
2024 }
2025 // Sign.
sgn() const2026 MPPP_NODISCARD int sgn() const
2027 {
2028 // NOTE: size is part of the common initial sequence.
2029 return detail::integral_sign(m_int.m_st._mp_size);
2030 }
2031 // Get an mpz_t view.
get_mpz_view() const2032 MPPP_NODISCARD mpz_view get_mpz_view() const
2033 {
2034 return mpz_view(*this);
2035 }
2036 // Negate in-place.
neg()2037 integer &neg()
2038 {
2039 m_int.neg();
2040 return *this;
2041 }
2042 // In-place absolute value.
abs()2043 integer &abs()
2044 {
2045 if (is_static()) {
2046 if (m_int.g_st()._mp_size < 0) {
2047 m_int.g_st()._mp_size = -m_int.g_st()._mp_size;
2048 }
2049 } else {
2050 mpz_abs(&m_int.g_dy(), &m_int.g_dy());
2051 }
2052 return *this;
2053 }
2054 // Compute next prime number (in-place version).
nextprime()2055 integer &nextprime()
2056 {
2057 detail::nextprime_impl(*this, *this);
2058 return *this;
2059 }
2060 // Test primality.
probab_prime_p(int reps=25) const2061 MPPP_NODISCARD int probab_prime_p(int reps = 25) const
2062 {
2063 if (mppp_unlikely(reps < 1)) {
2064 throw std::invalid_argument("The number of primality tests must be at least 1, but a value of "
2065 + detail::to_string(reps) + " was provided instead");
2066 }
2067 if (mppp_unlikely(sgn() < 0)) {
2068 throw std::invalid_argument("Cannot run primality tests on the negative number " + to_string());
2069 }
2070 return mpz_probab_prime_p(get_mpz_view(), reps);
2071 }
2072 // Integer square root (in-place version).
sqrt()2073 integer &sqrt()
2074 {
2075 return mppp::sqrt(*this, *this);
2076 }
2077 // Integer squaring (in-place version).
sqr()2078 integer &sqr()
2079 {
2080 return mppp::sqr(*this, *this);
2081 }
2082 // Test if value is odd.
odd_p() const2083 MPPP_NODISCARD bool odd_p() const
2084 {
2085 if (is_static()) {
2086 if (SSize <= s_storage::opt_size) {
2087 // NOTE: when SSize is an optimised size, we can be sure the first limb
2088 // has been zeroed even if the value of the integer is zero.
2089 return (m_int.g_st().m_limbs[0] & GMP_NUMB_MASK) & ::mp_limb_t(1);
2090 } else {
2091 // Otherwise, we add an extra check for zero.
2092 return m_int.g_st()._mp_size && ((m_int.g_st().m_limbs[0] & GMP_NUMB_MASK) & ::mp_limb_t(1));
2093 }
2094 }
2095 return mpz_odd_p(&m_int.g_dy());
2096 }
2097 // Test if value is even.
even_p() const2098 MPPP_NODISCARD bool even_p() const
2099 {
2100 return !odd_p();
2101 }
2102 // Return a reference to the internal union.
_get_union()2103 detail::integer_union<SSize> &_get_union()
2104 {
2105 return m_int;
2106 }
2107 // Return a const reference to the internal union.
_get_union() const2108 MPPP_NODISCARD const detail::integer_union<SSize> &_get_union() const
2109 {
2110 return m_int;
2111 }
2112 // Get a pointer to the dynamic storage.
get_mpz_t()2113 std::remove_extent<::mpz_t>::type *get_mpz_t()
2114 {
2115 promote();
2116 return &m_int.g_dy();
2117 }
2118 // Test if the value is zero.
is_zero() const2119 MPPP_NODISCARD bool is_zero() const
2120 {
2121 return m_int.m_st._mp_size == 0;
2122 }
2123
2124 private:
2125 // Implementation of is_one()/is_negative_one().
2126 template <int One>
is_one_impl() const2127 MPPP_NODISCARD bool is_one_impl() const
2128 {
2129 if (m_int.m_st._mp_size != One) {
2130 return false;
2131 }
2132 // Get the pointer to the limbs.
2133 const ::mp_limb_t *ptr = is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d;
2134 return (ptr[0] & GMP_NUMB_MASK) == 1u;
2135 }
2136
2137 public:
2138 // Test if the value is equal to one.
is_one() const2139 MPPP_NODISCARD bool is_one() const
2140 {
2141 return is_one_impl<1>();
2142 }
2143 // Test if the value is equal to minus one.
is_negative_one() const2144 MPPP_NODISCARD bool is_negative_one() const
2145 {
2146 return is_one_impl<-1>();
2147 }
2148
2149 private:
2150 // NOTE: this needs to be const instead of constexpr due to an MSVC bug.
2151 static const char binary_size_errmsg[];
2152
2153 public:
2154 // Size of the serialised binary representation.
binary_size() const2155 MPPP_NODISCARD std::size_t binary_size() const
2156 {
2157 const auto asize = size();
2158 // LCOV_EXCL_START
2159 // Here we have the very theoretical situation in which we run into possible overflow,
2160 // as the limb array and the size member are distinct objects and thus although individually
2161 // their size must fit in size_t, together they could exceed it.
2162 if (mppp_unlikely(asize > (std::numeric_limits<std::size_t>::max() - sizeof(detail::mpz_size_t))
2163 / sizeof(::mp_limb_t))) {
2164 throw std::overflow_error(binary_size_errmsg);
2165 }
2166 // LCOV_EXCL_STOP
2167 return sizeof(detail::mpz_size_t) + asize * sizeof(::mp_limb_t);
2168 }
2169
2170 private:
binary_save_impl(char * dest,std::size_t bs) const2171 void binary_save_impl(char *dest, std::size_t bs) const
2172 {
2173 assert(bs == binary_size());
2174 // NOLINTNEXTLINE(llvm-qualified-auto, readability-qualified-auto)
2175 auto ptr = reinterpret_cast<const char *>(&m_int.m_st._mp_size);
2176 // NOTE: std::copy() has the usual aliasing restrictions to take into account.
2177 // Here it should not matter, unless one is somehow trying to save an integer
2178 // into itself (I guess?). It's probably not necessary to put these aliasing
2179 // restrictions in the user docs.
2180 std::copy(ptr, ptr + sizeof(detail::mpz_size_t), detail::make_uai(dest));
2181 ptr = reinterpret_cast<const char *>(is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d);
2182 std::copy(ptr, ptr + (bs - sizeof(detail::mpz_size_t)), detail::make_uai(dest + sizeof(detail::mpz_size_t)));
2183 }
2184
2185 public:
2186 // Serialise into a memory buffer.
binary_save(char * dest) const2187 std::size_t binary_save(char *dest) const
2188 {
2189 const auto bs = binary_size();
2190 binary_save_impl(dest, bs);
2191 return bs;
2192 }
2193 // Serialise into a ``std::vector<char>``.
binary_save(std::vector<char> & dest) const2194 std::size_t binary_save(std::vector<char> &dest) const
2195 {
2196 const auto bs = binary_size();
2197 if (dest.size() < bs) {
2198 dest.resize(detail::safe_cast<decltype(dest.size())>(bs));
2199 }
2200 binary_save_impl(dest.data(), bs);
2201 return bs;
2202 }
2203 // Serialise into a ``std::array<char>``.
2204 template <std::size_t S>
binary_save(std::array<char,S> & dest) const2205 std::size_t binary_save(std::array<char, S> &dest) const
2206 {
2207 const auto bs = binary_size();
2208 if (bs > S) {
2209 return 0;
2210 }
2211 binary_save_impl(dest.data(), bs);
2212 return bs;
2213 }
2214 // Serialise into a ``std::ostream``.
binary_save(std::ostream & dest) const2215 std::size_t binary_save(std::ostream &dest) const
2216 {
2217 const auto bs = binary_size();
2218 // NOTE: there does not seem to be a reliable way of detecting how many bytes
2219 // are actually written via write(). See the question here and especially the comments:
2220 // https://stackoverflow.com/questions/14238572/how-many-bytes-actually-written-by-ostreamwrite
2221 // Seems almost like tellp() would work, but if an error occurs in the stream, then
2222 // it returns unconditionally -1, so it is not very useful for our purposes.
2223 // Thus, we will just return 0 on failure, and the full binary size otherwise.
2224 //
2225 // Write the raw data to stream.
2226 dest.write(reinterpret_cast<const char *>(&m_int.m_st._mp_size),
2227 detail::safe_cast<std::streamsize>(sizeof(detail::mpz_size_t)));
2228 if (!dest.good()) {
2229 // !dest.good() means that the last write operation failed. Bail out now.
2230 return 0;
2231 }
2232 dest.write(reinterpret_cast<const char *>(is_static() ? m_int.g_st().m_limbs.data() : m_int.g_dy()._mp_d),
2233 detail::safe_cast<std::streamsize>(bs - sizeof(detail::mpz_size_t)));
2234 return dest.good() ? bs : 0u;
2235 }
2236
2237 private:
2238 // Error message in case of invalid data during binary deserialisation.
2239 static const char bl_data_errmsg[];
2240 // A couple of helpers to check a deserialised integer.
bl_dynamic_check(const detail::make_unsigned_t<detail::mpz_size_t> & asize)2241 void bl_dynamic_check(const detail::make_unsigned_t<detail::mpz_size_t> &asize)
2242 {
2243 // NOTE: here we are sure that asize is nonzero, as we end up in dynamic storage iff asize > SSize,
2244 // and SSize is at least 1.
2245 assert(asize > 0u);
2246 if (mppp_unlikely(!(m_int.g_dy()._mp_d[static_cast<std::size_t>(asize - 1u)] & GMP_NUMB_MASK))) {
2247 // Reset to zero before throwing.
2248 m_int.g_dy()._mp_size = 0;
2249 throw std::invalid_argument(bl_data_errmsg);
2250 }
2251 }
bl_static_check(const detail::make_unsigned_t<detail::mpz_size_t> & asize)2252 void bl_static_check(const detail::make_unsigned_t<detail::mpz_size_t> &asize)
2253 {
2254 // NOTE: here we need to check that asize is nonzero.
2255 if (mppp_unlikely(asize && !(m_int.g_st().m_limbs[static_cast<std::size_t>(asize - 1u)] & GMP_NUMB_MASK))) {
2256 // Reset this to zero before throwing.
2257 m_int.g_st()._mp_size = 0;
2258 m_int.g_st().zero_upper_limbs(0);
2259 throw std::invalid_argument(bl_data_errmsg);
2260 }
2261 }
2262 // Read the size and asize of a serialised integer stored in a buffer
2263 // starting at src.
2264 static std::pair<detail::mpz_size_t, detail::make_unsigned_t<detail::mpz_size_t>>
bl_read_size_asize(const char * src)2265 bl_read_size_asize(const char *src)
2266 {
2267 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2268 detail::mpz_size_t size;
2269 std::copy(src, src + sizeof(detail::mpz_size_t), detail::make_uai(reinterpret_cast<char *>(&size)));
2270 // NOTE: we don't use std::size_t here for the asize as we don't have any assurance
2271 // that a value in the std::size_t range was written into the buffer.
2272 return std::make_pair(size, size >= 0 ? detail::make_unsigned(size) : detail::nint_abs(size));
2273 }
2274 // Low level implementation of binary load. src must point to the start of the serialised
2275 // limb array.
binary_load_impl(const char * src,const detail::mpz_size_t & size,const detail::make_unsigned_t<detail::mpz_size_t> & asize)2276 void binary_load_impl(const char *src, const detail::mpz_size_t &size,
2277 const detail::make_unsigned_t<detail::mpz_size_t> &asize)
2278 {
2279 // Check for overflow in asize.
2280 // LCOV_EXCL_START
2281 if (mppp_unlikely(asize > std::numeric_limits<std::size_t>::max() / sizeof(::mp_limb_t))) {
2282 throw std::overflow_error(binary_size_errmsg);
2283 }
2284 // LCOV_EXCL_STOP
2285 // Detect current storage.
2286 const bool s = is_static();
2287 if (s && asize <= SSize) {
2288 // this is static, the content of src fit into static storage.
2289 // Set the size.
2290 m_int.g_st()._mp_size = size;
2291 // Copy over the data from the source.
2292 std::copy(src, src + static_cast<std::size_t>(sizeof(::mp_limb_t) * asize),
2293 detail::make_uai(reinterpret_cast<char *>(m_int.g_st().m_limbs.data())));
2294 // Clear the upper limbs, if needed.
2295 m_int.g_st().zero_upper_limbs(static_cast<std::size_t>(asize));
2296 // Check the deserialised value.
2297 bl_static_check(asize);
2298 } else if (s && asize > SSize) {
2299 // this is static, the content of src do not fit into static storage.
2300 // Destroy static storage.
2301 m_int.g_st().~s_storage();
2302 // Construct the dynamic struct.
2303 ::new (static_cast<void *>(&m_int.m_dy)) d_storage;
2304 // Init the mpz. This will set the value to zero.
2305 detail::mpz_init_nlimbs(m_int.m_dy, static_cast<std::size_t>(asize));
2306 // Set the size.
2307 m_int.g_dy()._mp_size = size;
2308 // Copy over the data from the source.
2309 std::copy(src, src + static_cast<std::size_t>(sizeof(::mp_limb_t) * asize),
2310 detail::make_uai(reinterpret_cast<char *>(m_int.g_dy()._mp_d)));
2311 // Check the deserialised value.
2312 bl_dynamic_check(asize);
2313 } else if (!s && asize <= SSize) {
2314 // this is dynamic, src contains a static integer.
2315 // Destroy the dynamic this.
2316 m_int.destroy_dynamic();
2317 // Def construct the static integer. This will set everything to zero.
2318 ::new (static_cast<void *>(&m_int.m_st)) s_storage();
2319 // Set the size.
2320 m_int.g_st()._mp_size = size;
2321 // Copy over the data from the source.
2322 std::copy(src, src + static_cast<std::size_t>(sizeof(::mp_limb_t) * asize),
2323 detail::make_uai(reinterpret_cast<char *>(m_int.g_st().m_limbs.data())));
2324 // NOTE: no need to clear the upper limbs: they were already zeroed out
2325 // by the default constructor of static_int.
2326 // Check the deserialised value.
2327 bl_static_check(asize);
2328 } else {
2329 // this is dynamic, src contains a dynamic integer.
2330 // If this does not have enough storage, we need to allocate.
2331 if (detail::get_mpz_size(&m_int.g_dy()) < asize) {
2332 // Clear, but do not destroy, the dynamic storage.
2333 detail::mpz_clear_wrap(m_int.g_dy());
2334 // Re-init to zero with the necessary size.
2335 // NOTE: do not use g_dy() here, as in principle mpz_clear() could touch
2336 // the _mp_alloc member in unpredictable ways, and then g_dy() would assert
2337 // out in debug builds.
2338 detail::mpz_init_nlimbs(m_int.m_dy, static_cast<std::size_t>(asize));
2339 }
2340 // Set the size.
2341 m_int.g_dy()._mp_size = size;
2342 // Copy over the data from the source.
2343 std::copy(src, src + static_cast<std::size_t>(sizeof(::mp_limb_t) * asize),
2344 detail::make_uai(reinterpret_cast<char *>(m_int.g_dy()._mp_d)));
2345 // Check the deserialised value.
2346 bl_dynamic_check(asize);
2347 }
2348 }
2349 // Small helper to determine how many bytes have been read after
2350 // the successful deserialisation of an integer with abs size asize.
2351 // NOTE: since we deserialised the integer from a contiguous buffer, the
2352 // total number of bytes read has to be representable by std::size_t (unlike
2353 // in binary_size(), where we do have to check for overflow).
read_bytes(const detail::make_unsigned_t<detail::mpz_size_t> & asize)2354 static std::size_t read_bytes(const detail::make_unsigned_t<detail::mpz_size_t> &asize)
2355 {
2356 return static_cast<std::size_t>(sizeof(detail::mpz_size_t) + asize * sizeof(::mp_limb_t));
2357 }
2358
2359 public:
2360 // Load a value from a memory buffer.
binary_load(const char * src)2361 std::size_t binary_load(const char *src)
2362 {
2363 // NOTE: disable the use of structured bindings
2364 // on MSVC altogether, due to this clang-cl issue:
2365 // https://bugs.llvm.org/show_bug.cgi?id=41745
2366 #if MPPP_CPLUSPLUS >= 201703L && !defined(_MSC_VER)
2367 const auto [size, asize] = bl_read_size_asize(src);
2368 #else
2369 detail::mpz_size_t size;
2370 detail::make_unsigned_t<detail::mpz_size_t> asize;
2371 std::tie(size, asize) = bl_read_size_asize(src);
2372 #endif
2373 binary_load_impl(src + sizeof(detail::mpz_size_t), size, asize);
2374 return read_bytes(asize);
2375 }
2376
2377 private:
2378 // Deserialisation from vector-like type.
2379 template <typename Vector>
binary_load_vector(const Vector & src,const char * name)2380 std::size_t binary_load_vector(const Vector &src, const char *name)
2381 {
2382 // Verify we can at least read the size out of src.
2383 if (mppp_unlikely(src.size() < sizeof(detail::mpz_size_t))) {
2384 throw std::invalid_argument(std::string("Invalid vector size in the deserialisation of an integer via a ")
2385 + name + ": the " + name + " size must be at least "
2386 + std::to_string(sizeof(detail::mpz_size_t)) + " bytes, but it is only "
2387 + std::to_string(src.size()) + " bytes");
2388 }
2389 // Size in bytes of the limbs portion of the data.
2390 const auto lsize = src.size() - sizeof(detail::mpz_size_t);
2391 #if MPPP_CPLUSPLUS >= 201703L && !defined(_MSC_VER)
2392 // NOTE: not sure why the coverage is not detected here.
2393 // LCOV_EXCL_START
2394 const auto [size, asize] = bl_read_size_asize(src.data());
2395 // LCOV_EXCL_STOP
2396 #else
2397 detail::mpz_size_t size;
2398 detail::make_unsigned_t<detail::mpz_size_t> asize;
2399 std::tie(size, asize) = bl_read_size_asize(src.data());
2400 #endif
2401 // The number of entire limbs stored in the vector must be at least the integer
2402 // limb size stored at the beginning of the vector.
2403 if (mppp_unlikely(lsize / sizeof(::mp_limb_t) < asize)) {
2404 throw std::invalid_argument(
2405 std::string("Invalid vector size in the deserialisation of an integer via a ") + name
2406 + ": the number of limbs stored in the " + name + " (" + std::to_string(lsize / sizeof(::mp_limb_t))
2407 + ") is less than the integer size in limbs stored in the header of the vector ("
2408 + std::to_string(asize) + ")");
2409 }
2410 binary_load_impl(src.data() + sizeof(detail::mpz_size_t), size, asize);
2411 return read_bytes(asize);
2412 }
2413
2414 public:
2415 // Load a value from a vector.
binary_load(const std::vector<char> & src)2416 std::size_t binary_load(const std::vector<char> &src)
2417 {
2418 return binary_load_vector(src, "std::vector");
2419 }
2420 // Load a value from an array.
2421 template <std::size_t S>
binary_load(const std::array<char,S> & src)2422 std::size_t binary_load(const std::array<char, S> &src)
2423 {
2424 return binary_load_vector(src, "std::array");
2425 }
2426 // Load a value from a stream.
binary_load(std::istream & src)2427 std::size_t binary_load(std::istream &src)
2428 {
2429 // Let's start by reading size/asize.
2430 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2431 detail::mpz_size_t size;
2432 src.read(reinterpret_cast<char *>(&size), detail::safe_cast<std::streamsize>(sizeof(detail::mpz_size_t)));
2433 if (!src.good()) {
2434 // Something went wrong with reading, return 0.
2435 return 0;
2436 }
2437 // Determine asize.
2438 const auto asize = size >= 0 ? detail::make_unsigned(size) : detail::nint_abs(size);
2439 // Check for overflow.
2440 // LCOV_EXCL_START
2441 if (mppp_unlikely(asize > std::numeric_limits<std::size_t>::max() / sizeof(::mp_limb_t))) {
2442 throw std::overflow_error("Overflow in the computation of the size in bytes of an integer being "
2443 "deserialised via the stream interface");
2444 }
2445 // LCOV_EXCL_STOP
2446 // Size in bytes of the limbs array.
2447 const auto lsize = static_cast<std::size_t>(sizeof(::mp_limb_t) * asize);
2448 // Now let's read from the stream into a local buffer.
2449 // NOTE: of course here we could avoid the local buffer with a specific implementation
2450 // of binary_load_impl() adapted for reading from a stream. For the first version, let's
2451 // keep things simple - we can always improve the performance at a later stage
2452 // if needed at all.
2453 MPPP_MAYBE_TLS std::vector<char> buffer;
2454 if (lsize > buffer.size()) {
2455 // Enlarge the local buffer if needed.
2456 buffer.resize(detail::safe_cast<decltype(buffer.size())>(lsize));
2457 }
2458 src.read(buffer.data(), detail::safe_cast<std::streamsize>(lsize));
2459 if (!src.good()) {
2460 // Something went wrong with reading, return 0.
2461 return 0;
2462 }
2463 // Everything ok with the deserialisation of the integer in the local buffer.
2464 // Invoke the low-level deser routine.
2465 binary_load_impl(buffer.data(), size, asize);
2466 // NOTE: just recompute the binary size from scratch: the function we use elsewhere,
2467 // read_bytes(), assumes that all data is coming from a contiguous buffer in order to
2468 // avoid an overflow check, but here we don't have this guarantee.
2469 return binary_size();
2470 }
2471
2472 private:
2473 detail::integer_union<SSize> m_int;
2474 };
2475
2476 #if MPPP_CPLUSPLUS < 201703L
2477
2478 // NOTE: from C++17 static constexpr members are implicitly inline, and it's not necessary
2479 // any more (actually, it's deprecated) to re-declare them outside the class.
2480 // https://stackoverflow.com/questions/39646958/constexpr-static-member-before-after-c17
2481
2482 template <std::size_t SSize>
2483 constexpr std::size_t integer<SSize>::ssize;
2484
2485 #endif
2486
2487 template <std::size_t SSize>
2488 const char integer<SSize>::binary_size_errmsg[] = "Overflow in the computation of the binary size of an integer";
2489
2490 template <std::size_t SSize>
2491 const char integer<SSize>::bl_data_errmsg[]
2492 = "Invalid data detected in the binary deserialisation of an integer: the most "
2493 "significant limb of the value cannot be zero";
2494
2495 namespace detail
2496 {
2497
2498 // Swap u1 and u2. u1 must be static, u2 must be dynamic.
2499 template <std::size_t SSize>
integer_swap_static_dynamic(integer_union<SSize> & u1,integer_union<SSize> & u2)2500 inline void integer_swap_static_dynamic(integer_union<SSize> &u1, integer_union<SSize> &u2) noexcept
2501 {
2502 using s_storage = typename integer_union<SSize>::s_storage;
2503 using d_storage = typename integer_union<SSize>::d_storage;
2504
2505 assert(u1.is_static());
2506 assert(!u2.is_static());
2507
2508 // Copy the static in temp storage.
2509 const auto n1_copy(u1.g_st());
2510 // Destroy the static.
2511 u1.g_st().~s_storage();
2512 // Construct the dynamic struct, shallow-copying from n2.
2513 ::new (static_cast<void *>(&u1.m_dy)) d_storage(u2.g_dy());
2514 // Re-create n2 as a static copying from n1_copy.
2515 u2.g_dy().~d_storage();
2516 ::new (static_cast<void *>(&u2.m_st)) s_storage(n1_copy);
2517 }
2518
2519 } // namespace detail
2520
2521 // Swap.
2522 template <std::size_t SSize>
swap(integer<SSize> & n1,integer<SSize> & n2)2523 inline void swap(integer<SSize> &n1, integer<SSize> &n2) noexcept
2524 {
2525 auto &u1 = n1._get_union();
2526 auto &u2 = n2._get_union();
2527
2528 const bool s1 = u1.is_static(), s2 = u2.is_static();
2529 if (s1 && s2) {
2530 // Self swap is fine, handled in the static.
2531 u1.g_st().swap(u2.g_st());
2532 } else if (s1 && !s2) {
2533 detail::integer_swap_static_dynamic(u1, u2);
2534 } else if (!s1 && s2) {
2535 // Mirror of the above.
2536 detail::integer_swap_static_dynamic(u2, u1);
2537 } else {
2538 // Swap with other. Self swap is fine, mpz_swap() can have
2539 // aliasing arguments.
2540 mpz_swap(&u1.g_dy(), &u2.g_dy());
2541 }
2542 }
2543
2544 // Set to zero.
2545 template <std::size_t SSize>
set_zero(integer<SSize> & n)2546 inline integer<SSize> &set_zero(integer<SSize> &n)
2547 {
2548 return n.set_zero();
2549 }
2550
2551 // Set to one.
2552 template <std::size_t SSize>
set_one(integer<SSize> & n)2553 inline integer<SSize> &set_one(integer<SSize> &n)
2554 {
2555 return n.set_one();
2556 }
2557
2558 // Set to minus one.
2559 template <std::size_t SSize>
set_negative_one(integer<SSize> & n)2560 inline integer<SSize> &set_negative_one(integer<SSize> &n)
2561 {
2562 return n.set_negative_one();
2563 }
2564
2565 // Generic conversion function to C++ fundamental types.
2566 #if defined(MPPP_HAVE_CONCEPTS)
2567 template <integer_cpp_arithmetic T, std::size_t SSize>
2568 #else
2569 template <typename T, std::size_t SSize, detail::enable_if_t<is_integer_cpp_arithmetic<T>::value, int> = 0>
2570 #endif
get(T & rop,const integer<SSize> & n)2571 inline bool get(T &rop, const integer<SSize> &n)
2572 {
2573 return n.get(rop);
2574 }
2575
2576 // Generic conversion function to C++ complex types.
2577 #if defined(MPPP_HAVE_CONCEPTS)
2578 template <integer_cpp_complex T, std::size_t SSize>
2579 #else
2580 template <typename T, std::size_t SSize, detail::enable_if_t<is_integer_cpp_complex<T>::value, int> = 0>
2581 #endif
get(T & rop,const integer<SSize> & n)2582 inline bool get(T &rop, const integer<SSize> &n)
2583 {
2584 return n.get(rop);
2585 }
2586
2587 namespace detail
2588 {
2589
2590 // Machinery for the determination of the result of a binary operation involving integer.
2591 // Default is empty for SFINAE.
2592 template <typename, typename, typename = void>
2593 struct integer_common_type {
2594 };
2595
2596 template <std::size_t SSize>
2597 struct integer_common_type<integer<SSize>, integer<SSize>> {
2598 using type = integer<SSize>;
2599 };
2600
2601 template <std::size_t SSize, typename U>
2602 struct integer_common_type<integer<SSize>, U, enable_if_t<is_cpp_integral<U>::value>> {
2603 using type = integer<SSize>;
2604 };
2605
2606 template <std::size_t SSize, typename T>
2607 struct integer_common_type<T, integer<SSize>, enable_if_t<is_cpp_integral<T>::value>> {
2608 using type = integer<SSize>;
2609 };
2610
2611 template <std::size_t SSize, typename U>
2612 struct integer_common_type<
2613 integer<SSize>, U, enable_if_t<disjunction<is_integer_cpp_floating_point<U>, is_integer_cpp_complex<U>>::value>> {
2614 using type = U;
2615 };
2616
2617 template <std::size_t SSize, typename T>
2618 struct integer_common_type<
2619 T, integer<SSize>, enable_if_t<disjunction<is_integer_cpp_floating_point<T>, is_integer_cpp_complex<T>>::value>> {
2620 using type = T;
2621 };
2622
2623 template <typename T, typename U>
2624 using integer_common_t = typename integer_common_type<T, U>::type;
2625
2626 // Various utilities used in both the operators and the functions.
2627 template <typename T, typename U>
2628 struct is_same_ssize_integer : std::false_type {
2629 };
2630
2631 template <std::size_t SSize>
2632 struct is_same_ssize_integer<integer<SSize>, integer<SSize>> : std::true_type {
2633 };
2634
2635 template <typename T>
2636 struct is_integer : std::false_type {
2637 };
2638
2639 template <std::size_t SSize>
2640 struct is_integer<integer<SSize>> : std::true_type {
2641 };
2642
2643 } // namespace detail
2644
2645 template <typename T, typename U>
2646 using are_integer_op_types = detail::is_detected<detail::integer_common_t, T, U>;
2647
2648 #if defined(MPPP_HAVE_CONCEPTS)
2649
2650 template <typename T, typename U>
2651 MPPP_CONCEPT_DECL integer_op_types = are_integer_op_types<T, U>::value;
2652
2653 #endif
2654
2655 template <typename T, typename U>
2656 using are_integer_real_op_types
2657 = detail::conjunction<are_integer_op_types<T, U>, detail::negation<is_integer_cpp_complex<T>>,
2658 detail::negation<is_integer_cpp_complex<U>>>;
2659
2660 #if defined(MPPP_HAVE_CONCEPTS)
2661
2662 template <typename T, typename U>
2663 MPPP_CONCEPT_DECL integer_real_op_types = are_integer_real_op_types<T, U>::value;
2664
2665 #endif
2666
2667 template <typename T, typename U>
2668 using are_integer_integral_op_types
2669 = detail::disjunction<detail::is_same_ssize_integer<T, U>,
2670 detail::conjunction<detail::is_integer<T>, is_cpp_integral<U>>,
2671 detail::conjunction<detail::is_integer<U>, is_cpp_integral<T>>>;
2672
2673 #if defined(MPPP_HAVE_CONCEPTS)
2674
2675 template <typename T, typename U>
2676 MPPP_CONCEPT_DECL integer_integral_op_types = are_integer_integral_op_types<T, U>::value;
2677
2678 #endif
2679
2680 namespace detail
2681 {
2682
2683 // Metaprogramming for selecting the algorithm for static addition. The selection happens via
2684 // an std::integral_constant with 3 possible values:
2685 // - 0 (default case): use the GMP mpn functions,
2686 // - 1: selected when there are no nail bits and the static size is 1,
2687 // - 2: selected when there are no nail bits and the static size is 2.
2688 template <typename SInt>
2689 using integer_static_add_algo = std::integral_constant<
2690 int, (!GMP_NAIL_BITS && SInt::s_size == 1) ? 1 : ((!GMP_NAIL_BITS && SInt::s_size == 2) ? 2 : 0)>;
2691
2692 // General implementation via mpn.
2693 // Small helper to compute the size after subtraction via mpn. s is a strictly positive size.
integer_sub_compute_size(const::mp_limb_t * rdata,mpz_size_t s)2694 inline mpz_size_t integer_sub_compute_size(const ::mp_limb_t *rdata, mpz_size_t s)
2695 {
2696 assert(s > 0);
2697 mpz_size_t cur_idx = s - 1;
2698 for (; cur_idx >= 0; --cur_idx) {
2699 if ((rdata[cur_idx] & GMP_NUMB_MASK) != 0u) {
2700 break;
2701 }
2702 }
2703 return cur_idx + 1;
2704 }
2705
2706 // NOTE: this function (and its other overloads) will return true in case of success, false in case of failure
2707 // (i.e., the addition overflows and the static size is not enough).
2708 template <std::size_t SSize>
static_add_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,0> &)2709 inline bool static_add_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
2710 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
2711 const std::integral_constant<int, 0> &)
2712 {
2713 auto rdata = rop.m_limbs.data();
2714 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
2715 const auto size1 = op1._mp_size;
2716 // NOTE: cannot trust the size member from op2, as op2 could've been negated if
2717 // we are actually subtracting.
2718 const auto size2 = (sign2 >= 0) ? asize2 : -asize2;
2719 // mpn functions require nonzero arguments.
2720 if (mppp_unlikely(!sign2)) {
2721 rop._mp_size = size1;
2722 copy_limbs(data1, data1 + asize1, rdata);
2723 return true;
2724 }
2725 if (mppp_unlikely(!sign1)) {
2726 rop._mp_size = size2;
2727 copy_limbs(data2, data2 + asize2, rdata);
2728 return true;
2729 }
2730 // Check, for op1 and op2, whether:
2731 // - the asize is the max static size, and, if yes,
2732 // - the highest bit in the highest limb is set.
2733 // If this condition holds for op1 and op2, we return failure, as the computation might require
2734 // an extra limb.
2735 // NOTE: the reason we check this is that we do not want to run into the situation in which we have written
2736 // something into rop (via the mpn functions below), only to realize later that the computation overflows.
2737 // This would be bad because, in case of overlapping arguments, it would destroy one or two operands without
2738 // possibility of recovering. The alternative would be to do the computation in some local buffer and then
2739 // copy it out, but that is rather costly. Note that this means that in principle a computation that could fit in
2740 // static storage ends up triggering a promotion.
2741 const bool c1 = std::size_t(asize1) == SSize && ((data1[asize1 - 1] & GMP_NUMB_MASK) >> (GMP_NUMB_BITS - 1));
2742 const bool c2 = std::size_t(asize2) == SSize && ((data2[asize2 - 1] & GMP_NUMB_MASK) >> (GMP_NUMB_BITS - 1));
2743 if (mppp_unlikely(c1 || c2)) {
2744 return false;
2745 }
2746 if (sign1 == sign2) {
2747 // Same sign.
2748 if (asize1 >= asize2) {
2749 // The number of limbs of op1 >= op2.
2750 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2751 ::mp_limb_t cy;
2752 if (asize2 == 1) {
2753 // NOTE: we are not masking data2[0] with GMP_NUMB_MASK, I am assuming the mpn function
2754 // is able to deal with a limb with a nail.
2755 cy = mpn_add_1(rdata, data1, static_cast<::mp_size_t>(asize1), data2[0]);
2756 } else if (asize1 == asize2) {
2757 cy = mpn_add_n(rdata, data1, data2, static_cast<::mp_size_t>(asize1));
2758 } else {
2759 cy = mpn_add(rdata, data1, static_cast<::mp_size_t>(asize1), data2, static_cast<::mp_size_t>(asize2));
2760 }
2761 if (cy) {
2762 assert(asize1 < static_int<SSize>::s_size);
2763 rop._mp_size = size1 + sign1;
2764 // NOTE: there should be no need to use GMP_NUMB_MASK here.
2765 rdata[asize1] = 1u;
2766 } else {
2767 // Without carry, the size is unchanged.
2768 rop._mp_size = size1;
2769 }
2770 } else {
2771 // The number of limbs of op2 > op1.
2772 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2773 ::mp_limb_t cy;
2774 if (asize1 == 1) {
2775 cy = mpn_add_1(rdata, data2, static_cast<::mp_size_t>(asize2), data1[0]);
2776 } else {
2777 cy = mpn_add(rdata, data2, static_cast<::mp_size_t>(asize2), data1, static_cast<::mp_size_t>(asize1));
2778 }
2779 if (cy) {
2780 assert(asize2 < static_int<SSize>::s_size);
2781 rop._mp_size = size2 + sign2;
2782 rdata[asize2] = 1u;
2783 } else {
2784 rop._mp_size = size2;
2785 }
2786 }
2787 } else {
2788 if (asize1 > asize2 || (asize1 == asize2 && mpn_cmp(data1, data2, static_cast<::mp_size_t>(asize1)) >= 0)) {
2789 // abs(op1) >= abs(op2).
2790 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2791 ::mp_limb_t br;
2792 if (asize2 == 1) {
2793 br = mpn_sub_1(rdata, data1, static_cast<::mp_size_t>(asize1), data2[0]);
2794 } else if (asize1 == asize2) {
2795 br = mpn_sub_n(rdata, data1, data2, static_cast<::mp_size_t>(asize1));
2796 } else {
2797 br = mpn_sub(rdata, data1, static_cast<::mp_size_t>(asize1), data2, static_cast<::mp_size_t>(asize2));
2798 }
2799 assert(!br);
2800 rop._mp_size = integer_sub_compute_size(rdata, asize1);
2801 if (sign1 != 1) {
2802 rop._mp_size = -rop._mp_size;
2803 }
2804 } else {
2805 // abs(op2) > abs(op1).
2806 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2807 ::mp_limb_t br;
2808 if (asize1 == 1) {
2809 br = mpn_sub_1(rdata, data2, static_cast<::mp_size_t>(asize2), data1[0]);
2810 } else {
2811 br = mpn_sub(rdata, data2, static_cast<::mp_size_t>(asize2), data1, static_cast<::mp_size_t>(asize1));
2812 }
2813 assert(!br);
2814 rop._mp_size = integer_sub_compute_size(rdata, asize2);
2815 if (sign2 != 1) {
2816 rop._mp_size = -rop._mp_size;
2817 }
2818 }
2819 }
2820 return true;
2821 }
2822
2823 // Optimization for single-limb statics with no nails.
2824 template <std::size_t SSize>
static_add_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,1> &)2825 inline bool static_add_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
2826 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
2827 const std::integral_constant<int, 1> &)
2828 {
2829 ignore(asize1, asize2);
2830 auto rdata = rop.m_limbs.data();
2831 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
2832 // NOTE: both asizes have to be 0 or 1 here.
2833 assert((asize1 == 1 && data1[0] != 0u) || (asize1 == 0 && data1[0] == 0u));
2834 assert((asize2 == 1 && data2[0] != 0u) || (asize2 == 0 && data2[0] == 0u));
2835 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2836 ::mp_limb_t tmp;
2837 if (sign1 == sign2) {
2838 // When the signs are identical, we can implement addition as a true addition.
2839 if (mppp_unlikely(limb_add_overflow(data1[0], data2[0], &tmp))) {
2840 return false;
2841 }
2842 // Assign the output. asize can be zero (sign1 == sign2 == 0) or 1.
2843 rop._mp_size = sign1;
2844 rdata[0] = tmp;
2845 } else {
2846 // When the signs differ, we need to implement addition as a subtraction.
2847 // NOTE: this also includes the case in which only one of the operands is zero.
2848 if (data1[0] >= data2[0]) {
2849 // op1 is not smaller than op2.
2850 tmp = data1[0] - data2[0];
2851 // asize is either 1 or 0 (0 iff abs(op1) == abs(op2)).
2852 rop._mp_size = sign1 * (data1[0] != data2[0]);
2853 rdata[0] = tmp;
2854 } else {
2855 // NOTE: this has to be one, as data2[0] and data1[0] cannot be equal.
2856 rop._mp_size = sign2;
2857 rdata[0] = data2[0] - data1[0];
2858 }
2859 }
2860 return true;
2861 }
2862
2863 // Optimization for two-limbs statics with no nails.
2864 // Small helper to compare two statics of equal asize 1 or 2.
integer_compare_limbs_2(const::mp_limb_t * data1,const::mp_limb_t * data2,mpz_size_t asize)2865 inline int integer_compare_limbs_2(const ::mp_limb_t *data1, const ::mp_limb_t *data2, mpz_size_t asize)
2866 {
2867 // NOTE: this requires no nail bits.
2868 // NOLINTNEXTLINE(cert-dcl03-c, hicpp-static-assert, misc-static-assert)
2869 assert(!GMP_NAIL_BITS);
2870 assert(asize == 1 || asize == 2);
2871 // Start comparing from the top.
2872 auto cmp_idx = asize - 1;
2873 if (data1[cmp_idx] != data2[cmp_idx]) {
2874 return data1[cmp_idx] > data2[cmp_idx] ? 1 : -1;
2875 }
2876 // The top limbs are equal, move down one limb.
2877 // If we are already at the bottom limb, it means the two numbers are equal.
2878 if (cmp_idx == 0) {
2879 return 0;
2880 }
2881 --cmp_idx;
2882 if (data1[cmp_idx] != data2[cmp_idx]) {
2883 return data1[cmp_idx] > data2[cmp_idx] ? 1 : -1;
2884 }
2885 return 0;
2886 }
2887
2888 template <std::size_t SSize>
static_add_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,2> &)2889 inline bool static_add_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
2890 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
2891 const std::integral_constant<int, 2> &)
2892 {
2893 auto rdata = rop.m_limbs.data();
2894 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
2895 if (sign1 == sign2) {
2896 // NOTE: this handles the case in which the numbers have the same sign, including 0 + 0.
2897 //
2898 // NOTE: this is the implementation for 2 limbs, even if potentially the operands have 1 limb.
2899 // The idea here is that it's better to do a few computations more rather than paying the branching
2900 // cost. 1-limb operands will have the upper limb set to zero from the zero-initialisation of
2901 // the limbs of static ints.
2902 //
2903 // NOTE: the rop hi limb might spill over either from the addition of the hi limbs
2904 // of op1 and op2, or by the addition of carry coming over from the addition of
2905 // the lo limbs of op1 and op2.
2906 //
2907 // Add the hi and lo limbs.
2908 const auto a = data1[0], b = data2[0], c = data1[1], d = data2[1];
2909 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2910 ::mp_limb_t lo, hi1, hi2;
2911 const ::mp_limb_t cy_lo = limb_add_overflow(a, b, &lo), cy_hi1 = limb_add_overflow(c, d, &hi1),
2912 cy_hi2 = limb_add_overflow(hi1, cy_lo, &hi2);
2913 // The result will overflow if we have any carry relating to the high limb.
2914 if (mppp_unlikely(cy_hi1 || cy_hi2)) {
2915 return false;
2916 }
2917 // For the size compuation:
2918 // - if sign1 == 0, size is zero,
2919 // - if sign1 == +-1, then the size is either +-1 or +-2: asize is 2 if the result
2920 // has a nonzero 2nd limb, otherwise asize is 1.
2921 rop._mp_size = sign1 * ((hi2 != 0u) + 1);
2922 rdata[0] = lo;
2923 rdata[1] = hi2;
2924 } else {
2925 // When the signs differ, we need to implement addition as a subtraction.
2926 // NOTE: this also includes the case in which only one of the operands is zero.
2927 if (asize1 > asize2 || (asize1 == asize2 && integer_compare_limbs_2(data1, data2, asize1) >= 0)) {
2928 // op1 is >= op2 in absolute value.
2929 const auto lo = data1[0] - data2[0];
2930 // If there's a borrow, then hi1 > hi2, otherwise we would have a negative result.
2931 assert(data1[0] >= data2[0] || data1[1] > data2[1]);
2932 // This can never wrap around, at most it goes to zero.
2933 const auto hi = data1[1] - data2[1] - static_cast<::mp_limb_t>(data1[0] < data2[0]);
2934 // asize can be 0, 1 or 2. The sign1 (which cannot be zero due to the branch we are in)
2935 // takes care of the sign of the size.
2936 rop._mp_size = sign1 * size_from_lohi(lo, hi);
2937 rdata[0] = lo;
2938 rdata[1] = hi;
2939 } else {
2940 // op2 is > op1 in absolute value.
2941 const auto lo = data2[0] - data1[0];
2942 assert(data2[0] >= data1[0] || data2[1] > data1[1]);
2943 const auto hi = data2[1] - data1[1] - static_cast<::mp_limb_t>(data2[0] < data1[0]);
2944 // asize can be 1 or 2, but not zero as we know abs(op1) != abs(op2). Same idea as above.
2945 rop._mp_size = sign2 * (static_cast<int>(hi != 0u) + 1);
2946 rdata[0] = lo;
2947 rdata[1] = hi;
2948 }
2949 }
2950 return true;
2951 }
2952
2953 template <bool AddOrSub, std::size_t SSize>
static_addsub(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)2954 inline bool static_addsub(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
2955 {
2956 const mpz_size_t asize1 = std::abs(op1._mp_size), asize2 = std::abs(op2._mp_size);
2957 const int sign1 = integral_sign(op1._mp_size),
2958 // NOTE: effectively negate op2 if we are subtracting.
2959 sign2 = AddOrSub ? integral_sign(op2._mp_size) : -integral_sign(op2._mp_size);
2960 const bool retval
2961 = static_add_impl(rop, op1, op2, asize1, asize2, sign1, sign2, integer_static_add_algo<static_int<SSize>>{});
2962 if (integer_static_add_algo<static_int<SSize>>::value == 0 && retval) {
2963 // If we used the mpn functions and we actually wrote into rop, then
2964 // make sure we zero out the unused limbs.
2965 // NOTE: this is necessary because, in theory, we may end up using mpn functions also for SSize <= opt_size.
2966 // This is not the case on any tested build so far.
2967 rop.zero_unused_limbs();
2968 }
2969 return retval;
2970 }
2971 } // namespace detail
2972
2973 // Ternary addition.
2974 template <std::size_t SSize>
add(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)2975 inline integer<SSize> &add(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
2976 {
2977 const bool s1 = op1.is_static(), s2 = op2.is_static();
2978 bool sr = rop.is_static();
2979 if (mppp_likely(s1 && s2)) {
2980 // If both op1 and op2 are static, we will try to do the static add.
2981 // We might need to downgrade rop to static.
2982 if (!sr) {
2983 // NOTE: here we are sure rop is distinct from op1/op2, as
2984 // rop is dynamic and op1/op2 are both static.
2985 rop.set_zero();
2986 sr = true;
2987 }
2988 if (mppp_likely(detail::static_addsub<true>(rop._get_union().g_st(), op1._get_union().g_st(),
2989 op2._get_union().g_st()))) {
2990 return rop;
2991 }
2992 }
2993 if (sr) {
2994 rop._get_union().promote(SSize + 1u);
2995 }
2996 mpz_add(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
2997 return rop;
2998 }
2999
3000 namespace detail
3001 {
3002
3003 // Metaprogramming for selecting the algorithm for static add/sub with a single limb. The selection happens via
3004 // an std::integral_constant with 3 possible values:
3005 // - 0 (default case): use the GMP mpn functions,
3006 // - 1: selected when there are no nail bits and the static size is 1,
3007 // - 2: selected when there are no nail bits and the static size is 2.
3008 template <typename SInt>
3009 using integer_static_addsub_1_algo = std::integral_constant<
3010 int, (!GMP_NAIL_BITS && SInt::s_size == 1) ? 1 : ((!GMP_NAIL_BITS && SInt::s_size == 2) ? 2 : 0)>;
3011
3012 // mpn implementation.
3013 template <bool AddOrSub, std::size_t SSize>
static_addsub_1_impl(static_int<SSize> & rop,const static_int<SSize> & op1,mpz_size_t asize1,int sign1,::mp_limb_t l2,const std::integral_constant<int,0> &)3014 inline bool static_addsub_1_impl(static_int<SSize> &rop, const static_int<SSize> &op1, mpz_size_t asize1, int sign1,
3015 ::mp_limb_t l2, const std::integral_constant<int, 0> &)
3016 {
3017 auto rdata = rop.m_limbs.data();
3018 auto data1 = op1.m_limbs.data();
3019 const auto size1 = op1._mp_size;
3020 // mpn functions require nonzero arguments.
3021 if (mppp_unlikely(!l2)) {
3022 rop._mp_size = size1;
3023 copy_limbs(data1, data1 + asize1, rdata);
3024 return true;
3025 }
3026 // We now know l2 is not zero, so its "sign" must be either 1 or -1.
3027 constexpr int sign2 = AddOrSub ? 1 : -1;
3028 if (mppp_unlikely(!sign1)) {
3029 // NOTE: this has to be +-1 because l2 == 0 is handled above.
3030 rop._mp_size = sign2;
3031 rdata[0] = l2;
3032 return true;
3033 }
3034 // This is the same overflow check as in static_addsub(). As in static_addsub(), this is not as tight/precise
3035 // as it could be.
3036 const bool c1 = std::size_t(asize1) == SSize && ((data1[asize1 - 1] & GMP_NUMB_MASK) >> (GMP_NUMB_BITS - 1));
3037 const bool c2 = SSize == 1u && (l2 >> (GMP_NUMB_BITS - 1));
3038 if (mppp_unlikely(c1 || c2)) {
3039 return false;
3040 }
3041 if (sign1 == sign2) {
3042 // op1 and op2 have the same sign. Implement as a true addition.
3043 if (mpn_add_1(rdata, data1, static_cast<::mp_size_t>(asize1), l2)) {
3044 assert(asize1 < static_int<SSize>::s_size);
3045 // New size is old size + 1 if old size was positive, old size - 1 otherwise.
3046 rop._mp_size = size1 + sign2;
3047 // NOTE: there should be no need to use GMP_NUMB_MASK here.
3048 rdata[asize1] = 1u;
3049 } else {
3050 // Without carry, the size is unchanged.
3051 rop._mp_size = size1;
3052 }
3053 } else {
3054 // op1 and op2 have different signs. Implement as a subtraction.
3055 if (asize1 > 1 || (asize1 == 1 && (data1[0] & GMP_NUMB_MASK) >= l2)) {
3056 // abs(op1) >= abs(op2).
3057 const auto br = mpn_sub_1(rdata, data1, static_cast<::mp_size_t>(asize1), l2);
3058 ignore(br);
3059 assert(!br);
3060 // The asize can be the original one or original - 1 (we subtracted a limb). If size1 was positive,
3061 // sign2 has to be negative and we potentially subtract 1, if size1 was negative then sign2 has to be
3062 // positive and we potentially add 1.
3063 rop._mp_size = size1 + sign2 * !(rdata[asize1 - 1] & GMP_NUMB_MASK);
3064 } else {
3065 // abs(op2) > abs(op1).
3066 const auto br = mpn_sub_1(rdata, &l2, 1, data1[0]);
3067 ignore(br);
3068 assert(!br);
3069 // The size must be +-1, as abs(op2) == abs(op1) is handled above.
3070 assert((rdata[0] & GMP_NUMB_MASK));
3071 rop._mp_size = sign2;
3072 }
3073 }
3074 return true;
3075 }
3076
3077 // 1-limb optimisation (no nails).
3078 template <bool AddOrSub, std::size_t SSize>
static_addsub_1_impl(static_int<SSize> & rop,const static_int<SSize> & op1,mpz_size_t,int sign1,::mp_limb_t l2,const std::integral_constant<int,1> &)3079 inline bool static_addsub_1_impl(static_int<SSize> &rop, const static_int<SSize> &op1, mpz_size_t, int sign1,
3080 ::mp_limb_t l2, const std::integral_constant<int, 1> &)
3081 {
3082 const auto l1 = op1.m_limbs[0];
3083 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3084 ::mp_limb_t tmp;
3085 if ((sign1 >= 0 && AddOrSub) || (sign1 <= 0 && !AddOrSub)) {
3086 // op1 non-negative and addition, or op1 non-positive and subtraction. Implement
3087 // as a true addition.
3088 if (mppp_unlikely(limb_add_overflow(l1, l2, &tmp))) {
3089 return false;
3090 }
3091 // The size is 1 for addition, -1 for subtraction, unless
3092 // the result is zero.
3093 rop._mp_size = (AddOrSub ? 1 : -1) * (tmp != 0u);
3094 rop.m_limbs[0] = tmp;
3095 } else {
3096 // op1 negative and addition, or op1 positive and subtraction. Implement
3097 // as a subtraction.
3098 if (l1 >= l2) {
3099 // op1 has larger or equal abs.
3100 tmp = l1 - l2;
3101 // asize is 1 or 0, 0 iff l1 == l2. Sign is negative for add, positive for sub.
3102 rop._mp_size = (AddOrSub ? -1 : 1) * (tmp != 0u);
3103 rop.m_limbs[0] = tmp;
3104 } else {
3105 // op1 has smaller abs. The result will be positive for add, negative
3106 // for sub (cannot be zero as it is handled above).
3107 rop._mp_size = AddOrSub ? 1 : -1;
3108 rop.m_limbs[0] = l2 - l1;
3109 }
3110 }
3111 return true;
3112 }
3113
3114 // 2-limb optimisation (no nails).
3115 template <bool AddOrSub, std::size_t SSize>
static_addsub_1_impl(static_int<SSize> & rop,const static_int<SSize> & op1,mpz_size_t asize1,int sign1,::mp_limb_t l2,const std::integral_constant<int,2> &)3116 inline bool static_addsub_1_impl(static_int<SSize> &rop, const static_int<SSize> &op1, mpz_size_t asize1, int sign1,
3117 ::mp_limb_t l2, const std::integral_constant<int, 2> &)
3118 {
3119 auto rdata = rop.m_limbs.data();
3120 auto data1 = op1.m_limbs.data();
3121 if ((sign1 >= 0 && AddOrSub) || (sign1 <= 0 && !AddOrSub)) {
3122 // op1 non-negative and addition, or op1 non-positive and subtraction. Implement
3123 // as a true addition.
3124 // These two limbs will contain the result.
3125 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3126 ::mp_limb_t lo, hi;
3127 // Add l2 to the low limb, placing the result in lo.
3128 const ::mp_limb_t cy_lo = limb_add_overflow(data1[0], l2, &lo);
3129 // Add the carry from the low addition to the high limb, placing the result in hi.
3130 const ::mp_limb_t cy_hi = limb_add_overflow(data1[1], cy_lo, &hi);
3131 if (mppp_unlikely(cy_hi)) {
3132 return false;
3133 }
3134 // Compute the new asize. It can be 0, 1 or 2.
3135 rop._mp_size = (AddOrSub ? 1 : -1) * size_from_lohi(lo, hi);
3136 // Write out.
3137 rdata[0] = lo;
3138 rdata[1] = hi;
3139 } else {
3140 // op1 negative and addition, or op1 positive and subtraction. Implement
3141 // as a subtraction.
3142 // Compare their absolute values.
3143 if (asize1 == 2 || data1[0] >= l2) {
3144 // op1 is >= op2 in absolute value.
3145 const auto lo = data1[0] - l2;
3146 // Sub from hi the borrow.
3147 const auto hi = data1[1] - static_cast<::mp_limb_t>(data1[0] < l2);
3148 // The final asize can be 2, 1 or 0. Sign is negative for add, positive for sub.
3149 rop._mp_size = (AddOrSub ? -1 : 1) * size_from_lohi(lo, hi);
3150 rdata[0] = lo;
3151 rdata[1] = hi;
3152 } else {
3153 // op2 > op1 in absolute value.
3154 // Size has to be +-1.
3155 rop._mp_size = AddOrSub ? 1 : -1;
3156 rdata[0] = l2 - data1[0];
3157 rdata[1] = 0u;
3158 }
3159 }
3160 return true;
3161 }
3162
3163 template <bool AddOrSub, std::size_t SSize>
static_addsub_1(static_int<SSize> & rop,const static_int<SSize> & op1,::mp_limb_t op2)3164 inline bool static_addsub_1(static_int<SSize> &rop, const static_int<SSize> &op1, ::mp_limb_t op2)
3165 {
3166 const mpz_size_t asize1 = std::abs(op1._mp_size);
3167 const int sign1 = integral_sign(op1._mp_size);
3168 const bool retval = static_addsub_1_impl<AddOrSub>(rop, op1, asize1, sign1, op2,
3169 integer_static_addsub_1_algo<static_int<SSize>>{});
3170 if (integer_static_addsub_1_algo<static_int<SSize>>::value == 0 && retval) {
3171 // If we used the mpn functions and we actually wrote into rop, then
3172 // make sure we zero out the unused limbs.
3173 // NOTE: same as above, we may have used mpn function when SSize <= opt_size.
3174 rop.zero_unused_limbs();
3175 }
3176 return retval;
3177 }
3178
3179 // Implementation of add_ui().
3180 template <std::size_t SSize, typename T>
add_ui_impl(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3181 inline integer<SSize> &add_ui_impl(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3182 {
3183 if (op2 > GMP_NUMB_MAX) {
3184 // For the optimised version below to kick in we need to be sure we can safely convert
3185 // op2 to an ::mp_limb_t, modulo nail bits. Otherwise, we just call add() after converting
3186 // op2 to an integer.
3187 MPPP_MAYBE_TLS integer<SSize> tmp;
3188 tmp = op2;
3189 return add(rop, op1, tmp);
3190 }
3191 const bool s1 = op1.is_static();
3192 bool sr = rop.is_static();
3193 if (mppp_likely(s1)) {
3194 if (!sr) {
3195 rop.set_zero();
3196 sr = true;
3197 }
3198 if (mppp_likely(static_addsub_1<true>(rop._get_union().g_st(), op1._get_union().g_st(),
3199 static_cast<::mp_limb_t>(op2)))) {
3200 return rop;
3201 }
3202 }
3203 if (sr) {
3204 rop._get_union().promote(SSize + 1u);
3205 }
3206 // NOTE: at this point we know that:
3207 // - op2 fits in a limb (accounting for nail bits as well),
3208 // - op2 may overflow unsigned long, which is the unsigned integral data type
3209 // the GMP API expects.
3210 if (op2 <= std::numeric_limits<unsigned long>::max()) {
3211 // op2 actually fits in unsigned long, let's just invoke the mpz_add_ui() function directly.
3212 mpz_add_ui(&rop._get_union().g_dy(), op1.get_mpz_view(), static_cast<unsigned long>(op2));
3213 } else {
3214 // LCOV_EXCL_START
3215 // op2 overflows unsigned long, but still fits in a limb. We will create a fake mpz struct
3216 // with read-only access for use in the mpz_add() function.
3217 // NOTE: this branch is possible at the moment only on Windows 64 bit, where unsigned long
3218 // is 32bit and the mp_limb_t is 64bit. op2 could then be an unsigned long long (64bit) which
3219 // still fits in an ::mp_limb_t.
3220 ::mp_limb_t op2_copy[1] = {static_cast<::mp_limb_t>(op2)};
3221 // NOTE: we have 1 allocated limb, with address &op2_copy. The size has to be 1,
3222 // as op2 is unsigned, fits in an mp_limbt_t and it is not zero (otherwise we would've taken
3223 // the other branch).
3224 const detail::mpz_struct_t tmp_mpz{1, 1, op2_copy};
3225 mpz_add(&rop._get_union().g_dy(), op1.get_mpz_view(), &tmp_mpz);
3226 // LCOV_EXCL_STOP
3227 }
3228 return rop;
3229 }
3230
3231 // NOTE: special-case bool in order to avoid spurious compiler warnings when
3232 // mixing up bool and other integral types.
3233 template <std::size_t SSize>
add_ui_impl(integer<SSize> & rop,const integer<SSize> & op1,bool op2)3234 inline integer<SSize> &add_ui_impl(integer<SSize> &rop, const integer<SSize> &op1, bool op2)
3235 {
3236 return add_ui_impl(rop, op1, static_cast<unsigned>(op2));
3237 }
3238
3239 } // namespace detail
3240
3241 // Ternary addition with C++ unsigned integral types.
3242 #if defined(MPPP_HAVE_CONCEPTS)
3243 template <std::size_t SSize, cpp_unsigned_integral T>
3244 #else
3245 template <std::size_t SSize, typename T, detail::enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
3246 #endif
add_ui(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3247 inline integer<SSize> &add_ui(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3248 {
3249 return detail::add_ui_impl(rop, op1, op2);
3250 }
3251
3252 // Ternary addition with C++ signed integral types.
3253 #if defined(MPPP_HAVE_CONCEPTS)
3254 template <std::size_t SSize, cpp_signed_integral T>
3255 #else
3256 template <std::size_t SSize, typename T, detail::enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
3257 #endif
add_si(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3258 inline integer<SSize> &add_si(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3259 {
3260 if (op2 >= detail::uncvref_t<decltype(op2)>(0)) {
3261 return add_ui(rop, op1, detail::make_unsigned(op2));
3262 }
3263 return sub_ui(rop, op1, detail::nint_abs(op2));
3264 }
3265
3266 // Ternary subtraction.
3267 template <std::size_t SSize>
sub(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)3268 inline integer<SSize> &sub(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
3269 {
3270 const bool s1 = op1.is_static(), s2 = op2.is_static();
3271 bool sr = rop.is_static();
3272 if (mppp_likely(s1 && s2)) {
3273 if (!sr) {
3274 rop.set_zero();
3275 sr = true;
3276 }
3277 if (mppp_likely(detail::static_addsub<false>(rop._get_union().g_st(), op1._get_union().g_st(),
3278 op2._get_union().g_st()))) {
3279 return rop;
3280 }
3281 }
3282 if (sr) {
3283 rop._get_union().promote(SSize + 1u);
3284 }
3285 mpz_sub(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
3286 return rop;
3287 }
3288
3289 namespace detail
3290 {
3291
3292 // Implementation of sub_ui().
3293 template <std::size_t SSize, typename T>
sub_ui_impl(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3294 inline integer<SSize> &sub_ui_impl(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3295 {
3296 if (op2 > GMP_NUMB_MASK) {
3297 MPPP_MAYBE_TLS integer<SSize> tmp;
3298 tmp = op2;
3299 return sub(rop, op1, tmp);
3300 }
3301 const bool s1 = op1.is_static();
3302 bool sr = rop.is_static();
3303 if (mppp_likely(s1)) {
3304 if (!sr) {
3305 rop.set_zero();
3306 sr = true;
3307 }
3308 if (mppp_likely(static_addsub_1<false>(rop._get_union().g_st(), op1._get_union().g_st(),
3309 static_cast<::mp_limb_t>(op2)))) {
3310 return rop;
3311 }
3312 }
3313 if (sr) {
3314 rop._get_union().promote(SSize + 1u);
3315 }
3316 if (op2 <= std::numeric_limits<unsigned long>::max()) {
3317 mpz_sub_ui(&rop._get_union().g_dy(), op1.get_mpz_view(), static_cast<unsigned long>(op2));
3318 } else {
3319 // LCOV_EXCL_START
3320 ::mp_limb_t op2_copy[1] = {static_cast<::mp_limb_t>(op2)};
3321 const detail::mpz_struct_t tmp_mpz{1, 1, op2_copy};
3322 mpz_sub(&rop._get_union().g_dy(), op1.get_mpz_view(), &tmp_mpz);
3323 // LCOV_EXCL_STOP
3324 }
3325 return rop;
3326 }
3327
3328 // NOTE: special-case bool in order to avoid spurious compiler warnings when
3329 // mixing up bool and other integral types.
3330 template <std::size_t SSize>
sub_ui_impl(integer<SSize> & rop,const integer<SSize> & op1,bool op2)3331 inline integer<SSize> &sub_ui_impl(integer<SSize> &rop, const integer<SSize> &op1, bool op2)
3332 {
3333 return sub_ui_impl(rop, op1, static_cast<unsigned>(op2));
3334 }
3335
3336 } // namespace detail
3337
3338 // Ternary subtraction with C++ unsigned integral types.
3339 #if defined(MPPP_HAVE_CONCEPTS)
3340 template <std::size_t SSize, cpp_unsigned_integral T>
3341 #else
3342 template <std::size_t SSize, typename T, detail::enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
3343 #endif
sub_ui(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3344 inline integer<SSize> &sub_ui(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3345 {
3346 return detail::sub_ui_impl(rop, op1, op2);
3347 }
3348
3349 // Ternary subtraction with C++ signed integral types.
3350 #if defined(MPPP_HAVE_CONCEPTS)
3351 template <std::size_t SSize, cpp_signed_integral T>
3352 #else
3353 template <std::size_t SSize, typename T, detail::enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
3354 #endif
sub_si(integer<SSize> & rop,const integer<SSize> & op1,const T & op2)3355 inline integer<SSize> &sub_si(integer<SSize> &rop, const integer<SSize> &op1, const T &op2)
3356 {
3357 if (op2 >= detail::uncvref_t<decltype(op2)>(0)) {
3358 return sub_ui(rop, op1, detail::make_unsigned(op2));
3359 }
3360 return add_ui(rop, op1, detail::nint_abs(op2));
3361 }
3362
3363 namespace detail
3364 {
3365
3366 // The double limb multiplication optimization is available in the following cases:
3367 // - no nails, we are on a 64bit MSVC build, the limb type has exactly 64 bits and GMP_NUMB_BITS is 64,
3368 // - no nails, we have a 128bit unsigned available, the limb type has exactly 64 bits and GMP_NUMB_BITS is 64,
3369 // - no nails, the smallest 64 bit unsigned type has exactly 64 bits, the limb type has exactly 32 bits and
3370 // GMP_NUMB_BITS is 32.
3371 // NOTE: here we are checking that GMP_NUMB_BITS is the same as the limits ::digits property, which is probably
3372 // rather redundant on any conceivable architecture.
3373 using integer_have_dlimb_mul = std::integral_constant<bool,
3374 #if (defined(_MSC_VER) && defined(_WIN64) && (GMP_NUMB_BITS == 64)) \
3375 || (defined(MPPP_HAVE_GCC_INT128) && (GMP_NUMB_BITS == 64))
3376 !GMP_NAIL_BITS && nl_digits<::mp_limb_t>() == 64
3377 #elif GMP_NUMB_BITS == 32
3378 !GMP_NAIL_BITS && nl_digits<std::uint_least64_t>() == 64
3379 && nl_digits<::mp_limb_t>() == 32
3380 #else
3381 false
3382 #endif
3383 >;
3384
3385 template <typename SInt>
3386 using integer_static_mul_algo
3387 = std::integral_constant<int, (SInt::s_size == 1 && integer_have_dlimb_mul::value)
3388 ? 1
3389 : ((SInt::s_size == 2 && integer_have_dlimb_mul::value) ? 2 : 0)>;
3390
3391 // mpn implementation.
3392 // NOTE: this function (and the other overloads) returns 0 in case of success, otherwise it returns a hint
3393 // about the size in limbs of the result.
3394 template <std::size_t SSize>
static_mul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,0> &)3395 inline std::size_t static_mul_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
3396 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
3397 const std::integral_constant<int, 0> &)
3398 {
3399 // Handle zeroes.
3400 if (mppp_unlikely(!sign1 || !sign2)) {
3401 rop._mp_size = 0;
3402 return 0u;
3403 }
3404 auto rdata = rop.m_limbs.data();
3405 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
3406 // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
3407 const auto max_asize = static_cast<std::size_t>(asize1 + asize2);
3408 // Temporary storage, to be used if we cannot write into rop.
3409 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
3410 std::array<::mp_limb_t, SSize * 2u> res;
3411 // We can write directly into rop if these conditions hold:
3412 // - rop does not overlap with op1 and op2,
3413 // - SSize is large enough to hold the max size of the result.
3414 ::mp_limb_t *MPPP_RESTRICT res_data = (rdata != data1 && rdata != data2 && max_asize <= SSize) ? rdata : res.data();
3415 // Proceed to the multiplication.
3416 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3417 ::mp_limb_t hi;
3418 if (asize2 == 1) {
3419 // NOTE: the 1-limb versions do not write the hi limb, we have to write it ourselves.
3420 hi = mpn_mul_1(res_data, data1, static_cast<::mp_size_t>(asize1), data2[0]);
3421 res_data[asize1] = hi;
3422 } else if (asize1 == 1) {
3423 hi = mpn_mul_1(res_data, data2, static_cast<::mp_size_t>(asize2), data1[0]);
3424 res_data[asize2] = hi;
3425 } else if (asize1 == asize2) {
3426 mpn_mul_n(res_data, data1, data2, static_cast<::mp_size_t>(asize1));
3427 hi = res_data[2 * asize1 - 1];
3428 } else if (asize1 >= asize2) {
3429 hi = mpn_mul(res_data, data1, static_cast<::mp_size_t>(asize1), data2, static_cast<::mp_size_t>(asize2));
3430 } else {
3431 hi = mpn_mul(res_data, data2, static_cast<::mp_size_t>(asize2), data1, static_cast<::mp_size_t>(asize1));
3432 }
3433 // The actual size.
3434 const std::size_t asize = max_asize - unsigned(hi == 0u);
3435 if (res_data == rdata) {
3436 // If we wrote directly into rop, it means that we had enough storage in it to begin with.
3437 rop._mp_size = mpz_size_t(asize);
3438 if (sign1 != sign2) {
3439 rop._mp_size = -rop._mp_size;
3440 }
3441 return 0u;
3442 }
3443 // If we used the temporary storage, we need to check if we can write into rop.
3444 if (asize > SSize) {
3445 // Not enough space, return the size.
3446 return asize;
3447 }
3448 // Enough space, set size and copy limbs.
3449 rop._mp_size = mpz_size_t(asize);
3450 if (sign1 != sign2) {
3451 rop._mp_size = -rop._mp_size;
3452 }
3453 // NOTE: here we are sure that res_data != rdata, as we checked it earlier.
3454 copy_limbs_no(res_data, res_data + asize, rdata);
3455 return 0u;
3456 }
3457
3458 #if defined(_MSC_VER) && defined(_WIN64) && (GMP_NUMB_BITS == 64) && !GMP_NAIL_BITS
3459
dlimb_mul(::mp_limb_t op1,::mp_limb_t op2,::mp_limb_t * hi)3460 inline ::mp_limb_t dlimb_mul(::mp_limb_t op1, ::mp_limb_t op2, ::mp_limb_t *hi)
3461 {
3462 return ::UnsignedMultiply128(op1, op2, hi);
3463 }
3464
3465 #elif defined(MPPP_HAVE_GCC_INT128) && (GMP_NUMB_BITS == 64) && !GMP_NAIL_BITS
3466
dlimb_mul(::mp_limb_t op1,::mp_limb_t op2,::mp_limb_t * hi)3467 inline ::mp_limb_t dlimb_mul(::mp_limb_t op1, ::mp_limb_t op2, ::mp_limb_t *hi)
3468 {
3469 using dlimb_t = __uint128_t;
3470 const dlimb_t res = dlimb_t(op1) * op2;
3471 *hi = static_cast<::mp_limb_t>(res >> 64);
3472 return static_cast<::mp_limb_t>(res);
3473 }
3474
3475 #elif GMP_NUMB_BITS == 32 && !GMP_NAIL_BITS
3476
dlimb_mul(::mp_limb_t op1,::mp_limb_t op2,::mp_limb_t * hi)3477 inline ::mp_limb_t dlimb_mul(::mp_limb_t op1, ::mp_limb_t op2, ::mp_limb_t *hi)
3478 {
3479 using dlimb_t = std::uint_least64_t;
3480 const dlimb_t res = dlimb_t(op1) * op2;
3481 *hi = static_cast<::mp_limb_t>(res >> 32);
3482 return static_cast<::mp_limb_t>(res);
3483 }
3484
3485 #endif
3486
3487 // 1-limb optimization via dlimb.
3488 template <std::size_t SSize>
static_mul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t,mpz_size_t,int,int,const std::integral_constant<int,1> &)3489 inline std::size_t static_mul_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
3490 mpz_size_t, mpz_size_t, int, int, const std::integral_constant<int, 1> &)
3491 {
3492 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3493 ::mp_limb_t hi;
3494 const ::mp_limb_t lo = dlimb_mul(op1.m_limbs[0], op2.m_limbs[0], &hi);
3495 if (mppp_unlikely(hi)) {
3496 return 2u;
3497 }
3498 // The size will be zero if at least one operand is zero, otherwise +-1
3499 // depending on the signs of the operands.
3500 rop._mp_size = op1._mp_size * op2._mp_size;
3501 rop.m_limbs[0] = lo;
3502 return 0u;
3503 }
3504
3505 // 2-limb optimization via dlimb.
3506 template <std::size_t SSize>
static_mul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,2> &)3507 inline std::size_t static_mul_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
3508 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
3509 const std::integral_constant<int, 2> &)
3510 {
3511 if (asize1 <= 1 && asize2 <= 1) {
3512 // NOTE: this handles zeroes as well: we know that the limbs are all zeroes in such
3513 // a case (as we always guarantee unused limbs are zeroed), and the sign1 * sign2 multiplication takes care of
3514 // the size.
3515 rop.m_limbs[0] = dlimb_mul(op1.m_limbs[0], op2.m_limbs[0], &rop.m_limbs[1]);
3516 rop._mp_size = sign1 * sign2 * static_cast<mpz_size_t>(2 - (rop.m_limbs[1] == 0u));
3517 return 0u;
3518 }
3519 if (asize1 != asize2) {
3520 // The only possibility of success is 2limbs x 1limb.
3521 //
3522 // b a X
3523 // c
3524 // --------------------
3525 // tmp[2] tmp[1] tmp[0]
3526 //
3527 ::mp_limb_t a = op1.m_limbs[0], b = op1.m_limbs[1], c = op2.m_limbs[0];
3528 if (asize1 < asize2) {
3529 // Switch them around if needed.
3530 a = op2.m_limbs[0], b = op2.m_limbs[1], c = op1.m_limbs[0];
3531 }
3532 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3533 ::mp_limb_t ca_lo, ca_hi, cb_lo, cb_hi, tmp0, tmp1, tmp2;
3534 ca_lo = dlimb_mul(c, a, &ca_hi);
3535 cb_lo = dlimb_mul(c, b, &cb_hi);
3536 tmp0 = ca_lo;
3537 const auto cy = limb_add_overflow(cb_lo, ca_hi, &tmp1);
3538 tmp2 = cb_hi + cy;
3539 // Now determine the size. asize must be at least 2.
3540 const mpz_size_t asize = 2 + mpz_size_t(tmp2 != 0u);
3541 if (asize == 2) {
3542 // Size is good, write out the result.
3543 rop._mp_size = sign1 * sign2 * asize;
3544 rop.m_limbs[0] = tmp0;
3545 rop.m_limbs[1] = tmp1;
3546 return 0u;
3547 }
3548 }
3549 // Return 4 as a size hint. Real size could be 3, but GMP will require 4 limbs
3550 // of storage to perform the operation anyway.
3551 return 4u;
3552 }
3553
3554 template <std::size_t SSize>
static_mul(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)3555 inline std::size_t static_mul(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
3556 {
3557 // Cache a few quantities, detect signs.
3558 const mpz_size_t asize1 = std::abs(op1._mp_size), asize2 = std::abs(op2._mp_size);
3559 const int sign1 = integral_sign(op1._mp_size), sign2 = integral_sign(op2._mp_size);
3560 const std::size_t retval
3561 = static_mul_impl(rop, op1, op2, asize1, asize2, sign1, sign2, integer_static_mul_algo<static_int<SSize>>{});
3562 if (integer_static_mul_algo<static_int<SSize>>::value == 0 && retval == 0u) {
3563 // If we used the mpn functions, and actually wrote into the result,
3564 // zero the unused limbs on top (if necessary).
3565 // NOTE: same as above, we may end up using mpn functions for SSize <= opt_size.
3566 rop.zero_unused_limbs();
3567 }
3568 return retval;
3569 }
3570 } // namespace detail
3571
3572 // Ternary multiplication.
3573 template <std::size_t SSize>
mul(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)3574 inline integer<SSize> &mul(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
3575 {
3576 const bool s1 = op1.is_static(), s2 = op2.is_static();
3577 bool sr = rop.is_static();
3578 std::size_t size_hint = 0u;
3579 if (mppp_likely(s1 && s2)) {
3580 if (!sr) {
3581 rop.set_zero();
3582 sr = true;
3583 }
3584 size_hint = static_mul(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st());
3585 if (mppp_likely(size_hint == 0u)) {
3586 return rop;
3587 }
3588 }
3589 if (sr) {
3590 // We use the size hint from the static_mul if available, otherwise a normal promotion will take place.
3591 // NOTE: here the best way of proceeding would be to calculate the max size of the result based on
3592 // the op1/op2 sizes, but for whatever reason this computation has disastrous performance consequences
3593 // on micro-benchmarks. We need to understand if that's the case in real-world scenarios as well, and
3594 // revisit this.
3595 rop._get_union().promote(size_hint);
3596 }
3597 mpz_mul(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
3598 return rop;
3599 }
3600
3601 namespace detail
3602 {
3603
3604 // Selection of the algorithm for addmul: if optimised algorithms exist for both add and mul, then use the
3605 // optimised addmul algos. Otherwise, use the mpn one.
3606 template <typename SInt>
3607 using integer_static_addmul_algo = std::integral_constant<
3608 int, (integer_static_add_algo<SInt>::value == 2 && integer_static_mul_algo<SInt>::value == 2)
3609 ? 2
3610 : ((integer_static_add_algo<SInt>::value == 1 && integer_static_mul_algo<SInt>::value == 1) ? 1 : 0)>;
3611
3612 // NOTE: same return value as mul: 0 for success, otherwise a hint for the size of the result.
3613 template <std::size_t SSize>
static_addmul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asizer,mpz_size_t asize1,mpz_size_t asize2,int signr,int sign1,int sign2,const std::integral_constant<int,0> &)3614 inline std::size_t static_addmul_impl(static_int<SSize> &rop, const static_int<SSize> &op1,
3615 const static_int<SSize> &op2, mpz_size_t asizer, mpz_size_t asize1,
3616 mpz_size_t asize2, int signr, int sign1, int sign2,
3617 const std::integral_constant<int, 0> &)
3618 {
3619 // First try to do the static prod, if it does not work it's a failure.
3620 static_int<SSize> prod;
3621 if (mppp_unlikely(
3622 static_mul_impl(prod, op1, op2, asize1, asize2, sign1, sign2, std::integral_constant<int, 0>{}))) {
3623 // This is the maximum size a static addmul can have.
3624 return SSize * 2u + 1u;
3625 }
3626 // Determine sign and asize of the product.
3627 mpz_size_t asize_prod = prod._mp_size;
3628 int sign_prod = (asize_prod != 0);
3629 if (asize_prod < 0) {
3630 asize_prod = -asize_prod;
3631 sign_prod = -1;
3632 }
3633 // Try to do the add.
3634 if (mppp_unlikely(
3635 !static_add_impl(rop, rop, prod, asizer, asize_prod, signr, sign_prod, std::integral_constant<int, 0>{}))) {
3636 return SSize + 1u;
3637 }
3638 return 0u;
3639 }
3640
3641 template <std::size_t SSize>
static_addmul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t,mpz_size_t,mpz_size_t,int signr,int sign1,int sign2,const std::integral_constant<int,1> &)3642 inline std::size_t static_addmul_impl(static_int<SSize> &rop, const static_int<SSize> &op1,
3643 const static_int<SSize> &op2, mpz_size_t, mpz_size_t, mpz_size_t, int signr,
3644 int sign1, int sign2, const std::integral_constant<int, 1> &)
3645 {
3646 // First we do op1 * op2.
3647 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3648 ::mp_limb_t tmp;
3649 const ::mp_limb_t prod = dlimb_mul(op1.m_limbs[0], op2.m_limbs[0], &tmp);
3650 if (mppp_unlikely(tmp)) {
3651 return 3u;
3652 }
3653 // Determine the sign of the product: 0, 1 or -1.
3654 const int sign_prod = sign1 * sign2;
3655 // Now add/sub.
3656 if (signr == sign_prod) {
3657 // Same sign, do addition with overflow check.
3658 if (mppp_unlikely(limb_add_overflow(rop.m_limbs[0], prod, &tmp))) {
3659 return 2u;
3660 }
3661 // Assign the output.
3662 rop._mp_size = signr;
3663 rop.m_limbs[0] = tmp;
3664 } else {
3665 // When the signs differ, we need to implement addition as a subtraction.
3666 if (rop.m_limbs[0] >= prod) {
3667 // abs(rop) >= abs(prod).
3668 tmp = rop.m_limbs[0] - prod;
3669 // asize is either 1 or 0 (0 iff rop == prod).
3670 rop._mp_size = signr * static_cast<int>(tmp != 0u);
3671 rop.m_limbs[0] = tmp;
3672 } else {
3673 // NOTE: this cannot be zero, as rop and prod cannot be equal.
3674 rop._mp_size = sign_prod;
3675 rop.m_limbs[0] = prod - rop.m_limbs[0];
3676 }
3677 }
3678 return 0u;
3679 }
3680
3681 template <std::size_t SSize>
static_addmul_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asizer,mpz_size_t asize1,mpz_size_t asize2,int signr,int sign1,int sign2,const std::integral_constant<int,2> &)3682 inline std::size_t static_addmul_impl(static_int<SSize> &rop, const static_int<SSize> &op1,
3683 const static_int<SSize> &op2, mpz_size_t asizer, mpz_size_t asize1,
3684 mpz_size_t asize2, int signr, int sign1, int sign2,
3685 const std::integral_constant<int, 2> &)
3686 {
3687 if (mppp_unlikely(!asize1 || !asize2)) {
3688 // If op1 or op2 are zero, rop will be unchanged.
3689 return 0u;
3690 }
3691 // Handle op1 * op2.
3692 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
3693 std::array<::mp_limb_t, 2> prod;
3694 const int sign_prod = sign1 * sign2;
3695 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3696 mpz_size_t asize_prod;
3697 if (asize1 == 1 && asize2 == 1) {
3698 prod[0] = dlimb_mul(op1.m_limbs[0], op2.m_limbs[0], &prod[1]);
3699 asize_prod = (asize1 + asize2) - (prod[1] == 0u);
3700 } else {
3701 // The only possibility of success is 2limbs x 1limb.
3702 //
3703 // b a X
3704 // c
3705 // ---------------
3706 // prod[1] prod[0]
3707 //
3708 if (mppp_unlikely(asize1 == asize2)) {
3709 // This means that both sizes are 2.
3710 return 5u;
3711 }
3712 ::mp_limb_t a = op1.m_limbs[0], b = op1.m_limbs[1], c = op2.m_limbs[0];
3713 if (asize1 < asize2) {
3714 // Switch them around if needed.
3715 a = op2.m_limbs[0], b = op2.m_limbs[1], c = op1.m_limbs[0];
3716 }
3717 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3718 ::mp_limb_t ca_hi, cb_hi;
3719 // These are the three operations that build the result, two mults, one add.
3720 prod[0] = dlimb_mul(c, a, &ca_hi);
3721 prod[1] = dlimb_mul(c, b, &cb_hi);
3722 // NOTE: we can use this with overlapping arguments because the first two
3723 // are passed as copies.
3724 const auto cy = limb_add_overflow(prod[1], ca_hi, &prod[1]);
3725 // Check if the third limb exists, if so we return failure.
3726 if (mppp_unlikely(cb_hi || cy)) {
3727 return 4u;
3728 }
3729 asize_prod = 2;
3730 }
3731 // Proceed to the addition.
3732 if (signr == sign_prod) {
3733 // Add the hi and lo limbs.
3734 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
3735 ::mp_limb_t lo, hi1, hi2;
3736 const ::mp_limb_t cy_lo = limb_add_overflow(rop.m_limbs[0], prod[0], &lo),
3737 cy_hi1 = limb_add_overflow(rop.m_limbs[1], prod[1], &hi1),
3738 cy_hi2 = limb_add_overflow(hi1, cy_lo, &hi2);
3739 // The result will overflow if we have any carry relating to the high limb.
3740 if (mppp_unlikely(cy_hi1 || cy_hi2)) {
3741 return 3u;
3742 }
3743 // For the size compuation:
3744 // - cannot be zero, as prod is not zero,
3745 // - if signr == +-1, then the size is either +-1 or +-2: asize is 2 if the result
3746 // has a nonzero 2nd limb, otherwise asize is 1.
3747 rop._mp_size = signr * (static_cast<int>(hi2 != 0u) + 1);
3748 rop.m_limbs[0] = lo;
3749 rop.m_limbs[1] = hi2;
3750 } else {
3751 // When the signs differ, we need to implement addition as a subtraction.
3752 if (asizer > asize_prod
3753 || (asizer == asize_prod && integer_compare_limbs_2(&rop.m_limbs[0], &prod[0], asizer) >= 0)) {
3754 // rop >= prod in absolute value.
3755 const auto lo = rop.m_limbs[0] - prod[0];
3756 // If there's a borrow, then hi1 > hi2, otherwise we would have a negative result.
3757 assert(rop.m_limbs[0] >= prod[0] || rop.m_limbs[1] > prod[1]);
3758 // This can never wrap around, at most it goes to zero.
3759 const auto hi = rop.m_limbs[1] - prod[1] - static_cast<::mp_limb_t>(rop.m_limbs[0] < prod[0]);
3760 // asize can be 0, 1 or 2.
3761 rop._mp_size = signr * size_from_lohi(lo, hi);
3762 rop.m_limbs[0] = lo;
3763 rop.m_limbs[1] = hi;
3764 } else {
3765 // prod > rop in absolute value.
3766 const auto lo = prod[0] - rop.m_limbs[0];
3767 assert(prod[0] >= rop.m_limbs[0] || prod[1] > rop.m_limbs[1]);
3768 const auto hi = prod[1] - rop.m_limbs[1] - static_cast<::mp_limb_t>(prod[0] < rop.m_limbs[0]);
3769 // asize can be 1 or 2, but not zero as we know abs(prod) != abs(rop).
3770 rop._mp_size = sign_prod * (static_cast<int>(hi != 0u) + 1);
3771 rop.m_limbs[0] = lo;
3772 rop.m_limbs[1] = hi;
3773 }
3774 }
3775 return 0u;
3776 }
3777
3778 template <bool AddOrSub, std::size_t SSize>
static_addsubmul(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)3779 inline std::size_t static_addsubmul(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
3780 {
3781 // NOTE: according to the benchmarks, it seems a clear win here to use abs/integral_sign instead
3782 // of branching: there's about a ~10% penalty in the unsigned case, but ~20% faster for the signed case.
3783 const mpz_size_t asizer = std::abs(rop._mp_size), asize1 = std::abs(op1._mp_size), asize2 = std::abs(op2._mp_size);
3784 const int signr = integral_sign(rop._mp_size), sign1 = integral_sign(op1._mp_size),
3785 // NOTE: negate op2 in case we are doing a submul.
3786 sign2 = AddOrSub ? integral_sign(op2._mp_size) : -integral_sign(op2._mp_size);
3787 const std::size_t retval = static_addmul_impl(rop, op1, op2, asizer, asize1, asize2, signr, sign1, sign2,
3788 integer_static_addmul_algo<static_int<SSize>>{});
3789 if (integer_static_addmul_algo<static_int<SSize>>::value == 0 && retval == 0u) {
3790 // If we used the mpn functions, and actually wrote into the result,
3791 // zero the unused limbs on top (if necessary).
3792 // NOTE: as usual, potential of mpn use on optimised size.
3793 rop.zero_unused_limbs();
3794 }
3795 return retval;
3796 }
3797 } // namespace detail
3798
3799 // Ternary multiply–add.
3800 template <std::size_t SSize>
addmul(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)3801 inline integer<SSize> &addmul(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
3802 {
3803 const bool sr = rop.is_static(), s1 = op1.is_static(), s2 = op2.is_static();
3804 std::size_t size_hint = 0u;
3805 if (mppp_likely(sr && s1 && s2)) {
3806 size_hint
3807 = detail::static_addsubmul<true>(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st());
3808 if (mppp_likely(size_hint == 0u)) {
3809 return rop;
3810 }
3811 }
3812 if (sr) {
3813 rop._get_union().promote(size_hint);
3814 }
3815 mpz_addmul(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
3816 return rop;
3817 }
3818
3819 // Ternary multiply–sub.
3820 template <std::size_t SSize>
submul(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)3821 inline integer<SSize> &submul(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
3822 {
3823 const bool sr = rop.is_static(), s1 = op1.is_static(), s2 = op2.is_static();
3824 std::size_t size_hint = 0u;
3825 if (mppp_likely(sr && s1 && s2)) {
3826 size_hint = detail::static_addsubmul<false>(rop._get_union().g_st(), op1._get_union().g_st(),
3827 op2._get_union().g_st());
3828 if (mppp_likely(size_hint == 0u)) {
3829 return rop;
3830 }
3831 }
3832 if (sr) {
3833 rop._get_union().promote(size_hint);
3834 }
3835 mpz_submul(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
3836 return rop;
3837 }
3838
3839 namespace detail
3840 {
3841
3842 // mpn implementation.
3843 template <std::size_t SSize>
static_mul_2exp(static_int<SSize> & rop,const static_int<SSize> & n,std::size_t s)3844 inline std::size_t static_mul_2exp(static_int<SSize> &rop, const static_int<SSize> &n, std::size_t s)
3845 {
3846 static_assert(SSize > static_int<SSize>::opt_size, "Cannot use mpn functions for optimised static size.");
3847 mpz_size_t asize = n._mp_size;
3848 if (s == 0u || asize == 0) {
3849 // If shift is zero, or the operand is zero, write n into rop and return success.
3850 rop = n;
3851 return 0u;
3852 }
3853 // Finish setting up asize and sign.
3854 int sign = 1;
3855 if (asize < 0) {
3856 asize = -asize;
3857 sign = -1;
3858 }
3859 // ls: number of entire limbs shifted.
3860 // rs: effective shift that will be passed to the mpn function.
3861 const std::size_t ls = s / unsigned(GMP_NUMB_BITS), rs = s % unsigned(GMP_NUMB_BITS);
3862 // At the very minimum, the new asize will be the old asize plus ls.
3863 // Check if we can represent it first.
3864 // NOTE: use >= because we may need to increase by 1 new_asize at the end, as a size hint.
3865 // LCOV_EXCL_START
3866 if (mppp_unlikely(ls >= nl_max<std::size_t>() - static_cast<std::size_t>(asize))) {
3867 // NOTE: don't think this can be hit on any setup currently.
3868 throw std::overflow_error("A left bitshift value of " + detail::to_string(s) + " is too large");
3869 }
3870 // LCOV_EXCL_STOP
3871 const std::size_t new_asize = static_cast<std::size_t>(asize) + ls;
3872 if (new_asize < SSize) {
3873 // In this case the operation will always succeed, and we can write directly into rop.
3874 ::mp_limb_t ret = 0u;
3875 if (rs) {
3876 // Perform the shift via the mpn function, if we are effectively shifting at least 1 bit.
3877 // Overlapping is fine, as it is guaranteed that rop.m_limbs.data() + ls >= n.m_limbs.data().
3878 ret = mpn_lshift(rop.m_limbs.data() + ls, n.m_limbs.data(), static_cast<::mp_size_t>(asize), unsigned(rs));
3879 // Write bits spilling out.
3880 rop.m_limbs[new_asize] = ret;
3881 } else {
3882 // Otherwise, just copy over (the mpn function requires the shift to be at least 1).
3883 // NOTE: we have to use move_backward here because the ranges may be overlapping but not
3884 // starting at the same pointer (in which case we could've used copy_limbs()). Here we know ls is not
3885 // zero: we don't have a remainder, and s == 0 was already handled above. Hence, new_asize > asize.
3886 assert(new_asize > static_cast<std::size_t>(asize));
3887 std::move_backward(n.m_limbs.begin(), n.m_limbs.begin() + asize, rop.m_limbs.begin() + new_asize);
3888 }
3889 // Zero the lower limbs vacated by the shift. We need to do this as we don't know
3890 // the state of rop.
3891 std::fill(rop.m_limbs.data(), rop.m_limbs.data() + ls, ::mp_limb_t(0));
3892 // Set the size.
3893 rop._mp_size = sign * (static_cast<mpz_size_t>(new_asize) + (ret != 0u));
3894 return 0u;
3895 }
3896 if (new_asize == SSize) {
3897 if (rs) {
3898 // In this case the operation may fail, so we need to write to temporary storage.
3899 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
3900 std::array<::mp_limb_t, SSize> tmp;
3901 if (mpn_lshift(tmp.data(), n.m_limbs.data(), static_cast<::mp_size_t>(asize), unsigned(rs))) {
3902 return SSize + 1u;
3903 }
3904 // The shift was successful without spill over, copy the content from the tmp
3905 // storage into rop.
3906 copy_limbs_no(tmp.data(), tmp.data() + asize, rop.m_limbs.data() + ls);
3907 } else {
3908 // If we shifted by a multiple of the limb size, then we can write directly to rop.
3909 assert(new_asize > static_cast<std::size_t>(asize));
3910 std::move_backward(n.m_limbs.begin(), n.m_limbs.begin() + asize, rop.m_limbs.begin() + new_asize);
3911 }
3912 // Zero the lower limbs vacated by the shift.
3913 std::fill(rop.m_limbs.data(), rop.m_limbs.data() + ls, ::mp_limb_t(0));
3914 // Set the size.
3915 rop._mp_size = sign * static_cast<mpz_size_t>(new_asize);
3916 return 0u;
3917 }
3918 // Shift is too much, the size will overflow the static limit. Return a hint for the size of the result.
3919 // We checked above we can compute this safely.
3920 return new_asize + 1u;
3921 }
3922
3923 // 1-limb optimisation.
static_mul_2exp(static_int<1> & rop,const static_int<1> & n,std::size_t s)3924 inline std::size_t static_mul_2exp(static_int<1> &rop, const static_int<1> &n, std::size_t s)
3925 {
3926 const ::mp_limb_t l = n.m_limbs[0] & GMP_NUMB_MASK;
3927 if (s == 0u || l == 0u) {
3928 // If shift is zero, or the operand is zero, write n into rop and return success.
3929 rop = n;
3930 return 0u;
3931 }
3932 if (mppp_unlikely(s >= unsigned(GMP_NUMB_BITS) || (l >> (unsigned(GMP_NUMB_BITS) - s)))) {
3933 // The two conditions:
3934 // - if the shift is >= number of data bits, the operation will certainly
3935 // fail (as the operand is nonzero);
3936 // - shifting by s would overflow the single limb.
3937 // NOTE: s is at least 1, so in the right shift above we never risk UB due to too much shift.
3938 // NOTE: for the size hint: s / nbits is the number of entire limbs shifted, +1 because the shifted
3939 // limbs add to the current size (1), +1 because another limb might be needed.
3940 // NOTE: paranoia static assert to make sure there's no chance of overflowing.
3941 static_assert(GMP_NUMB_BITS > 1, "Invalid number of bits.");
3942 return s / unsigned(GMP_NUMB_BITS) + 2u;
3943 }
3944 // Write out.
3945 rop.m_limbs[0] = l << s;
3946 // Size is the same as the input value.
3947 rop._mp_size = n._mp_size;
3948 return 0u;
3949 }
3950
3951 // 2-limb optimisation.
static_mul_2exp(static_int<2> & rop,const static_int<2> & n,std::size_t s)3952 inline std::size_t static_mul_2exp(static_int<2> &rop, const static_int<2> &n, std::size_t s)
3953 {
3954 // NOTE: it looks like in this function abs + integral sign are a definite
3955 // win in all cases, possibly because there's enough work to do in the function
3956 // to make any branching advantage (in case of only unsigned operands)
3957 // to be negligible.
3958 const mpz_size_t asize = std::abs(n._mp_size);
3959 if (s == 0u || asize == 0) {
3960 rop = n;
3961 return 0u;
3962 }
3963 const int sign = integral_sign(n._mp_size);
3964 // Too much shift, this can never work on a nonzero value.
3965 static_assert(unsigned(GMP_NUMB_BITS) <= nl_max<unsigned>() / 2u, "Overflow error.");
3966 if (mppp_unlikely(s >= 2u * unsigned(GMP_NUMB_BITS))) {
3967 // NOTE: this is the generic formula to estimate the final size.
3968 // NOTE: paranoia static assert to make sure there's no chance of overflowing.
3969 static_assert(GMP_NUMB_BITS > 1, "Invalid number of bits.");
3970 // NOTE: asize is 2 at most, no need to check for overflow.
3971 return s / unsigned(GMP_NUMB_BITS) + 1u + static_cast<std::size_t>(asize);
3972 }
3973 if (s == unsigned(GMP_NUMB_BITS)) {
3974 // This case can be dealt with moving lo into hi, but only if asize is 1.
3975 if (mppp_unlikely(asize == 2)) {
3976 // asize is 2, shift is too much.
3977 return 3u;
3978 }
3979 rop.m_limbs[1u] = n.m_limbs[0u];
3980 rop.m_limbs[0u] = 0u;
3981 // The asize has to be 2.
3982 rop._mp_size = 2 * sign;
3983 return 0u;
3984 }
3985 // Temp hi lo limbs to store the result that will eventually go into rop.
3986 ::mp_limb_t lo = n.m_limbs[0u], hi = n.m_limbs[1u];
3987 if (s > unsigned(GMP_NUMB_BITS)) {
3988 if (mppp_unlikely(asize == 2)) {
3989 return s / unsigned(GMP_NUMB_BITS) + 1u + static_cast<std::size_t>(asize);
3990 }
3991 // Move lo to hi and set lo to zero.
3992 hi = n.m_limbs[0u];
3993 lo = 0u;
3994 // Update the shift.
3995 s = static_cast<std::size_t>(s - unsigned(GMP_NUMB_BITS));
3996 }
3997 // Check that hi will not be shifted too much. Note that
3998 // here and below s can never be zero, so we never shift too much.
3999 assert(s > 0u && s < unsigned(GMP_NUMB_BITS));
4000 if (mppp_unlikely((hi & GMP_NUMB_MASK) >> (unsigned(GMP_NUMB_BITS) - s))) {
4001 return 3u;
4002 }
4003 // Shift hi and lo. hi gets the carry over from lo.
4004 hi = ((hi & GMP_NUMB_MASK) << s) + ((lo & GMP_NUMB_MASK) >> (unsigned(GMP_NUMB_BITS) - s));
4005 // NOTE: here the result needs to be masked as well as the shift could
4006 // end up writing in nail bits.
4007 lo = ((lo & GMP_NUMB_MASK) << s) & GMP_NUMB_MASK;
4008 // Write rop.
4009 rop.m_limbs[0u] = lo;
4010 rop.m_limbs[1u] = hi;
4011 // asize is at least 1.
4012 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
4013 rop._mp_size = sign * (1 + (hi != 0u));
4014 return 0u;
4015 }
4016 } // namespace detail
4017
4018 // Ternary left shift.
4019 template <std::size_t SSize>
mul_2exp(integer<SSize> & rop,const integer<SSize> & n,::mp_bitcnt_t s)4020 inline integer<SSize> &mul_2exp(integer<SSize> &rop, const integer<SSize> &n, ::mp_bitcnt_t s)
4021 {
4022 const bool sn = n.is_static();
4023 bool sr = rop.is_static();
4024 std::size_t size_hint = 0u;
4025 if (mppp_likely(sn)) {
4026 // NOTE: we cast to size_t because it's more convenient for reasoning about number of limbs
4027 // in the implementation functions.
4028 // NOTE: do it before touching rop, for exception safety.
4029 const auto s_size = detail::safe_cast<std::size_t>(s);
4030 if (!sr) {
4031 rop.set_zero();
4032 sr = true;
4033 }
4034 size_hint = static_mul_2exp(rop._get_union().g_st(), n._get_union().g_st(), s_size);
4035 if (mppp_likely(size_hint == 0u)) {
4036 return rop;
4037 }
4038 }
4039 if (sr) {
4040 rop._get_union().promote(size_hint);
4041 }
4042 mpz_mul_2exp(&rop._get_union().g_dy(), n.get_mpz_view(), s);
4043 return rop;
4044 }
4045
4046 namespace detail
4047 {
4048
4049 // Selection of the algorithm for the
4050 // static squaring. We'll be using the
4051 // double-limb mul primitives if available.
4052 template <typename SInt>
4053 using integer_static_sqr_algo
4054 = std::integral_constant<int, (SInt::s_size == 1 && integer_have_dlimb_mul::value)
4055 ? 1
4056 : ((SInt::s_size == 2 && integer_have_dlimb_mul::value) ? 2 : 0)>;
4057
4058 // mpn implementation.
4059 // NOTE: this function (and the other overloads) returns 0 in case of success, otherwise it returns a hint
4060 // about the size in limbs of the result.
4061 template <std::size_t SSize>
static_sqr_impl(static_int<SSize> & rop,const static_int<SSize> & op,const std::integral_constant<int,0> &)4062 inline std::size_t static_sqr_impl(static_int<SSize> &rop, const static_int<SSize> &op,
4063 const std::integral_constant<int, 0> &)
4064 {
4065 const auto asize = static_cast<std::size_t>(std::abs(op._mp_size));
4066
4067 // Handle zero.
4068 if (mppp_unlikely(asize == 0u)) {
4069 rop._mp_size = 0;
4070 return 0u;
4071 }
4072
4073 // Temporary storage for mpn_sqr(). The largest possible
4074 // size needed is twice the static size.
4075 // NOTE: here we could add some logic, like in static_mul_impl(),
4076 // to check whether we can write directly into rop instead
4077 // of using this temporary storage. Need to profile first,
4078 // however, because I am not sure whether this is worth
4079 // it or not performance-wise.
4080 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4081 std::array<::mp_limb_t, SSize * 2u> res;
4082 const auto res_data = res.data();
4083
4084 // Run the mpn function.
4085 mpn_sqr(res_data, op.m_limbs.data(), static_cast<::mp_size_t>(asize));
4086
4087 // Compute the actual size of the result: it could be asize * 2
4088 // or 1 less, depending on whether the most significant limb of the
4089 // result is zero.
4090 const auto res_size = asize * 2u - static_cast<std::size_t>((res_data[asize * 2u - 1u] & GMP_NUMB_MASK) == 0u);
4091 if (res_size > SSize) {
4092 // Not enough space: return asize * 2, which is
4093 // what mpz_mul() would like to allocate for the result.
4094 return asize * 2u;
4095 }
4096
4097 // Enough space, write out the result.
4098 rop._mp_size = static_cast<mpz_size_t>(res_size);
4099 copy_limbs_no(res_data, res_data + res_size, rop.m_limbs.data());
4100
4101 return 0u;
4102 }
4103
4104 // 1-limb optimization via dlimb.
4105 template <std::size_t SSize>
static_sqr_impl(static_int<SSize> & rop,const static_int<SSize> & op,const std::integral_constant<int,1> &)4106 inline std::size_t static_sqr_impl(static_int<SSize> &rop, const static_int<SSize> &op,
4107 const std::integral_constant<int, 1> &)
4108 {
4109 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
4110 ::mp_limb_t hi;
4111 const ::mp_limb_t lo = dlimb_mul(op.m_limbs[0], op.m_limbs[0], &hi);
4112 if (mppp_unlikely(hi)) {
4113 return 2u;
4114 }
4115
4116 // NOTE: if op is zero, the output size is zero,
4117 // otherwise the output size is 1 (cannot be 2, handled
4118 // above).
4119 rop._mp_size = static_cast<mpz_size_t>(op._mp_size != 0);
4120 rop.m_limbs[0] = lo;
4121
4122 return 0;
4123 }
4124
4125 // 2-limb optimization via dlimb.
4126 template <std::size_t SSize>
static_sqr_impl(static_int<SSize> & rop,const static_int<SSize> & op,const std::integral_constant<int,2> &)4127 inline std::size_t static_sqr_impl(static_int<SSize> &rop, const static_int<SSize> &op,
4128 const std::integral_constant<int, 2> &)
4129 {
4130 const auto asize = std::abs(op._mp_size);
4131
4132 if (asize == 2) {
4133 // If asize is 2, this function cannot succeed.
4134 // Return 4 as a size hint. Real size could be 3, but GMP will require 4 limbs
4135 // of storage to perform the operation anyway.
4136 return 4u;
4137 }
4138
4139 // NOTE: this handles zeroes as well: we know that the limbs are all zeroes in such
4140 // a case (as we always guarantee unused limbs are zeroed).
4141 rop.m_limbs[0] = dlimb_mul(op.m_limbs[0], op.m_limbs[0], &rop.m_limbs[1]);
4142 // NOTE: if asize == 0, then rop's limbs will have been set
4143 // to zero via dlimb_mul(), and rop's size will also thus be zero.
4144 // Otherwise, rop's size is 1 or 2, depending on whether the top limb
4145 // is set or not.
4146 rop._mp_size = static_cast<mpz_size_t>(2 - (asize == 0) - (rop.m_limbs[1] == 0u));
4147
4148 return 0;
4149 }
4150
4151 template <std::size_t SSize>
static_sqr(static_int<SSize> & rop,const static_int<SSize> & op)4152 inline std::size_t static_sqr(static_int<SSize> &rop, const static_int<SSize> &op)
4153 {
4154 const std::size_t retval = static_sqr_impl(rop, op, integer_static_sqr_algo<static_int<SSize>>{});
4155
4156 if (integer_static_sqr_algo<static_int<SSize>>::value == 0 && retval == 0u) {
4157 // If we used the mpn functions, and actually wrote into the result,
4158 // zero the unused limbs on top (if necessary).
4159 // NOTE: as elsewhere, if we don't have double-limb primitives
4160 // available, we may end up using mpn functions also for
4161 // small sizes.
4162 rop.zero_unused_limbs();
4163 }
4164
4165 return retval;
4166 }
4167
4168 } // namespace detail
4169
4170 // Binary squaring.
4171 template <std::size_t SSize>
sqr(integer<SSize> & rop,const integer<SSize> & n)4172 inline integer<SSize> &sqr(integer<SSize> &rop, const integer<SSize> &n)
4173 {
4174 const bool sn = n.is_static();
4175 bool sr = rop.is_static();
4176 std::size_t size_hint = 0u;
4177 if (mppp_likely(sn)) {
4178 if (!sr) {
4179 rop.set_zero();
4180 sr = true;
4181 }
4182 size_hint = static_sqr(rop._get_union().g_st(), n._get_union().g_st());
4183 if (mppp_likely(size_hint == 0u)) {
4184 return rop;
4185 }
4186 }
4187 if (sr) {
4188 rop._get_union().promote(size_hint);
4189 }
4190 mpz_mul(&rop._get_union().g_dy(), n.get_mpz_view(), n.get_mpz_view());
4191 return rop;
4192 }
4193
4194 // Unary squaring.
4195 template <std::size_t SSize>
sqr(const integer<SSize> & n)4196 inline integer<SSize> sqr(const integer<SSize> &n)
4197 {
4198 integer<SSize> retval;
4199 sqr(retval, n);
4200 return retval;
4201 }
4202
4203 namespace detail
4204 {
4205
4206 // Detect the presence of dual-limb division/remainder. This is currently possible only if:
4207 // - we are on a 32bit build (with the usual constraints that the types have exactly 32/64 bits and no nails),
4208 // - we are on a 64bit build and we have the 128bit int type available (plus usual constraints).
4209 // NOTE: starting from MSVC 2019, there are some 128bit division intrinsics
4210 // available:
4211 // https://docs.microsoft.com/en-us/cpp/intrinsics/udiv128
4212 using integer_have_dlimb_div = std::integral_constant<bool,
4213 #if defined(MPPP_HAVE_GCC_INT128) && (GMP_NUMB_BITS == 64)
4214 !GMP_NAIL_BITS && nl_digits<::mp_limb_t>() == 64
4215 #elif GMP_NUMB_BITS == 32
4216 !GMP_NAIL_BITS && nl_digits<std::uint_least64_t>() == 64
4217 && nl_digits<::mp_limb_t>() == 32
4218 #else
4219 false
4220 #endif
4221 >;
4222
4223 // Selection of the algorithm for static modular squaring:
4224 // - for 1 limb, we need both the double-limb multiplication AND
4225 // division/remainder primitives,
4226 // - otherwise we just use the mpn functions.
4227 // NOTE: the integer_have_dlimb_mul and integer_have_dlimb_div machinery
4228 // already checks for lack of nail bits, bit sizes, etc.
4229 template <typename SInt>
4230 using integer_static_sqrm_algo = std::integral_constant<
4231 int, (SInt::s_size == 1 && integer_have_dlimb_mul::value && integer_have_dlimb_div::value) ? 1 : 0>;
4232
4233 // mpn implementation.
4234 // NOTE: this assumes that mod is not zero.
4235 template <std::size_t SSize>
static_sqrm_impl(static_int<SSize> & rop,const static_int<SSize> & op,const static_int<SSize> & mod,const std::integral_constant<int,0> &)4236 inline void static_sqrm_impl(static_int<SSize> &rop, const static_int<SSize> &op, const static_int<SSize> &mod,
4237 const std::integral_constant<int, 0> &)
4238 {
4239 // Fetch the asize of the operand.
4240 const auto asize = static_cast<std::size_t>(std::abs(op._mp_size));
4241
4242 // Handle zero operand.
4243 if (mppp_unlikely(asize == 0u)) {
4244 rop._mp_size = 0;
4245 return;
4246 }
4247
4248 // Fetch the asize of the mod argument.
4249 const auto mod_asize = static_cast<std::size_t>(std::abs(mod._mp_size));
4250 assert(mod_asize != 0u);
4251
4252 // Temporary storage for mpn_sqr(). The largest possible
4253 // size needed is twice the static size.
4254 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4255 std::array<::mp_limb_t, SSize * 2u> sqr_res;
4256 const auto sqr_res_data = sqr_res.data();
4257
4258 // Run the squaring function.
4259 mpn_sqr(sqr_res_data, op.m_limbs.data(), static_cast<::mp_size_t>(asize));
4260 // Compute the actual size of the result of the squaring: it could be asize * 2
4261 // or 1 less, depending on whether the most significant limb of the
4262 // result is zero.
4263 const auto sqr_res_asize
4264 = asize * 2u - static_cast<std::size_t>((sqr_res_data[asize * 2u - 1u] & GMP_NUMB_MASK) == 0u);
4265
4266 if (mod_asize > sqr_res_asize) {
4267 // The divisor is larger than the square of op.
4268 // Copy the square of op to rop and exit.
4269 rop._mp_size = static_cast<mpz_size_t>(sqr_res_asize);
4270 copy_limbs_no(sqr_res_data, sqr_res_data + sqr_res_asize, rop.m_limbs.data());
4271
4272 return;
4273 }
4274
4275 // Temporary storage for the modulo operation.
4276 // Need space for both quotient and remainder.
4277 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4278 std::array<::mp_limb_t, SSize * 2u> q_res, r_res;
4279 // Run the modulo operation.
4280 // NOTE: here we could add some logic, like in tdiv_qr(),
4281 // to check whether we can write directly into rop instead
4282 // of using this temporary storage. Need to profile first,
4283 // however, because I am not sure whether this is worth
4284 // it or not performance-wise.
4285 // NOTE: ret_size will never be negative, as the
4286 // dividend is a square and thus also never negative.
4287 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
4288 mpz_size_t ret_size;
4289 if (mod_asize == 1u) {
4290 // Optimization when the divisor has 1 limb.
4291 r_res[0] = mpn_divrem_1(q_res.data(), 0, sqr_res_data, static_cast<::mp_size_t>(sqr_res_asize), mod.m_limbs[0]);
4292 // The size can be 1 or zero, depending on the value
4293 // of the only limb in r_res.
4294 ret_size = static_cast<mpz_size_t>((r_res[0] & GMP_NUMB_MASK) != 0u);
4295 } else {
4296 // The general case.
4297 mpn_tdiv_qr(q_res.data(), r_res.data(), 0, sqr_res_data, static_cast<::mp_size_t>(sqr_res_asize),
4298 mod.m_limbs.data(), static_cast<::mp_size_t>(mod_asize));
4299 // Determine the size of the output, which will
4300 // be in the [0, mod_asize] range.
4301 ret_size = static_cast<mpz_size_t>(mod_asize);
4302 while (ret_size && !(r_res[static_cast<std::size_t>(ret_size - 1)] & GMP_NUMB_MASK)) {
4303 --ret_size;
4304 }
4305 }
4306
4307 // Write out the result.
4308 rop._mp_size = ret_size;
4309 copy_limbs_no(r_res.data(), r_res.data() + ret_size, rop.m_limbs.data());
4310 }
4311
4312 #if defined(MPPP_HAVE_GCC_INT128) && (GMP_NUMB_BITS == 64) && !GMP_NAIL_BITS
4313
static_sqrm_impl_1(::mp_limb_t op,::mp_limb_t mod)4314 inline ::mp_limb_t static_sqrm_impl_1(::mp_limb_t op, ::mp_limb_t mod)
4315 {
4316 using dlimb_t = __uint128_t;
4317 return static_cast<::mp_limb_t>((dlimb_t(op) * op) % mod);
4318 }
4319
4320 #elif GMP_NUMB_BITS == 32 && !GMP_NAIL_BITS
4321
static_sqrm_impl_1(::mp_limb_t op,::mp_limb_t mod)4322 inline ::mp_limb_t static_sqrm_impl_1(::mp_limb_t op, ::mp_limb_t mod)
4323 {
4324 using dlimb_t = std::uint_least64_t;
4325 return static_cast<::mp_limb_t>((dlimb_t(op) * op) % mod);
4326 }
4327
4328 #endif
4329
4330 // 1-limb optimization via dlimb.
4331 // NOTE: this assumes that mod is not zero.
4332 template <std::size_t SSize>
static_sqrm_impl(static_int<SSize> & rop,const static_int<SSize> & op,const static_int<SSize> & mod,const std::integral_constant<int,1> &)4333 inline void static_sqrm_impl(static_int<SSize> &rop, const static_int<SSize> &op, const static_int<SSize> &mod,
4334 const std::integral_constant<int, 1> &)
4335 {
4336 assert(mod._mp_size != 0);
4337
4338 // NOTE: no need for masking, as
4339 // we are sure that there are no nails if
4340 // this overload is selected.
4341 const auto ret = static_sqrm_impl_1(op.m_limbs[0], mod.m_limbs[0]);
4342
4343 rop._mp_size = static_cast<mpz_size_t>(ret != 0u);
4344 rop.m_limbs[0] = ret;
4345 }
4346
4347 template <std::size_t SSize>
static_sqrm(static_int<SSize> & rop,const static_int<SSize> & op,const static_int<SSize> & mod)4348 inline void static_sqrm(static_int<SSize> &rop, const static_int<SSize> &op, const static_int<SSize> &mod)
4349 {
4350 static_sqrm_impl(rop, op, mod, integer_static_sqrm_algo<static_int<SSize>>{});
4351
4352 if (integer_static_sqrm_algo<static_int<SSize>>::value == 0) {
4353 // If we used the mpn functions, zero the unused limbs on top (if necessary).
4354 // NOTE: as elsewhere, if we don't have double-limb primitives
4355 // available, we may end up using mpn functions also for
4356 // small sizes.
4357 rop.zero_unused_limbs();
4358 }
4359 }
4360
4361 } // namespace detail
4362
4363 // Ternary modular squaring.
4364 template <std::size_t SSize>
sqrm(integer<SSize> & rop,const integer<SSize> & op,const integer<SSize> & mod)4365 inline integer<SSize> &sqrm(integer<SSize> &rop, const integer<SSize> &op, const integer<SSize> &mod)
4366 {
4367 if (mppp_unlikely(mod.sgn() == 0)) {
4368 throw zero_division_error("Integer division by zero");
4369 }
4370
4371 const bool sr = rop.is_static(), so = op.is_static(), sm = mod.is_static();
4372
4373 if (mppp_likely(so && sm)) {
4374 if (!sr) {
4375 rop.set_zero();
4376 }
4377
4378 detail::static_sqrm(rop._get_union().g_st(), op._get_union().g_st(), mod._get_union().g_st());
4379
4380 // Modular squaring can never fail.
4381 return rop;
4382 }
4383
4384 if (sr) {
4385 rop._get_union().promote();
4386 }
4387
4388 // NOTE: use temp storage to avoid issues with overlapping
4389 // arguments.
4390 MPPP_MAYBE_TLS detail::mpz_raii tmp;
4391 mpz_mul(&tmp.m_mpz, op.get_mpz_view(), op.get_mpz_view());
4392 mpz_tdiv_r(&rop._get_union().g_dy(), &tmp.m_mpz, mod.get_mpz_view());
4393
4394 return rop;
4395 }
4396
4397 // Binary modular squaring.
4398 template <std::size_t SSize>
sqrm(const integer<SSize> & op,const integer<SSize> & mod)4399 inline integer<SSize> sqrm(const integer<SSize> &op, const integer<SSize> &mod)
4400 {
4401 integer<SSize> retval;
4402 sqrm(retval, op, mod);
4403 return retval;
4404 }
4405
4406 // Binary negation.
4407 template <std::size_t SSize>
neg(integer<SSize> & rop,const integer<SSize> & n)4408 inline integer<SSize> &neg(integer<SSize> &rop, const integer<SSize> &n)
4409 {
4410 rop = n;
4411 return rop.neg();
4412 }
4413
4414 // Unary negation.
4415 template <std::size_t SSize>
neg(const integer<SSize> & n)4416 inline integer<SSize> neg(const integer<SSize> &n)
4417 {
4418 integer<SSize> ret(n);
4419 ret.neg();
4420 return ret;
4421 }
4422
4423 // Binary absolute value.
4424 template <std::size_t SSize>
abs(integer<SSize> & rop,const integer<SSize> & n)4425 inline integer<SSize> &abs(integer<SSize> &rop, const integer<SSize> &n)
4426 {
4427 rop = n;
4428 return rop.abs();
4429 }
4430
4431 // Unary absolute value.
4432 template <std::size_t SSize>
abs(const integer<SSize> & n)4433 inline integer<SSize> abs(const integer<SSize> &n)
4434 {
4435 integer<SSize> ret(n);
4436 ret.abs();
4437 return ret;
4438 }
4439
4440 namespace detail
4441 {
4442
4443 // Selection of the algorithm for static division:
4444 // - for 1 limb, we can always do static division,
4445 // - for 2 limbs, we need the dual limb division if avaiable,
4446 // - otherwise we just use the mpn functions.
4447 template <typename SInt>
4448 using integer_static_div_algo
4449 = std::integral_constant<int,
4450 SInt::s_size == 1 ? 1 : ((SInt::s_size == 2 && integer_have_dlimb_div::value) ? 2 : 0)>;
4451
4452 // mpn implementation.
4453 template <std::size_t SSize>
static_tdiv_qr_impl(static_int<SSize> & q,static_int<SSize> & r,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,0> &)4454 inline void static_tdiv_qr_impl(static_int<SSize> &q, static_int<SSize> &r, const static_int<SSize> &op1,
4455 const static_int<SSize> &op2, mpz_size_t asize1, mpz_size_t asize2, int sign1,
4456 int sign2, const std::integral_constant<int, 0> &)
4457 {
4458 // First we check if the divisor is larger than the dividend (in abs limb size), as the mpn function
4459 // requires asize1 >= asize2.
4460 if (asize2 > asize1) {
4461 // Copy op1 into the remainder.
4462 r = op1;
4463 // Zero out q.
4464 q._mp_size = 0;
4465 return;
4466 }
4467 // NOTE: we checked outside that the divisor is not zero, and now asize1 >= asize2. Hence,
4468 // both operands are nonzero.
4469 // We need to take care of potentially overlapping arguments. We know that q and r are distinct, but op1
4470 // could overlap with q or r, and op2 could overlap with op1, q or r.
4471 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4472 std::array<::mp_limb_t, SSize> op1_alt, op2_alt;
4473 const ::mp_limb_t *data1 = op1.m_limbs.data();
4474 const ::mp_limb_t *data2 = op2.m_limbs.data();
4475 if (&op1 == &q || &op1 == &r) {
4476 copy_limbs_no(data1, data1 + asize1, op1_alt.data());
4477 data1 = op1_alt.data();
4478 }
4479 if (&op2 == &q || &op2 == &r || &op1 == &op2) {
4480 copy_limbs_no(data2, data2 + asize2, op2_alt.data());
4481 data2 = op2_alt.data();
4482 }
4483 // Verify that all pointers are distinct. Used exclusively for debugging purposes.
4484 assert(([&q, &r, data1, data2]() -> bool {
4485 const ::mp_limb_t *ptrs[] = {q.m_limbs.data(), r.m_limbs.data(), data1, data2};
4486 std::sort(ptrs, ptrs + 4, std::less<const ::mp_limb_t *>());
4487 return std::unique(ptrs, ptrs + 4) == (ptrs + 4);
4488 }()));
4489 // Proceed to the division.
4490 if (asize2 == 1) {
4491 // Optimization when the divisor has 1 limb.
4492 r.m_limbs[0]
4493 = mpn_divrem_1(q.m_limbs.data(), ::mp_size_t(0), data1, static_cast<::mp_size_t>(asize1), data2[0]);
4494 } else {
4495 // General implementation.
4496 mpn_tdiv_qr(q.m_limbs.data(), r.m_limbs.data(), ::mp_size_t(0), data1, static_cast<::mp_size_t>(asize1), data2,
4497 static_cast<::mp_size_t>(asize2));
4498 }
4499 // Complete the quotient: compute size and sign.
4500 q._mp_size = asize1 - asize2 + 1;
4501 while (q._mp_size && !(q.m_limbs[static_cast<std::size_t>(q._mp_size - 1)] & GMP_NUMB_MASK)) {
4502 --q._mp_size;
4503 }
4504 if (sign1 != sign2) {
4505 q._mp_size = -q._mp_size;
4506 }
4507 // Complete the remainder.
4508 r._mp_size = asize2;
4509 while (r._mp_size && !(r.m_limbs[static_cast<std::size_t>(r._mp_size - 1)] & GMP_NUMB_MASK)) {
4510 --r._mp_size;
4511 }
4512 if (sign1 == -1) {
4513 r._mp_size = -r._mp_size;
4514 }
4515 }
4516
4517 // 1-limb optimisation.
4518 template <std::size_t SSize>
static_tdiv_qr_impl(static_int<SSize> & q,static_int<SSize> & r,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2,const std::integral_constant<int,1> &)4519 inline void static_tdiv_qr_impl(static_int<SSize> &q, static_int<SSize> &r, const static_int<SSize> &op1,
4520 const static_int<SSize> &op2, mpz_size_t, mpz_size_t, int sign1, int sign2,
4521 const std::integral_constant<int, 1> &)
4522 {
4523 // NOTE: here we have to use GMP_NUMB_MASK because if s_size is 1 this implementation is *always*
4524 // called, even if we have nail bits (whereas the optimisation for other operations currently kicks in
4525 // only without nail bits). Thus, we need to discard from m_limbs[0] the nail bits before doing the
4526 // division.
4527 const ::mp_limb_t n = op1.m_limbs[0] & GMP_NUMB_MASK, d = op2.m_limbs[0] & GMP_NUMB_MASK, q_ = n / d, r_ = n % d;
4528 // Write q.
4529 q._mp_size = sign1 * sign2 * (n >= d);
4530 // NOTE: there should be no need here to mask.
4531 q.m_limbs[0] = q_;
4532 // Write r.
4533 // Following C++11, the sign of r is the sign of op1:
4534 // http://stackoverflow.com/questions/13100711/operator-modulo-change-in-c-11
4535 r._mp_size = sign1 * (r_ != 0u);
4536 r.m_limbs[0] = r_;
4537 }
4538
4539 // Implementation of the 2-limb division/remainder primitives,
4540 // parametrised on the double limb type and limb bit width.
4541 // These assume that there are no nail bits and that
4542 // the bit width of DLimb is exactly twice the bit
4543 // width of the limb type.
4544
4545 // Quotient+remainder.
4546 template <typename DLimb, int NBits>
dlimb_tdiv_qr_impl(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2,::mp_limb_t * MPPP_RESTRICT r1,::mp_limb_t * MPPP_RESTRICT r2)4547 inline void dlimb_tdiv_qr_impl(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4548 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2,
4549 ::mp_limb_t *MPPP_RESTRICT r1, ::mp_limb_t *MPPP_RESTRICT r2)
4550 {
4551 const auto op1 = op11 + (DLimb(op12) << NBits);
4552 const auto op2 = op21 + (DLimb(op22) << NBits);
4553 const auto q = op1 / op2, r = op1 % op2;
4554 *q1 = static_cast<::mp_limb_t>(q & ::mp_limb_t(-1));
4555 *q2 = static_cast<::mp_limb_t>(q >> NBits);
4556 *r1 = static_cast<::mp_limb_t>(r & ::mp_limb_t(-1));
4557 *r2 = static_cast<::mp_limb_t>(r >> NBits);
4558 }
4559
4560 // Quotient only.
4561 template <typename DLimb, int NBits>
dlimb_tdiv_q_impl(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2)4562 inline void dlimb_tdiv_q_impl(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4563 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2)
4564 {
4565 const auto op1 = op11 + (DLimb(op12) << NBits);
4566 const auto op2 = op21 + (DLimb(op22) << NBits);
4567 const auto q = op1 / op2;
4568 *q1 = static_cast<::mp_limb_t>(q & ::mp_limb_t(-1));
4569 *q2 = static_cast<::mp_limb_t>(q >> NBits);
4570 }
4571
4572 #if defined(MPPP_HAVE_GCC_INT128) && (GMP_NUMB_BITS == 64) && !GMP_NAIL_BITS
4573
dlimb_tdiv_qr(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2,::mp_limb_t * MPPP_RESTRICT r1,::mp_limb_t * MPPP_RESTRICT r2)4574 inline void dlimb_tdiv_qr(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4575 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2, ::mp_limb_t *MPPP_RESTRICT r1,
4576 ::mp_limb_t *MPPP_RESTRICT r2)
4577 {
4578 dlimb_tdiv_qr_impl<__uint128_t, 64>(op11, op12, op21, op22, q1, q2, r1, r2);
4579 }
4580
dlimb_tdiv_q(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2)4581 inline void dlimb_tdiv_q(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4582 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2)
4583 {
4584 dlimb_tdiv_q_impl<__uint128_t, 64>(op11, op12, op21, op22, q1, q2);
4585 }
4586
4587 #elif GMP_NUMB_BITS == 32 && !GMP_NAIL_BITS
4588
dlimb_tdiv_qr(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2,::mp_limb_t * MPPP_RESTRICT r1,::mp_limb_t * MPPP_RESTRICT r2)4589 inline void dlimb_tdiv_qr(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4590 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2, ::mp_limb_t *MPPP_RESTRICT r1,
4591 ::mp_limb_t *MPPP_RESTRICT r2)
4592 {
4593 dlimb_tdiv_qr_impl<std::uint_least64_t, 32>(op11, op12, op21, op22, q1, q2, r1, r2);
4594 }
4595
dlimb_tdiv_q(::mp_limb_t op11,::mp_limb_t op12,::mp_limb_t op21,::mp_limb_t op22,::mp_limb_t * MPPP_RESTRICT q1,::mp_limb_t * MPPP_RESTRICT q2)4596 inline void dlimb_tdiv_q(::mp_limb_t op11, ::mp_limb_t op12, ::mp_limb_t op21, ::mp_limb_t op22,
4597 ::mp_limb_t *MPPP_RESTRICT q1, ::mp_limb_t *MPPP_RESTRICT q2)
4598 {
4599 dlimb_tdiv_q_impl<std::uint_least64_t, 32>(op11, op12, op21, op22, q1, q2);
4600 }
4601
4602 #endif
4603
4604 // 2-limbs optimisation.
4605 template <std::size_t SSize>
static_tdiv_qr_impl(static_int<SSize> & q,static_int<SSize> & r,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,2> &)4606 inline void static_tdiv_qr_impl(static_int<SSize> &q, static_int<SSize> &r, const static_int<SSize> &op1,
4607 const static_int<SSize> &op2, mpz_size_t asize1, mpz_size_t asize2, int sign1,
4608 int sign2, const std::integral_constant<int, 2> &)
4609 {
4610 if (asize1 < 2 && asize2 < 2) {
4611 // NOTE: testing indicates that it pays off to optimize the case in which the operands have
4612 // fewer than 2 limbs. This a slightly modified version of the 1-limb division from above,
4613 // without the need to mask as this function is called only if there are no nail bits.
4614 const ::mp_limb_t n = op1.m_limbs[0], d = op2.m_limbs[0], q_ = n / d, r_ = n % d;
4615 q._mp_size = sign1 * sign2 * (n >= d);
4616 q.m_limbs[0] = q_;
4617 q.m_limbs[1] = 0u;
4618 r._mp_size = sign1 * (r_ != 0u);
4619 r.m_limbs[0] = r_;
4620 r.m_limbs[1] = 0u;
4621 return;
4622 }
4623 // Perform the division.
4624 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
4625 ::mp_limb_t q1, q2, r1, r2;
4626 dlimb_tdiv_qr(op1.m_limbs[0], op1.m_limbs[1], op2.m_limbs[0], op2.m_limbs[1], &q1, &q2, &r1, &r2);
4627 // Write out.
4628 q._mp_size = sign1 * sign2 * size_from_lohi(q1, q2);
4629 q.m_limbs[0] = q1;
4630 q.m_limbs[1] = q2;
4631 r._mp_size = sign1 * size_from_lohi(r1, r2);
4632 r.m_limbs[0] = r1;
4633 r.m_limbs[1] = r2;
4634 }
4635
4636 template <std::size_t SSize>
static_tdiv_qr(static_int<SSize> & q,static_int<SSize> & r,const static_int<SSize> & op1,const static_int<SSize> & op2)4637 inline void static_tdiv_qr(static_int<SSize> &q, static_int<SSize> &r, const static_int<SSize> &op1,
4638 const static_int<SSize> &op2)
4639 {
4640 const auto s1(op1._mp_size), s2(op2._mp_size);
4641 const mpz_size_t asize1 = std::abs(s1), asize2 = std::abs(s2);
4642 const int sign1 = integral_sign(s1), sign2 = integral_sign(s2);
4643 static_tdiv_qr_impl(q, r, op1, op2, asize1, asize2, sign1, sign2, integer_static_div_algo<static_int<SSize>>{});
4644 if (integer_static_div_algo<static_int<SSize>>::value == 0) {
4645 // If we used the mpn functions, zero the unused limbs on top (if necessary).
4646 // NOTE: as usual, potential of mpn use on optimised size.
4647 q.zero_unused_limbs();
4648 r.zero_unused_limbs();
4649 }
4650 }
4651 } // namespace detail
4652
4653 // Truncated division with remainder.
4654 template <std::size_t SSize>
tdiv_qr(integer<SSize> & q,integer<SSize> & r,const integer<SSize> & n,const integer<SSize> & d)4655 inline void tdiv_qr(integer<SSize> &q, integer<SSize> &r, const integer<SSize> &n, const integer<SSize> &d)
4656 {
4657 if (mppp_unlikely(&q == &r)) {
4658 throw std::invalid_argument("When performing a division with remainder, the quotient 'q' and the "
4659 "remainder 'r' must be distinct objects");
4660 }
4661 if (mppp_unlikely(d.sgn() == 0)) {
4662 throw zero_division_error("Integer division by zero");
4663 }
4664 const bool sq = q.is_static(), sr = r.is_static(), s1 = n.is_static(), s2 = d.is_static();
4665 if (mppp_likely(s1 && s2)) {
4666 if (!sq) {
4667 q.set_zero();
4668 }
4669 if (!sr) {
4670 r.set_zero();
4671 }
4672 static_tdiv_qr(q._get_union().g_st(), r._get_union().g_st(), n._get_union().g_st(), d._get_union().g_st());
4673 // Division can never fail.
4674 return;
4675 }
4676 if (sq) {
4677 q._get_union().promote();
4678 }
4679 if (sr) {
4680 r._get_union().promote();
4681 }
4682 mpz_tdiv_qr(&q._get_union().g_dy(), &r._get_union().g_dy(), n.get_mpz_view(), d.get_mpz_view());
4683 }
4684
4685 namespace detail
4686 {
4687
4688 // mpn implementation.
4689 template <std::size_t SSize>
static_tdiv_q_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,0> &)4690 inline void static_tdiv_q_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4691 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
4692 const std::integral_constant<int, 0> &)
4693 {
4694 // First we check if the divisor is larger than the dividend (in abs limb size), as the mpn function
4695 // requires asize1 >= asize2.
4696 if (asize2 > asize1) {
4697 // Zero out q.
4698 q._mp_size = 0;
4699 return;
4700 }
4701 // NOTE: we checked outside that the divisor is not zero, and now asize1 >= asize2. Hence,
4702 // both operands are nonzero.
4703 // We need to take care of potentially overlapping arguments. op1 could overlap with q, and op2
4704 // could overlap with op1 or q.
4705 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4706 std::array<::mp_limb_t, SSize> op1_alt, op2_alt;
4707 const ::mp_limb_t *data1 = op1.m_limbs.data();
4708 const ::mp_limb_t *data2 = op2.m_limbs.data();
4709 if (&op1 == &q) {
4710 copy_limbs_no(data1, data1 + asize1, op1_alt.data());
4711 data1 = op1_alt.data();
4712 }
4713 if (&op2 == &q || &op1 == &op2) {
4714 copy_limbs_no(data2, data2 + asize2, op2_alt.data());
4715 data2 = op2_alt.data();
4716 }
4717 // Verify that all pointers are distinct.
4718 assert(([&q, data1, data2]() -> bool {
4719 const ::mp_limb_t *ptrs[] = {q.m_limbs.data(), data1, data2};
4720 std::sort(ptrs, ptrs + 3, std::less<const ::mp_limb_t *>());
4721 return std::unique(ptrs, ptrs + 3) == (ptrs + 3);
4722 }()));
4723 // Proceed to the division.
4724 if (asize2 == 1) {
4725 // Optimization when the divisor has 1 limb.
4726 mpn_divrem_1(q.m_limbs.data(), ::mp_size_t(0), data1, static_cast<::mp_size_t>(asize1), data2[0]);
4727 } else {
4728 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
4729 std::array<::mp_limb_t, SSize> r_unused;
4730 // General implementation.
4731 mpn_tdiv_qr(q.m_limbs.data(), r_unused.data(), ::mp_size_t(0), data1, static_cast<::mp_size_t>(asize1), data2,
4732 static_cast<::mp_size_t>(asize2));
4733 }
4734 // Complete the quotient: compute size and sign.
4735 q._mp_size = asize1 - asize2 + 1;
4736 while (q._mp_size && !(q.m_limbs[static_cast<std::size_t>(q._mp_size - 1)] & GMP_NUMB_MASK)) {
4737 --q._mp_size;
4738 }
4739 if (sign1 != sign2) {
4740 q._mp_size = -q._mp_size;
4741 }
4742 }
4743
4744 // 1-limb optimisation.
4745 template <std::size_t SSize>
static_tdiv_q_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2,const std::integral_constant<int,1> &)4746 inline void static_tdiv_q_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4747 mpz_size_t, mpz_size_t, int sign1, int sign2, const std::integral_constant<int, 1> &)
4748 {
4749 // NOTE: here we have to use GMP_NUMB_MASK because if s_size is 1 this implementation is *always*
4750 // called, even if we have nail bits (whereas the optimisation for other operations currently kicks in
4751 // only without nail bits). Thus, we need to discard from m_limbs[0] the nail bits before doing the
4752 // division.
4753 const ::mp_limb_t n = op1.m_limbs[0] & GMP_NUMB_MASK, d = op2.m_limbs[0] & GMP_NUMB_MASK, q_ = n / d;
4754 // Write q.
4755 q._mp_size = sign1 * sign2 * (n >= d);
4756 // NOTE: there should be no need here to mask.
4757 q.m_limbs[0] = q_;
4758 }
4759
4760 // 2-limbs optimisation.
4761 template <std::size_t SSize>
static_tdiv_q_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,2> &)4762 inline void static_tdiv_q_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4763 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
4764 const std::integral_constant<int, 2> &)
4765 {
4766 if (asize1 < 2 && asize2 < 2) {
4767 // NOTE: testing indicates that it pays off to optimize the case in which the operands have
4768 // fewer than 2 limbs. This a slightly modified version of the 1-limb division from above,
4769 // without the need to mask as this function is called only if there are no nail bits.
4770 const ::mp_limb_t n = op1.m_limbs[0], d = op2.m_limbs[0], q_ = n / d;
4771 q._mp_size = sign1 * sign2 * (n >= d);
4772 q.m_limbs[0] = q_;
4773 q.m_limbs[1] = 0u;
4774 return;
4775 }
4776 // Perform the division.
4777 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
4778 ::mp_limb_t q1, q2;
4779 dlimb_tdiv_q(op1.m_limbs[0], op1.m_limbs[1], op2.m_limbs[0], op2.m_limbs[1], &q1, &q2);
4780 // Write out.
4781 q._mp_size = sign1 * sign2 * size_from_lohi(q1, q2);
4782 q.m_limbs[0] = q1;
4783 q.m_limbs[1] = q2;
4784 }
4785
4786 template <std::size_t SSize>
static_tdiv_q(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2)4787 inline void static_tdiv_q(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2)
4788 {
4789 const auto s1(op1._mp_size), s2(op2._mp_size);
4790 const mpz_size_t asize1 = std::abs(s1), asize2 = std::abs(s2);
4791 const int sign1 = integral_sign(s1), sign2 = integral_sign(s2);
4792 // NOTE: use the same algorithm dispatching as in the division with remainder.
4793 static_tdiv_q_impl(q, op1, op2, asize1, asize2, sign1, sign2, integer_static_div_algo<static_int<SSize>>{});
4794 if (integer_static_div_algo<static_int<SSize>>::value == 0) {
4795 // If we used the mpn functions, zero the unused limbs on top (if necessary).
4796 // NOTE: as usual, potential of mpn use on optimised size.
4797 q.zero_unused_limbs();
4798 }
4799 }
4800 } // namespace detail
4801
4802 // Truncated division without remainder.
4803 template <std::size_t SSize>
tdiv_q(integer<SSize> & q,const integer<SSize> & n,const integer<SSize> & d)4804 inline integer<SSize> &tdiv_q(integer<SSize> &q, const integer<SSize> &n, const integer<SSize> &d)
4805 {
4806 if (mppp_unlikely(d.sgn() == 0)) {
4807 throw zero_division_error("Integer division by zero");
4808 }
4809 const bool sq = q.is_static(), s1 = n.is_static(), s2 = d.is_static();
4810 if (mppp_likely(s1 && s2)) {
4811 if (!sq) {
4812 q.set_zero();
4813 }
4814 static_tdiv_q(q._get_union().g_st(), n._get_union().g_st(), d._get_union().g_st());
4815 // Division can never fail.
4816 return q;
4817 }
4818 if (sq) {
4819 q._get_union().promote();
4820 }
4821 mpz_tdiv_q(&q._get_union().g_dy(), n.get_mpz_view(), d.get_mpz_view());
4822 return q;
4823 }
4824
4825 namespace detail
4826 {
4827
4828 // mpn implementation.
4829 // NOTE: the Gcd flag specifies whether the divisor op2 is a strictly positive quantity.
4830 template <bool Gcd, std::size_t SSize>
static_divexact_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,0> &)4831 inline void static_divexact_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4832 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
4833 const std::integral_constant<int, 0> &)
4834 {
4835 assert(!Gcd || sign2 == 1);
4836 if (asize1 == 0) {
4837 // Special casing if the numerator is zero (the mpn functions do not work with zero operands).
4838 q._mp_size = 0;
4839 return;
4840 }
4841 #if defined(MPPP_GMP_HAVE_MPN_DIVEXACT_1)
4842 if (asize2 == 1) {
4843 // Optimisation in case the dividend has only 1 limb.
4844 // NOTE: overlapping arguments are fine here.
4845 mpn_divexact_1(q.m_limbs.data(), op1.m_limbs.data(), static_cast<::mp_size_t>(asize1), op2.m_limbs[0]);
4846 // Complete the quotient: compute size and sign.
4847 q._mp_size = asize1 - asize2 + 1;
4848 while (q._mp_size && !(q.m_limbs[static_cast<std::size_t>(q._mp_size - 1)] & GMP_NUMB_MASK)) {
4849 --q._mp_size;
4850 }
4851 if (sign1 != (Gcd ? 1 : sign2)) {
4852 q._mp_size = -q._mp_size;
4853 }
4854 return;
4855 }
4856 #else
4857 // Avoid compiler warnings for unused parameters.
4858 ignore(sign1, sign2, asize2);
4859 #endif
4860 // General implementation (via the mpz function).
4861 MPPP_MAYBE_TLS mpz_raii tmp;
4862 const auto v1 = op1.get_mpz_view();
4863 const auto v2 = op2.get_mpz_view();
4864 mpz_divexact(&tmp.m_mpz, &v1, &v2);
4865 // Copy over from the tmp struct into q.
4866 q._mp_size = tmp.m_mpz._mp_size;
4867 copy_limbs_no(tmp.m_mpz._mp_d, tmp.m_mpz._mp_d + (q._mp_size >= 0 ? q._mp_size : -q._mp_size), q.m_limbs.data());
4868 }
4869
4870 // 1-limb optimisation.
4871 template <bool Gcd, std::size_t SSize>
static_divexact_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2,const std::integral_constant<int,1> &)4872 inline void static_divexact_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4873 mpz_size_t, mpz_size_t, int sign1, int sign2, const std::integral_constant<int, 1> &)
4874 {
4875 assert(!Gcd || sign2 == 1);
4876 const ::mp_limb_t n = op1.m_limbs[0] & GMP_NUMB_MASK, d = op2.m_limbs[0] & GMP_NUMB_MASK, q_ = n / d;
4877 // Write q.
4878 q._mp_size = sign1 * (Gcd ? 1 : sign2) * (n >= d);
4879 q.m_limbs[0] = q_;
4880 }
4881
4882 // 2-limbs optimisation.
4883 template <bool Gcd, std::size_t SSize>
static_divexact_impl(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2,const std::integral_constant<int,2> &)4884 inline void static_divexact_impl(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2,
4885 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2,
4886 const std::integral_constant<int, 2> &)
4887 {
4888 assert(!Gcd || sign2 == 1);
4889 if (asize1 < 2 && asize2 < 2) {
4890 // 1-limb optimisation.
4891 const ::mp_limb_t n = op1.m_limbs[0], d = op2.m_limbs[0], q_ = n / d;
4892 q._mp_size = sign1 * (Gcd ? 1 : sign2) * (n >= d);
4893 q.m_limbs[0] = q_;
4894 q.m_limbs[1] = 0u;
4895 return;
4896 }
4897 // General case.
4898 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
4899 ::mp_limb_t q1, q2;
4900 dlimb_tdiv_q(op1.m_limbs[0], op1.m_limbs[1], op2.m_limbs[0], op2.m_limbs[1], &q1, &q2);
4901 q._mp_size = sign1 * (Gcd ? 1 : sign2) * size_from_lohi(q1, q2);
4902 q.m_limbs[0] = q1;
4903 q.m_limbs[1] = q2;
4904 }
4905
4906 template <std::size_t SSize>
static_divexact(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2)4907 inline void static_divexact(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2)
4908 {
4909 const auto s1(op1._mp_size), s2(op2._mp_size);
4910 const mpz_size_t asize1 = std::abs(s1), asize2 = std::abs(s2);
4911 const int sign1 = integral_sign(s1), sign2 = integral_sign(s2);
4912 // NOTE: op2 divides exactly op1, so either op1 is zero or the absolute size
4913 // of op1 must not be smaller than op2's.
4914 assert(asize1 == 0 || asize2 <= asize1);
4915 // NOTE: use integer_static_div_algo for the algorithm selection.
4916 static_divexact_impl<false>(q, op1, op2, asize1, asize2, sign1, sign2,
4917 integer_static_div_algo<static_int<SSize>>{});
4918 if (integer_static_div_algo<static_int<SSize>>::value == 0) {
4919 // If we used the mpn functions, zero the unused limbs on top (if necessary).
4920 // NOTE: as usual, potential of mpn use on optimised size.
4921 q.zero_unused_limbs();
4922 }
4923 }
4924 } // namespace detail
4925
4926 // Exact division (ternary version).
4927 template <std::size_t SSize>
divexact(integer<SSize> & rop,const integer<SSize> & n,const integer<SSize> & d)4928 inline integer<SSize> &divexact(integer<SSize> &rop, const integer<SSize> &n, const integer<SSize> &d)
4929 {
4930 const bool sr = rop.is_static(), s1 = n.is_static(), s2 = d.is_static();
4931 if (mppp_likely(s1 && s2)) {
4932 if (!sr) {
4933 rop.set_zero();
4934 }
4935 static_divexact(rop._get_union().g_st(), n._get_union().g_st(), d._get_union().g_st());
4936 // Division can never fail.
4937 return rop;
4938 }
4939 if (sr) {
4940 rop._get_union().promote();
4941 }
4942 mpz_divexact(&rop._get_union().g_dy(), n.get_mpz_view(), d.get_mpz_view());
4943 return rop;
4944 }
4945
4946 // Exact division (binary version).
4947 template <std::size_t SSize>
divexact(const integer<SSize> & n,const integer<SSize> & d)4948 inline integer<SSize> divexact(const integer<SSize> &n, const integer<SSize> &d)
4949 {
4950 integer<SSize> retval;
4951 divexact(retval, n, d);
4952 return retval;
4953 }
4954
4955 namespace detail
4956 {
4957
4958 template <std::size_t SSize>
static_divexact_gcd(static_int<SSize> & q,const static_int<SSize> & op1,const static_int<SSize> & op2)4959 inline void static_divexact_gcd(static_int<SSize> &q, const static_int<SSize> &op1, const static_int<SSize> &op2)
4960 {
4961 const auto s1(op1._mp_size);
4962 const mpz_size_t asize1 = std::abs(s1), asize2 = op2._mp_size;
4963 const int sign1 = integral_sign(s1);
4964 // NOTE: op2 divides exactly op1, so either op1 is zero or the absolute size
4965 // of op1 must not be smaller than op2's.
4966 assert(asize1 == 0 || asize2 <= asize1);
4967 assert(asize2 > 0);
4968 // NOTE: use integer_static_div_algo for the algorithm selection.
4969 static_divexact_impl<true>(q, op1, op2, asize1, asize2, sign1, 1, integer_static_div_algo<static_int<SSize>>{});
4970 if (integer_static_div_algo<static_int<SSize>>::value == 0) {
4971 // If we used the mpn functions, zero the unused limbs on top (if necessary).
4972 // NOTE: as usual, potential of mpn use on optimised size.
4973 q.zero_unused_limbs();
4974 }
4975 }
4976 } // namespace detail
4977
4978 // Exact division with positive divisor (ternary version).
4979 template <std::size_t SSize>
divexact_gcd(integer<SSize> & rop,const integer<SSize> & n,const integer<SSize> & d)4980 inline integer<SSize> &divexact_gcd(integer<SSize> &rop, const integer<SSize> &n, const integer<SSize> &d)
4981 {
4982 assert(d.sgn() > 0);
4983 const bool sr = rop.is_static(), s1 = n.is_static(), s2 = d.is_static();
4984 if (mppp_likely(s1 && s2)) {
4985 if (!sr) {
4986 rop.set_zero();
4987 }
4988 static_divexact_gcd(rop._get_union().g_st(), n._get_union().g_st(), d._get_union().g_st());
4989 // Division can never fail.
4990 return rop;
4991 }
4992 if (sr) {
4993 rop._get_union().promote();
4994 }
4995 // NOTE: there's no public mpz_divexact_gcd() function in GMP, just use
4996 // mpz_divexact() directly.
4997 mpz_divexact(&rop._get_union().g_dy(), n.get_mpz_view(), d.get_mpz_view());
4998 return rop;
4999 }
5000
5001 // Exact division with positive divisor (binary version).
5002 template <std::size_t SSize>
divexact_gcd(const integer<SSize> & n,const integer<SSize> & d)5003 inline integer<SSize> divexact_gcd(const integer<SSize> &n, const integer<SSize> &d)
5004 {
5005 integer<SSize> retval;
5006 divexact_gcd(retval, n, d);
5007 return retval;
5008 }
5009
5010 namespace detail
5011 {
5012
5013 // mpn implementation.
5014 template <std::size_t SSize>
static_tdiv_q_2exp(static_int<SSize> & rop,const static_int<SSize> & n,::mp_bitcnt_t s)5015 inline void static_tdiv_q_2exp(static_int<SSize> &rop, const static_int<SSize> &n, ::mp_bitcnt_t s)
5016 {
5017 static_assert(SSize > static_int<SSize>::opt_size, "Cannot use mpn functions for optimised static size.");
5018 mpz_size_t asize = n._mp_size;
5019 if (s == 0u || asize == 0) {
5020 // If shift is zero, or the operand is zero, write n into rop and return.
5021 rop = n;
5022 return;
5023 }
5024 // Finish setting up asize and sign.
5025 int sign = 1;
5026 if (asize < 0) {
5027 asize = -asize;
5028 sign = -1;
5029 }
5030 // ls: number of entire limbs shifted.
5031 // rs: effective shift that will be passed to the mpn function.
5032 const auto ls = s / unsigned(GMP_NUMB_BITS), rs = s % unsigned(GMP_NUMB_BITS);
5033 if (ls >= static_cast<std::size_t>(asize)) {
5034 // If we shift by a number of entire limbs equal to or larger than the asize,
5035 // the result will be zero.
5036 rop._mp_size = 0;
5037 return;
5038 }
5039 // The maximum new asize will be the old asize minus ls.
5040 const mpz_size_t new_asize = asize - static_cast<mpz_size_t>(ls);
5041 if (rs) {
5042 // Perform the shift via the mpn function, if we are effectively shifting at least 1 bit.
5043 // Overlapping is fine, as it is guaranteed that rop.m_limbs.data() <= n.m_limbs.data() + ls.
5044 mpn_rshift(rop.m_limbs.data(), n.m_limbs.data() + ls, static_cast<::mp_size_t>(new_asize), unsigned(rs));
5045 } else {
5046 // Otherwise, just copy over (the mpn function requires the shift to be at least 1).
5047 // NOTE: std::move requires that the destination iterator is not within the input range.
5048 // Here we are sure that ls > 0 because we don't have a remainder, and the zero shift case
5049 // was already handled above.
5050 assert(ls);
5051 std::move(n.m_limbs.begin() + ls, n.m_limbs.begin() + asize, rop.m_limbs.begin());
5052 }
5053 // Set the size. We need to check if the top limb is zero.
5054 rop._mp_size = sign * (new_asize - ((rop.m_limbs[static_cast<std::size_t>(new_asize - 1)] & GMP_NUMB_MASK) == 0u));
5055 }
5056
5057 // 1-limb optimisation.
static_tdiv_q_2exp(static_int<1> & rop,const static_int<1> & n,::mp_bitcnt_t s)5058 inline void static_tdiv_q_2exp(static_int<1> &rop, const static_int<1> &n, ::mp_bitcnt_t s)
5059 {
5060 const ::mp_limb_t l = n.m_limbs[0] & GMP_NUMB_MASK;
5061 if (s == 0u || l == 0u) {
5062 rop = n;
5063 return;
5064 }
5065 if (s >= unsigned(GMP_NUMB_BITS)) {
5066 // We are shifting by the limb's bit size or greater, the result will be zero.
5067 rop._mp_size = 0;
5068 rop.m_limbs[0] = 0u;
5069 return;
5070 }
5071 // Compute the result.
5072 const auto res = l >> s;
5073 // Size is the original one or zero.
5074 rop._mp_size = static_cast<int>(res != 0u) * n._mp_size;
5075 rop.m_limbs[0] = res;
5076 }
5077
5078 // 2-limb optimisation.
static_tdiv_q_2exp(static_int<2> & rop,const static_int<2> & n,::mp_bitcnt_t s)5079 inline void static_tdiv_q_2exp(static_int<2> &rop, const static_int<2> &n, ::mp_bitcnt_t s)
5080 {
5081 mpz_size_t asize = n._mp_size;
5082 if (s == 0u || asize == 0) {
5083 rop = n;
5084 return;
5085 }
5086 int sign = 1;
5087 if (asize < 0) {
5088 asize = -asize;
5089 sign = -1;
5090 }
5091 // If shift is too large, zero the result and return.
5092 static_assert(unsigned(GMP_NUMB_BITS) <= nl_max<unsigned>() / 2u, "Overflow error.");
5093 if (s >= 2u * unsigned(GMP_NUMB_BITS)) {
5094 rop._mp_size = 0;
5095 rop.m_limbs[0u] = 0u;
5096 rop.m_limbs[1u] = 0u;
5097 return;
5098 }
5099 if (s >= unsigned(GMP_NUMB_BITS)) {
5100 // NOTE: here the effective shift < GMP_NUMB_BITS, otherwise it would have been caught
5101 // in the check above.
5102 const auto lo = (n.m_limbs[1u] & GMP_NUMB_MASK) >> (s - unsigned(GMP_NUMB_BITS));
5103 // The size could be zero or +-1, depending
5104 // on the new content of m_limbs[0] and the previous
5105 // sign of _mp_size.
5106 rop._mp_size = static_cast<int>(lo != 0u) * sign;
5107 rop.m_limbs[0u] = lo;
5108 rop.m_limbs[1u] = 0u;
5109 return;
5110 }
5111 assert(s > 0u && s < unsigned(GMP_NUMB_BITS));
5112 // This represents the bits in hi that will be shifted down into lo.
5113 // We move them up so we can add tmp to the new lo to account for them.
5114 // NOTE: mask the final result to avoid spillover into potential nail bits.
5115 const auto tmp = ((n.m_limbs[1u] & GMP_NUMB_MASK) << (unsigned(GMP_NUMB_BITS) - s)) & GMP_NUMB_MASK;
5116 rop.m_limbs[0u] = ((n.m_limbs[0u] & GMP_NUMB_MASK) >> s) + tmp;
5117 rop.m_limbs[1u] = (n.m_limbs[1u] & GMP_NUMB_MASK) >> s;
5118 // The effective shift was less than 1 entire limb. The new asize must be the old one,
5119 // or one less than that.
5120 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5121 rop._mp_size = sign * (asize - ((rop.m_limbs[std::size_t(asize - 1)] & GMP_NUMB_MASK) == 0u));
5122 }
5123 } // namespace detail
5124
5125 // Ternary right shift.
5126 template <std::size_t SSize>
tdiv_q_2exp(integer<SSize> & rop,const integer<SSize> & n,::mp_bitcnt_t s)5127 inline integer<SSize> &tdiv_q_2exp(integer<SSize> &rop, const integer<SSize> &n, ::mp_bitcnt_t s)
5128 {
5129 const bool sn = n.is_static(), sr = rop.is_static();
5130 if (mppp_likely(sn)) {
5131 if (!sr) {
5132 rop.set_zero();
5133 }
5134 static_tdiv_q_2exp(rop._get_union().g_st(), n._get_union().g_st(), s);
5135 return rop;
5136 }
5137 if (sr) {
5138 rop._get_union().promote();
5139 }
5140 mpz_tdiv_q_2exp(&rop._get_union().g_dy(), n.get_mpz_view(), s);
5141 return rop;
5142 }
5143
5144 namespace detail
5145 {
5146
5147 // mpn implementation.
5148 template <std::size_t SSize>
static_cmp(const static_int<SSize> & n1,const static_int<SSize> & n2)5149 inline int static_cmp(const static_int<SSize> &n1, const static_int<SSize> &n2)
5150 {
5151 if (n1._mp_size < n2._mp_size) {
5152 return -1;
5153 }
5154 if (n2._mp_size < n1._mp_size) {
5155 return 1;
5156 }
5157 // The two sizes are equal, compare the absolute values.
5158 const auto asize = n1._mp_size >= 0 ? n1._mp_size : -n1._mp_size;
5159 if (asize) {
5160 // NOTE: reduce the value of the comparison to the [-1, 1] range, so that
5161 // if we need to negate it below we ensure not to run into overflows.
5162 const int cmp_abs
5163 = integral_sign(mpn_cmp(n1.m_limbs.data(), n2.m_limbs.data(), static_cast<::mp_size_t>(asize)));
5164 // If the values are non-negative, return the comparison of the absolute values, otherwise invert it.
5165 return (n1._mp_size >= 0) ? cmp_abs : -cmp_abs;
5166 }
5167 // Both operands are zero.
5168 // NOTE: we do this special casing in order to avoid calling mpn_cmp() on zero operands. It seems to
5169 // work, but the official GMP docs say one is not supposed to call mpn functions on zero operands.
5170 return 0;
5171 }
5172
5173 // 1-limb optimisation.
static_cmp(const static_int<1> & n1,const static_int<1> & n2)5174 inline int static_cmp(const static_int<1> &n1, const static_int<1> &n2)
5175 {
5176 if (n1._mp_size < n2._mp_size) {
5177 return -1;
5178 }
5179 if (n2._mp_size < n1._mp_size) {
5180 return 1;
5181 }
5182 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5183 int cmp_abs = (n1.m_limbs[0u] & GMP_NUMB_MASK) > (n2.m_limbs[0u] & GMP_NUMB_MASK);
5184 if (cmp_abs == 0) {
5185 cmp_abs = -static_cast<int>((n1.m_limbs[0u] & GMP_NUMB_MASK) < (n2.m_limbs[0u] & GMP_NUMB_MASK));
5186 }
5187 return (n1._mp_size >= 0) ? cmp_abs : -cmp_abs;
5188 }
5189
5190 // 2-limb optimisation.
static_cmp(const static_int<2> & n1,const static_int<2> & n2)5191 inline int static_cmp(const static_int<2> &n1, const static_int<2> &n2)
5192 {
5193 if (n1._mp_size < n2._mp_size) {
5194 return -1;
5195 }
5196 if (n2._mp_size < n1._mp_size) {
5197 return 1;
5198 }
5199 auto asize = n1._mp_size >= 0 ? n1._mp_size : -n1._mp_size;
5200 while (asize != 0) {
5201 --asize;
5202 if ((n1.m_limbs[std::size_t(asize)] & GMP_NUMB_MASK) > (n2.m_limbs[std::size_t(asize)] & GMP_NUMB_MASK)) {
5203 return (n1._mp_size >= 0) ? 1 : -1;
5204 }
5205 if ((n1.m_limbs[std::size_t(asize)] & GMP_NUMB_MASK) < (n2.m_limbs[std::size_t(asize)] & GMP_NUMB_MASK)) {
5206 return (n1._mp_size >= 0) ? -1 : 1;
5207 }
5208 }
5209 return 0;
5210 }
5211 } // namespace detail
5212
5213 // Comparison function.
5214 template <std::size_t SSize>
cmp(const integer<SSize> & op1,const integer<SSize> & op2)5215 inline int cmp(const integer<SSize> &op1, const integer<SSize> &op2)
5216 {
5217 const bool s1 = op1.is_static(), s2 = op2.is_static();
5218 if (mppp_likely(s1 && s2)) {
5219 return static_cmp(op1._get_union().g_st(), op2._get_union().g_st());
5220 }
5221 return mpz_cmp(op1.get_mpz_view(), op2.get_mpz_view());
5222 }
5223
5224 // Sign function.
5225 template <std::size_t SSize>
sgn(const integer<SSize> & n)5226 inline int sgn(const integer<SSize> &n)
5227 {
5228 return n.sgn();
5229 }
5230
5231 // Test if integer is odd.
5232 template <std::size_t SSize>
odd_p(const integer<SSize> & n)5233 inline bool odd_p(const integer<SSize> &n)
5234 {
5235 return n.odd_p();
5236 }
5237
5238 // Test if integer is even.
5239 template <std::size_t SSize>
even_p(const integer<SSize> & n)5240 inline bool even_p(const integer<SSize> &n)
5241 {
5242 return n.even_p();
5243 }
5244
5245 // Test if an integer is zero.
5246 template <std::size_t SSize>
is_zero(const integer<SSize> & n)5247 inline bool is_zero(const integer<SSize> &n)
5248 {
5249 return n.is_zero();
5250 }
5251
5252 // Test if an integer is equal to one.
5253 template <std::size_t SSize>
is_one(const integer<SSize> & n)5254 inline bool is_one(const integer<SSize> &n)
5255 {
5256 return n.is_one();
5257 }
5258
5259 // Test if an integer is equal to minus one.
5260 template <std::size_t SSize>
is_negative_one(const integer<SSize> & n)5261 inline bool is_negative_one(const integer<SSize> &n)
5262 {
5263 return n.is_negative_one();
5264 }
5265
5266 namespace detail
5267 {
5268
5269 // 1-limb implementation.
static_not_impl(static_int<1> & rop,const static_int<1> & op,mpz_size_t,int sign)5270 inline bool static_not_impl(static_int<1> &rop, const static_int<1> &op, mpz_size_t, int sign)
5271 {
5272 const ::mp_limb_t l = op.m_limbs[0] & GMP_NUMB_MASK;
5273 if (sign >= 0) {
5274 if (mppp_unlikely(l == GMP_NUMB_MAX)) {
5275 // NOTting would overflow the limb.
5276 return false;
5277 }
5278 rop._mp_size = -1;
5279 rop.m_limbs[0] = l + 1u;
5280 return true;
5281 }
5282 // Size can be zero or 1.
5283 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5284 rop._mp_size = l != 1u;
5285 rop.m_limbs[0] = l - 1u;
5286 return true;
5287 }
5288
5289 // 2-limbs implementation.
static_not_impl(static_int<2> & rop,const static_int<2> & op,mpz_size_t,int sign)5290 inline bool static_not_impl(static_int<2> &rop, const static_int<2> &op, mpz_size_t, int sign)
5291 {
5292 const ::mp_limb_t lo = op.m_limbs[0] & GMP_NUMB_MASK, hi = op.m_limbs[1] & GMP_NUMB_MASK;
5293 if (sign >= 0) {
5294 if (mppp_unlikely(lo == GMP_NUMB_MAX && hi == GMP_NUMB_MAX)) {
5295 // The static has the maximum possible value, NOT will overflow it.
5296 return false;
5297 }
5298 const ::mp_limb_t new_lo = (lo + 1u) & GMP_NUMB_MASK, new_hi = hi + unsigned(lo == GMP_NUMB_MAX);
5299 // asize can be -1 or -2, never zero.
5300 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5301 rop._mp_size = -1 - (new_hi != 0u);
5302 rop.m_limbs[0] = new_lo;
5303 rop.m_limbs[1] = new_hi;
5304 return true;
5305 }
5306 const ::mp_limb_t new_lo = (lo - 1u) & GMP_NUMB_MASK, new_hi = hi - unsigned(lo == 0u);
5307 // Size could be 0, 1 or 2.
5308 rop._mp_size = size_from_lohi(new_lo, new_hi);
5309 rop.m_limbs[0] = new_lo;
5310 rop.m_limbs[1] = new_hi;
5311 return true;
5312 }
5313
5314 // n-limbs implementation.
5315 template <std::size_t SSize>
static_not_impl(static_int<SSize> & rop,const static_int<SSize> & op,mpz_size_t asize,int sign)5316 inline bool static_not_impl(static_int<SSize> &rop, const static_int<SSize> &op, mpz_size_t asize, int sign)
5317 {
5318 auto data = op.m_limbs.data();
5319 if (sign >= 0) {
5320 if (mppp_unlikely(static_cast<mpz_size_t>(SSize) == asize && std::all_of(data, data + asize, [](::mp_limb_t l) {
5321 return (l & GMP_NUMB_MASK) == GMP_NUMB_MAX;
5322 }))) {
5323 // If the current size is the max possible static size, and the value
5324 // is the max possible value, then we will overflow.
5325 return false;
5326 }
5327 if (sign) {
5328 const auto cy
5329 = static_cast<mpz_size_t>(mpn_add_1(rop.m_limbs.data(), data, static_cast<::mp_size_t>(asize), 1));
5330 if (cy) {
5331 // If there's a carry, we'll need to write into the upper limb.
5332 assert(asize < static_cast<mpz_size_t>(SSize));
5333 rop.m_limbs[static_cast<std::size_t>(asize)] = 1;
5334 }
5335 rop._mp_size = -asize - cy;
5336 } else {
5337 // Special case zero, as mpn functions don't want zero operands.
5338 rop.m_limbs[0] = 1;
5339 rop._mp_size = -1;
5340 }
5341 return true;
5342 }
5343 mpn_sub_1(rop.m_limbs.data(), data, static_cast<::mp_size_t>(asize), 1);
5344 // Size will be the original asize minus possibly 1, if the original topmost limb
5345 // has become zero.
5346 rop._mp_size
5347 = asize - static_cast<mpz_size_t>((rop.m_limbs[static_cast<std::size_t>(asize - 1)] & GMP_NUMB_MASK) == 0u);
5348 return true;
5349 }
5350
5351 template <std::size_t SSize>
static_not(static_int<SSize> & rop,const static_int<SSize> & op)5352 inline bool static_not(static_int<SSize> &rop, const static_int<SSize> &op)
5353 {
5354 mpz_size_t asize = op._mp_size;
5355 int sign = asize != 0;
5356 if (asize < 0) {
5357 asize = -asize;
5358 sign = -1;
5359 }
5360 // NOTE: currently the implementation dispatches only on the static size: there's no need to
5361 // zero upper limbs as mpn functions are never used in case of optimised size.
5362 return static_not_impl(rop, op, asize, sign);
5363 }
5364 } // namespace detail
5365
5366 // Bitwise NOT.
5367 template <std::size_t SSize>
bitwise_not(integer<SSize> & rop,const integer<SSize> & op)5368 inline integer<SSize> &bitwise_not(integer<SSize> &rop, const integer<SSize> &op)
5369 {
5370 bool sr = rop.is_static();
5371 const bool s = op.is_static();
5372 if (mppp_likely(s)) {
5373 if (!sr) {
5374 rop.set_zero();
5375 sr = true;
5376 }
5377 if (mppp_likely(static_not(rop._get_union().g_st(), op._get_union().g_st()))) {
5378 return rop;
5379 }
5380 }
5381 if (sr) {
5382 rop._get_union().promote();
5383 }
5384 mpz_com(&rop._get_union().g_dy(), op.get_mpz_view());
5385 return rop;
5386 }
5387
5388 namespace detail
5389 {
5390
5391 // 1-limb implementation.
static_ior_impl(static_int<1> & rop,const static_int<1> & op1,const static_int<1> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5392 inline void static_ior_impl(static_int<1> &rop, const static_int<1> &op1, const static_int<1> &op2, mpz_size_t,
5393 mpz_size_t, int sign1, int sign2)
5394 {
5395 const ::mp_limb_t l1 = op1.m_limbs[0] & GMP_NUMB_MASK, l2 = op2.m_limbs[0] & GMP_NUMB_MASK;
5396 if (sign1 >= 0 && sign2 >= 0) {
5397 // The easy case: both are nonnegative.
5398 // NOTE: no need to mask, as we masked l1 and l2 already, and OR
5399 // won't turn on any upper bit.
5400 ::mp_limb_t ret = l1 | l2;
5401 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5402 rop._mp_size = ret != 0u;
5403 rop.m_limbs[0] = ret;
5404 return;
5405 }
5406 // NOTE: my understanding is the following:
5407 // - the size of the operands is considered to be their real bit size, rounded up to GMP_NUMB_BITS,
5408 // plus a phantom sign bit on top due to the fake two's complement representation for negative numbers.
5409 // We never need to represent explicitly this fake bit;
5410 // - the size of the result will be the max operand size.
5411 // For instance, for ORing 25 and -5 we start like this:
5412 // (0) ... 0 1 1 0 0 1 | <-- 25 (5 "real bits", plus a fake bit on top at index GMP_NUMB_BITS)
5413 // (0) ... 0 0 0 1 0 1 <-- 5
5414 // We do the two's complement of 5 to produce -5:
5415 // (0) ... 0 1 1 0 0 1 | <-- 25
5416 // (1) ... 1 1 1 0 1 1 <-- -5 (in two's complement)
5417 // ---------------
5418 // (1) ... 1 1 1 0 1 1
5419 // The result has the sign bit set, thus it's a negative number. We take again
5420 // the two's complement to get its absolute value:
5421 // (0) ... 0 0 0 1 0 1
5422 // So the final result is -5.
5423 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5424 // NOTE: at least 1 of the operands is strictly negative, so the result
5425 // has to be negative as well (because of ORing the sign bits).
5426 rop._mp_size = -1;
5427 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5428 switch (sign_mask) {
5429 case 1u:
5430 // op1 negative, op2 nonnegative.
5431 // NOTE: here we know that the values we are 2sc-ing are not zero
5432 // (they are strictly negative). The complement will turn on
5433 // the nail bits, which will remain turned on after the ORing.
5434 // The final 2sc will set them back to zero, so no need to mask.
5435 rop.m_limbs[0] = ~((~l1 + 1u) | l2) + 1u;
5436 break;
5437 case 2u:
5438 // op1 nonnegative, op2 negative.
5439 rop.m_limbs[0] = ~((~l2 + 1u) | l1) + 1u;
5440 break;
5441 case 3u:
5442 // Both negative.
5443 rop.m_limbs[0] = ~((~l1 + 1u) | (~l2 + 1u)) + 1u;
5444 break;
5445 }
5446 }
5447
5448 // Small helper to compute the two's complement of a nonzero 2-limbs static integer.
twosc(std::array<::mp_limb_t,2> & arr,::mp_limb_t lo,::mp_limb_t hi)5449 inline void twosc(std::array<::mp_limb_t, 2> &arr, ::mp_limb_t lo, ::mp_limb_t hi)
5450 {
5451 assert(hi != 0u || lo != 0u);
5452 arr[0] = (~lo + 1u) & GMP_NUMB_MASK;
5453 arr[1] = (~hi + unsigned(lo == 0u)) & GMP_NUMB_MASK;
5454 }
5455
5456 // 2-limbs implementation.
static_ior_impl(static_int<2> & rop,const static_int<2> & op1,const static_int<2> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5457 inline void static_ior_impl(static_int<2> &rop, const static_int<2> &op1, const static_int<2> &op2, mpz_size_t,
5458 mpz_size_t, int sign1, int sign2)
5459 {
5460 const ::mp_limb_t lo1 = (op1.m_limbs[0] & GMP_NUMB_MASK), hi1 = (op1.m_limbs[1] & GMP_NUMB_MASK),
5461 lo2 = (op2.m_limbs[0] & GMP_NUMB_MASK), hi2 = (op2.m_limbs[1] & GMP_NUMB_MASK);
5462 if (sign1 >= 0 && sign2 >= 0) {
5463 // The easy case: both are nonnegative.
5464 const ::mp_limb_t lo = lo1 | lo2, hi = hi1 | hi2;
5465 rop._mp_size = size_from_lohi(lo, hi);
5466 rop.m_limbs[0] = lo;
5467 rop.m_limbs[1] = hi;
5468 return;
5469 }
5470 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5471 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
5472 std::array<::mp_limb_t, 2> tmp1, tmp2;
5473 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5474 switch (sign_mask) {
5475 case 1u:
5476 // op1 negative, op2 nonnegative.
5477 twosc(tmp1, lo1, hi1);
5478 // NOTE: here lo2, hi2 and the 2 limbs in tmp1 have already
5479 // been masked for nail bits.
5480 twosc(rop.m_limbs, tmp1[0] | lo2, tmp1[1] | hi2);
5481 break;
5482 case 2u:
5483 // op1 nonnegative, op2 negative.
5484 twosc(tmp2, lo2, hi2);
5485 twosc(rop.m_limbs, tmp2[0] | lo1, tmp2[1] | hi1);
5486 break;
5487 case 3u:
5488 // Both negative.
5489 twosc(tmp1, lo1, hi1);
5490 twosc(tmp2, lo2, hi2);
5491 twosc(rop.m_limbs, tmp1[0] | tmp2[0], tmp1[1] | tmp2[1]);
5492 break;
5493 }
5494 // Size is -1 or -2: we could have 1-limb operands, and even with
5495 // 2-limbs operands, the final 2sc - in case of negative values - could
5496 // zero out the upper limb.
5497 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5498 rop._mp_size = -2 + (rop.m_limbs[1] == 0u);
5499 }
5500
5501 // Compute the two's complement of an n-limbs nonzero integer. The new size of
5502 // the integer will be returned.
twosc(::mp_limb_t * rop,const::mp_limb_t * sp,mpz_size_t n)5503 inline mpz_size_t twosc(::mp_limb_t *rop, const ::mp_limb_t *sp, mpz_size_t n)
5504 {
5505 // Make sure the size is nonzero, and the value is nonzero as well.
5506 assert(n > 0);
5507 assert(std::any_of(sp, sp + n, [](::mp_limb_t l) { return (l & GMP_NUMB_MASK) != 0u; }));
5508 // Create a copy so we can compare to the original value later.
5509 auto size = n;
5510 // Flip the bits.
5511 mpn_com(rop, sp, static_cast<::mp_size_t>(size));
5512 // Compute the new size.
5513 if ((rop[size - 1] & GMP_NUMB_MASK) == 0u) {
5514 --size;
5515 for (; size != 0 && (rop[size - 1] & GMP_NUMB_MASK) == 0u; --size) {
5516 }
5517 }
5518 // Add 1.
5519 if (size != 0) {
5520 // If rop is nonzero, use the mpn_add_1() primitive, storing the carry
5521 // and updating the size if necessary.
5522 if (mpn_add_1(rop, rop, size, 1) != 0u) {
5523 // This needs to hold as sp is nonzero: 2sc can never
5524 // overflow the highest limb.
5525 assert(size < n);
5526 rop[size++] = 1;
5527 }
5528 } else {
5529 // If rop is zero, we cannot use mpn functions, just set the value directly.
5530 rop[0] = 1;
5531 size = 1;
5532 }
5533 // NOTE: the new size cannot be greater than n, the only way for this
5534 // to be possible would be if the input was zero (but this is prevented)
5535 // by the top assert).
5536 assert(size <= n);
5537 return size;
5538 }
5539
5540 // mpn implementation.
5541 template <std::size_t SSize>
static_ior_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2)5542 inline void static_ior_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
5543 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2)
5544 {
5545 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
5546 // Handle zeroes.
5547 if (!sign1) {
5548 // NOTE: manual copy rather than assignment, to avoid
5549 // a few branches.
5550 rop._mp_size = op2._mp_size;
5551 copy_limbs(data2, data2 + asize2, rop.m_limbs.data());
5552 return;
5553 }
5554 if (!sign2) {
5555 rop._mp_size = op1._mp_size;
5556 copy_limbs(data1, data1 + asize1, rop.m_limbs.data());
5557 return;
5558 }
5559 // Make sure data1/asize1 refer to the largest operand.
5560 if (asize1 < asize2) {
5561 std::swap(data1, data2);
5562 std::swap(asize1, asize2);
5563 std::swap(sign1, sign2);
5564 }
5565 if (sign1 > 0 && sign2 > 0) {
5566 // The easy case: both are nonnegative.
5567 // Set the size.
5568 rop._mp_size = asize1;
5569 // Compute the ior.
5570 mpn_ior_n(rop.m_limbs.data(), data1, data2, static_cast<::mp_size_t>(asize2));
5571 // Copy extra limbs from data1.
5572 copy_limbs(data1 + asize2, data1 + asize1, rop.m_limbs.data() + asize2);
5573 return;
5574 }
5575 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5576 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
5577 std::array<::mp_limb_t, SSize> tmp1, tmp2;
5578 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5579 switch (sign_mask) {
5580 case 1u:
5581 // op1 negative, op2 nonnegative.
5582 twosc(tmp1.data(), data1, asize1);
5583 // NOTE: in all 3 cases, the mpn_ior_n() is done with the minimum size among the operands
5584 // (asize2). In this case, due to the twosc, the first most significant limbs in tmp1 might
5585 // be zero, but according to the mpn docs this is not a problem.
5586 mpn_ior_n(rop.m_limbs.data(), tmp1.data(), data2, static_cast<::mp_size_t>(asize2));
5587 // Copy over the remaining limbs from the largest operand.
5588 copy_limbs(tmp1.data() + asize2, tmp1.data() + asize1, rop.m_limbs.data() + asize2);
5589 // The final twosc. This will return the effective abs size, which we need to negate.
5590 rop._mp_size = -twosc(rop.m_limbs.data(), rop.m_limbs.data(), asize1);
5591 break;
5592 case 2u:
5593 // op1 nonnegative, op2 negative.
5594 twosc(tmp2.data(), data2, asize2);
5595 // NOTE: after the twosc, the limbs in tmp2 from asize2 to asize1 should be set
5596 // to all 1s. We don't need to actually do that: ORing op1 with these high all-1 limbs
5597 // produces all-1 limbs in the result, and the final twosc will flip them back to zero.
5598 mpn_ior_n(rop.m_limbs.data(), data1, tmp2.data(), static_cast<::mp_size_t>(asize2));
5599 rop._mp_size = -twosc(rop.m_limbs.data(), rop.m_limbs.data(), asize2);
5600 break;
5601 case 3u:
5602 twosc(tmp1.data(), data1, asize1);
5603 twosc(tmp2.data(), data2, asize2);
5604 mpn_ior_n(rop.m_limbs.data(), tmp1.data(), tmp2.data(), static_cast<::mp_size_t>(asize2));
5605 rop._mp_size = -twosc(rop.m_limbs.data(), rop.m_limbs.data(), asize2);
5606 break;
5607 }
5608 }
5609
5610 // The dispatching function for the static implementation.
5611 template <std::size_t SSize>
static_ior(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)5612 inline void static_ior(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
5613 {
5614 mpz_size_t asize1 = op1._mp_size, asize2 = op2._mp_size;
5615 int sign1 = asize1 != 0, sign2 = asize2 != 0;
5616 if (asize1 < 0) {
5617 asize1 = -asize1;
5618 sign1 = -1;
5619 }
5620 if (asize2 < 0) {
5621 asize2 = -asize2;
5622 sign2 = -1;
5623 }
5624 // NOTE: currently the implementation dispatches only on the static size: there's no need to
5625 // zero upper limbs as mpn functions are never used in case of optimised size.
5626 static_ior_impl(rop, op1, op2, asize1, asize2, sign1, sign2);
5627 }
5628 } // namespace detail
5629
5630 // Bitwise OR.
5631 template <std::size_t SSize>
bitwise_ior(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)5632 inline integer<SSize> &bitwise_ior(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
5633 {
5634 const bool sr = rop.is_static(), s1 = op1.is_static(), s2 = op2.is_static();
5635 if (mppp_likely(s1 && s2)) {
5636 if (!sr) {
5637 rop.set_zero();
5638 }
5639 static_ior(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st());
5640 return rop;
5641 }
5642 if (sr) {
5643 rop._get_union().promote();
5644 }
5645 mpz_ior(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
5646 return rop;
5647 }
5648
5649 namespace detail
5650 {
5651
5652 // 1-limb implementation.
static_and_impl(static_int<1> & rop,const static_int<1> & op1,const static_int<1> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5653 inline bool static_and_impl(static_int<1> &rop, const static_int<1> &op1, const static_int<1> &op2, mpz_size_t,
5654 mpz_size_t, int sign1, int sign2)
5655 {
5656 const ::mp_limb_t l1 = op1.m_limbs[0] & GMP_NUMB_MASK, l2 = op2.m_limbs[0] & GMP_NUMB_MASK;
5657 if (sign1 >= 0 && sign2 >= 0) {
5658 // The easy case: both are nonnegative.
5659 // NOTE: no need to mask, as we masked l1 and l2 already, and AND
5660 // won't turn on any upper bit.
5661 ::mp_limb_t ret = l1 & l2;
5662 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5663 rop._mp_size = ret != 0u;
5664 rop.m_limbs[0] = ret;
5665 return true;
5666 }
5667 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5668 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5669 switch (sign_mask) {
5670 case 1u: {
5671 // op1 negative, op2 nonnegative.
5672 // The result will be nonnegative, and it does not need to be masked:
5673 // nail bits will be switched off by ANDing with l2.
5674 const ::mp_limb_t ret = (~l1 + 1u) & l2;
5675 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5676 rop._mp_size = ret != 0u;
5677 rop.m_limbs[0] = ret;
5678 return true;
5679 }
5680 case 2u: {
5681 // op1 nonnegative, op2 negative.
5682 // This is the symmetric of above.
5683 const ::mp_limb_t ret = l1 & (~l2 + 1u);
5684 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5685 rop._mp_size = ret != 0u;
5686 rop.m_limbs[0] = ret;
5687 return true;
5688 }
5689 case 3u: {
5690 // Both negative. In this case, the result will be negative,
5691 // unless the ANDing of the 2scs results in zero: in that
5692 // case, we return failure as we need extra storage for the result.
5693 ::mp_limb_t ret = (~l1 + 1u) & (~l2 + 1u);
5694 // NOTE: need to mask here, as nail bits will have been flipped
5695 // by the NOTing above.
5696 if (mppp_unlikely(!(ret & GMP_NUMB_MASK))) {
5697 return false;
5698 }
5699 // The NOT here will flip back the nail bits, no need to mask.
5700 ret = ~ret + 1u;
5701 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5702 rop._mp_size = -(ret != 0u);
5703 rop.m_limbs[0] = ret;
5704 return true;
5705 }
5706 }
5707 // Keep the compiler happy.
5708 // LCOV_EXCL_START
5709 assert(false);
5710 return true;
5711 // LCOV_EXCL_STOP
5712 }
5713
5714 // 2-limbs implementation.
static_and_impl(static_int<2> & rop,const static_int<2> & op1,const static_int<2> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5715 inline bool static_and_impl(static_int<2> &rop, const static_int<2> &op1, const static_int<2> &op2, mpz_size_t,
5716 mpz_size_t, int sign1, int sign2)
5717 {
5718 const ::mp_limb_t lo1 = (op1.m_limbs[0] & GMP_NUMB_MASK), hi1 = (op1.m_limbs[1] & GMP_NUMB_MASK),
5719 lo2 = (op2.m_limbs[0] & GMP_NUMB_MASK), hi2 = (op2.m_limbs[1] & GMP_NUMB_MASK);
5720 if (sign1 >= 0 && sign2 >= 0) {
5721 // The easy case: both are nonnegative.
5722 const ::mp_limb_t lo = lo1 & lo2, hi = hi1 & hi2;
5723 rop._mp_size = size_from_lohi(lo, hi);
5724 rop.m_limbs[0] = lo;
5725 rop.m_limbs[1] = hi;
5726 return true;
5727 }
5728 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5729 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
5730 std::array<::mp_limb_t, 2> tmp1, tmp2;
5731 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5732 switch (sign_mask) {
5733 case 1u:
5734 // op1 negative, op2 nonnegative.
5735 twosc(tmp1, lo1, hi1);
5736 // NOTE: here lo2, hi2 and the 2 limbs in tmp1 have already
5737 // been masked for nail bits.
5738 rop.m_limbs[0] = tmp1[0] & lo2;
5739 rop.m_limbs[1] = tmp1[1] & hi2;
5740 rop._mp_size = size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
5741 return true;
5742 case 2u:
5743 // op1 nonnegative, op2 negative.
5744 twosc(tmp2, lo2, hi2);
5745 rop.m_limbs[0] = tmp2[0] & lo1;
5746 rop.m_limbs[1] = tmp2[1] & hi1;
5747 rop._mp_size = size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
5748 return true;
5749 case 3u: {
5750 // Both negative.
5751 twosc(tmp1, lo1, hi1);
5752 twosc(tmp2, lo2, hi2);
5753 const ::mp_limb_t new_lo = tmp1[0] & tmp2[0], new_hi = tmp1[1] & tmp2[1];
5754 if (mppp_unlikely(!new_lo && !new_hi)) {
5755 // When both operands are negative, we could end up in a situation
5756 // where, after the 2scs, the ANDing returns zero. In this case,
5757 // we need extra storage.
5758 return false;
5759 }
5760 twosc(rop.m_limbs, new_lo, new_hi);
5761 rop._mp_size = -size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
5762 return true;
5763 }
5764 }
5765 // LCOV_EXCL_START
5766 assert(false);
5767 return true;
5768 // LCOV_EXCL_STOP
5769 }
5770
5771 // Small helper to compute the abs size of a static int, knowing it must
5772 // be at most asize > 0.
5773 template <std::size_t SSize>
compute_static_int_asize(const static_int<SSize> & r,mpz_size_t asize)5774 inline mpz_size_t compute_static_int_asize(const static_int<SSize> &r, mpz_size_t asize)
5775 {
5776 assert(asize > 0);
5777 if (!(r.m_limbs[static_cast<std::size_t>(asize - 1)] & GMP_NUMB_MASK)) {
5778 --asize;
5779 for (; asize && !(r.m_limbs[static_cast<std::size_t>(asize - 1)] & GMP_NUMB_MASK); --asize) {
5780 }
5781 }
5782 return asize;
5783 }
5784
5785 // mpn implementation.
5786 template <std::size_t SSize>
static_and_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2)5787 inline bool static_and_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
5788 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2)
5789 {
5790 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
5791 // Handle zeroes.
5792 if (!sign1 || !sign2) {
5793 rop._mp_size = 0;
5794 return true;
5795 }
5796 // Make sure data1/asize1 refer to the largest operand.
5797 if (asize1 < asize2) {
5798 std::swap(data1, data2);
5799 std::swap(asize1, asize2);
5800 std::swap(sign1, sign2);
5801 }
5802 if (sign1 > 0 && sign2 > 0) {
5803 // The easy case: both are nonnegative.
5804 // Compute the and.
5805 mpn_and_n(rop.m_limbs.data(), data1, data2, static_cast<::mp_size_t>(asize2));
5806 // The asize will be at most asize2. Upper limbs could be zero due to the ANDing.
5807 rop._mp_size = compute_static_int_asize(rop, asize2);
5808 return true;
5809 }
5810 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5811 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
5812 std::array<::mp_limb_t, SSize> tmp1, tmp2, tmpr;
5813 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5814 switch (sign_mask) {
5815 case 1u:
5816 // op1 negative, op2 nonnegative.
5817 twosc(tmp1.data(), data1, asize1);
5818 // NOTE: in all 3 cases, the mpn_and_n() is done with the minimum size among the operands
5819 // (asize2). In this case, due to the twosc, the first most significant limbs in tmp1 might
5820 // be zero, but according to the mpn docs this is not a problem.
5821 mpn_and_n(rop.m_limbs.data(), tmp1.data(), data2, static_cast<::mp_size_t>(asize2));
5822 // NOTE: size cannot be larger than asize2, as all the limbs above that limit from op1
5823 // will be set to zero by the ANDing.
5824 rop._mp_size = compute_static_int_asize(rop, asize2);
5825 return true;
5826 case 2u:
5827 // op1 nonnegative, op2 negative.
5828 twosc(tmp2.data(), data2, asize2);
5829 // Do the AND.
5830 mpn_and_n(rop.m_limbs.data(), data1, tmp2.data(), static_cast<::mp_size_t>(asize2));
5831 // Copy over the upper limbs of op1 to rop: the limbs in tmp2 from asize2 to asize1
5832 // are (virtually) set to all 1s by the twosc, so ANDing with the corresponding limbs
5833 // in op1 means simply copying op1 over.
5834 copy_limbs(data1 + asize2, data1 + asize1, rop.m_limbs.data() + asize2);
5835 // Compute the final size. It can be at most asize1.
5836 rop._mp_size = compute_static_int_asize(rop, asize1);
5837 return true;
5838 case 3u:
5839 twosc(tmp1.data(), data1, asize1);
5840 twosc(tmp2.data(), data2, asize2);
5841 // Write in temp storage, as we might overflow and we don't want to spoil
5842 // rop in that case.
5843 mpn_and_n(tmpr.data(), tmp1.data(), tmp2.data(), static_cast<::mp_size_t>(asize2));
5844 // Copy over the upper limbs of op1 to rop (same as above). Non overlapping,
5845 // as we are only using local storage.
5846 copy_limbs_no(tmp1.data() + asize2, tmp1.data() + asize1, tmpr.data() + asize2);
5847 // Check for zero.
5848 if (mppp_unlikely(std::all_of(tmpr.data(), tmpr.data() + asize1,
5849 [](::mp_limb_t l) { return (l & GMP_NUMB_MASK) == 0u; }))) {
5850 return false;
5851 }
5852 rop._mp_size = -twosc(rop.m_limbs.data(), tmpr.data(), asize1);
5853 return true;
5854 }
5855 // LCOV_EXCL_START
5856 assert(false);
5857 return true;
5858 // LCOV_EXCL_STOP
5859 }
5860
5861 template <std::size_t SSize>
static_and(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)5862 inline bool static_and(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
5863 {
5864 mpz_size_t asize1 = op1._mp_size, asize2 = op2._mp_size;
5865 int sign1 = asize1 != 0, sign2 = asize2 != 0;
5866 if (asize1 < 0) {
5867 asize1 = -asize1;
5868 sign1 = -1;
5869 }
5870 if (asize2 < 0) {
5871 asize2 = -asize2;
5872 sign2 = -1;
5873 }
5874 // NOTE: currently the implementation dispatches only on the static size: there's no need to
5875 // zero upper limbs as mpn functions are never used in case of optimised size.
5876 return static_and_impl(rop, op1, op2, asize1, asize2, sign1, sign2);
5877 }
5878 } // namespace detail
5879
5880 // Bitwise AND.
5881 template <std::size_t SSize>
bitwise_and(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)5882 inline integer<SSize> &bitwise_and(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
5883 {
5884 const bool s1 = op1.is_static(), s2 = op2.is_static();
5885 bool sr = rop.is_static();
5886 if (mppp_likely(s1 && s2)) {
5887 if (!sr) {
5888 rop.set_zero();
5889 sr = true;
5890 }
5891 if (mppp_likely(static_and(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st()))) {
5892 return rop;
5893 }
5894 }
5895 if (sr) {
5896 rop._get_union().promote();
5897 }
5898 mpz_and(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
5899 return rop;
5900 }
5901
5902 namespace detail
5903 {
5904
5905 // 1-limb implementation.
static_xor_impl(static_int<1> & rop,const static_int<1> & op1,const static_int<1> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5906 inline bool static_xor_impl(static_int<1> &rop, const static_int<1> &op1, const static_int<1> &op2, mpz_size_t,
5907 mpz_size_t, int sign1, int sign2)
5908 {
5909 const ::mp_limb_t l1 = op1.m_limbs[0] & GMP_NUMB_MASK, l2 = op2.m_limbs[0] & GMP_NUMB_MASK;
5910 if (sign1 >= 0 && sign2 >= 0) {
5911 // The easy case: both are nonnegative.
5912 // NOTE: no need to mask, as we masked l1 and l2 already, and XOR
5913 // won't turn on any upper bit.
5914 ::mp_limb_t ret = l1 ^ l2;
5915 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5916 rop._mp_size = ret != 0u;
5917 rop.m_limbs[0] = ret;
5918 return false;
5919 }
5920 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5921 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5922 switch (sign_mask) {
5923 case 1u: {
5924 // op1 negative, op2 nonnegative. In this case, the result will be negative,
5925 // unless the XORing results in zero: in that
5926 // case, we return failure as we need extra storage for the result.
5927 ::mp_limb_t ret = (~l1 + 1u) ^ l2;
5928 // NOTE: need to mask here, as nail bits will have been flipped
5929 // by the NOTing above.
5930 if (mppp_unlikely(!(ret & GMP_NUMB_MASK))) {
5931 return false;
5932 }
5933 // The NOT here will flip back the nail bits, no need to mask.
5934 ret = ~ret + 1u;
5935 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5936 rop._mp_size = -(ret != 0u);
5937 rop.m_limbs[0] = ret;
5938 return true;
5939 }
5940 case 2u: {
5941 // Specular of the above.
5942 ::mp_limb_t ret = l1 ^ (~l2 + 1u);
5943 if (mppp_unlikely(!(ret & GMP_NUMB_MASK))) {
5944 return false;
5945 }
5946 ret = ~ret + 1u;
5947 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5948 rop._mp_size = -(ret != 0u);
5949 rop.m_limbs[0] = ret;
5950 return true;
5951 }
5952 case 3u: {
5953 // Both negative: the result will be nonnegative.
5954 // NOTE: the XOR will zero the nail bits, no need to mask.
5955 const ::mp_limb_t ret = (~l1 + 1u) ^ (~l2 + 1u);
5956 // NOLINTNEXTLINE(readability-implicit-bool-conversion)
5957 rop._mp_size = (ret != 0u);
5958 rop.m_limbs[0] = ret;
5959 return true;
5960 }
5961 }
5962 // LCOV_EXCL_START
5963 assert(false);
5964 return true;
5965 // LCOV_EXCL_STOP
5966 }
5967
5968 // 2-limbs implementation.
static_xor_impl(static_int<2> & rop,const static_int<2> & op1,const static_int<2> & op2,mpz_size_t,mpz_size_t,int sign1,int sign2)5969 inline bool static_xor_impl(static_int<2> &rop, const static_int<2> &op1, const static_int<2> &op2, mpz_size_t,
5970 mpz_size_t, int sign1, int sign2)
5971 {
5972 const ::mp_limb_t lo1 = (op1.m_limbs[0] & GMP_NUMB_MASK), hi1 = (op1.m_limbs[1] & GMP_NUMB_MASK),
5973 lo2 = (op2.m_limbs[0] & GMP_NUMB_MASK), hi2 = (op2.m_limbs[1] & GMP_NUMB_MASK);
5974 if (sign1 >= 0 && sign2 >= 0) {
5975 // The easy case: both are nonnegative.
5976 const ::mp_limb_t lo = lo1 ^ lo2, hi = hi1 ^ hi2;
5977 rop._mp_size = size_from_lohi(lo, hi);
5978 rop.m_limbs[0] = lo;
5979 rop.m_limbs[1] = hi;
5980 return true;
5981 }
5982 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
5983 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
5984 std::array<::mp_limb_t, 2> tmp1, tmp2;
5985 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
5986 switch (sign_mask) {
5987 case 1u: {
5988 // op1 negative, op2 nonnegative. Result will be negative, unless
5989 // it overflows.
5990 twosc(tmp1, lo1, hi1);
5991 // NOTE: here lo2, hi2 and the 2 limbs in tmp1 have already
5992 // been masked for nail bits. The XOR will not change nail bits.
5993 const ::mp_limb_t new_lo = tmp1[0] ^ lo2, new_hi = tmp1[1] ^ hi2;
5994 if (mppp_unlikely(!new_lo && !new_hi)) {
5995 return false;
5996 }
5997 twosc(rop.m_limbs, new_lo, new_hi);
5998 rop._mp_size = -size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
5999 return true;
6000 }
6001 case 2u: {
6002 // Mirror of the above.
6003 twosc(tmp2, lo2, hi2);
6004 const ::mp_limb_t new_lo = tmp2[0] ^ lo1, new_hi = tmp2[1] ^ hi1;
6005 if (mppp_unlikely(!new_lo && !new_hi)) {
6006 return false;
6007 }
6008 twosc(rop.m_limbs, new_lo, new_hi);
6009 rop._mp_size = -size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
6010 return true;
6011 }
6012 case 3u: {
6013 // Both negative.
6014 twosc(tmp1, lo1, hi1);
6015 twosc(tmp2, lo2, hi2);
6016 rop.m_limbs[0] = tmp1[0] ^ tmp2[0];
6017 rop.m_limbs[1] = tmp1[1] ^ tmp2[1];
6018 rop._mp_size = size_from_lohi(rop.m_limbs[0], rop.m_limbs[1]);
6019 return true;
6020 }
6021 }
6022 // LCOV_EXCL_START
6023 assert(false);
6024 return true;
6025 // LCOV_EXCL_STOP
6026 }
6027
6028 // mpn implementation.
6029 template <std::size_t SSize>
static_xor_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2,int sign1,int sign2)6030 inline bool static_xor_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
6031 mpz_size_t asize1, mpz_size_t asize2, int sign1, int sign2)
6032 {
6033 auto data1 = op1.m_limbs.data(), data2 = op2.m_limbs.data();
6034 // Handle zeroes.
6035 if (!sign1) {
6036 // NOTE: manual copy rather than assignment, to avoid
6037 // a few branches.
6038 rop._mp_size = op2._mp_size;
6039 copy_limbs(data2, data2 + asize2, rop.m_limbs.data());
6040 return true;
6041 }
6042 if (!sign2) {
6043 rop._mp_size = op1._mp_size;
6044 copy_limbs(data1, data1 + asize1, rop.m_limbs.data());
6045 return true;
6046 }
6047 // Make sure data1/asize1 refer to the largest operand.
6048 if (asize1 < asize2) {
6049 std::swap(data1, data2);
6050 std::swap(asize1, asize2);
6051 std::swap(sign1, sign2);
6052 }
6053 if (sign1 > 0 && sign2 > 0) {
6054 // The easy case: both are nonnegative.
6055 // Compute the xor.
6056 mpn_xor_n(rop.m_limbs.data(), data1, data2, static_cast<::mp_size_t>(asize2));
6057 // Limbs from asize2 to asize1 in op1 get copied as-is, as they are XORed with
6058 // zeroes from op2.
6059 copy_limbs(data1 + asize2, data1 + asize1, rop.m_limbs.data() + asize2);
6060 // The asize will be at most asize1. Upper limbs could be zero due to the XORing
6061 // (e.g., the values are identical).
6062 rop._mp_size = compute_static_int_asize(rop, asize1);
6063 return true;
6064 }
6065 const unsigned sign_mask = unsigned(sign1 < 0) + (unsigned(sign2 < 0) << 1);
6066 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
6067 std::array<::mp_limb_t, SSize> tmp1, tmp2, tmpr;
6068 // NOLINTNEXTLINE(hicpp-multiway-paths-covered)
6069 switch (sign_mask) {
6070 case 1u:
6071 // op1 negative, op2 nonnegative.
6072 twosc(tmp1.data(), data1, asize1);
6073 // NOTE: in all 3 cases, the mpn_xor_n() is done with the minimum size among the operands
6074 // (asize2). In this case, due to the twosc, the first most significant limbs in tmp1 might
6075 // be zero, but according to the mpn docs this is not a problem.
6076 mpn_xor_n(tmpr.data(), tmp1.data(), data2, static_cast<::mp_size_t>(asize2));
6077 // Copy over the limbs in tmp1 from asize2 to asize1.
6078 copy_limbs_no(tmp1.data() + asize2, tmp1.data() + asize1, tmpr.data() + asize2);
6079 // Check for zero.
6080 if (mppp_unlikely(std::all_of(tmpr.data(), tmpr.data() + asize1,
6081 [](::mp_limb_t l) { return (l & GMP_NUMB_MASK) == 0u; }))) {
6082 return false;
6083 }
6084 rop._mp_size = -twosc(rop.m_limbs.data(), tmpr.data(), asize1);
6085 return true;
6086 case 2u:
6087 // op1 nonnegative, op2 negative.
6088 twosc(tmp2.data(), data2, asize2);
6089 // Do the XOR.
6090 mpn_xor_n(tmpr.data(), data1, tmp2.data(), static_cast<::mp_size_t>(asize2));
6091 // The limbs in tmp2 from asize2 to asize1 have all been set to 1: XORing them
6092 // with the corresponding limbs in op1 means bit-flipping the limbs in op1.
6093 if (asize2 != asize1) {
6094 // NOTE: mpn functions require nonzero operands, so we need to branch here.
6095 mpn_com(tmpr.data() + asize2, data1 + asize2, asize1 - asize2);
6096 }
6097 // Check for zero.
6098 if (mppp_unlikely(std::all_of(tmpr.data(), tmpr.data() + asize1,
6099 [](::mp_limb_t l) { return (l & GMP_NUMB_MASK) == 0u; }))) {
6100 return false;
6101 }
6102 rop._mp_size = -twosc(rop.m_limbs.data(), tmpr.data(), asize1);
6103 return true;
6104 case 3u:
6105 twosc(tmp1.data(), data1, asize1);
6106 twosc(tmp2.data(), data2, asize2);
6107 mpn_xor_n(rop.m_limbs.data(), tmp1.data(), tmp2.data(), static_cast<::mp_size_t>(asize2));
6108 // Same as above, regarding the all-1 limbs in tmp2.
6109 if (asize2 != asize1) {
6110 // NOTE: mpn functions require nonzero operands, so we need to branch here.
6111 mpn_com(rop.m_limbs.data() + asize2, tmp1.data() + asize2, asize1 - asize2);
6112 }
6113 rop._mp_size = compute_static_int_asize(rop, asize1);
6114 return true;
6115 }
6116 // LCOV_EXCL_START
6117 assert(false);
6118 return true;
6119 // LCOV_EXCL_STOP
6120 }
6121
6122 template <std::size_t SSize>
static_xor(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)6123 inline bool static_xor(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
6124 {
6125 mpz_size_t asize1 = op1._mp_size, asize2 = op2._mp_size;
6126 int sign1 = asize1 != 0, sign2 = asize2 != 0;
6127 if (asize1 < 0) {
6128 asize1 = -asize1;
6129 sign1 = -1;
6130 }
6131 if (asize2 < 0) {
6132 asize2 = -asize2;
6133 sign2 = -1;
6134 }
6135 // NOTE: currently the implementation dispatches only on the static size: there's no need to
6136 // zero upper limbs as mpn functions are never used in case of optimised size.
6137 return static_xor_impl(rop, op1, op2, asize1, asize2, sign1, sign2);
6138 }
6139 } // namespace detail
6140
6141 // Bitwise XOR.
6142 template <std::size_t SSize>
bitwise_xor(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)6143 inline integer<SSize> &bitwise_xor(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
6144 {
6145 const bool s1 = op1.is_static(), s2 = op2.is_static();
6146 bool sr = rop.is_static();
6147 if (mppp_likely(s1 && s2)) {
6148 if (!sr) {
6149 rop.set_zero();
6150 sr = true;
6151 }
6152 if (mppp_likely(static_xor(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st()))) {
6153 return rop;
6154 }
6155 }
6156 if (sr) {
6157 rop._get_union().promote();
6158 }
6159 mpz_xor(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
6160 return rop;
6161 }
6162
6163 namespace detail
6164 {
6165
6166 // mpn/mpz implementation.
6167 template <std::size_t SSize>
static_gcd_impl(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2,mpz_size_t asize1,mpz_size_t asize2)6168 inline void static_gcd_impl(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2,
6169 mpz_size_t asize1, mpz_size_t asize2)
6170 {
6171 // NOTE: performance testing indicates that, even if mpz_gcd() does special casing
6172 // for zeroes and 1-limb values, it is still worth it to repeat the special casing
6173 // here. This is probably because if we manage to actually call mpn_gcd_1() here,
6174 // we avoid interacting with dynamically allocated memory below in the thread-local
6175 // object. If we ever start using mpn_gcd(), we will probably have to re-do a
6176 // performance analysis.
6177 //
6178 // Handle zeroes.
6179 if (!asize1) {
6180 // NOTE: we want the result to be positive, and to copy only the set limbs.
6181 rop._mp_size = asize2;
6182 copy_limbs(op2.m_limbs.data(), op2.m_limbs.data() + asize2, rop.m_limbs.data());
6183 return;
6184 }
6185 if (!asize2) {
6186 rop._mp_size = asize1;
6187 copy_limbs(op1.m_limbs.data(), op1.m_limbs.data() + asize1, rop.m_limbs.data());
6188 return;
6189 }
6190 // Special casing if an operand has asize 1.
6191 if (asize1 == 1) {
6192 rop._mp_size = 1;
6193 rop.m_limbs[0] = mpn_gcd_1(op2.m_limbs.data(), static_cast<::mp_size_t>(asize2), op1.m_limbs[0]);
6194 return;
6195 }
6196 if (asize2 == 1) {
6197 rop._mp_size = 1;
6198 rop.m_limbs[0] = mpn_gcd_1(op1.m_limbs.data(), static_cast<::mp_size_t>(asize1), op2.m_limbs[0]);
6199 return;
6200 }
6201 // General case, via mpz.
6202 // NOTE: there is an mpn_gcd() function, but it seems difficult to use. Apparently, and contrary to
6203 // what stated in the latest documentation, the mpn function requires odd operands and bit size
6204 // (not only limb size!) of the second operand not greater than the first. See for instance the old
6205 // documentation:
6206 // ftp://ftp.gnu.org/old-gnu/Manuals/gmp-3.1.1/html_chapter/gmp_9.html
6207 // Indeed, compiling GMP in debug mode and then trying to use the mpn function without respecting the above
6208 // results in assertion failures. For now let's keep it like this, the small operand cases are handled above
6209 // (partially) via mpn_gcd_1(), and in the future we can also think about binary GCD for 1/2 limbs optimisation.
6210 MPPP_MAYBE_TLS mpz_raii tmp;
6211 const auto v1 = op1.get_mpz_view();
6212 const auto v2 = op2.get_mpz_view();
6213 mpz_gcd(&tmp.m_mpz, &v1, &v2);
6214 // Copy over.
6215 rop._mp_size = tmp.m_mpz._mp_size;
6216 assert(rop._mp_size > 0);
6217 copy_limbs_no(tmp.m_mpz._mp_d, tmp.m_mpz._mp_d + rop._mp_size, rop.m_limbs.data());
6218 }
6219
6220 // 1-limb optimization.
static_gcd_impl(static_int<1> & rop,const static_int<1> & op1,const static_int<1> & op2,mpz_size_t asize1,mpz_size_t asize2)6221 inline void static_gcd_impl(static_int<1> &rop, const static_int<1> &op1, const static_int<1> &op2, mpz_size_t asize1,
6222 mpz_size_t asize2)
6223 {
6224 // Handle the special cases first.
6225 if (asize1 == 0) {
6226 // gcd(0, n) = abs(n). This also covers the convention
6227 // that gcd(0, 0) = 0.
6228 // NOTE: don't use the copy assignment operator of static_int,
6229 // as the size needs to be set to positive.
6230 rop._mp_size = asize2;
6231 rop.m_limbs = op2.m_limbs;
6232 return;
6233 }
6234 if (asize2 == 0) {
6235 // gcd(n, 0) = abs(n).
6236 // NOTE: op1 is not zero, its size can only be 1.
6237 rop._mp_size = 1;
6238 rop.m_limbs = op1.m_limbs;
6239 return;
6240 }
6241 // At this point, asize1 == asize2 == 1, and the result
6242 // will also have size 1.
6243 rop._mp_size = 1;
6244 // Compute the limb value.
6245 // NOTE: as an alternative, here we could have a custom binary GCD. See for
6246 // instance this implementation by Howard Hinnant using compiler intrinsics:
6247 // https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/8WB2Z9d7A0w
6248 // Testing however shows that mpn_gcd_1() is quite well optimised, so let's
6249 // use it and keep in mind the option about the binary GCD for the future.
6250 // NOTE: at the commit c146af86e416cf1348d0b3fc454600b47b523f4f we have an implementation
6251 // of the binary GCD, just in case.
6252 // NOTE: currently, the binary GCD is faster on Zen processors, but I believe
6253 // that is because the current GMP version does not have optimised GCD assembly for Zen.
6254 // We need to benchmark again when the next GMP version comes out. If the binary GCD
6255 // is still faster, we should consider using it instead of mpn_gcd_1(), as even on Intel
6256 // processors the binary GCD is only marginally slower than mpn_gcd_1().
6257 rop.m_limbs[0] = mpn_gcd_1(op1.m_limbs.data(), static_cast<::mp_size_t>(1), op2.m_limbs[0]);
6258 }
6259
6260 template <std::size_t SSize>
static_gcd(static_int<SSize> & rop,const static_int<SSize> & op1,const static_int<SSize> & op2)6261 inline void static_gcd(static_int<SSize> &rop, const static_int<SSize> &op1, const static_int<SSize> &op2)
6262 {
6263 static_gcd_impl(rop, op1, op2, std::abs(op1._mp_size), std::abs(op2._mp_size));
6264 if (SSize > 1u) {
6265 // If we used the generic function, zero the unused limbs on top (if necessary).
6266 // NOTE: as usual, potential of mpn/mpz use on optimised size (e.g., with 2-limb
6267 // static ints we are currently invoking mpz_gcd() - this could produce a result
6268 // with only the lower limb, and the higher limb is not zeroed out).
6269 rop.zero_unused_limbs();
6270 }
6271 }
6272 } // namespace detail
6273
6274 // GCD (ternary version).
6275 template <std::size_t SSize>
gcd(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)6276 inline integer<SSize> &gcd(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
6277 {
6278 const bool sr = rop.is_static(), s1 = op1.is_static(), s2 = op2.is_static();
6279 if (mppp_likely(s1 && s2)) {
6280 if (!sr) {
6281 rop.set_zero();
6282 }
6283 static_gcd(rop._get_union().g_st(), op1._get_union().g_st(), op2._get_union().g_st());
6284 return rop;
6285 }
6286 if (sr) {
6287 rop._get_union().promote();
6288 }
6289 mpz_gcd(&rop._get_union().g_dy(), op1.get_mpz_view(), op2.get_mpz_view());
6290 return rop;
6291 }
6292
6293 // GCD (binary version).
6294 template <std::size_t SSize>
gcd(const integer<SSize> & op1,const integer<SSize> & op2)6295 inline integer<SSize> gcd(const integer<SSize> &op1, const integer<SSize> &op2)
6296 {
6297 integer<SSize> retval;
6298 gcd(retval, op1, op2);
6299 return retval;
6300 }
6301
6302 namespace detail
6303 {
6304
6305 // The general-purpose implementation of ternary LCM.
6306 template <std::size_t SSize>
integer_ternary_lcm_generic(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)6307 inline void integer_ternary_lcm_generic(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
6308 {
6309 // Temporary working variable.
6310 MPPP_MAYBE_TLS integer<SSize> g;
6311
6312 // rop = (abs(op1) / gcd(op1, op2)) * abs(op2).
6313 gcd(g, op1, op2);
6314 divexact_gcd(g, op1, g);
6315 mul(g, g, op2);
6316 abs(rop, g);
6317 }
6318
6319 template <std::size_t SSize>
integer_ternary_lcm_impl(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)6320 inline void integer_ternary_lcm_impl(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
6321 {
6322 integer_ternary_lcm_generic(rop, op1, op2);
6323 }
6324
integer_ternary_lcm_impl(integer<1> & rop,const integer<1> & op1,const integer<1> & op2)6325 inline void integer_ternary_lcm_impl(integer<1> &rop, const integer<1> &op1, const integer<1> &op2)
6326 {
6327 if (mppp_likely(op1.is_static() && op2.is_static())) {
6328 // NOTE: the idea here is that if both op1 and op2
6329 // are static, we can use static integer primitives
6330 // directly in parts of the algorithm, thus avoiding
6331 // branching wrt the general-purpose primitives.
6332 integer<1> g;
6333
6334 auto &g_st = g._get_union().g_st();
6335 const auto &op1_st = op1._get_union().g_st();
6336 const auto &op2_st = op2._get_union().g_st();
6337
6338 // Use the static primitives for the first two
6339 // steps, as we are sure that no overflow
6340 // is possible.
6341 static_gcd(g_st, op1_st, op2_st);
6342 static_divexact_gcd(g_st, op1_st, g_st);
6343
6344 // Switch to the general-purpose multiplication,
6345 // as the result could overflow.
6346 // NOTE: perhaps using the static mul primitive
6347 // here could help us squeeze a bit extra performance,
6348 // at the price of duplicating the overflow handling
6349 // logic. Keep in mind for the future.
6350 mul(rop, g, op2);
6351 // Turn rop into abs(rop).
6352 // NOTE: this is always safe to do because
6353 // rop will consist of at most 2 limbs.
6354 rop._get_union().m_st._mp_size = std::abs(rop._get_union().m_st._mp_size);
6355 } else {
6356 // op1/op2 are not both statics. Run
6357 // the general-purpose implementation.
6358 integer_ternary_lcm_generic(rop, op1, op2);
6359 }
6360 }
6361
6362 } // namespace detail
6363
6364 // LCM (ternary version).
6365 template <std::size_t SSize>
lcm(integer<SSize> & rop,const integer<SSize> & op1,const integer<SSize> & op2)6366 inline integer<SSize> &lcm(integer<SSize> &rop, const integer<SSize> &op1, const integer<SSize> &op2)
6367 {
6368 // Handle the special case lcm(0, 0) == 0.
6369 if (mppp_unlikely(op1.is_zero() && op2.is_zero())) {
6370 rop.set_zero();
6371 } else {
6372 detail::integer_ternary_lcm_impl(rop, op1, op2);
6373 }
6374
6375 return rop;
6376 }
6377
6378 namespace detail
6379 {
6380
6381 // The general-purpose implementation of binary LCM.
6382 template <std::size_t SSize>
integer_binary_lcm_generic(const integer<SSize> & op1,const integer<SSize> & op2)6383 inline integer<SSize> integer_binary_lcm_generic(const integer<SSize> &op1, const integer<SSize> &op2)
6384 {
6385 // retval = (abs(op1) / gcd(op1, op2)) * abs(op2).
6386 auto retval = gcd(op1, op2);
6387 divexact_gcd(retval, op1, retval);
6388 mul(retval, retval, op2);
6389 abs(retval, retval);
6390
6391 return retval;
6392 }
6393
6394 template <std::size_t SSize>
integer_binary_lcm_impl(const integer<SSize> & op1,const integer<SSize> & op2)6395 inline integer<SSize> integer_binary_lcm_impl(const integer<SSize> &op1, const integer<SSize> &op2)
6396 {
6397 return integer_binary_lcm_generic(op1, op2);
6398 }
6399
integer_binary_lcm_impl(const integer<1> & op1,const integer<1> & op2)6400 inline integer<1> integer_binary_lcm_impl(const integer<1> &op1, const integer<1> &op2)
6401 {
6402 if (mppp_likely(op1.is_static() && op2.is_static())) {
6403 integer<1> retval;
6404
6405 auto &r_st = retval._get_union().g_st();
6406 const auto &op1_st = op1._get_union().g_st();
6407 const auto &op2_st = op2._get_union().g_st();
6408
6409 static_gcd(r_st, op1_st, op2_st);
6410 static_divexact_gcd(r_st, op1_st, r_st);
6411
6412 mul(retval, retval, op2);
6413 // Turn retval into abs(retval).
6414 // NOTE: this is always safe to do because
6415 // rop will consist of at most 2 limbs.
6416 retval._get_union().m_st._mp_size = std::abs(retval._get_union().m_st._mp_size);
6417
6418 return retval;
6419 } else {
6420 return integer_binary_lcm_generic(op1, op2);
6421 }
6422 }
6423
6424 } // namespace detail
6425
6426 // LCM (binary version).
6427 // NOTE: don't implement on top of the ternary primitives, a custom
6428 // implementation avoids the creation of an unnecessary temporary.
6429 template <std::size_t SSize>
lcm(const integer<SSize> & op1,const integer<SSize> & op2)6430 inline integer<SSize> lcm(const integer<SSize> &op1, const integer<SSize> &op2)
6431 {
6432 // Handle the special case lcm(0, 0) == 0.
6433 if (mppp_unlikely(op1.is_zero() && op2.is_zero())) {
6434 return integer<SSize>{};
6435 } else {
6436 return detail::integer_binary_lcm_impl(op1, op2);
6437 }
6438 }
6439
6440 // Factorial.
6441 template <std::size_t SSize>
fac_ui(integer<SSize> & rop,unsigned long n)6442 inline integer<SSize> &fac_ui(integer<SSize> &rop, unsigned long n)
6443 {
6444 // NOTE: we put a limit here because the GMP function just crashes and burns
6445 // if n is too large, and n does not even need to be that large.
6446 constexpr auto max_fac = 1000000ull;
6447 if (mppp_unlikely(n > max_fac)) {
6448 throw std::invalid_argument(
6449 "The value " + detail::to_string(n)
6450 + " is too large to be used as input for the factorial function (the maximum allowed value is "
6451 + detail::to_string(max_fac) + ")");
6452 }
6453 // NOTE: let's get through a static temporary and then assign it to the rop,
6454 // so that rop will be static/dynamic according to the size of tmp.
6455 MPPP_MAYBE_TLS detail::mpz_raii tmp;
6456 mpz_fac_ui(&tmp.m_mpz, n);
6457 return rop = &tmp.m_mpz;
6458 }
6459
6460 // Binomial coefficient (ternary version).
6461 template <std::size_t SSize>
bin_ui(integer<SSize> & rop,const integer<SSize> & n,unsigned long k)6462 inline integer<SSize> &bin_ui(integer<SSize> &rop, const integer<SSize> &n, unsigned long k)
6463 {
6464 MPPP_MAYBE_TLS detail::mpz_raii tmp;
6465 mpz_bin_ui(&tmp.m_mpz, n.get_mpz_view(), k);
6466 return rop = &tmp.m_mpz;
6467 }
6468
6469 // Binomial coefficient (binary version).
6470 template <std::size_t SSize>
bin_ui(const integer<SSize> & n,unsigned long k)6471 inline integer<SSize> bin_ui(const integer<SSize> &n, unsigned long k)
6472 {
6473 integer<SSize> retval;
6474 bin_ui(retval, n, k);
6475 return retval;
6476 }
6477
6478 namespace detail
6479 {
6480
6481 // These helpers are used here and in pow() as well.
6482 template <typename T, enable_if_t<is_integral<T>::value, int> = 0>
integer_exp_to_ulong(const T & exp)6483 inline unsigned long integer_exp_to_ulong(const T &exp)
6484 {
6485 #if !defined(__INTEL_COMPILER)
6486 assert(exp >= T(0));
6487 #endif
6488 // NOTE: make_unsigned_t<T> is T if T is already unsigned.
6489 // Don't use the make_unsigned() helper, as exp might be
6490 // unsigned already.
6491 static_assert(!std::is_same<T, bool>::value, "Cannot use the bool type in make_unsigned_t.");
6492 if (mppp_unlikely(static_cast<make_unsigned_t<T>>(exp) > nl_max<unsigned long>())) {
6493 throw std::overflow_error("Cannot convert the integral value " + detail::to_string(exp)
6494 + " to unsigned long: the value is too large");
6495 }
6496 return static_cast<unsigned long>(exp);
6497 }
6498
6499 // NOTE: special case bool, otherwise we end up invoking make_unsigned_t<bool> in the
6500 // previous overload, which is not well-defined:
6501 // https://en.cppreference.com/w/cpp/types/make_unsigned
integer_exp_to_ulong(bool exp)6502 inline unsigned long integer_exp_to_ulong(bool exp)
6503 {
6504
6505 return static_cast<unsigned long>(exp);
6506 }
6507
6508 template <std::size_t SSize>
integer_exp_to_ulong(const integer<SSize> & exp)6509 inline unsigned long integer_exp_to_ulong(const integer<SSize> &exp)
6510 {
6511 try {
6512 return static_cast<unsigned long>(exp);
6513 } catch (const std::overflow_error &) {
6514 // Rewrite the error message.
6515 throw std::overflow_error("Cannot convert the integral value " + exp.to_string()
6516 + " to unsigned long: the value is too large");
6517 }
6518 }
6519
6520 template <typename T, std::size_t SSize>
binomial_impl(const integer<SSize> & n,const T & k)6521 inline integer<SSize> binomial_impl(const integer<SSize> &n, const T &k)
6522 {
6523 // NOTE: here we re-use some helper member functions used in the implementation of pow().
6524 if (sgn(k) >= 0) {
6525 return bin_ui(n, integer_exp_to_ulong(k));
6526 }
6527 // This is the case k < 0, handled according to:
6528 // https://arxiv.org/abs/1105.3689/
6529 if (n.sgn() >= 0) {
6530 // n >= 0, k < 0.
6531 return integer<SSize>{};
6532 }
6533 // n < 0, k < 0.
6534 if (k <= n) {
6535 // The formula is: (-1)**(n-k) * binomial(-k-1,n-k).
6536 // Cache n-k.
6537 const integer<SSize> nmk{n - k};
6538 integer<SSize> tmp{k};
6539 ++tmp;
6540 tmp.neg();
6541 auto retval = bin_ui(tmp, integer_exp_to_ulong(nmk));
6542 if (nmk.odd_p()) {
6543 retval.neg();
6544 }
6545 return retval;
6546 }
6547 return integer<SSize>{};
6548 }
6549
6550 template <typename T, std::size_t SSize, enable_if_t<is_integral<T>::value, int> = 0>
binomial_impl(const T & n,const integer<SSize> & k)6551 inline integer<SSize> binomial_impl(const T &n, const integer<SSize> &k)
6552 {
6553 return binomial_impl(integer<SSize>{n}, k);
6554 }
6555 } // namespace detail
6556
6557 // Generic binomial coefficient.
6558 #if defined(MPPP_HAVE_CONCEPTS)
6559 template <typename T, typename U>
6560 requires integer_integral_op_types<T, U>
6561 inline auto
6562 #else
6563 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
6564 inline detail::integer_common_t<T, U>
6565 #endif
binomial(const T & n,const U & k)6566 binomial(const T &n, const U &k)
6567 {
6568 return detail::binomial_impl(n, k);
6569 }
6570
6571 namespace detail
6572 {
6573
6574 template <std::size_t SSize>
nextprime_impl(integer<SSize> & rop,const integer<SSize> & n)6575 inline void nextprime_impl(integer<SSize> &rop, const integer<SSize> &n)
6576 {
6577 MPPP_MAYBE_TLS mpz_raii tmp;
6578 mpz_nextprime(&tmp.m_mpz, n.get_mpz_view());
6579 rop = &tmp.m_mpz;
6580 }
6581 } // namespace detail
6582
6583 // Compute next prime number (binary version).
6584 template <std::size_t SSize>
nextprime(integer<SSize> & rop,const integer<SSize> & n)6585 inline integer<SSize> &nextprime(integer<SSize> &rop, const integer<SSize> &n)
6586 {
6587 // NOTE: nextprime on negative numbers always returns 2.
6588 detail::nextprime_impl(rop, n);
6589 return rop;
6590 }
6591
6592 // Compute next prime number (unary version).
6593 template <std::size_t SSize>
nextprime(const integer<SSize> & n)6594 inline integer<SSize> nextprime(const integer<SSize> &n)
6595 {
6596 integer<SSize> retval;
6597 detail::nextprime_impl(retval, n);
6598 return retval;
6599 }
6600
6601 // Test primality.
6602 template <std::size_t SSize>
probab_prime_p(const integer<SSize> & n,int reps=25)6603 inline int probab_prime_p(const integer<SSize> &n, int reps = 25)
6604 {
6605 return n.probab_prime_p(reps);
6606 }
6607
6608 // Ternary exponentiation.
6609 template <std::size_t SSize>
pow_ui(integer<SSize> & rop,const integer<SSize> & base,unsigned long exp)6610 inline integer<SSize> &pow_ui(integer<SSize> &rop, const integer<SSize> &base, unsigned long exp)
6611 {
6612 MPPP_MAYBE_TLS detail::mpz_raii tmp;
6613 mpz_pow_ui(&tmp.m_mpz, base.get_mpz_view(), exp);
6614 return rop = &tmp.m_mpz;
6615 }
6616
6617 // Binary exponentiation.
6618 template <std::size_t SSize>
pow_ui(const integer<SSize> & base,unsigned long exp)6619 inline integer<SSize> pow_ui(const integer<SSize> &base, unsigned long exp)
6620 {
6621 integer<SSize> retval;
6622 pow_ui(retval, base, exp);
6623 return retval;
6624 }
6625
6626 namespace detail
6627 {
6628
6629 // Various helpers for the implementation of pow().
6630 template <typename T, enable_if_t<is_integral<T>::value, int> = 0>
integer_exp_is_odd(const T & exp)6631 inline bool integer_exp_is_odd(const T &exp)
6632 {
6633 return (exp % T(2)) != T(0);
6634 }
6635
6636 template <std::size_t SSize>
integer_exp_is_odd(const integer<SSize> & exp)6637 inline bool integer_exp_is_odd(const integer<SSize> &exp)
6638 {
6639 return exp.odd_p();
6640 }
6641
6642 // Implementation of pow().
6643 // integer -- integral overload.
6644 template <typename T, std::size_t SSize,
6645 enable_if_t<disjunction<std::is_same<T, integer<SSize>>, is_integral<T>>::value, int> = 0>
pow_impl(const integer<SSize> & base,const T & exp)6646 inline integer<SSize> pow_impl(const integer<SSize> &base, const T &exp)
6647 {
6648 integer<SSize> rop;
6649 if (sgn(exp) >= 0) {
6650 pow_ui(rop, base, integer_exp_to_ulong(exp));
6651 } else if (mppp_unlikely(is_zero(base))) {
6652 // 0**-n is a division by zero.
6653 throw zero_division_error("Cannot raise zero to the negative power " + detail::to_string(exp));
6654 } else if (base.is_one()) {
6655 // 1**-n == 1.
6656 rop.set_one();
6657 } else if (base.is_negative_one()) {
6658 if (integer_exp_is_odd(exp)) {
6659 // (-1)**(-2n-1) == -1.
6660 rop.set_negative_one();
6661 } else {
6662 // (-1)**(-2n) == 1.
6663 rop.set_one();
6664 }
6665 } else {
6666 // m**-n == 1 / m**n == 0, truncated division.
6667 rop.set_zero();
6668 }
6669 return rop;
6670 }
6671
6672 // C++ integral -- integer overload.
6673 template <typename T, std::size_t SSize, enable_if_t<is_integral<T>::value, int> = 0>
pow_impl(const T & base,const integer<SSize> & exp)6674 inline integer<SSize> pow_impl(const T &base, const integer<SSize> &exp)
6675 {
6676 return pow_impl(integer<SSize>{base}, exp);
6677 }
6678
6679 // integer -- FP/complex overload.
6680 template <typename T, std::size_t SSize,
6681 enable_if_t<disjunction<std::is_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
pow_impl(const integer<SSize> & base,const T & exp)6682 inline T pow_impl(const integer<SSize> &base, const T &exp)
6683 {
6684 return std::pow(static_cast<T>(base), exp);
6685 }
6686
6687 // FP/complex -- integer overload.
6688 template <typename T, std::size_t SSize,
6689 enable_if_t<disjunction<std::is_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
pow_impl(const T & base,const integer<SSize> & exp)6690 inline T pow_impl(const T &base, const integer<SSize> &exp)
6691 {
6692 return std::pow(base, static_cast<T>(exp));
6693 }
6694
6695 } // namespace detail
6696
6697 // Generic binary exponentiation.
6698 template <typename T, typename U>
6699 #if defined(MPPP_HAVE_CONCEPTS)
6700 requires integer_op_types<T, U>
6701 inline auto
6702 #else
6703 inline detail::integer_common_t<T, U>
6704 #endif
pow(const T & base,const U & exp)6705 pow(const T &base, const U &exp)
6706 {
6707 return detail::pow_impl(base, exp);
6708 }
6709
6710 namespace detail
6711 {
6712
6713 // Implementation of sqrt.
6714 template <std::size_t SSize>
sqrt_impl(integer<SSize> & rop,const integer<SSize> & n)6715 inline void sqrt_impl(integer<SSize> &rop, const integer<SSize> &n)
6716 {
6717 if (mppp_unlikely(n._get_union().m_st._mp_size < 0)) {
6718 throw std::domain_error("Cannot compute the integer square root of the negative number " + n.to_string());
6719 }
6720 const bool sr = rop.is_static(), sn = n.is_static();
6721 if (mppp_likely(sn)) {
6722 if (!sr) {
6723 rop.set_zero();
6724 }
6725 auto &rs = rop._get_union().g_st();
6726 const auto &ns = n._get_union().g_st();
6727 // NOTE: we know n is not negative, from the check above.
6728 assert(ns._mp_size >= 0);
6729 // NOTE: cast this to the unsigned counterpart, this will make
6730 // the computation of new_size below more efficient.
6731 const auto size = static_cast<make_unsigned_t<mpz_size_t>>(ns._mp_size);
6732 if (mppp_likely(size)) {
6733 // In case of overlap we need to go through a tmp variable.
6734 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
6735 std::array<::mp_limb_t, SSize> tmp;
6736 const bool overlap = (&rs == &ns);
6737 const auto rs_data = rs.m_limbs.data(), out_ptr = overlap ? tmp.data() : rs_data;
6738 mpn_sqrtrem(out_ptr, nullptr, ns.m_limbs.data(), static_cast<::mp_size_t>(size));
6739 // Compute the size of the output (which is ceil(size / 2)).
6740 const auto new_size = size / 2u + size % 2u;
6741 assert(!new_size || (out_ptr[new_size - 1u] & GMP_NUMB_MASK));
6742 // Write out the result.
6743 rs._mp_size = static_cast<mpz_size_t>(new_size);
6744 if (overlap) {
6745 copy_limbs_no(out_ptr, out_ptr + new_size, rs_data);
6746 }
6747 // Clear out unused limbs, if needed.
6748 rs.zero_upper_limbs(static_cast<std::size_t>(new_size));
6749 } else {
6750 // Special casing for zero.
6751 rs._mp_size = 0;
6752 rs.zero_upper_limbs(0);
6753 }
6754 } else {
6755 if (sr) {
6756 rop.promote();
6757 }
6758 mpz_sqrt(&rop._get_union().g_dy(), n.get_mpz_view());
6759 }
6760 }
6761 } // namespace detail
6762
6763 // Binary sqrt.
6764 template <std::size_t SSize>
sqrt(integer<SSize> & rop,const integer<SSize> & n)6765 inline integer<SSize> &sqrt(integer<SSize> &rop, const integer<SSize> &n)
6766 {
6767 detail::sqrt_impl(rop, n);
6768 return rop;
6769 }
6770
6771 // Unary sqrt.
6772 template <std::size_t SSize>
sqrt(const integer<SSize> & n)6773 inline integer<SSize> sqrt(const integer<SSize> &n)
6774 {
6775 integer<SSize> retval;
6776 detail::sqrt_impl(retval, n);
6777 return retval;
6778 }
6779
6780 namespace detail
6781 {
6782
6783 // Static sqrtrem implementation.
6784 template <std::size_t SSize>
static_sqrtrem(static_int<SSize> & rops,static_int<SSize> & rems,const static_int<SSize> & ns)6785 inline void static_sqrtrem(static_int<SSize> &rops, static_int<SSize> &rems, const static_int<SSize> &ns)
6786 {
6787 // NOTE: we require non-negative n (this is checked in sqrtrem()).
6788 assert(ns._mp_size >= 0);
6789 // NOTE: cast this to the unsigned counterpart, this will make
6790 // the computation of rop_size below more efficient.
6791 const auto size = static_cast<make_unsigned_t<mpz_size_t>>(ns._mp_size);
6792 if (mppp_likely(size)) {
6793 // NOTE: rop and n must be separate. rem and n can coincide. See:
6794 // https://gmplib.org/manual/Low_002dlevel-Functions.html
6795 // In case of overlap of rop and n, we need to go through a tmp variable.
6796 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
6797 std::array<::mp_limb_t, SSize> tmp;
6798 const bool overlap = (&rops == &ns);
6799 const auto rops_data = rops.m_limbs.data(), out_ptr = overlap ? tmp.data() : rops_data,
6800 rems_data = rems.m_limbs.data();
6801 // Do the computation. The function will return the size of the remainder.
6802 const auto rem_size = mpn_sqrtrem(out_ptr, rems_data, ns.m_limbs.data(), static_cast<::mp_size_t>(size));
6803 // Compute the size of the output (which is ceil(size / 2)).
6804 const auto rop_size = size / 2u + size % 2u;
6805 assert(!rop_size || (out_ptr[rop_size - 1u] & GMP_NUMB_MASK));
6806 // Write out the result, clearing the unused limbs.
6807 rops._mp_size = static_cast<mpz_size_t>(rop_size);
6808 if (overlap) {
6809 copy_limbs_no(out_ptr, out_ptr + rop_size, rops_data);
6810 }
6811 rops.zero_upper_limbs(static_cast<std::size_t>(rop_size));
6812 rems._mp_size = static_cast<mpz_size_t>(rem_size);
6813 rems.zero_upper_limbs(static_cast<std::size_t>(rem_size));
6814 } else {
6815 // Special casing for zero.
6816 rops._mp_size = 0;
6817 rops.zero_upper_limbs(0);
6818 rems._mp_size = 0;
6819 rems.zero_upper_limbs(0);
6820 }
6821 }
6822
6823 } // namespace detail
6824
6825 // sqrt with remainder.
6826 template <std::size_t SSize>
sqrtrem(integer<SSize> & rop,integer<SSize> & rem,const integer<SSize> & n)6827 inline void sqrtrem(integer<SSize> &rop, integer<SSize> &rem, const integer<SSize> &n)
6828 {
6829 if (mppp_unlikely(&rop == &rem)) {
6830 throw std::invalid_argument("When performing an integer square root with remainder, the result 'rop' and the "
6831 "remainder 'rem' must be distinct objects");
6832 }
6833 if (mppp_unlikely(n.sgn() == -1)) {
6834 throw zero_division_error("Cannot compute the integer square root with remainder of the negative number "
6835 + n.to_string());
6836 }
6837 const bool srop = rop.is_static(), srem = rem.is_static(), ns = n.is_static();
6838 if (mppp_likely(ns)) {
6839 if (!srop) {
6840 rop.set_zero();
6841 }
6842 if (!srem) {
6843 rem.set_zero();
6844 }
6845 // NOTE: sqrtrem() can never fail.
6846 static_sqrtrem(rop._get_union().g_st(), rem._get_union().g_st(), n._get_union().g_st());
6847 } else {
6848 if (srop) {
6849 rop._get_union().promote();
6850 }
6851 if (srem) {
6852 rem._get_union().promote();
6853 }
6854 mpz_sqrtrem(&rop._get_union().g_dy(), &rem._get_union().g_dy(), n.get_mpz_view());
6855 }
6856 }
6857
6858 // Detect perfect square.
6859 template <std::size_t SSize>
perfect_square_p(const integer<SSize> & n)6860 inline bool perfect_square_p(const integer<SSize> &n)
6861 {
6862 const auto &u = n._get_union();
6863 // NOTE: the size is part of the common initial sequence.
6864 const auto size = u.m_st._mp_size;
6865 if (mppp_likely(size > 0)) {
6866 // n is positive. Extract a pointer to the limbs
6867 // and call the mpn function.
6868 // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
6869 const mp_limb_t *ptr;
6870 if (mppp_likely(n.is_static())) {
6871 ptr = u.g_st().m_limbs.data();
6872 } else {
6873 ptr = u.g_dy()._mp_d;
6874 }
6875 // NOTE: as usual, we assume that we can freely cast any valid mpz_size_t to
6876 // mp_size_t when calling mpn functions.
6877 return mpn_perfect_square_p(ptr, static_cast<::mp_size_t>(size)) != 0;
6878 } else {
6879 // n is zero or negative. It is a perfect square
6880 // only if zero.
6881 return size == 0;
6882 }
6883 }
6884
6885 // m-th root, ternary version.
6886 template <std::size_t SSize>
root(integer<SSize> & rop,const integer<SSize> & n,unsigned long m)6887 inline bool root(integer<SSize> &rop, const integer<SSize> &n, unsigned long m)
6888 {
6889 if (mppp_unlikely(!m)) {
6890 throw std::domain_error("Cannot compute the integer m-th root of an integer if m is zero");
6891 }
6892 if (mppp_unlikely(!(m % 2u) && n.sgn() == -1)) {
6893 throw std::domain_error("Cannot compute the integer root of degree " + std::to_string(m)
6894 + " of the negative number " + n.to_string());
6895 }
6896 MPPP_MAYBE_TLS detail::mpz_raii tmp;
6897 const auto ret = mpz_root(&tmp.m_mpz, n.get_mpz_view(), m);
6898 rop = &tmp.m_mpz;
6899 return ret != 0;
6900 }
6901
6902 // m-th root, binary version.
6903 template <std::size_t SSize>
root(const integer<SSize> & n,unsigned long m)6904 inline integer<SSize> root(const integer<SSize> &n, unsigned long m)
6905 {
6906 integer<SSize> retval;
6907 root(retval, n, m);
6908 return retval;
6909 }
6910
6911 // m-th root with remainder.
6912 template <std::size_t SSize>
rootrem(integer<SSize> & rop,integer<SSize> & rem,const integer<SSize> & n,unsigned long m)6913 inline void rootrem(integer<SSize> &rop, integer<SSize> &rem, const integer<SSize> &n, unsigned long m)
6914 {
6915 if (mppp_unlikely(!m)) {
6916 throw std::domain_error("Cannot compute the integer m-th root with remainder of an integer if m is zero");
6917 }
6918 if (mppp_unlikely(!(m % 2u) && n.sgn() == -1)) {
6919 throw std::domain_error("Cannot compute the integer root with remainder of degree " + std::to_string(m)
6920 + " of the negative number " + n.to_string());
6921 }
6922 MPPP_MAYBE_TLS detail::mpz_raii tmp_rop;
6923 MPPP_MAYBE_TLS detail::mpz_raii tmp_rem;
6924 mpz_rootrem(&tmp_rop.m_mpz, &tmp_rem.m_mpz, n.get_mpz_view(), m);
6925 rop = &tmp_rop.m_mpz;
6926 rem = &tmp_rem.m_mpz;
6927 }
6928
6929 // Detect perfect power.
6930 template <std::size_t SSize>
perfect_power_p(const integer<SSize> & n)6931 inline bool perfect_power_p(const integer<SSize> &n)
6932 {
6933 return mpz_perfect_power_p(n.get_mpz_view()) != 0;
6934 }
6935
6936 namespace detail
6937 {
6938
6939 // Helper to get the base from a stream's flags.
stream_flags_to_base(std::ios_base::fmtflags flags)6940 inline int stream_flags_to_base(std::ios_base::fmtflags flags)
6941 {
6942 switch (flags & std::ios_base::basefield) {
6943 case std::ios_base::dec:
6944 return 10;
6945 case std::ios_base::hex:
6946 return 16;
6947 case std::ios_base::oct:
6948 return 8;
6949 default:
6950 // NOTE: in case more than one base
6951 // flag is set, or no base flags are set,
6952 // just use 10.
6953 return 10;
6954 }
6955 }
6956
6957 // Helper to get the fill type from a stream's flags.
stream_flags_to_fill(std::ios_base::fmtflags flags)6958 inline int stream_flags_to_fill(std::ios_base::fmtflags flags)
6959 {
6960 switch (flags & std::ios_base::adjustfield) {
6961 case std::ios_base::left:
6962 return 1;
6963 case std::ios_base::right:
6964 return 2;
6965 case std::ios_base::internal:
6966 return 3;
6967 default:
6968 // NOTE: assume right fill if no fill is set,
6969 // or if multiple values are set.
6970 return 2;
6971 }
6972 }
6973
6974 MPPP_DLL_PUBLIC std::ostream &integer_stream_operator_impl(std::ostream &, const mpz_struct_t *, int);
6975
6976 } // namespace detail
6977
6978 // Output stream operator.
6979 template <std::size_t SSize>
operator <<(std::ostream & os,const integer<SSize> & n)6980 inline std::ostream &operator<<(std::ostream &os, const integer<SSize> &n)
6981 {
6982 return detail::integer_stream_operator_impl(os, n.get_mpz_view(), n.sgn());
6983 }
6984
6985 // Binary size.
6986 template <std::size_t SSize>
binary_size(const integer<SSize> & n)6987 inline std::size_t binary_size(const integer<SSize> &n)
6988 {
6989 return n.binary_size();
6990 }
6991
6992 // Save in binary format.
6993 template <std::size_t SSize, typename T>
binary_save(const integer<SSize> & n,T && dest)6994 inline auto binary_save(const integer<SSize> &n, T &&dest) -> decltype(n.binary_save(std::forward<T>(dest)))
6995 {
6996 return n.binary_save(std::forward<T>(dest));
6997 }
6998
6999 // Load in binary format.
7000 template <std::size_t SSize, typename T>
binary_load(integer<SSize> & n,T && src)7001 inline auto binary_load(integer<SSize> &n, T &&src) -> decltype(n.binary_load(std::forward<T>(src)))
7002 {
7003 return n.binary_load(std::forward<T>(src));
7004 }
7005
7006 // Hash value.
7007 template <std::size_t SSize>
hash(const integer<SSize> & n)7008 inline std::size_t hash(const integer<SSize> &n)
7009 {
7010 const detail::mpz_size_t size = n._get_union().m_st._mp_size;
7011 const std::size_t asize
7012 = size >= 0 ? static_cast<std::size_t>(size) : static_cast<std::size_t>(detail::nint_abs(size));
7013 const ::mp_limb_t *ptr
7014 = n._get_union().is_static() ? n._get_union().g_st().m_limbs.data() : n._get_union().g_dy()._mp_d;
7015 // Init the retval as the signed size.
7016 auto retval = static_cast<std::size_t>(size);
7017 // Combine the limbs.
7018 for (std::size_t i = 0; i < asize; ++i) {
7019 // The hash combiner. This is lifted directly from Boost. See also:
7020 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
7021 retval ^= (ptr[i] & GMP_NUMB_MASK) + std::size_t(0x9e3779b9ul) + (retval << 6) + (retval >> 2);
7022 }
7023 return retval;
7024 }
7025
7026 // Free the caches.
7027 MPPP_DLL_PUBLIC void free_integer_caches();
7028
7029 namespace detail
7030 {
7031
7032 // Dispatching for the binary addition operator.
7033 template <std::size_t SSize>
dispatch_binary_add(const integer<SSize> & op1,const integer<SSize> & op2)7034 inline integer<SSize> dispatch_binary_add(const integer<SSize> &op1, const integer<SSize> &op2)
7035 {
7036 integer<SSize> retval;
7037 add(retval, op1, op2);
7038 return retval;
7039 }
7040
7041 // NOTE: use the add_si/add_ui functions when adding to C++ integrals.
7042 template <std::size_t SSize, typename T, enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
dispatch_binary_add(const integer<SSize> & op1,T n)7043 inline integer<SSize> dispatch_binary_add(const integer<SSize> &op1, T n)
7044 {
7045 integer<SSize> retval;
7046 add_ui(retval, op1, n);
7047 return retval;
7048 }
7049
7050 template <std::size_t SSize, typename T, enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
dispatch_binary_add(const integer<SSize> & op1,T n)7051 inline integer<SSize> dispatch_binary_add(const integer<SSize> &op1, T n)
7052 {
7053 integer<SSize> retval;
7054 add_si(retval, op1, n);
7055 return retval;
7056 }
7057
7058 template <std::size_t SSize, typename T, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_add(T n,const integer<SSize> & op2)7059 inline integer<SSize> dispatch_binary_add(T n, const integer<SSize> &op2)
7060 {
7061 return dispatch_binary_add(op2, n);
7062 }
7063
7064 template <std::size_t SSize, typename T,
7065 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_add(const integer<SSize> & op1,const T & x)7066 inline T dispatch_binary_add(const integer<SSize> &op1, const T &x)
7067 {
7068 return static_cast<T>(op1) + x;
7069 }
7070
7071 template <std::size_t SSize, typename T,
7072 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_add(const T & x,const integer<SSize> & op2)7073 inline T dispatch_binary_add(const T &x, const integer<SSize> &op2)
7074 {
7075 return dispatch_binary_add(op2, x);
7076 }
7077
7078 // Dispatching for in-place add.
7079 template <std::size_t SSize>
dispatch_in_place_add(integer<SSize> & retval,const integer<SSize> & n)7080 inline void dispatch_in_place_add(integer<SSize> &retval, const integer<SSize> &n)
7081 {
7082 add(retval, retval, n);
7083 }
7084
7085 template <std::size_t SSize, typename T, enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
dispatch_in_place_add(integer<SSize> & retval,const T & n)7086 inline void dispatch_in_place_add(integer<SSize> &retval, const T &n)
7087 {
7088 add_ui(retval, retval, n);
7089 }
7090
7091 template <std::size_t SSize, typename T, enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
dispatch_in_place_add(integer<SSize> & retval,const T & n)7092 inline void dispatch_in_place_add(integer<SSize> &retval, const T &n)
7093 {
7094 add_si(retval, retval, n);
7095 }
7096
7097 template <std::size_t SSize, typename T,
7098 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_add(integer<SSize> & retval,const T & x)7099 inline void dispatch_in_place_add(integer<SSize> &retval, const T &x)
7100 {
7101 retval = static_cast<T>(retval) + x;
7102 }
7103
7104 template <typename T, std::size_t SSize,
7105 enable_if_t<disjunction<is_cpp_arithmetic<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_add(T & rop,const integer<SSize> & op)7106 inline void dispatch_in_place_add(T &rop, const integer<SSize> &op)
7107 {
7108 rop = static_cast<T>(rop + op);
7109 }
7110
7111 } // namespace detail
7112
7113 // Identity operator.
7114 template <std::size_t SSize>
operator +(const integer<SSize> & n)7115 inline integer<SSize> operator+(const integer<SSize> &n)
7116 {
7117 // NOTE: here potentially we could avoid a copy via either
7118 // a universal reference or maybe passing by copy n and then
7119 // moving in. Not sure how critical this is. Same in the negated
7120 // copy operator.
7121 return n;
7122 }
7123
7124 // Binary addition.
7125 template <typename T, typename U>
7126 #if defined(MPPP_HAVE_CONCEPTS)
7127 requires integer_op_types<T, U>
7128 inline auto
7129 #else
7130 inline detail::integer_common_t<T, U>
7131 #endif
operator +(const T & op1,const U & op2)7132 operator+(const T &op1, const U &op2)
7133 {
7134 return detail::dispatch_binary_add(op1, op2);
7135 }
7136
7137 // In-place addition operator.
7138 #if defined(MPPP_HAVE_CONCEPTS)
7139 template <typename T, typename U>
7140 requires integer_op_types<T, U>
7141 #else
7142 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7143 #endif
operator +=(T & rop,const U & op)7144 inline T &operator+=(T &rop, const U &op)
7145 {
7146 detail::dispatch_in_place_add(rop, op);
7147 return rop;
7148 }
7149
7150 // Prefix increment.
7151 template <std::size_t SSize>
operator ++(integer<SSize> & n)7152 inline integer<SSize> &operator++(integer<SSize> &n)
7153 {
7154 add_ui(n, n, 1u);
7155 return n;
7156 }
7157
7158 // Suffix increment.
7159 template <std::size_t SSize>
operator ++(integer<SSize> & n,int)7160 inline integer<SSize> operator++(integer<SSize> &n, int)
7161 {
7162 auto retval(n);
7163 ++n;
7164 return retval;
7165 }
7166
7167 namespace detail
7168 {
7169
7170 // Dispatching for the binary subtraction operator.
7171 template <std::size_t SSize>
dispatch_binary_sub(const integer<SSize> & op1,const integer<SSize> & op2)7172 inline integer<SSize> dispatch_binary_sub(const integer<SSize> &op1, const integer<SSize> &op2)
7173 {
7174 integer<SSize> retval;
7175 sub(retval, op1, op2);
7176 return retval;
7177 }
7178
7179 template <typename T, std::size_t SSize, enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
dispatch_binary_sub(const integer<SSize> & op1,T n)7180 inline integer<SSize> dispatch_binary_sub(const integer<SSize> &op1, T n)
7181 {
7182 integer<SSize> retval;
7183 sub_ui(retval, op1, n);
7184 return retval;
7185 }
7186
7187 template <typename T, std::size_t SSize, enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
dispatch_binary_sub(const integer<SSize> & op1,T n)7188 inline integer<SSize> dispatch_binary_sub(const integer<SSize> &op1, T n)
7189 {
7190 integer<SSize> retval;
7191 sub_si(retval, op1, n);
7192 return retval;
7193 }
7194
7195 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_sub(T n,const integer<SSize> & op2)7196 inline integer<SSize> dispatch_binary_sub(T n, const integer<SSize> &op2)
7197 {
7198 auto retval = dispatch_binary_sub(op2, n);
7199 retval.neg();
7200 return retval;
7201 }
7202
7203 template <typename T, std::size_t SSize,
7204 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_sub(const integer<SSize> & op1,const T & x)7205 inline T dispatch_binary_sub(const integer<SSize> &op1, const T &x)
7206 {
7207 return static_cast<T>(op1) - x;
7208 }
7209
7210 template <typename T, std::size_t SSize,
7211 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_sub(const T & x,const integer<SSize> & op2)7212 inline T dispatch_binary_sub(const T &x, const integer<SSize> &op2)
7213 {
7214 return -dispatch_binary_sub(op2, x);
7215 }
7216
7217 // Dispatching for in-place sub.
7218 template <std::size_t SSize>
dispatch_in_place_sub(integer<SSize> & retval,const integer<SSize> & n)7219 inline void dispatch_in_place_sub(integer<SSize> &retval, const integer<SSize> &n)
7220 {
7221 sub(retval, retval, n);
7222 }
7223
7224 template <typename T, std::size_t SSize, enable_if_t<is_cpp_unsigned_integral<T>::value, int> = 0>
dispatch_in_place_sub(integer<SSize> & retval,const T & n)7225 inline void dispatch_in_place_sub(integer<SSize> &retval, const T &n)
7226 {
7227 sub_ui(retval, retval, n);
7228 }
7229
7230 template <typename T, std::size_t SSize, enable_if_t<is_cpp_signed_integral<T>::value, int> = 0>
dispatch_in_place_sub(integer<SSize> & retval,const T & n)7231 inline void dispatch_in_place_sub(integer<SSize> &retval, const T &n)
7232 {
7233 sub_si(retval, retval, n);
7234 }
7235
7236 template <typename T, std::size_t SSize,
7237 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_sub(integer<SSize> & retval,const T & x)7238 inline void dispatch_in_place_sub(integer<SSize> &retval, const T &x)
7239 {
7240 retval = static_cast<T>(retval) - x;
7241 }
7242
7243 template <typename T, std::size_t SSize,
7244 enable_if_t<disjunction<is_cpp_arithmetic<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_sub(T & rop,const integer<SSize> & op)7245 inline void dispatch_in_place_sub(T &rop, const integer<SSize> &op)
7246 {
7247 rop = static_cast<T>(rop - op);
7248 }
7249
7250 } // namespace detail
7251
7252 // Negated copy.
7253 template <std::size_t SSize>
operator -(const integer<SSize> & n)7254 integer<SSize> operator-(const integer<SSize> &n)
7255 {
7256 auto retval(n);
7257 retval.neg();
7258 return retval;
7259 }
7260
7261 // Binary subtraction.
7262 template <typename T, typename U>
7263 #if defined(MPPP_HAVE_CONCEPTS)
7264 requires integer_op_types<T, U>
7265 inline auto
7266 #else
7267 inline detail::integer_common_t<T, U>
7268 #endif
operator -(const T & op1,const U & op2)7269 operator-(const T &op1, const U &op2)
7270 {
7271 return detail::dispatch_binary_sub(op1, op2);
7272 }
7273
7274 // In-place subtraction operator.
7275 #if defined(MPPP_HAVE_CONCEPTS)
7276 template <typename T, typename U>
7277 requires integer_op_types<T, U>
7278 #else
7279 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7280 #endif
operator -=(T & rop,const U & op)7281 inline T &operator-=(T &rop, const U &op)
7282 {
7283 detail::dispatch_in_place_sub(rop, op);
7284 return rop;
7285 }
7286
7287 // Prefix decrement.
7288 template <std::size_t SSize>
operator --(integer<SSize> & n)7289 inline integer<SSize> &operator--(integer<SSize> &n)
7290 {
7291 sub_ui(n, n, 1u);
7292 return n;
7293 }
7294
7295 // Suffix decrement.
7296 template <std::size_t SSize>
operator --(integer<SSize> & n,int)7297 inline integer<SSize> operator--(integer<SSize> &n, int)
7298 {
7299 auto retval(n);
7300 --n;
7301 return retval;
7302 }
7303
7304 namespace detail
7305 {
7306
7307 // Dispatching for the binary multiplication operator.
7308 template <std::size_t SSize>
dispatch_binary_mul(const integer<SSize> & op1,const integer<SSize> & op2)7309 inline integer<SSize> dispatch_binary_mul(const integer<SSize> &op1, const integer<SSize> &op2)
7310 {
7311 integer<SSize> retval;
7312 mul(retval, op1, op2);
7313 return retval;
7314 }
7315
7316 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_mul(const integer<SSize> & op1,T n)7317 inline integer<SSize> dispatch_binary_mul(const integer<SSize> &op1, T n)
7318 {
7319 // NOTE: with respect to addition, here we separate the retval
7320 // from the operands. Having a separate destination is generally better
7321 // for multiplication.
7322 integer<SSize> retval;
7323 mul(retval, op1, integer<SSize>{n});
7324 return retval;
7325 }
7326
7327 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_mul(T n,const integer<SSize> & op2)7328 inline integer<SSize> dispatch_binary_mul(T n, const integer<SSize> &op2)
7329 {
7330 return dispatch_binary_mul(op2, n);
7331 }
7332
7333 template <typename T, std::size_t SSize,
7334 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_mul(const integer<SSize> & op1,const T & x)7335 inline T dispatch_binary_mul(const integer<SSize> &op1, const T &x)
7336 {
7337 return static_cast<T>(op1) * x;
7338 }
7339
7340 template <typename T, std::size_t SSize,
7341 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_mul(const T & x,const integer<SSize> & op2)7342 inline T dispatch_binary_mul(const T &x, const integer<SSize> &op2)
7343 {
7344 return dispatch_binary_mul(op2, x);
7345 }
7346
7347 // Dispatching for in-place multiplication.
7348 template <std::size_t SSize>
dispatch_in_place_mul(integer<SSize> & retval,const integer<SSize> & n)7349 inline void dispatch_in_place_mul(integer<SSize> &retval, const integer<SSize> &n)
7350 {
7351 mul(retval, retval, n);
7352 }
7353
7354 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_mul(integer<SSize> & retval,const T & n)7355 inline void dispatch_in_place_mul(integer<SSize> &retval, const T &n)
7356 {
7357 mul(retval, retval, integer<SSize>{n});
7358 }
7359
7360 template <typename T, std::size_t SSize,
7361 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_mul(integer<SSize> & retval,const T & x)7362 inline void dispatch_in_place_mul(integer<SSize> &retval, const T &x)
7363 {
7364 retval = static_cast<T>(retval) * x;
7365 }
7366
7367 template <typename T, std::size_t SSize,
7368 enable_if_t<disjunction<is_cpp_arithmetic<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_mul(T & rop,const integer<SSize> & op)7369 inline void dispatch_in_place_mul(T &rop, const integer<SSize> &op)
7370 {
7371 rop = static_cast<T>(rop * op);
7372 }
7373
7374 } // namespace detail
7375
7376 // Binary multiplication.
7377 template <typename T, typename U>
7378 #if defined(MPPP_HAVE_CONCEPTS)
7379 requires integer_op_types<T, U>
7380 inline auto
7381 #else
7382 inline detail::integer_common_t<T, U>
7383 #endif
operator *(const T & op1,const U & op2)7384 operator*(const T &op1, const U &op2)
7385 {
7386 return detail::dispatch_binary_mul(op1, op2);
7387 }
7388
7389 // In-place multiplication operator.
7390 #if defined(MPPP_HAVE_CONCEPTS)
7391 template <typename T, typename U>
7392 requires integer_op_types<T, U>
7393 #else
7394 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7395 #endif
operator *=(T & rop,const U & op)7396 inline T &operator*=(T &rop, const U &op)
7397 {
7398 detail::dispatch_in_place_mul(rop, op);
7399 return rop;
7400 }
7401
7402 namespace detail
7403 {
7404
7405 // Dispatching for the binary division operator.
7406 template <std::size_t SSize>
dispatch_binary_div(const integer<SSize> & op1,const integer<SSize> & op2)7407 inline integer<SSize> dispatch_binary_div(const integer<SSize> &op1, const integer<SSize> &op2)
7408 {
7409 integer<SSize> retval;
7410 tdiv_q(retval, op1, op2);
7411 return retval;
7412 }
7413
7414 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_div(const integer<SSize> & op1,T n)7415 inline integer<SSize> dispatch_binary_div(const integer<SSize> &op1, T n)
7416 {
7417 integer<SSize> retval;
7418 tdiv_q(retval, op1, integer<SSize>{n});
7419 return retval;
7420 }
7421
7422 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_div(T n,const integer<SSize> & op2)7423 inline integer<SSize> dispatch_binary_div(T n, const integer<SSize> &op2)
7424 {
7425 integer<SSize> retval;
7426 tdiv_q(retval, integer<SSize>{n}, op2);
7427 return retval;
7428 }
7429
7430 template <typename T, std::size_t SSize,
7431 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_div(const integer<SSize> & op1,const T & x)7432 inline T dispatch_binary_div(const integer<SSize> &op1, const T &x)
7433 {
7434 return static_cast<T>(op1) / x;
7435 }
7436
7437 template <typename T, std::size_t SSize,
7438 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_binary_div(const T & x,const integer<SSize> & op2)7439 inline T dispatch_binary_div(const T &x, const integer<SSize> &op2)
7440 {
7441 return x / static_cast<T>(op2);
7442 }
7443
7444 // Dispatching for in-place div.
7445 template <std::size_t SSize>
dispatch_in_place_div(integer<SSize> & retval,const integer<SSize> & n)7446 inline void dispatch_in_place_div(integer<SSize> &retval, const integer<SSize> &n)
7447 {
7448 tdiv_q(retval, retval, n);
7449 }
7450
7451 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_div(integer<SSize> & retval,const T & n)7452 inline void dispatch_in_place_div(integer<SSize> &retval, const T &n)
7453 {
7454 tdiv_q(retval, retval, integer<SSize>{n});
7455 }
7456
7457 template <typename T, std::size_t SSize,
7458 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_div(integer<SSize> & retval,const T & x)7459 inline void dispatch_in_place_div(integer<SSize> &retval, const T &x)
7460 {
7461 retval = static_cast<T>(retval) / x;
7462 }
7463
7464 template <typename T, std::size_t SSize,
7465 enable_if_t<disjunction<is_cpp_arithmetic<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_in_place_div(T & rop,const integer<SSize> & op)7466 inline void dispatch_in_place_div(T &rop, const integer<SSize> &op)
7467 {
7468 rop = static_cast<T>(rop / op);
7469 }
7470
7471 // Dispatching for the binary modulo operator.
7472 template <std::size_t SSize>
dispatch_binary_mod(const integer<SSize> & op1,const integer<SSize> & op2)7473 inline integer<SSize> dispatch_binary_mod(const integer<SSize> &op1, const integer<SSize> &op2)
7474 {
7475 integer<SSize> q, retval;
7476 tdiv_qr(q, retval, op1, op2);
7477 return retval;
7478 }
7479
7480 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_mod(const integer<SSize> & op1,T n)7481 inline integer<SSize> dispatch_binary_mod(const integer<SSize> &op1, T n)
7482 {
7483 integer<SSize> q, retval;
7484 tdiv_qr(q, retval, op1, integer<SSize>{n});
7485 return retval;
7486 }
7487
7488 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_binary_mod(T n,const integer<SSize> & op2)7489 inline integer<SSize> dispatch_binary_mod(T n, const integer<SSize> &op2)
7490 {
7491 integer<SSize> q, retval;
7492 tdiv_qr(q, retval, integer<SSize>{n}, op2);
7493 return retval;
7494 }
7495
7496 // Dispatching for in-place mod.
7497 template <std::size_t SSize>
dispatch_in_place_mod(integer<SSize> & retval,const integer<SSize> & n)7498 inline void dispatch_in_place_mod(integer<SSize> &retval, const integer<SSize> &n)
7499 {
7500 integer<SSize> q;
7501 tdiv_qr(q, retval, retval, n);
7502 }
7503
7504 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_mod(integer<SSize> & retval,const T & n)7505 inline void dispatch_in_place_mod(integer<SSize> &retval, const T &n)
7506 {
7507 integer<SSize> q;
7508 tdiv_qr(q, retval, retval, integer<SSize>{n});
7509 }
7510
7511 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_mod(T & rop,const integer<SSize> & op)7512 inline void dispatch_in_place_mod(T &rop, const integer<SSize> &op)
7513 {
7514 rop = static_cast<T>(rop % op);
7515 }
7516 } // namespace detail
7517
7518 // Binary division.
7519 template <typename T, typename U>
7520 #if defined(MPPP_HAVE_CONCEPTS)
7521 requires integer_op_types<T, U>
7522 inline auto
7523 #else
7524 inline detail::integer_common_t<T, U>
7525 #endif
operator /(const T & n,const U & d)7526 operator/(const T &n, const U &d)
7527 {
7528 return detail::dispatch_binary_div(n, d);
7529 }
7530
7531 // In-place division operator.
7532 #if defined(MPPP_HAVE_CONCEPTS)
7533 template <typename T, typename U>
7534 requires integer_op_types<T, U>
7535 #else
7536 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7537 #endif
operator /=(T & rop,const U & op)7538 inline T &operator/=(T &rop, const U &op)
7539 {
7540 detail::dispatch_in_place_div(rop, op);
7541 return rop;
7542 }
7543
7544 // Binary modulo operator.
7545 #if defined(MPPP_HAVE_CONCEPTS)
7546 template <typename T, typename U>
7547 requires integer_integral_op_types<T, U>
7548 inline auto
7549 #else
7550 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
7551 inline detail::integer_common_t<T, U>
7552 #endif
operator %(const T & n,const U & d)7553 operator%(const T &n, const U &d)
7554 {
7555 return detail::dispatch_binary_mod(n, d);
7556 }
7557
7558 // In-place modulo operator.
7559 #if defined(MPPP_HAVE_CONCEPTS)
7560 template <typename T, typename U>
7561 requires integer_integral_op_types<T, U>
7562 #else
7563 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
7564 #endif
operator %=(T & rop,const U & op)7565 inline T &operator%=(T &rop, const U &op)
7566 {
7567 detail::dispatch_in_place_mod(rop, op);
7568 return rop;
7569 }
7570
7571 // Binary left shift operator.
7572 #if defined(MPPP_HAVE_CONCEPTS)
7573 template <cpp_integral T, std::size_t SSize>
7574 #else
7575 template <typename T, std::size_t SSize, detail::enable_if_t<is_cpp_integral<T>::value, int> = 0>
7576 #endif
operator <<(const integer<SSize> & n,T s)7577 inline integer<SSize> operator<<(const integer<SSize> &n, T s)
7578 {
7579 integer<SSize> retval;
7580 mul_2exp(retval, n, detail::safe_cast<::mp_bitcnt_t>(s));
7581 return retval;
7582 }
7583
7584 // In-place left shift operator.
7585 #if defined(MPPP_HAVE_CONCEPTS)
7586 template <cpp_integral T, std::size_t SSize>
7587 #else
7588 template <typename T, std::size_t SSize, detail::enable_if_t<is_cpp_integral<T>::value, int> = 0>
7589 #endif
operator <<=(integer<SSize> & rop,T s)7590 inline integer<SSize> &operator<<=(integer<SSize> &rop, T s)
7591 {
7592 mul_2exp(rop, rop, detail::safe_cast<::mp_bitcnt_t>(s));
7593 return rop;
7594 }
7595
7596 // Binary right shift operator.
7597 #if defined(MPPP_HAVE_CONCEPTS)
7598 template <cpp_integral T, std::size_t SSize>
7599 #else
7600 template <typename T, std::size_t SSize, detail::enable_if_t<is_cpp_integral<T>::value, int> = 0>
7601 #endif
operator >>(const integer<SSize> & n,T s)7602 inline integer<SSize> operator>>(const integer<SSize> &n, T s)
7603 {
7604 integer<SSize> retval;
7605 tdiv_q_2exp(retval, n, detail::safe_cast<::mp_bitcnt_t>(s));
7606 return retval;
7607 }
7608
7609 // In-place right shift operator.
7610 #if defined(MPPP_HAVE_CONCEPTS)
7611 template <cpp_integral T, std::size_t SSize>
7612 #else
7613 template <typename T, std::size_t SSize, detail::enable_if_t<is_cpp_integral<T>::value, int> = 0>
7614 #endif
operator >>=(integer<SSize> & rop,T s)7615 inline integer<SSize> &operator>>=(integer<SSize> &rop, T s)
7616 {
7617 tdiv_q_2exp(rop, rop, detail::safe_cast<::mp_bitcnt_t>(s));
7618 return rop;
7619 }
7620
7621 namespace detail
7622 {
7623
7624 // Equality operator.
7625 // NOTE: special implementation instead of using cmp, this should be faster.
7626 template <std::size_t SSize>
dispatch_equality(const integer<SSize> & a,const integer<SSize> & b)7627 inline bool dispatch_equality(const integer<SSize> &a, const integer<SSize> &b)
7628 {
7629 const mp_size_t size_a = a._get_union().m_st._mp_size, size_b = b._get_union().m_st._mp_size;
7630 if (size_a != size_b) {
7631 return false;
7632 }
7633 const std::size_t asize
7634 = size_a >= 0 ? static_cast<std::size_t>(size_a) : static_cast<std::size_t>(nint_abs(size_a));
7635 const ::mp_limb_t *ptr_a = a.is_static() ? a._get_union().g_st().m_limbs.data() : a._get_union().g_dy()._mp_d;
7636 const ::mp_limb_t *ptr_b = b.is_static() ? b._get_union().g_st().m_limbs.data() : b._get_union().g_dy()._mp_d;
7637 auto limb_cmp
7638 = [](const ::mp_limb_t &l1, const ::mp_limb_t &l2) { return (l1 & GMP_NUMB_MASK) == (l2 & GMP_NUMB_MASK); };
7639 // NOTE: cannot understand why, but MSVC wants a checked iterator as 3rd
7640 // parameter here.
7641 return std::equal(ptr_a, ptr_a + asize, make_uai(ptr_b), limb_cmp);
7642 }
7643
7644 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_equality(const integer<SSize> & a,T n)7645 inline bool dispatch_equality(const integer<SSize> &a, T n)
7646 {
7647 return dispatch_equality(a, integer<SSize>{n});
7648 }
7649
7650 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_equality(T n,const integer<SSize> & a)7651 inline bool dispatch_equality(T n, const integer<SSize> &a)
7652 {
7653 return dispatch_equality(a, n);
7654 }
7655
7656 template <typename T, std::size_t SSize,
7657 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_equality(const integer<SSize> & a,const T & x)7658 inline bool dispatch_equality(const integer<SSize> &a, const T &x)
7659 {
7660 return static_cast<T>(a) == x;
7661 }
7662
7663 template <typename T, std::size_t SSize,
7664 enable_if_t<disjunction<is_cpp_floating_point<T>, is_cpp_complex<T>>::value, int> = 0>
dispatch_equality(const T & x,const integer<SSize> & a)7665 inline bool dispatch_equality(const T &x, const integer<SSize> &a)
7666 {
7667 return dispatch_equality(a, x);
7668 }
7669
7670 // Less-than operator.
7671
7672 // 1-limb specialisation.
static_less_than(const static_int<1> & op1,const static_int<1> & op2)7673 inline bool static_less_than(const static_int<1> &op1, const static_int<1> &op2)
7674 {
7675 // NOTE: an implementation with 1 branch less and similar performance
7676 // is available at fdb84f57027ebf579a380f07501435eb17fd6db1.
7677
7678 const auto size1 = op1._mp_size, size2 = op2._mp_size;
7679
7680 // Compare sizes.
7681 // NOTE: under the assumption that we have positive/negative
7682 // integers with equal probability, it makes sense to check
7683 // for the sizes first.
7684 if (size1 < size2) {
7685 return true;
7686 }
7687 if (size1 > size2) {
7688 return false;
7689 }
7690
7691 // Sizes are equal, compare the only limb.
7692 const auto l1 = op1.m_limbs[0] & GMP_NUMB_MASK;
7693 const auto l2 = op2.m_limbs[0] & GMP_NUMB_MASK;
7694
7695 const auto lt = l1 < l2;
7696 const auto gt = l1 > l2;
7697
7698 // NOTE: as usual, this branchless formulation is slightly
7699 // better for signed ints, slightly worse for unsigned
7700 // (wrt an if statement).
7701 return (size1 >= 0 && lt) || (size1 < 0 && gt);
7702 }
7703
7704 // mpn implementation.
7705 template <std::size_t SSize>
static_less_than(const static_int<SSize> & n1,const static_int<SSize> & n2)7706 inline bool static_less_than(const static_int<SSize> &n1, const static_int<SSize> &n2)
7707 {
7708 const auto size1 = n1._mp_size, size2 = n2._mp_size;
7709
7710 // Compare sizes.
7711 if (size1 < size2) {
7712 return true;
7713 }
7714 if (size1 > size2) {
7715 return false;
7716 }
7717
7718 // The two sizes are equal, compare the absolute values.
7719 if (size1) {
7720 const int cmp_abs = mpn_cmp(n1.m_limbs.data(), n2.m_limbs.data(), static_cast<::mp_size_t>(std::abs(size1)));
7721 return (size1 >= 0 && cmp_abs < 0) || (size1 < 0 && cmp_abs > 0);
7722 }
7723 // Both operands are zero.
7724 // NOTE: we do this special casing in order to avoid calling mpn_cmp() on zero operands. It seems to
7725 // work, but the official GMP docs say one is not supposed to call mpn functions on zero operands.
7726 return false;
7727 }
7728
7729 template <std::size_t SSize>
dispatch_less_than(const integer<SSize> & op1,const integer<SSize> & op2)7730 inline bool dispatch_less_than(const integer<SSize> &op1, const integer<SSize> &op2)
7731 {
7732 const bool s1 = op1.is_static(), s2 = op2.is_static();
7733 if (mppp_likely(s1 && s2)) {
7734 return static_less_than(op1._get_union().g_st(), op2._get_union().g_st());
7735 }
7736
7737 return mpz_cmp(op1.get_mpz_view(), op2.get_mpz_view()) < 0;
7738 }
7739
7740 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_less_than(const integer<SSize> & a,T n)7741 inline bool dispatch_less_than(const integer<SSize> &a, T n)
7742 {
7743 return dispatch_less_than(a, integer<SSize>{n});
7744 }
7745
7746 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
7747 bool dispatch_less_than(T, const integer<SSize> &);
7748
7749 template <typename T, std::size_t SSize, enable_if_t<is_cpp_floating_point<T>::value, int> = 0>
dispatch_less_than(const integer<SSize> & a,T x)7750 inline bool dispatch_less_than(const integer<SSize> &a, T x)
7751 {
7752 return static_cast<T>(a) < x;
7753 }
7754
7755 template <typename T, std::size_t SSize, enable_if_t<is_cpp_floating_point<T>::value, int> = 0>
7756 bool dispatch_less_than(T, const integer<SSize> &);
7757
7758 // Greater-than operator.
7759
7760 // NOTE: this is essentially the same as static_less_than.
static_greater_than(const static_int<1> & op1,const static_int<1> & op2)7761 inline bool static_greater_than(const static_int<1> &op1, const static_int<1> &op2)
7762 {
7763 const auto size1 = op1._mp_size, size2 = op2._mp_size;
7764
7765 if (size1 > size2) {
7766 return true;
7767 }
7768 if (size1 < size2) {
7769 return false;
7770 }
7771
7772 const auto l1 = op1.m_limbs[0] & GMP_NUMB_MASK;
7773 const auto l2 = op2.m_limbs[0] & GMP_NUMB_MASK;
7774
7775 const auto lt = l1 < l2;
7776 const auto gt = l1 > l2;
7777
7778 return (size1 >= 0 && gt) || (size1 < 0 && lt);
7779 }
7780
7781 template <std::size_t SSize>
static_greater_than(const static_int<SSize> & n1,const static_int<SSize> & n2)7782 inline bool static_greater_than(const static_int<SSize> &n1, const static_int<SSize> &n2)
7783 {
7784 const auto size1 = n1._mp_size, size2 = n2._mp_size;
7785
7786 if (size1 > size2) {
7787 return true;
7788 }
7789 if (size1 < size2) {
7790 return false;
7791 }
7792
7793 if (size1) {
7794 const int cmp_abs = mpn_cmp(n1.m_limbs.data(), n2.m_limbs.data(), static_cast<::mp_size_t>(std::abs(size1)));
7795 return (size1 >= 0 && cmp_abs > 0) || (size1 < 0 && cmp_abs < 0);
7796 }
7797 return false;
7798 }
7799
7800 template <std::size_t SSize>
dispatch_greater_than(const integer<SSize> & op1,const integer<SSize> & op2)7801 inline bool dispatch_greater_than(const integer<SSize> &op1, const integer<SSize> &op2)
7802 {
7803 const bool s1 = op1.is_static(), s2 = op2.is_static();
7804 if (mppp_likely(s1 && s2)) {
7805 return static_greater_than(op1._get_union().g_st(), op2._get_union().g_st());
7806 }
7807
7808 return mpz_cmp(op1.get_mpz_view(), op2.get_mpz_view()) > 0;
7809 }
7810
7811 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_greater_than(const integer<SSize> & a,T n)7812 inline bool dispatch_greater_than(const integer<SSize> &a, T n)
7813 {
7814 return dispatch_greater_than(a, integer<SSize>{n});
7815 }
7816
7817 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_greater_than(T n,const integer<SSize> & a)7818 inline bool dispatch_greater_than(T n, const integer<SSize> &a)
7819 {
7820 return dispatch_less_than(a, integer<SSize>{n});
7821 }
7822
7823 template <typename T, std::size_t SSize, enable_if_t<is_cpp_floating_point<T>::value, int> = 0>
dispatch_greater_than(const integer<SSize> & a,T x)7824 inline bool dispatch_greater_than(const integer<SSize> &a, T x)
7825 {
7826 return static_cast<T>(a) > x;
7827 }
7828
7829 template <typename T, std::size_t SSize, enable_if_t<is_cpp_floating_point<T>::value, int> = 0>
dispatch_greater_than(T x,const integer<SSize> & a)7830 inline bool dispatch_greater_than(T x, const integer<SSize> &a)
7831 {
7832 return dispatch_less_than(a, x);
7833 }
7834
7835 // NOTE: implement these here as we need visibility of dispatch_greater_than().
7836 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int>>
dispatch_less_than(T n,const integer<SSize> & a)7837 inline bool dispatch_less_than(T n, const integer<SSize> &a)
7838 {
7839 return dispatch_greater_than(a, integer<SSize>{n});
7840 }
7841
7842 template <typename T, std::size_t SSize, enable_if_t<is_cpp_floating_point<T>::value, int>>
dispatch_less_than(T x,const integer<SSize> & a)7843 inline bool dispatch_less_than(T x, const integer<SSize> &a)
7844 {
7845 return dispatch_greater_than(a, x);
7846 }
7847 } // namespace detail
7848
7849 // Equality operator.
7850 #if defined(MPPP_HAVE_CONCEPTS)
7851 template <typename T, typename U>
7852 requires integer_op_types<T, U>
7853 #else
7854 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7855 #endif
operator ==(const T & op1,const U & op2)7856 inline bool operator==(const T &op1, const U &op2)
7857 {
7858 return detail::dispatch_equality(op1, op2);
7859 }
7860
7861 // Inequality operator.
7862 #if defined(MPPP_HAVE_CONCEPTS)
7863 template <typename T, typename U>
7864 requires integer_op_types<T, U>
7865 #else
7866 template <typename T, typename U, detail::enable_if_t<are_integer_op_types<T, U>::value, int> = 0>
7867 #endif
operator !=(const T & op1,const U & op2)7868 inline bool operator!=(const T &op1, const U &op2)
7869 {
7870 return !(op1 == op2);
7871 }
7872
7873 // Less-than operator.
7874 #if defined(MPPP_HAVE_CONCEPTS)
7875 template <typename T, typename U>
7876 requires integer_real_op_types<T, U>
7877 #else
7878 template <typename T, typename U, detail::enable_if_t<are_integer_real_op_types<T, U>::value, int> = 0>
7879 #endif
operator <(const T & op1,const U & op2)7880 inline bool operator<(const T &op1, const U &op2)
7881 {
7882 return detail::dispatch_less_than(op1, op2);
7883 }
7884
7885 // Less-than or equal operator.
7886 #if defined(MPPP_HAVE_CONCEPTS)
7887 template <typename T, typename U>
7888 requires integer_real_op_types<T, U>
7889 #else
7890 template <typename T, typename U, detail::enable_if_t<are_integer_real_op_types<T, U>::value, int> = 0>
7891 #endif
operator <=(const T & op1,const U & op2)7892 inline bool operator<=(const T &op1, const U &op2)
7893 {
7894 return !(op1 > op2);
7895 }
7896
7897 // Greater-than operator.
7898 #if defined(MPPP_HAVE_CONCEPTS)
7899 template <typename T, typename U>
7900 requires integer_real_op_types<T, U>
7901 #else
7902 template <typename T, typename U, detail::enable_if_t<are_integer_real_op_types<T, U>::value, int> = 0>
7903 #endif
operator >(const T & op1,const U & op2)7904 inline bool operator>(const T &op1, const U &op2)
7905 {
7906 return detail::dispatch_greater_than(op1, op2);
7907 }
7908
7909 // Greater-than or equal operator.
7910 #if defined(MPPP_HAVE_CONCEPTS)
7911 template <typename T, typename U>
7912 requires integer_real_op_types<T, U>
7913 #else
7914 template <typename T, typename U, detail::enable_if_t<are_integer_real_op_types<T, U>::value, int> = 0>
7915 #endif
operator >=(const T & op1,const U & op2)7916 inline bool operator>=(const T &op1, const U &op2)
7917 {
7918 return !(op1 < op2);
7919 }
7920
7921 // Unary bitwise NOT.
7922 template <std::size_t SSize>
operator ~(const integer<SSize> & op)7923 integer<SSize> operator~(const integer<SSize> &op)
7924 {
7925 integer<SSize> retval;
7926 bitwise_not(retval, op);
7927 return retval;
7928 }
7929
7930 namespace detail
7931 {
7932
7933 // Dispatch for binary OR.
7934 template <std::size_t SSize>
dispatch_operator_or(const integer<SSize> & op1,const integer<SSize> & op2)7935 inline integer<SSize> dispatch_operator_or(const integer<SSize> &op1, const integer<SSize> &op2)
7936 {
7937 integer<SSize> retval;
7938 bitwise_ior(retval, op1, op2);
7939 return retval;
7940 }
7941
7942 template <std::size_t SSize, typename T, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_or(const integer<SSize> & op1,const T & op2)7943 inline integer<SSize> dispatch_operator_or(const integer<SSize> &op1, const T &op2)
7944 {
7945 return dispatch_operator_or(op1, integer<SSize>{op2});
7946 }
7947
7948 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_or(const T & op1,const integer<SSize> & op2)7949 inline integer<SSize> dispatch_operator_or(const T &op1, const integer<SSize> &op2)
7950 {
7951 return dispatch_operator_or(op2, op1);
7952 }
7953
7954 // Dispatching for in-place OR.
7955 template <std::size_t SSize>
dispatch_in_place_or(integer<SSize> & rop,const integer<SSize> & op)7956 inline void dispatch_in_place_or(integer<SSize> &rop, const integer<SSize> &op)
7957 {
7958 bitwise_ior(rop, rop, op);
7959 }
7960
7961 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_or(integer<SSize> & rop,const T & op)7962 inline void dispatch_in_place_or(integer<SSize> &rop, const T &op)
7963 {
7964 dispatch_in_place_or(rop, integer<SSize>{op});
7965 }
7966
7967 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_or(T & rop,const integer<SSize> & op)7968 inline void dispatch_in_place_or(T &rop, const integer<SSize> &op)
7969 {
7970 rop = static_cast<T>(rop | op);
7971 }
7972 } // namespace detail
7973
7974 // Binary bitwise OR operator
7975 #if defined(MPPP_HAVE_CONCEPTS)
7976 template <typename T, typename U>
7977 requires integer_integral_op_types<T, U>
7978 inline auto
7979 #else
7980 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
7981 inline detail::integer_common_t<T, U>
7982 #endif
operator |(const T & op1,const U & op2)7983 operator|(const T &op1, const U &op2)
7984 {
7985 return detail::dispatch_operator_or(op1, op2);
7986 }
7987
7988 // In-place bitwise OR operator.
7989 #if defined(MPPP_HAVE_CONCEPTS)
7990 template <typename T, typename U>
7991 requires integer_integral_op_types<T, U>
7992 #else
7993 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
7994 #endif
operator |=(T & rop,const U & op)7995 inline T &operator|=(T &rop, const U &op)
7996 {
7997 detail::dispatch_in_place_or(rop, op);
7998 return rop;
7999 }
8000
8001 namespace detail
8002 {
8003
8004 // Dispatch for binary AND.
8005 template <std::size_t SSize>
dispatch_operator_and(const integer<SSize> & op1,const integer<SSize> & op2)8006 inline integer<SSize> dispatch_operator_and(const integer<SSize> &op1, const integer<SSize> &op2)
8007 {
8008 integer<SSize> retval;
8009 bitwise_and(retval, op1, op2);
8010 return retval;
8011 }
8012
8013 template <std::size_t SSize, typename T, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_and(const integer<SSize> & op1,const T & op2)8014 inline integer<SSize> dispatch_operator_and(const integer<SSize> &op1, const T &op2)
8015 {
8016 return dispatch_operator_and(op1, integer<SSize>{op2});
8017 }
8018
8019 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_and(const T & op1,const integer<SSize> & op2)8020 inline integer<SSize> dispatch_operator_and(const T &op1, const integer<SSize> &op2)
8021 {
8022 return dispatch_operator_and(op2, op1);
8023 }
8024
8025 // Dispatching for in-place AND.
8026 template <std::size_t SSize>
dispatch_in_place_and(integer<SSize> & rop,const integer<SSize> & op)8027 inline void dispatch_in_place_and(integer<SSize> &rop, const integer<SSize> &op)
8028 {
8029 bitwise_and(rop, rop, op);
8030 }
8031
8032 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_and(integer<SSize> & rop,const T & op)8033 inline void dispatch_in_place_and(integer<SSize> &rop, const T &op)
8034 {
8035 dispatch_in_place_and(rop, integer<SSize>{op});
8036 }
8037
8038 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_and(T & rop,const integer<SSize> & op)8039 inline void dispatch_in_place_and(T &rop, const integer<SSize> &op)
8040 {
8041 rop = static_cast<T>(rop & op);
8042 }
8043 } // namespace detail
8044
8045 // Binary bitwise AND operator.
8046 #if defined(MPPP_HAVE_CONCEPTS)
8047 template <typename T, typename U>
8048 requires integer_integral_op_types<T, U>
8049 inline auto
8050 #else
8051 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
8052 inline detail::integer_common_t<T, U>
8053 #endif
operator &(const T & op1,const U & op2)8054 operator&(const T &op1, const U &op2)
8055 {
8056 return detail::dispatch_operator_and(op1, op2);
8057 }
8058
8059 // In-place bitwise AND operator.
8060 #if defined(MPPP_HAVE_CONCEPTS)
8061 template <typename T, typename U>
8062 requires integer_integral_op_types<T, U>
8063 #else
8064 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
8065 #endif
operator &=(T & rop,const U & op)8066 inline T &operator&=(T &rop, const U &op)
8067 {
8068 detail::dispatch_in_place_and(rop, op);
8069 return rop;
8070 }
8071
8072 namespace detail
8073 {
8074
8075 // Dispatch for binary XOR.
8076 template <std::size_t SSize>
dispatch_operator_xor(const integer<SSize> & op1,const integer<SSize> & op2)8077 inline integer<SSize> dispatch_operator_xor(const integer<SSize> &op1, const integer<SSize> &op2)
8078 {
8079 integer<SSize> retval;
8080 bitwise_xor(retval, op1, op2);
8081 return retval;
8082 }
8083
8084 template <std::size_t SSize, typename T, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_xor(const integer<SSize> & op1,const T & op2)8085 inline integer<SSize> dispatch_operator_xor(const integer<SSize> &op1, const T &op2)
8086 {
8087 return dispatch_operator_xor(op1, integer<SSize>{op2});
8088 }
8089
8090 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_operator_xor(const T & op1,const integer<SSize> & op2)8091 inline integer<SSize> dispatch_operator_xor(const T &op1, const integer<SSize> &op2)
8092 {
8093 return dispatch_operator_xor(op2, op1);
8094 }
8095
8096 // Dispatching for in-place XOR.
8097 template <std::size_t SSize>
dispatch_in_place_xor(integer<SSize> & rop,const integer<SSize> & op)8098 inline void dispatch_in_place_xor(integer<SSize> &rop, const integer<SSize> &op)
8099 {
8100 bitwise_xor(rop, rop, op);
8101 }
8102
8103 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_xor(integer<SSize> & rop,const T & op)8104 inline void dispatch_in_place_xor(integer<SSize> &rop, const T &op)
8105 {
8106 dispatch_in_place_xor(rop, integer<SSize>{op});
8107 }
8108
8109 template <typename T, std::size_t SSize, enable_if_t<is_cpp_integral<T>::value, int> = 0>
dispatch_in_place_xor(T & rop,const integer<SSize> & op)8110 inline void dispatch_in_place_xor(T &rop, const integer<SSize> &op)
8111 {
8112 rop = static_cast<T>(rop ^ op);
8113 }
8114 } // namespace detail
8115
8116 // Binary bitwise XOR operator.
8117 #if defined(MPPP_HAVE_CONCEPTS)
8118 template <typename T, typename U>
8119 requires integer_integral_op_types<T, U>
8120 inline auto
8121 #else
8122 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
8123 inline detail::integer_common_t<T, U>
8124 #endif
operator ^(const T & op1,const U & op2)8125 operator^(const T &op1, const U &op2)
8126 {
8127 return detail::dispatch_operator_xor(op1, op2);
8128 }
8129
8130 // In-place bitwise XOR operator.
8131 #if defined(MPPP_HAVE_CONCEPTS)
8132 template <typename T, typename U>
8133 requires integer_integral_op_types<T, U>
8134 #else
8135 template <typename T, typename U, detail::enable_if_t<are_integer_integral_op_types<T, U>::value, int> = 0>
8136 #endif
operator ^=(T & rop,const U & op)8137 inline T &operator^=(T &rop, const U &op)
8138 {
8139 detail::dispatch_in_place_xor(rop, op);
8140 return rop;
8141 }
8142
8143 } // namespace mppp
8144
8145 #if defined(MPPP_WITH_BOOST_S11N)
8146
8147 // Disable tracking for mppp::integer.
8148 // NOTE: this code has been lifted from the Boost
8149 // macro, which does not support directly
8150 // class templates.
8151
8152 namespace boost
8153 {
8154
8155 namespace serialization
8156 {
8157
8158 template <std::size_t SSize>
8159 struct tracking_level<mppp::integer<SSize>> {
8160 typedef mpl::integral_c_tag tag;
8161 typedef mpl::int_<track_never> type;
8162 BOOST_STATIC_CONSTANT(int, value = tracking_level::type::value);
8163 BOOST_STATIC_ASSERT((mpl::greater<implementation_level<mppp::integer<SSize>>, mpl::int_<primitive_type>>::value));
8164 };
8165
8166 } // namespace serialization
8167
8168 } // namespace boost
8169
8170 #endif
8171
8172 namespace std
8173 {
8174
8175 // Specialisation of std::hash for mppp::integer.
8176 template <size_t SSize>
8177 struct hash<mppp::integer<SSize>> {
8178 // NOTE: these typedefs have been deprecated in C++17.
8179 #if MPPP_CPLUSPLUS < 201703L
8180 // The argument type.
8181 using argument_type = mppp::integer<SSize>;
8182 // The result type.
8183 using result_type = size_t;
8184 #endif
8185 // Call operator.
operator ()std::hash8186 size_t operator()(const mppp::integer<SSize> &n) const
8187 {
8188 return mppp::hash(n);
8189 }
8190 };
8191
8192 } // namespace std
8193
8194 #if defined(_MSC_VER)
8195
8196 #pragma warning(pop)
8197
8198 #endif
8199
8200 #include <mp++/detail/integer_literals.hpp>
8201
8202 // Support for pretty printing in xeus-cling.
8203 #if defined(__CLING__)
8204
8205 #if __has_include(<nlohmann/json.hpp>)
8206
8207 #include <nlohmann/json.hpp>
8208
8209 namespace mppp
8210 {
8211
8212 template <std::size_t SSize>
mime_bundle_repr(const integer<SSize> & n)8213 inline nlohmann::json mime_bundle_repr(const integer<SSize> &n)
8214 {
8215 auto bundle = nlohmann::json::object();
8216
8217 bundle["text/plain"] = n.to_string();
8218
8219 return bundle;
8220 }
8221
8222 } // namespace mppp
8223
8224 #endif
8225
8226 #endif
8227
8228 #endif
8229