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