1 #ifndef AEU_MULTIPRECISION
2 #define AEU_MULTIPRECISION
9 #define gpu __host__ __device__
10 #include <cuda/std/utility>
11 #include <cuda/std/array>
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
23 #ifdef AESI_CRYPTOPP_INTEGRATION
24 #include <cryptopp/integer.h>
27 #ifdef AESI_GMP_INTEGRATION
38 using block = uint32_t;
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;
48 enum Comparison { equal = 0, less = 1, greater = 2, equivalent = 3 };
56 template <std::
size_t bitness = 512>
requires (bitness % blockBitLength == 0)
58 static_assert(bitness >
sizeof(uint64_t),
"Use built-in types for numbers 64-bit or less.");
60 static constexpr std::size_t blocksNumber = bitness / blockBitLength;
63 template <
typename T1,
typename T2>
64 using pair = cuda::std::pair<T1, T2>;
65 using blockLine = cuda::std::array<block, blocksNumber>;
67 template<
typename T1,
typename T2>
68 using pair = std::pair<T1, T2>;
69 using blockLine = std::array<block, blocksNumber>;
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);
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);
115 gpu constexpr
Aeu() noexcept =
default;
120 gpu constexpr
Aeu(
const Aeu& copy) noexcept =
default;
126 gpu constexpr
Aeu& operator=(
const Aeu& other) noexcept { blocks = other.blocks;
return *
this; }
134 template <
typename Integral>
requires (std::is_unsigned_v<Integral>)
135 gpu constexpr
Aeu(Integral value) noexcept {
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);
143 makeComplement(blocks);
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;
158 constexpr
const Char* characters = [] {
159 if constexpr (std::is_same_v<char, Char>) {
160 return "09aAfFoObBxX";
162 return L
"09aAfFoObBxX";
165 std::size_t position = 0;
167 if constexpr (std::is_same_v<Char, char>) {
168 for(; !std::isalnum(data[position]) && position < size; ++position) ;
170 for(; !std::iswalnum(data[position]) && position < size; ++position) ;
173 const auto base = [&data, &size, &position, &characters] {
174 if (data[position] == characters[0] && size > position + 1) {
175 switch (data[position + 1]) {
178 position += 2;
return 2u;
181 position += 2;
return 8u;
184 position += 2;
return 16u;
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;
202 this->operator*=(base);
203 this->operator+=(digit);
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) {}
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()) {}
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()) {}
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));
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));
252 std::basic_string<block> buffer (tBlocksNumber, 0u);
253 mpz_export(buffer.data(), &tBlocksNumber, -1,
sizeof(block), -1, 0, value.get_mpz_t());
255 for (std::size_t i = 0; i < tBlocksNumber; ++i)
256 setBlock(i, buffer[i]);
270 gpu constexpr
auto operator+()
const noexcept ->
Aeu {
return *
this; }
277 gpu constexpr
auto operator-()
const noexcept ->
Aeu {
278 Aeu result = *
this; makeComplement(result.blocks);
return result;
285 gpu constexpr
auto operator++() noexcept ->
Aeu& {
286 for(std::size_t i = 0; i < blocksNumber; ++i) {
287 if(blocks[i] < blockMax) {
290 }
else blocks[i] = 0u;
299 gpu constexpr
auto operator++(
int) & noexcept ->
Aeu {
Aeu old = *
this; operator++();
return old; }
305 gpu constexpr
auto operator--() noexcept ->
Aeu& {
306 for(std::size_t i = 0; i < blocksNumber; ++i) {
310 }
else blocks[i] = blockMax;
319 gpu constexpr
auto operator--(
int) & noexcept ->
Aeu {
Aeu old = *
this; operator--();
return old; }
329 template <
typename Un
signed>
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;
340 gpu constexpr
auto operator+(
const Aeu& addendum)
const noexcept ->
Aeu {
341 Aeu result = *
this; result += addendum;
return result;
349 template <
typename Un
signed>
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;
363 gpu constexpr
auto operator+=(
const Aeu& addendum) noexcept ->
Aeu& {
364 addLine(blocks, addendum.blocks);
return *
this;
376 gpu constexpr
auto operator-(
const Aeu& subtrahend)
const noexcept ->
Aeu {
377 Aeu result = *
this; result -= subtrahend;
return result;
385 gpu constexpr
auto operator-=(
const Aeu& subtrahend) noexcept ->
Aeu& {
386 return this->operator+=(-subtrahend);
397 template <
typename Un
signed>
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;
408 gpu constexpr
auto operator*(
const Aeu& factor)
const noexcept ->
Aeu {
409 Aeu result = *
this; result *= factor;
return result;
418 template <
typename Un
signed>
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);
424 for(std::size_t i = 0; i < longerLength; ++i) {
425 uint64_t tBlock = blocks[i], carryOut = 0;
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;
434 if(smallerLength + i < buffer.size())
435 buffer[smallerLength + i] += carryOut;
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;
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) {
460 for(std::size_t i = 0; i < longerLength; ++i) {
461 uint64_t tBlock = longerLine[i], carryOut = 0;
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;
470 if(smallerLength < blocksNumber && smallerLength + i < buffer.size())
471 buffer[smallerLength + i] += carryOut;
477 const std::size_t thisLength = this->filledBlocksNumber(), valueLength = factor.filledBlocksNumber();
478 if(thisLength > valueLength)
479 blocks = multiplyLines(blocks, thisLength, factor.blocks, valueLength);
481 blocks = multiplyLines(factor.blocks, valueLength, blocks, thisLength);
496 gpu constexpr
auto operator/(
const Aeu& divisor)
const noexcept ->
Aeu {
497 Aeu quotient, _; divide(*
this, divisor, quotient, _);
return quotient;
506 gpu constexpr
auto operator/=(
const Aeu& divisor) noexcept ->
Aeu& {
507 return this->operator=(this->
operator/(divisor));
519 gpu constexpr
auto operator%(
const Aeu& modulo)
const noexcept ->
Aeu {
520 Aeu _, remainder; divide(*
this, modulo, _, remainder);
return remainder;
528 gpu constexpr
auto operator%=(
const Aeu& modulo) noexcept ->
Aeu& {
529 return this->operator=(this->
operator%(modulo));
541 gpu constexpr
auto operator~()
const noexcept ->
Aeu {
543 for(std::size_t i = 0; i < blocksNumber; ++i)
544 result.blocks[i] = ~blocks[i];
554 gpu constexpr
auto operator^(
const Aeu& other)
const noexcept ->
Aeu {
555 Aeu result = *
this; result ^= other;
return result;
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];
575 gpu constexpr
auto operator&(
const Aeu& other)
const noexcept ->
Aeu {
576 Aeu result = *
this; result &= other;
return result;
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];
596 gpu constexpr
auto operator|(
const Aeu& other)
const noexcept ->
Aeu {
597 Aeu result = *
this; result |= other;
return result;
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];
617 template <
typename Un
signed>
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;
628 template <
typename Un
signed>
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;
632 const std::size_t quotient = bitShift / blockBitLength, remainder = bitShift % blockBitLength;
633 const block stamp = (1UL << (blockBitLength - remainder)) - 1;
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));
638 blocks[quotient] = (blocks[0] & stamp) << remainder;
640 for (std::size_t i = 0; i < quotient; ++i)
651 template <
typename Un
signed>
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;
662 template <
typename Un
signed>
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;
666 const std::size_t quotient = bitShift / blockBitLength, remainder = bitShift % blockBitLength;
667 const block stamp = (1UL << remainder) - 1;
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);
672 blocks[blocksNumber - 1 - quotient] = (blocks[blocksNumber - 1] & ~stamp) >> remainder;
674 for(
long long i = blocksNumber - quotient; i < blocksNumber; ++i)
688 template <
typename Un
signed>
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];
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;
701 gpu constexpr
auto operator==(
const Aeu& other)
const noexcept ->
bool {
return blocks == other.blocks; };
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; };
720 template <
typename Un
signed>
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);
725 return Comparison::greater;
726 else if(blocks[0] < cmp)
727 return Comparison::less;
else return Comparison::equal;
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]);
741 return Comparison::greater;
else if(base < other)
return Comparison::less;
else return Comparison::equal;
750 template <std::
size_t otherBitness = bitness> [[nodiscard]]
751 gpu constexpr
auto compareTo(
const Aeu<otherBitness>& other)
const noexcept -> Comparison {
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);
760 if constexpr (otherBitness != blocksNumber * blockBitLength) {
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)
769 return Comparison::greater;
773 return Comparison::equal;
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); }
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;
804 return std::strong_ordering::equivalent;
814 template <
typename Un
signed>
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;
824 return std::strong_ordering::equivalent;
839 gpu constexpr
auto setBit(std::size_t index,
bool bit) noexcept ->
void {
841 if(index >= bitness)
return;
843 const std::size_t blockNumber = index / blockBitLength, bitNumber = index % blockBitLength;
844 assert(blockNumber < blocksNumber && bitNumber < blockBitLength);
846 blocks[blockNumber] |= (1U << bitNumber);
848 blocks[blockNumber] &= (~(1U << bitNumber));
858 gpu constexpr
auto getBit(std::size_t index)
const noexcept ->
bool {
860 if(index >= bitness)
return false;
862 const std::size_t blockNumber = index / blockBitLength, bitNumber = index % blockBitLength;
863 assert(blockNumber < blocksNumber && bitNumber < blockBitLength);
864 return blocks[blockNumber] & (1U << bitNumber);
873 gpu constexpr
auto setByte(std::size_t index,
byte byte) noexcept ->
void {
875 if(index > blocksNumber *
sizeof(block))
return;
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;
889 gpu constexpr
auto getByte(std::size_t index)
const noexcept ->
byte {
891 if(index > blocksNumber *
sizeof(block))
return 0;
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;
904 gpu constexpr
auto setBlock(std::size_t index, block block) noexcept ->
void {
906 if(index >= blocksNumber)
return;
908 blocks[index] = block;
918 gpu constexpr
auto getBlock(std::size_t index)
const noexcept -> block {
920 if(index >= blocksNumber)
return block();
922 return blocks[index];
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) ;
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;
938 return lastBlock *
sizeof(block);
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);
950 for(int8_t byteN =
sizeof(block) - 1; byteN >= 0; --byteN) {
951 const auto byte = (blocks[lastBlock] & (0xffU << (byteN * bitsInByte))) >> (byteN * bitsInByte);
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;
958 return ((lastBlock - 1) *
sizeof(block) + byteN) * bitsInByte;
960 return lastBlock *
sizeof(block);
968 gpu constexpr
auto isOdd()
const noexcept ->
bool {
return (0x1 & blocks[0]) == 1; }
975 gpu constexpr
auto isEven()
const noexcept ->
bool {
return (0x1 & blocks[0]) == 0; }
982 gpu constexpr
auto isZero()
const noexcept ->
bool {
return filledBlocksNumber() == 0; }
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;
1000 gpu
static constexpr
auto getBitness() noexcept -> std::size_t {
return bitness; }
1007 gpu
static constexpr
auto totalBlocksNumber() noexcept -> std::size_t {
return blocksNumber; }
1013 gpu constexpr
auto swap(
Aeu& other) noexcept ->
void {
1014 Aeu t = other; other.operator=(*this); this->operator=(t);
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);
1031 quotient =
Aeu {}; remainder =
Aeu {};
1033 if(ratio == Comparison::greater) {
1034 const auto bitsUsed = number.filledBlocksNumber() * blockBitLength;
1035 for(
long long i = bitsUsed - 1; i >= 0; --i) {
1037 remainder.setBit(0, number.getBit(i));
1039 if(remainder >= divisor) {
1040 remainder -= divisor;
1041 quotient.setBit(i,
true);
1044 }
else if(ratio == Comparison::less)
1045 remainder = number;
else quotient = 1u;
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;
1068 gpu
static constexpr
auto gcd(
const Aeu& first,
const Aeu& second,
Aeu& bezoutX,
Aeu& bezoutY) noexcept ->
Aeu {
1069 Aeu gcd, quotient, remainder;
1071 const auto ratio = first.compareTo(second);
1072 if(ratio == Comparison::greater) {
1074 divide(first, second, quotient, remainder);
1077 divide(second, first, quotient, remainder);
1080 bezoutX = 0u; bezoutY = 1u;
1081 for(
Aeu tX = 1u, tY = 0u; remainder != 0u; ) {
1082 Aeu tGcd = gcd; gcd = remainder;
1084 Aeu t = bezoutX; bezoutX = tX - quotient * bezoutX; tX = t;
1085 t = bezoutY; bezoutY = tY - quotient * bezoutY; tY = t;
1087 divide(tGcd, gcd, quotient, remainder);
1090 if(ratio != Comparison::greater)
1091 bezoutX.swap(bezoutY);
1103 gpu
static constexpr
auto gcd(
const Aeu& first,
const Aeu& second) noexcept ->
Aeu {
1104 Aeu gcd, quotient, remainder;
1106 const auto ratio = first.compareTo(second);
1107 if(ratio == Comparison::greater) {
1109 divide(first, second, quotient, remainder);
1112 divide(second, first, quotient, remainder);
1115 for(
Aeu tX = 1u, tY = 0u; remainder != 0u; ) {
1116 Aeu tGcd = gcd; gcd = remainder;
1117 divide(tGcd, gcd, quotient, remainder);
1130 gpu
static constexpr
auto lcm(
const Aeu& first,
const Aeu& second) noexcept ->
Aeu {
return first / gcd(first, second) * second; }
1141 template <std::
size_t powerBitness = bitness> [[nodiscard]]
1149 auto [_, b] = divide(base, modulo);
1151 for(
unsigned iteration = 0; power.filledBlocksNumber() * blockBitLength != iteration; iteration++) {
1152 if(power.getBit(iteration)) {
1153 const auto [quotient, remainder] = divide(output * b, modulo);
1157 const auto [quotient, remainder] = divide(b * b, modulo);
1165 gpu
static constexpr
auto powm(
const Aeu& base,
const Aeu& power,
const Aeu& mod) noexcept ->
Aeu {
1167 auto [_, b] = divide(base, mod);
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);
1175 const auto [quotient, remainder] = divide(b * b, mod);
1189 gpu
static constexpr
auto power2(std::size_t power) noexcept ->
Aeu {
Aeu result {}; result.setBit(power,
true);
return result; }
1197 gpu constexpr
auto squareRoot()
const noexcept ->
Aeu {
1198 Aeu x, y = power2((bitCount() + 1) / 2);
1202 y = (x + this->operator/(x)) >> 1u;
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;
1225 std::size_t position = 0;
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';
1233 buffer[0] = L
'0'; buffer[1] = L
'b';
1237 }
else if constexpr (base == 8) {
1238 if constexpr (std::is_same_v<Char, char>) {
1239 buffer[0] =
'0'; buffer[1] =
'o';
1242 buffer[0] = L
'0'; buffer[1] = L
'o';
1246 }
else if constexpr (base == 16) {
1247 if constexpr (std::is_same_v<Char, char>) {
1248 buffer[0] =
'0'; buffer[1] =
'x';
1251 buffer[0] = L
'0'; buffer[1] = L
'x';
1259 buffer[position++] = [] {
if constexpr (std::is_same_v<Char, char>) {
return '0'; }
else {
return L
'0'; }}();
1263 if constexpr (base == 16) {
1264 long long iter = blocks.size() - 1;
1265 for (; blocks[iter] == 0 && iter >= 0; --iter);
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]);
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]);
1277 const auto startPosition = position;
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>();
1285 buffer[position++] = L
'0' + remainder.template integralCast<byte>();
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;
1296 buffer[position++] = Char {};
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();
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 ;
1320 } (flags & std::ios::basefield, os, flags & std::ios::showbase);
1326 long long iter = number.blocks.size() - 1;
1327 for(; number.blocks[iter] == 0 && iter >= 0; --iter) ;
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];
1337 constexpr
auto bufferSize =
static_cast<std::size_t
>(
static_cast<double>(bitness) / 2.95);
1338 Char buffer [bufferSize] {}; std::size_t filled = 0;
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>();
1347 for(; filled > 0; --filled)
1348 os << buffer[filled - 1];
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 {
1367 for(
auto it = blocks.rbegin(); it != blocks.rend(); ++it)
1368 if(!is.read(
reinterpret_cast<char*
>(&*it),
sizeof(block)))
break;
1370 for(
auto& tBlock: blocks)
1371 if(!is.read(
reinterpret_cast<char*
>(&tBlock),
sizeof(block)))
break;
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 {
1385 for(
auto it = blocks.rbegin(); it != blocks.rend(); ++it)
1386 if(!os.write(
reinterpret_cast<const char*
>(&*it),
sizeof(block)))
break;
1388 for(
auto& block: blocks)
1389 if(!os.write(
reinterpret_cast<const char*
>(&block),
sizeof(block)))
break;
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);
1415 template <std::
size_t newBitness>
requires (newBitness != bitness) [[nodiscard]]
1416 gpu constexpr
auto precisionCast()
const noexcept ->
Aeu<newBitness> {
1420 for(std::size_t blockIdx = 0; blockIdx < blockBoarder; ++blockIdx)
1421 result.setBlock(blockIdx, getBlock(blockIdx));
1428 #if defined __CUDACC__
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]);
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]));
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
requires(bitness % blockBitLength==0) class Aeu final
Definition: Aeu.h:56
Long precision unsigned integer.