Aesi Multiprecision
C++ class library of long integer arithmetic for GPU parallelization
Aeu.h
Go to the documentation of this file.
1 #ifndef AEU_MULTIPRECISION
2 #define AEU_MULTIPRECISION
3 
5 #include <iostream>
6 #include <cassert>
7 
8 #ifdef __CUDACC__
9 #define gpu __host__ __device__
10  #include <cuda/std/utility>
11  #include <cuda/std/array>
12 #else
13 #define gpu
14  #include <utility>
15  #include <array>
16 #endif
18 
19 #if defined AESI_UNSAFE
20  #warning Enabled nightly mode for the library. Functions and methods input arguments are not checked for validity. Be really gentle
21 #endif
22 
23 #ifdef AESI_CRYPTOPP_INTEGRATION
24 #include <cryptopp/integer.h>
25 #endif
26 
27 #ifdef AESI_GMP_INTEGRATION
28 #include <gmpxx.h>
29 #endif
30 
36 namespace {
37  using byte = uint8_t;
38  using block = uint32_t;
39 
40  constexpr inline std::size_t bitsInByte = 8, blockBitLength = sizeof(block) * bitsInByte;
41  constexpr inline uint64_t blockBase = 1ULL << blockBitLength;
42  constexpr inline block blockMax = 0xff'ff'ff'ffu;
43 
48  enum Comparison { equal = 0, less = 1, greater = 2, equivalent = 3 };
49 }
50 
56 template <std::size_t bitness = 512> requires (bitness % blockBitLength == 0)
57 class Aeu final {
58  static_assert(bitness > sizeof(uint64_t), "Use built-in types for numbers 64-bit or less.");
59 
60  static constexpr std::size_t blocksNumber = bitness / blockBitLength;
61 
62 #ifdef __CUDACC__
63  template <typename T1, typename T2>
64  using pair = cuda::std::pair<T1, T2>;
65  using blockLine = cuda::std::array<block, blocksNumber>;
66 #else
67  template<typename T1, typename T2>
68  using pair = std::pair<T1, T2>;
69  using blockLine = std::array<block, blocksNumber>;
70 #endif
71 
72  /* -------------------------- @name Class members. ----------------------- */
76  blockLine blocks;
77  /* ----------------------------------------------------------------------- */
78 
79  /* ------------------------ @name Helper functions. ---------------------- */
86  gpu static constexpr auto addLine(blockLine& dst, const blockLine& src) noexcept -> uint64_t {
87  uint64_t carryOut = 0;
88  for (std::size_t i = 0; i < blocksNumber; ++i) {
89  uint64_t sum = static_cast<uint64_t>(dst[i]) + static_cast<uint64_t>(src[i]) + carryOut;
90  carryOut = sum / blockBase; dst[i] = static_cast<block>(sum % blockBase);
91  }
92  return carryOut;
93  }
94 
100  gpu static constexpr auto makeComplement(blockLine& line) noexcept -> uint64_t {
101  uint64_t carryOut = 1;
102  for(std::size_t i = 0; i < blocksNumber; ++i) {
103  const uint64_t sum = blockBase - 1ULL - static_cast<uint64_t>(line[i]) + carryOut;
104  carryOut = sum / blockBase; line[i] = static_cast<block>(sum % blockBase);
105  }
106  return carryOut;
107  }
108  /* ----------------------------------------------------------------------- */
109 
110 public:
111  /* --------------------- @name Different constructors. ------------------- */
115  gpu constexpr Aeu() noexcept = default;
116 
120  gpu constexpr Aeu(const Aeu& copy) noexcept = default;
121 
126  gpu constexpr Aeu& operator=(const Aeu& other) noexcept { blocks = other.blocks; return *this; }
127 
134  template <typename Integral> requires (std::is_unsigned_v<Integral>)
135  gpu constexpr Aeu(Integral value) noexcept {
136  if(value != 0) {
137  uint64_t tValue = (value < 0 ? static_cast<uint64_t>(value * -1) : static_cast<uint64_t>(value));
138  for (std::size_t i = 0; i < blocksNumber; ++i) {
139  blocks[i] = static_cast<block>(tValue % blockBase);
140  tValue /= blockBase;
141  }
142  if(value < 0)
143  makeComplement(blocks);
144  } else
145  blocks = {};
146  }
147 
154  template <typename Char> requires (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>)
155  gpu constexpr Aeu(const Char* data, std::size_t size) noexcept : Aeu {} {
156  if(size == 0) return;
157 
158  constexpr const Char* characters = [] {
159  if constexpr (std::is_same_v<char, Char>) {
160  return "09aAfFoObBxX";
161  } else {
162  return L"09aAfFoObBxX";
163  }
164  } ();
165  std::size_t position = 0;
166 
167  if constexpr (std::is_same_v<Char, char>) {
168  for(; !std::isalnum(data[position]) && position < size; ++position) ;
169  } else {
170  for(; !std::iswalnum(data[position]) && position < size; ++position) ;
171  }
172 
173  const auto base = [&data, &size, &position, &characters] {
174  if (data[position] == characters[0] && size > position + 1) {
175  switch (data[position + 1]) {
176  case characters[8]:
177  case characters[9]:
178  position += 2; return 2u;
179  case characters[6]:
180  case characters[7]:
181  position += 2; return 8u;
182  case characters[10]:
183  case characters[11]:
184  position += 2; return 16u;
185  default:
186  return 10u;
187  }
188  } return 10u;
189  } ();
190  for(; position < size; ++position) {
191  const auto digit = [&characters] (Char ch) {
192  if(characters[0] <= ch && ch <= characters[1])
193  return static_cast<unsigned>(ch) - static_cast<unsigned>(characters[0]);
194  if(characters[2] <= ch && ch <= characters[4])
195  return static_cast<unsigned>(ch) - static_cast<unsigned>(characters[2]) + 10u;
196  if(characters[3] <= ch && ch <= characters[5])
197  return static_cast<unsigned>(ch) - static_cast<unsigned>(characters[3]) + 10u;
198  return 99u;
199  } (data[position]);
200 
201  if(digit < base) {
202  this->operator*=(base);
203  this->operator+=(digit);
204  }
205  }
206  }
207 
213  template <typename Char, std::size_t arrayLength> requires (arrayLength > 1 && (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>))
214  gpu constexpr Aeu(const Char (&literal)[arrayLength]) noexcept : Aeu(literal, arrayLength) {}
215 
221  template <typename String, typename Char = typename String::value_type> requires (std::is_same_v<std::basic_string<Char>,
222  std::decay_t<String>> || std::is_same_v<std::basic_string_view<Char>, std::decay_t<String>>)
223  gpu constexpr Aeu(String&& stringView) noexcept : Aeu(stringView.data(), stringView.size()) {}
224 
225  template <typename String, typename Char = typename String::value_type> requires (std::is_same_v<std::basic_string<Char>,
226  std::decay_t<String>> || std::is_same_v<std::basic_string_view<Char>, std::decay_t<String>>)
227  gpu constexpr Aeu(const String& stringView) noexcept : Aeu(stringView.data(), stringView.size()) {}
228 
229 #ifdef AESI_CRYPTOPP_INTEGRATION
234  constexpr Aeu(const CryptoPP::Integer& value): Aeu {} {
235  const auto byteCount = value.ByteCount();
236  if(byteCount * 8 > bitness)
237  throw std::invalid_argument("Accessed overflow on construction object from CryptoPP::Integer");
238  for(std::size_t i = 0; i < byteCount; ++i)
239  setByte(i, value.GetByte(i));
240  }
241 #endif
242 
243 #ifdef AESI_GMP_INTEGRATION
248  constexpr Aeu(const mpz_class& value) : Aeu {} {
249  const auto bitLength = mpz_sizeinbase(value.get_mpz_t(), 2);
250  auto tBlocksNumber = static_cast<std::size_t>(1 + ((bitLength - 1) / sizeof(block) * 8));
251 
252  std::basic_string<block> buffer (tBlocksNumber, 0u);
253  mpz_export(buffer.data(), &tBlocksNumber, -1, sizeof(block), -1, 0, value.get_mpz_t());
254 
255  for (std::size_t i = 0; i < tBlocksNumber; ++i)
256  setBlock(i, buffer[i]);
257  }
258 #endif
259  /* ----------------------------------------------------------------------- */
260 
261 
262  /* --------------------- @name Arithmetic operators. --------------------- */
263  /* ------------------------- @name Unary operators. -------------------------- */
269  [[nodiscard]]
270  gpu constexpr auto operator+() const noexcept -> Aeu { return *this; }
271 
276  [[nodiscard]]
277  gpu constexpr auto operator-() const noexcept -> Aeu {
278  Aeu result = *this; makeComplement(result.blocks); return result;
279  }
280 
285  gpu constexpr auto operator++() noexcept -> Aeu& {
286  for(std::size_t i = 0; i < blocksNumber; ++i) {
287  if(blocks[i] < blockMax) {
288  ++blocks[i];
289  break;
290  } else blocks[i] = 0u;
291  }
292  return *this;
293  }
294 
299  gpu constexpr auto operator++(int) & noexcept -> Aeu { Aeu old = *this; operator++(); return old; }
300 
305  gpu constexpr auto operator--() noexcept -> Aeu& {
306  for(std::size_t i = 0; i < blocksNumber; ++i) {
307  if(blocks[i] > 0u) {
308  --blocks[i];
309  break;
310  } else blocks[i] = blockMax;
311  }
312  return *this;
313  }
314 
319  gpu constexpr auto operator--(int) & noexcept -> Aeu { Aeu old = *this; operator--(); return old; }
320  /* --------------------------------------------------------------------------- */
321 
322 
323  /* ------------------------ @name Addition operators. ------------------------ */
329  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned>) [[nodiscard]]
330  gpu constexpr auto operator+(Unsigned addendum) const noexcept -> Aeu {
331  Aeu result = *this; result.operator+=(addendum); return result;
332  }
333 
339  [[nodiscard]]
340  gpu constexpr auto operator+(const Aeu& addendum) const noexcept -> Aeu {
341  Aeu result = *this; result += addendum; return result;
342  }
343 
349  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned>)
350  gpu constexpr auto operator+=(Unsigned addendum) noexcept -> Aeu& {
351  for(std::size_t i = 0; i < blocksNumber; ++i) {
352  const auto currentSum = static_cast<uint64_t>(blocks[i]) + static_cast<uint64_t>(addendum);
353  addendum = currentSum / blockBase; blocks[i] = currentSum % blockBase;
354  }
355  return *this;
356  }
357 
363  gpu constexpr auto operator+=(const Aeu& addendum) noexcept -> Aeu& {
364  addLine(blocks, addendum.blocks); return *this;
365  }
366  /* --------------------------------------------------------------------------- */
367 
368 
369  /* ----------------------- @name Subtraction operators. ---------------------- */
375  [[nodiscard]]
376  gpu constexpr auto operator-(const Aeu& subtrahend) const noexcept -> Aeu {
377  Aeu result = *this; result -= subtrahend; return result;
378  }
379 
385  gpu constexpr auto operator-=(const Aeu& subtrahend) noexcept -> Aeu& {
386  return this->operator+=(-subtrahend);
387  }
388  /* --------------------------------------------------------------------------- */
389 
390 
391  /* --------------------- @name Multiplication operators. --------------------- */
397  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned>) [[nodiscard]]
398  gpu constexpr auto operator*(Unsigned factor) const noexcept -> Aeu {
399  Aeu result = *this; result.operator*=(factor); return result;
400  }
401 
407  [[nodiscard]]
408  gpu constexpr auto operator*(const Aeu& factor) const noexcept -> Aeu {
409  Aeu result = *this; result *= factor; return result;
410  }
411 
418  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned>)
419  gpu constexpr auto operator*=(Unsigned factor) noexcept -> Aeu& {
420  if constexpr (std::is_same_v<Unsigned, uint64_t>) {
421  const auto longerLength = filledBlocksNumber(), smallerLength = (factor > blockMax ? 2UL : 1UL);
422  blockLine buffer {};
423 
424  for(std::size_t i = 0; i < longerLength; ++i) {
425  uint64_t tBlock = blocks[i], carryOut = 0;
426 
427  for(std::size_t j = 0; j < smallerLength && i + j < buffer.size(); ++j) {
428  const auto product = tBlock * ((factor >> blockBitLength * j) & 0x00'00'00'00'ff'ff'ff'ff) + carryOut;
429  const auto block = static_cast<uint64_t>(buffer[i + j]) + (product % blockBase);
430  carryOut = product / blockBase + block / blockBase;
431  buffer[i + j] = block % blockBase;
432  }
433 
434  if(smallerLength + i < buffer.size())
435  buffer[smallerLength + i] += carryOut;
436  }
437 
438  blocks = buffer;
439  return *this;
440  } else {
441  uint64_t carryOut = 0;
442  for (std::size_t i = 0; i < blocksNumber; ++i) {
443  const auto product = static_cast<uint64_t>(factor) * static_cast<uint64_t>(blocks[i]) + carryOut;
444  blocks[i] = product % blockBase; carryOut = product / blockBase;
445  }
446  return *this;
447  }
448  };
449 
455  gpu constexpr auto operator*=(const Aeu& factor) noexcept -> Aeu& {
456  constexpr auto multiplyLines = [] (const blockLine& longerLine, const std::size_t longerLength,
457  const blockLine& smallerLine, const std::size_t smallerLength) {
458  blockLine buffer {};
459 
460  for(std::size_t i = 0; i < longerLength; ++i) {
461  uint64_t tBlock = longerLine[i], carryOut = 0;
462 
463  for(std::size_t j = 0; j < smallerLength && i + j < buffer.size(); ++j) {
464  const auto product = tBlock * static_cast<uint64_t>(smallerLine[j]) + carryOut;
465  const auto block = static_cast<uint64_t>(buffer[i + j]) + (product % blockBase);
466  carryOut = product / blockBase + block / blockBase;
467  buffer[i + j] = block % blockBase;
468  }
469 
470  if(smallerLength < blocksNumber && smallerLength + i < buffer.size())
471  buffer[smallerLength + i] += carryOut;
472  }
473 
474  return buffer;
475  };
476 
477  const std::size_t thisLength = this->filledBlocksNumber(), valueLength = factor.filledBlocksNumber();
478  if(thisLength > valueLength)
479  blocks = multiplyLines(blocks, thisLength, factor.blocks, valueLength);
480  else
481  blocks = multiplyLines(factor.blocks, valueLength, blocks, thisLength);
482 
483  return *this;
484  }
485  /* --------------------------------------------------------------------------- */
486 
487 
488  /* ------------------------ @name Division operators. ------------------------ */
495  [[nodiscard]]
496  gpu constexpr auto operator/(const Aeu& divisor) const noexcept -> Aeu {
497  Aeu quotient, _; divide(*this, divisor, quotient, _); return quotient;
498  }
499 
506  gpu constexpr auto operator/=(const Aeu& divisor) noexcept -> Aeu& {
507  return this->operator=(this->operator/(divisor));
508  }
509  /* --------------------------------------------------------------------------- */
510 
511 
512  /* ------------------------- @name Modulo operators. ------------------------- */
518  [[nodiscard]]
519  gpu constexpr auto operator%(const Aeu& modulo) const noexcept -> Aeu {
520  Aeu _, remainder; divide(*this, modulo, _, remainder); return remainder;
521  }
522 
528  gpu constexpr auto operator%=(const Aeu& modulo) noexcept -> Aeu& {
529  return this->operator=(this->operator%(modulo));
530  }
531  /* --------------------------------------------------------------------------- */
532  /* ----------------------------------------------------------------------- */
533 
534 
535  /* ----------------------- @name Bitwise operators. ---------------------- */
540  [[nodiscard]]
541  gpu constexpr auto operator~() const noexcept -> Aeu {
542  Aeu result;
543  for(std::size_t i = 0; i < blocksNumber; ++i)
544  result.blocks[i] = ~blocks[i];
545  return result;
546  }
547 
553  [[nodiscard]]
554  gpu constexpr auto operator^(const Aeu& other) const noexcept -> Aeu {
555  Aeu result = *this; result ^= other; return result;
556  }
557 
563  gpu constexpr auto operator^=(const Aeu& other) noexcept -> Aeu& {
564  for(std::size_t i = 0; i < blocksNumber; ++i)
565  blocks[i] ^= other.blocks[i];
566  return *this;
567  }
568 
574  [[nodiscard]]
575  gpu constexpr auto operator&(const Aeu& other) const noexcept -> Aeu {
576  Aeu result = *this; result &= other; return result;
577  }
578 
584  gpu constexpr auto operator&=(const Aeu& other) noexcept -> Aeu& {
585  for(std::size_t i = 0; i < blocksNumber; ++i)
586  blocks[i] &= other.blocks[i];
587  return *this;
588  }
589 
595  [[nodiscard]]
596  gpu constexpr auto operator|(const Aeu& other) const noexcept -> Aeu {
597  Aeu result = *this; result |= other; return result;
598  }
599 
605  gpu constexpr auto operator|=(const Aeu& other) noexcept -> Aeu& {
606  for(std::size_t i = 0; i < blocksNumber; ++i)
607  blocks[i] |= other.blocks[i];
608  return *this;
609  }
610 
617  template <typename Unsigned> requires (std::is_integral_v<Unsigned> && std::is_unsigned_v<Unsigned>) [[nodiscard]]
618  gpu constexpr auto operator<<(Unsigned bitShift) const noexcept -> Aeu {
619  Aeu result = *this; result.operator<<=(bitShift); return result;
620  }
621 
628  template <typename Unsigned> requires (std::is_integral_v<Unsigned> && std::is_unsigned_v<Unsigned>)
629  gpu constexpr auto operator<<=(Unsigned bitShift) noexcept -> Aeu& {
630  if(bitShift >= bitness || bitShift == 0) return *this;
631 
632  const std::size_t quotient = bitShift / blockBitLength, remainder = bitShift % blockBitLength;
633  const block stamp = (1UL << (blockBitLength - remainder)) - 1;
634 
635  for (long long i = blocksNumber - 1; i >= (quotient + (remainder ? 1 : 0)); --i)
636  blocks[i] = ((blocks[i - quotient] & stamp) << remainder) | ((blocks[i - quotient - (remainder ? 1 : 0)] & ~stamp) >> ((blockBitLength - remainder) % blockBitLength));
637 
638  blocks[quotient] = (blocks[0] & stamp) << remainder;
639 
640  for (std::size_t i = 0; i < quotient; ++i)
641  blocks[i] = 0;
642  return *this;
643  }
644 
651  template <typename Unsigned> requires (std::is_integral_v<Unsigned> && std::is_unsigned_v<Unsigned>) [[nodiscard]]
652  gpu constexpr auto operator>>(Unsigned bitShift) const noexcept -> Aeu {
653  Aeu result = *this; result >>= bitShift; return result;
654  }
655 
662  template <typename Unsigned> requires (std::is_integral_v<Unsigned> && std::is_unsigned_v<Unsigned>)
663  gpu constexpr auto operator>>=(Unsigned bitShift) noexcept -> Aeu& {
664  if(bitShift >= bitness || bitShift == 0) return *this;
665 
666  const std::size_t quotient = bitShift / blockBitLength, remainder = bitShift % blockBitLength;
667  const block stamp = (1UL << remainder) - 1;
668 
669  for(std::size_t i = 0; i < blocksNumber - (quotient + (remainder ? 1 : 0)); ++i)
670  blocks[i] = ((blocks[i + quotient + (remainder ? 1 : 0)] & stamp) << ((blockBitLength - remainder) % blockBitLength)) | ((blocks[i + quotient] & ~stamp) >> remainder);
671 
672  blocks[blocksNumber - 1 - quotient] = (blocks[blocksNumber - 1] & ~stamp) >> remainder;
673 
674  for(long long i = blocksNumber - quotient; i < blocksNumber; ++i)
675  blocks[i] = 0;
676  return *this;
677  }
678  /* ----------------------------------------------------------------------- */
679 
680 
681  /* --------------------- @name Comparison operators. --------------------- */
682  /* ------------------------ @name Equality operators. ------------------------ */
688  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned> && sizeof(Unsigned) < 8)
689  gpu constexpr auto operator==(Unsigned other) const noexcept -> bool {
690  return filledBlocksNumber() <= 1 && static_cast<block>(other) == blocks[0];
691  }
692  gpu constexpr auto operator==(uint64_t other) const noexcept -> bool {
693  return filledBlocksNumber() <= 2 && ((static_cast<uint64_t>(blocks[1]) << blockBitLength) | static_cast<uint64_t>(blocks[0])) == other;
694  }
695 
701  gpu constexpr auto operator==(const Aeu& other) const noexcept -> bool { return blocks == other.blocks; };
702 
708  template <std::size_t otherBitness> requires (otherBitness != bitness)
709  gpu constexpr auto operator==(const Aeu<otherBitness>& other) const noexcept -> bool { return compareTo(other) == Comparison::equal; };
710  /* --------------------------------------------------------------------------- */
711 
712 
713  /* ----------------------- @name Comparison operators. ----------------------- */
720  template <typename Unsigned> requires (std::is_unsigned_v<Unsigned> && sizeof(Unsigned) < 8) [[nodiscard]]
721  gpu constexpr auto compareTo(Unsigned other) const noexcept -> Comparison {
722  if(filledBlocksNumber() > 1) return Comparison::greater;
723  const auto cmp = static_cast<block>(other);
724  if(blocks[0] > cmp)
725  return Comparison::greater;
726  else if(blocks[0] < cmp)
727  return Comparison::less; else return Comparison::equal;
728  }
729 
736  [[nodiscard]]
737  gpu constexpr auto compareTo(uint64_t other) const noexcept -> Comparison {
738  if(filledBlocksNumber() > 2) return Comparison::greater;
739  const auto base = (static_cast<uint64_t>(blocks[1]) << blockBitLength) | static_cast<uint64_t>(blocks[0]);
740  if(base > other)
741  return Comparison::greater; else if(base < other) return Comparison::less; else return Comparison::equal;
742  }
743 
750  template <std::size_t otherBitness = bitness> /*requires (otherBitness != bitness)*/ [[nodiscard]]
751  gpu constexpr auto compareTo(const Aeu<otherBitness>& other) const noexcept -> Comparison {
752  /* First compare in common precision. */
753  const auto lowerBlockBorder = (blocksNumber < other.totalBlocksNumber() ? blocksNumber : other.totalBlocksNumber());
754  for(long long i = lowerBlockBorder - 1; i >= 0; --i) {
755  const block thisBlock = blocks[i], otherBlock = other.getBlock(i);
756  if(thisBlock != otherBlock)
757  return (thisBlock > otherBlock ? Comparison::greater : Comparison::less);
758  }
759 
760  if constexpr (otherBitness != blocksNumber * blockBitLength) {
761  /* If larger number contains data out of lower number's range, it's greater. */
762  if (other.totalBlocksNumber() > blocksNumber) {
763  for (long long i = other.totalBlocksNumber() - 1; i > lowerBlockBorder - 1; --i)
764  if (other.getBlock(i) != 0)
765  return Comparison::less;
766  } else if (blocksNumber > other.totalBlocksNumber()) {
767  for (long long i = blocksNumber - 1; i > lowerBlockBorder - 1; --i)
768  if (blocks[i] != 0)
769  return Comparison::greater;
770  }
771  }
772 
773  return Comparison::equal;
774  }
775  /* --------------------------------------------------------------------------- */
776 
777 
778  /* ------------------------ @name Spaceship operators. ----------------------- */
779 #if (defined(__CUDACC__) || __cplusplus < 202002L || defined (PRE_CPP_20)) && !defined DOXYGEN_SKIP
783  gpu constexpr auto operator!=(const Aeu& value) const noexcept -> bool { return !this->operator==(value); }
784  gpu constexpr auto operator<(const Aeu& value) const noexcept -> bool { return this->compareTo(value) == Comparison::less; }
785  gpu constexpr auto operator<=(const Aeu& value) const noexcept -> bool { return !this->operator>(value); }
786  gpu constexpr auto operator>(const Aeu& value) const noexcept -> bool { return this->compareTo(value) == Comparison::greater; }
787  gpu constexpr auto operator>=(const Aeu& value) const noexcept -> bool { return !this->operator<(value); }
788 #else
795  gpu constexpr auto operator<=>(const Aeu& other) const noexcept -> std::strong_ordering {
796  switch(this->compareTo(other)) {
797  case Comparison::less:
798  return std::strong_ordering::less;
799  case Comparison::greater:
800  return std::strong_ordering::greater;
801  case Comparison::equal:
802  return std::strong_ordering::equal;
803  default:
804  return std::strong_ordering::equivalent;
805  }
806  };
807 
814  template <typename Unsigned>
815  gpu constexpr auto operator<=>(const Unsigned& other) const noexcept -> std::strong_ordering {
816  switch(this->compareTo(other)) {
817  case Comparison::less:
818  return std::strong_ordering::less;
819  case Comparison::greater:
820  return std::strong_ordering::greater;
821  case Comparison::equal:
822  return std::strong_ordering::equal;
823  default:
824  return std::strong_ordering::equivalent;
825  }
826  };
827 #endif
828  /* --------------------------------------------------------------------------- */
829  /* ----------------------------------------------------------------------- */
830 
831 
832  /* ---------------------- @name Supporting methods. ---------------------- */
839  gpu constexpr auto setBit(std::size_t index, bool bit) noexcept -> void {
840 #ifndef AESI_UNSAFE
841  if(index >= bitness) return;
842 #endif
843  const std::size_t blockNumber = index / blockBitLength, bitNumber = index % blockBitLength;
844  assert(blockNumber < blocksNumber && bitNumber < blockBitLength);
845  if(bit)
846  blocks[blockNumber] |= (1U << bitNumber);
847  else
848  blocks[blockNumber] &= (~(1U << bitNumber));
849  }
850 
857  [[nodiscard]]
858  gpu constexpr auto getBit(std::size_t index) const noexcept -> bool {
859 #ifndef AESI_UNSAFE
860  if(index >= bitness) return false;
861 #endif
862  const std::size_t blockNumber = index / blockBitLength, bitNumber = index % blockBitLength;
863  assert(blockNumber < blocksNumber && bitNumber < blockBitLength);
864  return blocks[blockNumber] & (1U << bitNumber);
865  }
866 
873  gpu constexpr auto setByte(std::size_t index, byte byte) noexcept -> void {
874 #ifndef AESI_UNSAFE
875  if(index > blocksNumber * sizeof(block)) return;
876 #endif
877  const std::size_t blockNumber = index / sizeof(block), byteInBlock = index % sizeof(block), shift = byteInBlock * bitsInByte;
878  assert(blockNumber < blocksNumber && byteInBlock < sizeof(block));
879  blocks[blockNumber] &= ~(0xffU << shift); blocks[blockNumber] |= static_cast<block>(byte) << shift;
880  }
881 
888  [[nodiscard]]
889  gpu constexpr auto getByte(std::size_t index) const noexcept -> byte {
890 #ifndef AESI_UNSAFE
891  if(index > blocksNumber * sizeof(block)) return 0;
892 #endif
893  const std::size_t blockNumber = index / sizeof(block), byteInBlock = index % sizeof(block), shift = byteInBlock * bitsInByte;
894  assert(blockNumber < blocksNumber && byteInBlock < sizeof(block));
895  return (blocks[blockNumber] & (0xffU << shift)) >> shift;
896  }
897 
904  gpu constexpr auto setBlock(std::size_t index, block block) noexcept -> void {
905 #ifndef AESI_UNSAFE
906  if(index >= blocksNumber) return;
907 #endif
908  blocks[index] = block;
909  }
910 
917  [[nodiscard]]
918  gpu constexpr auto getBlock(std::size_t index) const noexcept -> block {
919 #ifndef AESI_UNSAFE
920  if(index >= blocksNumber) return block();
921 #endif
922  return blocks[index];
923  }
924 
929  [[nodiscard]]
930  gpu constexpr auto byteCount() const noexcept -> std::size_t {
931  std::size_t lastBlock = blocksNumber - 1;
932  for(; lastBlock > 0 && blocks[lastBlock] == 0; --lastBlock) ;
933 
934  for(int8_t byteN = sizeof(block) - 1; byteN >= 0; --byteN) {
935  if((blocks[lastBlock] & (0xffU << (byteN * bitsInByte))) >> (byteN * bitsInByte))
936  return lastBlock * sizeof(block) + byteN + 1;
937  }
938  return lastBlock * sizeof(block);
939  }
940 
945  [[nodiscard]]
946  gpu constexpr auto bitCount() const noexcept -> std::size_t {
947  std::size_t lastBlock = blocksNumber - 1;
948  for(; lastBlock > 0 && blocks[lastBlock] == 0; --lastBlock);
949 
950  for(int8_t byteN = sizeof(block) - 1; byteN >= 0; --byteN) {
951  const auto byte = (blocks[lastBlock] & (0xffU << (byteN * bitsInByte))) >> (byteN * bitsInByte);
952  if(!byte) continue;
953 
954  for(int8_t bitN = bitsInByte - 1; bitN >= 0; --bitN) {
955  if((byte & (0x1u << bitN)) >> bitN)
956  return (lastBlock * sizeof(block) + byteN) * bitsInByte + bitN + 1;
957  }
958  return ((lastBlock - 1) * sizeof(block) + byteN) * bitsInByte;
959  }
960  return lastBlock * sizeof(block);
961  }
962 
967  [[nodiscard]]
968  gpu constexpr auto isOdd() const noexcept -> bool { return (0x1 & blocks[0]) == 1; }
969 
974  [[nodiscard]]
975  gpu constexpr auto isEven() const noexcept -> bool { return (0x1 & blocks[0]) == 0; }
976 
981  [[nodiscard]]
982  gpu constexpr auto isZero() const noexcept -> bool { return filledBlocksNumber() == 0; }
983 
988  [[nodiscard]]
989  gpu constexpr auto filledBlocksNumber() const noexcept -> std::size_t {
990  for(long long i = blocksNumber - 1; i >= 0; --i)
991  if(blocks[i]) return i + 1;
992  return 0;
993  }
994 
999  [[nodiscard]]
1000  gpu static constexpr auto getBitness() noexcept -> std::size_t { return bitness; }
1001 
1006  [[nodiscard]]
1007  gpu static constexpr auto totalBlocksNumber() noexcept -> std::size_t { return blocksNumber; }
1008 
1013  gpu constexpr auto swap(Aeu& other) noexcept -> void {
1014  Aeu t = other; other.operator=(*this); this->operator=(t);
1015  }
1016  /* ----------------------------------------------------------------------- */
1017 
1018 
1019  /* -------------- @name Public arithmetic and number theory. ------------- */
1028  gpu static constexpr auto divide(const Aeu& number, const Aeu& divisor, Aeu& quotient, Aeu& remainder) noexcept -> void {
1029  const auto ratio = number.compareTo(divisor);
1030 
1031  quotient = Aeu {}; remainder = Aeu {};
1032 
1033  if(ratio == Comparison::greater) {
1034  const auto bitsUsed = number.filledBlocksNumber() * blockBitLength;
1035  for(long long i = bitsUsed - 1; i >= 0; --i) {
1036  remainder <<= 1u;
1037  remainder.setBit(0, number.getBit(i));
1038 
1039  if(remainder >= divisor) {
1040  remainder -= divisor;
1041  quotient.setBit(i, true);
1042  }
1043  }
1044  } else if(ratio == Comparison::less)
1045  remainder = number; else quotient = 1u;
1046  }
1047 
1054  [[nodiscard]]
1055  gpu static constexpr auto divide(const Aeu& number, const Aeu& divisor) noexcept -> pair<Aeu, Aeu> {
1056  pair<Aeu, Aeu> results; divide(number, divisor, results.first, results.second); return results;
1057  }
1058 
1068  gpu static constexpr auto gcd(const Aeu& first, const Aeu& second, Aeu& bezoutX, Aeu& bezoutY) noexcept -> Aeu {
1069  Aeu gcd, quotient, remainder;
1070 
1071  const auto ratio = first.compareTo(second);
1072  if(ratio == Comparison::greater) {
1073  gcd = second;
1074  divide(first, second, quotient, remainder);
1075  } else {
1076  gcd = first;
1077  divide(second, first, quotient, remainder);
1078  }
1079 
1080  bezoutX = 0u; bezoutY = 1u;
1081  for(Aeu tX = 1u, tY = 0u; remainder != 0u; ) {
1082  Aeu tGcd = gcd; gcd = remainder;
1083 
1084  Aeu t = bezoutX; bezoutX = tX - quotient * bezoutX; tX = t;
1085  t = bezoutY; bezoutY = tY - quotient * bezoutY; tY = t;
1086 
1087  divide(tGcd, gcd, quotient, remainder);
1088  }
1089 
1090  if(ratio != Comparison::greater)
1091  bezoutX.swap(bezoutY);
1092 
1093  return gcd;
1094  }
1095 
1103  gpu static constexpr auto gcd(const Aeu& first, const Aeu& second) noexcept -> Aeu {
1104  Aeu gcd, quotient, remainder;
1105 
1106  const auto ratio = first.compareTo(second);
1107  if(ratio == Comparison::greater) {
1108  gcd = second;
1109  divide(first, second, quotient, remainder);
1110  } else {
1111  gcd = first;
1112  divide(second, first, quotient, remainder);
1113  }
1114 
1115  for(Aeu tX = 1u, tY = 0u; remainder != 0u; ) {
1116  Aeu tGcd = gcd; gcd = remainder;
1117  divide(tGcd, gcd, quotient, remainder);
1118  }
1119 
1120  return gcd;
1121  }
1122 
1129  [[nodiscard]]
1130  gpu static constexpr auto lcm(const Aeu& first, const Aeu& second) noexcept -> Aeu { return first / gcd(first, second) * second; }
1131 
1141  template <std::size_t powerBitness = bitness> [[nodiscard]]
1142  gpu static constexpr auto powm(const Aeu& base, const Aeu<powerBitness>& power, const Aeu& modulo) noexcept -> Aeu {
1143  if(base == 1u)
1144  return base;
1145  if(base == 0u)
1146  return { 1u };
1147 
1148  Aeu output = 1u;
1149  auto [_, b] = divide(base, modulo);
1150 
1151  for(unsigned iteration = 0; power.filledBlocksNumber() * blockBitLength != iteration; iteration++) {
1152  if(power.getBit(iteration)) {
1153  const auto [quotient, remainder] = divide(output * b, modulo);
1154  output = remainder;
1155  }
1156 
1157  const auto [quotient, remainder] = divide(b * b, modulo);
1158  b = remainder;
1159  }
1160 
1161  return output;
1162  }
1163 
1164  [[nodiscard]]
1165  gpu static constexpr auto powm(const Aeu& base, const Aeu& power, const Aeu& mod) noexcept -> Aeu {
1166  Aeu output = 1u;
1167  auto [_, b] = divide(base, mod);
1168 
1169  for(std::size_t iteration = 0; power.filledBlocksNumber() * blockBitLength != iteration; iteration++) {
1170  if(power.getBit(iteration)) {
1171  const auto [quotient, remainder] = divide(output * b, mod);
1172  output = remainder;
1173  }
1174 
1175  const auto [quotient, remainder] = divide(b * b, mod);
1176  b = remainder;
1177  }
1178 
1179  return output;
1180  }
1181 
1188  [[nodiscard]]
1189  gpu static constexpr auto power2(std::size_t power) noexcept -> Aeu { Aeu result {}; result.setBit(power, true); return result; }
1190 
1196  [[nodiscard]]
1197  gpu constexpr auto squareRoot() const noexcept -> Aeu {
1198  Aeu x, y = power2((bitCount() + 1) / 2);
1199 
1200  do {
1201  x = y;
1202  y = (x + this->operator/(x)) >> 1u;
1203  } while (y < x);
1204 
1205  return x;
1206  }
1207  /* ----------------------------------------------------------------------- */
1208 
1209 
1210  /* ----------------- @name Public input-output operators. ---------------- */
1221  template <byte base, typename Char> requires (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t> && (base == 2 || base == 8 || base == 10 || base == 16))
1222  gpu constexpr auto getString(Char* const buffer, std::size_t bufferSize, bool showBase = false, bool hexUppercase = false) const noexcept -> std::size_t {
1223  if(bufferSize < 2) return 0;
1224 
1225  std::size_t position = 0;
1226 
1227  if (showBase && bufferSize > 3) {
1228  if constexpr (base == 2) {
1229  if constexpr (std::is_same_v<Char, char>) {
1230  buffer[0] = '0'; buffer[1] = 'b';
1231  // memcpy(buffer, "0b", 2 * sizeof(Char));
1232  } else {
1233  buffer[0] = L'0'; buffer[1] = L'b';
1234  // memcpy(buffer, L"0b", 2 * sizeof(Char));
1235  }
1236  position += 2;
1237  } else if constexpr (base == 8) {
1238  if constexpr (std::is_same_v<Char, char>) {
1239  buffer[0] = '0'; buffer[1] = 'o';
1240  // memcpy(buffer, "0o", 2 * sizeof(Char));
1241  } else {
1242  buffer[0] = L'0'; buffer[1] = L'o';
1243  // memcpy(buffer, L"0o", 2 * sizeof(Char));
1244  }
1245  position += 2;
1246  } else if constexpr (base == 16) {
1247  if constexpr (std::is_same_v<Char, char>) {
1248  buffer[0] = '0'; buffer[1] = 'x';
1249  // memcpy(buffer, "0x", 2 * sizeof(Char));
1250  } else {
1251  buffer[0] = L'0'; buffer[1] = L'x';
1252  // memcpy(buffer, L"0x", 2 * sizeof(Char));
1253  }
1254  position += 2;
1255  }
1256  }
1257 
1258  if(isZero()) {
1259  buffer[position++] = [] { if constexpr (std::is_same_v<Char, char>) { return '0'; } else { return L'0'; }}();
1260  return position;
1261  }
1262 
1263  if constexpr (base == 16) {
1264  long long iter = blocks.size() - 1;
1265  for (; blocks[iter] == 0 && iter >= 0; --iter);
1266 
1267  if constexpr (std::is_same_v<Char, char>) {
1268  position += snprintf(buffer + position, bufferSize - position, (hexUppercase ? "%X" : "%x"), blocks[iter--]);
1269  for (; iter >= 0; --iter)
1270  position += snprintf(buffer + position, bufferSize - position, (hexUppercase ? "%08X" : "%08x"), blocks[iter]);
1271  } else {
1272  position += swprintf(buffer + position, bufferSize - position, (hexUppercase ? L"%X" : L"%x"), blocks[iter--]);
1273  for (; iter >= 0; --iter)
1274  position += swprintf(buffer + position, bufferSize - position, (hexUppercase ? L"%08X" : L"%08x"), blocks[iter]);
1275  }
1276  } else {
1277  const auto startPosition = position;
1278 
1279  Aeu copy = *this;
1280  while (!copy.isZero() && position < bufferSize) {
1281  auto [quotient, remainder] = divide(copy, base);
1282  if constexpr (std::is_same_v<Char, char>) {
1283  buffer[position++] = '0' + remainder.template integralCast<byte>();
1284  } else {
1285  buffer[position++] = L'0' + remainder.template integralCast<byte>();
1286  }
1287  copy = quotient;
1288  }
1289  const auto digitsTotal = position - startPosition;
1290  for (std::size_t i = 0; i * 2 < digitsTotal; ++i) {
1291  Char t = buffer[startPosition + i];
1292  buffer[startPosition + i] = buffer[startPosition + digitsTotal - 1 - i];
1293  buffer[startPosition + digitsTotal - 1 - i] = t;
1294  }
1295  }
1296  buffer[position++] = Char {};
1297  return position;
1298  }
1299 
1311  template <typename Char> requires (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>)
1312  friend constexpr auto operator<<(std::basic_ostream<Char>& os, const Aeu& number) -> std::basic_ostream<Char>& {
1313  auto flags = os.flags();
1314 
1315  const auto base = [] (long baseField, std::basic_ostream<Char>& ss, bool showbase) {
1316  auto base = (baseField == std::ios::hex ? 16u : (baseField == std::ios::oct ? 8u : 10u));
1317  if(showbase && base != 10)
1318  ss << [&base] { if constexpr (std::is_same_v<Char, char>) { return base == 8 ? "0o" : "0x"; } else { return base == 8 ? L"0o" : L"0x"; }} () << std::noshowbase ;
1319  return base;
1320  } (flags & std::ios::basefield, os, flags & std::ios::showbase);
1321 
1322  if(number.isZero())
1323  return os << '0';
1324 
1325  if(base == 16) {
1326  long long iter = number.blocks.size() - 1;
1327  for(; number.blocks[iter] == 0 && iter >= 0; --iter) ;
1328 
1329  os << number.blocks[iter--];
1330  for (; iter >= 0; --iter) {
1331  os.fill([] { if constexpr (std::is_same_v<Char, char>) { return '0'; } else { return L'0'; } } ());
1332  os.width(8); os << std::right << number.blocks[iter];
1333  }
1334  } else {
1335  /* Well, here we use a pre-calculated magic number to ratio the length of numbers in decimal or octal notation according to bitness.
1336  * * It is 2.95-98 for octal and 3.2 for decimal. */
1337  constexpr auto bufferSize = static_cast<std::size_t>(static_cast<double>(bitness) / 2.95);
1338  Char buffer [bufferSize] {}; std::size_t filled = 0;
1339 
1340  Aeu copy = number;
1341  while(!copy.isZero() && filled < bufferSize) {
1342  const auto [quotient, remainder] = divide(copy, base);
1343  buffer[filled++] = [] { if constexpr (std::is_same_v<Char, char>) { return '0'; } else { return L'0'; } } () + remainder.template integralCast<byte>();
1344  copy = quotient;
1345  }
1346 
1347  for(; filled > 0; --filled)
1348  os << buffer[filled - 1];
1349  }
1350 
1351  return os;
1352  }
1353 
1354 
1363  template <typename Char> requires (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>)
1364  constexpr auto readBinary(std::basic_istream<Char>& is, bool bigEndian = true) -> void {
1365  blocks = {};
1366  if(bigEndian) {
1367  for(auto it = blocks.rbegin(); it != blocks.rend(); ++it)
1368  if(!is.read(reinterpret_cast<char*>(&*it), sizeof(block))) break;
1369  } else {
1370  for(auto& tBlock: blocks)
1371  if(!is.read(reinterpret_cast<char*>(&tBlock), sizeof(block))) break;
1372  }
1373  }
1374 
1375 
1382  template <typename Char> requires (std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>)
1383  constexpr auto writeBinary(std::basic_ostream<Char>& os, bool bigEndian = true) const noexcept -> void {
1384  if(bigEndian) {
1385  for(auto it = blocks.rbegin(); it != blocks.rend(); ++it)
1386  if(!os.write(reinterpret_cast<const char*>(&*it), sizeof(block))) break;
1387  } else {
1388  for(auto& block: blocks)
1389  if(!os.write(reinterpret_cast<const char*>(&block), sizeof(block))) break;
1390  }
1391  }
1392  /* ----------------------------------------------------------------------- */
1393 
1394 
1395  /* -------------------- @name Public casting operators. ------------------ */
1401  template <typename Integral> requires (std::is_integral_v<Integral>) [[nodiscard]]
1402  gpu constexpr auto integralCast() const noexcept -> Integral {
1403  const uint64_t value = (static_cast<uint64_t>(blocks[1]) << blockBitLength) | static_cast<uint64_t>(blocks[0]);
1404  return static_cast<Integral>(value);
1405  }
1406 
1415  template <std::size_t newBitness> requires (newBitness != bitness) [[nodiscard]]
1416  gpu constexpr auto precisionCast() const noexcept -> Aeu<newBitness> {
1417  Aeu<newBitness> result {};
1418 
1419  const std::size_t blockBoarder = (newBitness > bitness ? Aeu<bitness>::totalBlocksNumber() : Aeu<newBitness>::totalBlocksNumber());
1420  for(std::size_t blockIdx = 0; blockIdx < blockBoarder; ++blockIdx)
1421  result.setBlock(blockIdx, getBlock(blockIdx));
1422 
1423  return result;
1424  }
1425  /* ----------------------------------------------------------------------- */
1426 
1427 
1428 #if defined __CUDACC__
1429  /* ------------------- @name Atomic-like CUDA operators. ----------------- */
1436  __device__ constexpr auto tryAtomicSet(const Aeu& value) noexcept -> void {
1437  for(std::size_t i = 0; i < blocksNumber; ++i)
1438  atomicExch(&blocks[i], value.blocks[i]);
1439  }
1440 
1447  __device__ constexpr auto tryAtomicExchange(const Aeu& value) noexcept -> void {
1448  for(std::size_t i = 0; i < blocksNumber; ++i)
1449  atomicExch(&value.blocks[i], atomicExch(&blocks[i], value.blocks[i]));
1450  }
1451  /* ----------------------------------------------------------------------- */
1452 #endif
1453 };
1454 
1455 /* -------------------------------------------- @name Type-definitions ------------------------------------------- */
1460 
1465 
1470 
1475 
1480 
1485 
1490 
1495 
1500 
1505 
1510 /* ---------------------------------------------------------------------------------------------------------------- */
1511 
1512 /* ------------------------------------------ @name Integral conversions ----------------------------------------- */
1519 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1520 gpu constexpr auto operator+(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) + value; }
1521 
1528 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1529 gpu constexpr auto operator-(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) - value; }
1530 
1537 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1538 gpu constexpr auto operator*(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) * value; }
1539 
1546 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1547 gpu constexpr auto operator/(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) / value; }
1548 
1555 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1556 gpu constexpr auto operator%(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) % value; }
1557 
1564 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1565 gpu constexpr auto operator^(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) ^ value; }
1566 
1573 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1574 gpu constexpr auto operator&(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) & value; }
1575 
1582 template <std::size_t bitness, typename Integral> requires (std::is_integral_v<Integral>)
1583 gpu constexpr auto operator|(Integral number, const Aeu<bitness>& value) noexcept { return Aeu<bitness>(number) | value; }
1584 /* ---------------------------------------------------------------------------------------------------------------- */
1585 
1586 #endif //AEU_MULTIPRECISION
requires(bitness % blockBitLength==0) class Aeu final
Definition: Aeu.h:56
Long precision unsigned integer.