diff --git a/.gitmodules b/.gitmodules index 568dab1eb26..e1960d2144b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -268,6 +268,6 @@ [submodule "contrib/hashidsxx"] path = contrib/hashidsxx url = https://github.com/schoentoon/hashidsxx.git -[submodule "contrib/base58"] - path = contrib/base58 +[submodule "contrib/base-x"] + path = contrib/base-x url = https://github.com/Kronuz/base-x.git diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index a356ade7eb8..2ade6c139f6 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -153,7 +153,7 @@ endif() add_contrib (sqlite-cmake sqlite-amalgamation) add_contrib (s2geometry-cmake s2geometry) -add_contrib (base58-cmake base58) +add_contrib (base-x-cmake base-x) # Put all targets defined here and in subdirectories under "contrib/" folders in GUI-based IDEs. # Some of third-party projects may override CMAKE_FOLDER or FOLDER property of their targets, so they would not appear diff --git a/contrib/base58-cmake/CMakeLists.txt b/contrib/base-x-cmake/CMakeLists.txt similarity index 65% rename from contrib/base58-cmake/CMakeLists.txt rename to contrib/base-x-cmake/CMakeLists.txt index 26783e0177d..48cb54d307f 100644 --- a/contrib/base58-cmake/CMakeLists.txt +++ b/contrib/base-x-cmake/CMakeLists.txt @@ -1,13 +1,13 @@ -set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/base58") +set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/base-x") set (SRCS ${LIBRARY_DIR}/base_x.hh ${LIBRARY_DIR}/uinteger_t.hh ) -add_library(_base58 ${SRCS}) +add_library(_base-x ${SRCS}) -target_include_directories(_base58 SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) +target_include_directories(_base-x SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) if (XCODE OR XCODE_VERSION) # https://gitlab.kitware.com/cmake/cmake/issues/17457 @@ -16,7 +16,7 @@ if (XCODE OR XCODE_VERSION) if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/dummy.c") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.c" "") endif () - target_sources(_base58 PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/dummy.c") + target_sources(_base-x PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/dummy.c") endif () -add_library(ch_contrib::base58 ALIAS _base58) \ No newline at end of file +add_library(ch_contrib::base-x ALIAS _base-x) \ No newline at end of file diff --git a/contrib/base-x/.gitignore b/contrib/base-x/.gitignore new file mode 100644 index 00000000000..b63b40c8b71 --- /dev/null +++ b/contrib/base-x/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +test +*.o +*.dSYM \ No newline at end of file diff --git a/contrib/base-x/.travis.yml b/contrib/base-x/.travis.yml new file mode 100755 index 00000000000..f55132e614f --- /dev/null +++ b/contrib/base-x/.travis.yml @@ -0,0 +1,36 @@ +sudo: false + +language: cpp + +compiler: + - clang + - gcc + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - g++-6 + - clang-3.8 + +install: + - if [ "$CXX" = "g++" ]; then export CXX="g++-6"; fi + - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.8"; fi + - sudo apt-get install -qq git cmake + +before_script: + # not much better than git submodules, but there was never a need/want for the repo in this repo + - cd .. + - git clone https://github.com/google/googletest.git + - cd googletest + - git reset --hard d62d6c6556d96dda924382547c54a4b3afedb22c + - cmake CMakeLists.txt + - make + + - cd ../base-x/tests + - make + +script: + - make run diff --git a/contrib/base-x/LICENSE b/contrib/base-x/LICENSE new file mode 100644 index 00000000000..f7b3408abac --- /dev/null +++ b/contrib/base-x/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/contrib/base-x/README.md b/contrib/base-x/README.md new file mode 100644 index 00000000000..5dc4a068043 --- /dev/null +++ b/contrib/base-x/README.md @@ -0,0 +1,97 @@ +# base-x [![License][license-img]][license-url] [![GitHub Stars][stars-img]][stars-url] [![GitHub Forks][forks-img]][forks-url] [![GitHub Watchers][watchers-img]][watchers-url] [![Tweet][tweet-img]][tweet-url] + +[![Build Status](https://travis-ci.org/Kronuz/base-x.svg?branch=master)](https://travis-ci.org/Kronuz/base-x) + + +### BaseX encoder / decoder for C++ + +This is a fast base encoder / decoder of any given alphabet. + + +#### Example + +``` cpp +// example.cc +// g++ -std=c++14 -o example example.cc + +#include +#include "base_x.hh" + +int main() { + auto encoded = Base58::base58().encode("Hello world!"); + + std::cout << encoded << std::endl; + // => 1LDlk6QWOejX6rPrJ + + return 0; +} +``` + + +#### Compilation + +* g++ and clang++ are supported. +* C++14 is required. + + +### Alphabets + +See below for a list of commonly recognized alphabets, and their respective base. + +Base | Factory | Alphabet +-----|---------------------|------------- + 2 | base2::base2() | `01` + 2 | base8::base8() | `01234567` + 11 | bas11::bas11() | `0123456789a` + 16 | base16::base16() | `0123456789abcdef` + 32 | base32::base32() | `0123456789ABCDEFGHJKMNPQRSTVWXYZ` + 36 | base36::base36() | `0123456789abcdefghijklmnopqrstuvwxyz` + 58 | base58::base58() | `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz` + 58 | base58::bitcoin() | `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz` + 58 | base58::gmp() | `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv` + 58 | base58::ripple() | `rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz` + 58 | base58::flickr() | `123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ` + 62 | base62::base62() | `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz` + 62 | base62::inverted() | `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` + 64 | base64::base64() | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/` + 64 | base64::urlsafe() | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_` + 66 | base66::base66() | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~` + + +### How it works + +It encodes octet arrays by doing long divisions on all significant digits in the +array, creating a representation of that number in the new base. + +**If you need standard hex encoding, or base64 encoding, this module is NOT +appropriate.** + + +## Author +[**German Mendez Bravo (Kronuz)**](https://kronuz.io/) + +[![Follow on GitHub][github-follow-img]][github-follow-url] +[![Follow on Twitter][twitter-follow-img]][twitter-follow-url] + + +## License + +MIT License. See [LICENSE](LICENSE) for details. + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com + + +[license-url]: https://github.com/Kronuz/base-x/blob/master/LICENSE +[license-img]: https://img.shields.io/github/license/Kronuz/base-x.svg +[stars-url]: https://github.com/Kronuz/base-x/stargazers +[stars-img]: https://img.shields.io/github/stars/Kronuz/base-x.svg?style=social&label=Stars +[forks-url]: https://github.com/Kronuz/base-x/network/members +[forks-img]: https://img.shields.io/github/forks/Kronuz/base-x.svg?style=social&label=Forks +[watchers-url]: https://github.com/Kronuz/base-x/watchers +[watchers-img]: https://img.shields.io/github/watchers/Kronuz/base-x.svg?style=social&label=Watchers +[tweet-img]: https://img.shields.io/twitter/url/https/github.com/Kronuz/base-x.svg?style=social +[tweet-url]: https://twitter.com/intent/tweet?text=Base-X+encoding%2Fdecoding+for+modern+C%2B%2B+by+%40germbravo:&url=https%3A%2F%2Fgithub.com%2FKronuz%2Fbase-x +[github-follow-url]: https://github.com/Kronuz +[github-follow-img]: https://img.shields.io/github/followers/Kronuz.svg?style=social&label=Follow +[twitter-follow-url]: https://twitter.com/intent/follow?screen_name=germbravo +[twitter-follow-img]: https://img.shields.io/twitter/follow/germbravo.svg?style=social&label=Follow diff --git a/contrib/base-x/base_x.hh b/contrib/base-x/base_x.hh new file mode 100644 index 00000000000..fdc06fead2f --- /dev/null +++ b/contrib/base-x/base_x.hh @@ -0,0 +1,614 @@ +/* +base_x.hh +BaseX encoder / decoder for C++ + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef __BASE_X__H_ +#define __BASE_X__H_ + +#include // for std::find_if, std::reverse +#include // for std::invalid_argument +#include // for std::string +#include // for std::enable_if_t + +#include "uinteger_t.hh" + + +class BaseX { + char _chr[256]; + int _ord[256]; + + const int size; + const int alphabet_base; + const unsigned base_size; + const unsigned alphabet_base_bits; + const unsigned block_size; + const uinteger_t::digit alphabet_base_mask; + const unsigned padding_size; + const char padding; + const int flags; + + constexpr char chr(unsigned char ord) const { + return _chr[ord]; + } + + constexpr int ord(unsigned char chr) const { + return _ord[chr]; + } + +public: + static constexpr int ignore_case = (1 << 0); + static constexpr int with_checksum = (1 << 1); + static constexpr int with_check = (1 << 2); + static constexpr int block_padding = (1 << 3); + + template + constexpr BaseX(int flgs, const char (&alphabet)[alphabet_size1], const char (&extended)[extended_size1], const char (&padding_string)[padding_size1], const char (&translate)[translate_size1]) : + _chr(), + _ord(), + size(alphabet_size1 - 1 + extended_size1 - 1), + alphabet_base(alphabet_size1 - 1), + base_size(uinteger_t::base_size(alphabet_base)), + alphabet_base_bits(uinteger_t::base_bits(alphabet_base)), + block_size((flgs & BaseX::block_padding) ? alphabet_base_bits : 0), + alphabet_base_mask(alphabet_base - 1), + padding_size(padding_size1 - 1), + padding(padding_size ? padding_string[0] : '\0'), + flags(flgs) + { + for (int c = 0; c < 256; ++c) { + _chr[c] = 0; + _ord[c] = alphabet_base; + } + for (int cp = 0; cp < alphabet_base; ++cp) { + auto ch = alphabet[cp]; + _chr[cp] = ch; + ASSERT(_ord[(unsigned char)ch] == alphabet_base); // Duplicate character in the alphabet + _ord[(unsigned char)ch] = cp; + if (flags & BaseX::ignore_case) { + if (ch >= 'A' && ch <='Z') { + _ord[(unsigned char)ch - 'A' + 'a'] = cp; + } else if (ch >= 'a' && ch <='z') { + _ord[(unsigned char)ch - 'a' + 'A'] = cp; + } + } + } + for (std::size_t i = 0; i < extended_size1 - 1; ++i) { + auto ch = extended[i]; + auto cp = alphabet_base + i; + _chr[cp] = ch; + ASSERT(_ord[(unsigned char)ch] == alphabet_base); // Duplicate character in the extended alphabet + _ord[(unsigned char)ch] = cp; + if (flags & BaseX::ignore_case) { + if (ch >= 'A' && ch <='Z') { + _ord[(unsigned char)ch - 'A' + 'a'] = cp; + } else if (ch >= 'a' && ch <='z') { + _ord[(unsigned char)ch - 'a' + 'A'] = cp; + } + } + } + int cp = -1; + for (std::size_t i = 0; i < translate_size1 - 1; ++i) { + auto ch = translate[i]; + auto ncp = _ord[(unsigned char)ch]; + if (ncp >= alphabet_base) { + ASSERT(_ord[(unsigned char)ch] == alphabet_base); // Invalid translation character + _ord[(unsigned char)ch] = cp; + if (flags & BaseX::ignore_case) { + if (ch >= 'A' && ch <='Z') { + _ord[(unsigned char)ch - 'A' + 'a'] = cp; + } else if (ch >= 'a' && ch <='z') { + _ord[(unsigned char)ch - 'a' + 'A'] = cp; + } + } + } else { + cp = ncp; + } + } + } + + // Get string representation of value + template ::value>> + void encode(Result& result, const uinteger_t& input) const { + std::size_t bp = 0; + uinteger_t quotient; + if (block_size) { + bp = ((input.bits() + 7) & 0xf8) % block_size; + bp = bp ? (block_size - bp) % block_size : 0; + if (bp) { + quotient = input << bp; + } + } + const uinteger_t& num = bp ? quotient : input; + auto num_sz = num.size(); + if (num_sz) { + int sum = 0; + result.reserve(num_sz * base_size); + if (alphabet_base_bits) { + std::size_t shift = 0; + auto ptr = reinterpret_cast(num.data()); + uinteger_t::digit v = *ptr++; + v <<= uinteger_t::half_digit_bits; + for (auto i = num_sz * 2 - 1; i; --i) { + v >>= uinteger_t::half_digit_bits; + v |= (static_cast(*ptr++) << uinteger_t::half_digit_bits); + do { + auto d = static_cast((v >> shift) & alphabet_base_mask); + result.push_back(chr(d)); + shift += alphabet_base_bits; + sum += d; + } while (shift <= uinteger_t::half_digit_bits); + shift -= uinteger_t::half_digit_bits; + } + v >>= (shift + uinteger_t::half_digit_bits); + while (v) { + auto d = static_cast(v & alphabet_base_mask); + result.push_back(chr(d)); + v >>= alphabet_base_bits; + sum += d; + } + auto s = chr(0); + auto rit_f = std::find_if(result.rbegin(), result.rend(), [s](const char& c) { return c != s; }); + result.resize(result.rend() - rit_f); // shrink + } else { + uinteger_t uint_base = alphabet_base; + if (!bp) { + quotient = num; + } + do { + auto r = quotient.divmod(uint_base); + auto d = static_cast(r.second); + result.push_back(chr(d)); + quotient = std::move(r.first); + sum += d; + } while (quotient); + } + std::reverse(result.begin(), result.end()); + if (padding_size) { + Result p; + p.resize((padding_size - (result.size() % padding_size)) % padding_size, padding); + result.append(p); + } + if (flags & BaseX::with_check) { + auto chk = static_cast(num % size); + result.push_back(chr(chk)); + sum += chk; + } + if (flags & BaseX::with_checksum) { + auto sz = result.size(); + sz = (sz + sz / size) % size; + sum += sz; + sum = (size - sum % size) % size; + result.push_back(chr(sum)); + } + } else { + result.push_back(chr(0)); + } + } + + template ::value>> + Result encode(const uinteger_t& num) const { + Result result; + encode(result, num); + return result; + } + + template ::value>> + void encode(Result& result, const unsigned char* decoded, std::size_t decoded_size) const { + encode(result, uinteger_t(decoded, decoded_size, 256)); + } + + template ::value>> + Result encode(const unsigned char* decoded, std::size_t decoded_size) const { + Result result; + encode(result, uinteger_t(decoded, decoded_size, 256)); + return result; + } + + template ::value>> + void encode(Result& result, const char* decoded, std::size_t decoded_size) const { + encode(result, uinteger_t(decoded, decoded_size, 256)); + } + + template ::value>> + Result encode(const char* decoded, std::size_t decoded_size) const { + Result result; + encode(result, uinteger_t(decoded, decoded_size, 256)); + return result; + } + + template ::value>> + void encode(Result& result, T (&s)[N]) const { + encode(result, s, N - 1); + } + + template ::value>> + Result encode(T (&s)[N]) const { + Result result; + encode(result, s, N - 1); + return result; + } + + template ::value>> + void encode(Result& result, const std::string& binary) const { + return encode(result, binary.data(), binary.size()); + } + + template ::value>> + Result encode(const std::string& binary) const { + Result result; + encode(result, binary.data(), binary.size()); + return result; + } + + void decode(uinteger_t& result, const char* encoded, std::size_t encoded_size) const { + result = 0; + int sum = 0; + int sumsz = 0; + int direction = 1; + + auto sz = encoded_size; + if (flags & BaseX::with_checksum) --sz; + if (flags & BaseX::with_check) --sz; + + int bp = 0; + + if (alphabet_base_bits) { + for (; sz; --sz, encoded += direction) { + auto c = *encoded; + if (c == padding) break; + auto d = ord(static_cast(c)); + if (d < 0) continue; // ignored character + if (d >= alphabet_base) { + throw std::invalid_argument("Error: Invalid character: '" + std::string(1, c) + "' at " + std::to_string(encoded_size - sz)); + } + sum += d; + ++sumsz; + result = (result << alphabet_base_bits) | d; + bp += block_size; + } + } else { + uinteger_t uint_base = alphabet_base; + for (; sz; --sz, encoded += direction) { + auto c = *encoded; + if (c == padding) break; + auto d = ord(static_cast(c)); + if (d < 0) continue; // ignored character + if (d >= alphabet_base) { + throw std::invalid_argument("Error: Invalid character: '" + std::string(1, c) + "' at " + std::to_string(encoded_size - sz)); + } + sum += d; + ++sumsz; + result = (result * uint_base) + d; + bp += block_size; + } + } + + for (; sz && *encoded == padding; --sz, ++encoded); + + result >>= (bp & 7); + + if (flags & BaseX::with_check) { + auto c = *encoded; + auto d = ord(static_cast(c)); + if (d < 0 || d >= size) { + throw std::invalid_argument("Error: Invalid character: '" + std::string(1, c) + "' at " + std::to_string(encoded_size - sz)); + } + auto chk = static_cast(result % size); + if (d != chk) { + throw std::invalid_argument("Error: Invalid check"); + } + sum += chk; + ++sumsz; + ++encoded; + } + + if (flags & BaseX::with_checksum) { + auto c = *encoded; + auto d = ord(static_cast(c)); + if (d < 0 || d >= size) { + throw std::invalid_argument("Error: Invalid character: '" + std::string(1, c) + "' at " + std::to_string(encoded_size - sz)); + } + sum += d; + sum += (sumsz + sumsz / size) % size; + if (sum % size) { + throw std::invalid_argument("Error: Invalid checksum"); + } + } + } + + template ::value>> + void decode(Result& result, const char* encoded, std::size_t encoded_size) const { + uinteger_t num; + decode(num, encoded, encoded_size); + result = num.template str(256); + } + + template ::value or std::is_integral::value>> + Result decode(const char* encoded, std::size_t encoded_size) const { + Result result; + decode(result, encoded, encoded_size); + return result; + } + + template ::value or std::is_integral::value>> + void decode(Result& result, T (&s)[N]) const { + decode(result, s, N - 1); + } + + template ::value or std::is_integral::value>> + Result decode(T (&s)[N]) const { + Result result; + decode(result, s, N - 1); + return result; + } + + template ::value or std::is_integral::value>> + void decode(Result& result, const std::string& encoded) const { + decode(result, encoded.data(), encoded.size()); + } + + template ::value or std::is_integral::value>> + Result decode(const std::string& encoded) const { + Result result; + decode(result, encoded.data(), encoded.size()); + return result; + } + + bool is_valid(const char* encoded, std::size_t encoded_size) const { + int sum = 0; + int sumsz = 0; + if (flags & BaseX::with_checksum) --sumsz; + for (; encoded_size; --encoded_size, ++encoded) { + auto d = ord(static_cast(*encoded)); + if (d < 0) continue; // ignored character + if (d >= alphabet_base) { + return false; + } + sum += d; + ++sumsz; + } + if (flags & BaseX::with_checksum) { + sum += (sumsz + sumsz / size) % size; + if (sum % size) { + return false; + } + } + return true; + } + + template + bool is_valid(T (&s)[N]) const { + return is_valid(s, N - 1); + } + + bool is_valid(const std::string& encoded) const { + return is_valid(encoded.data(), encoded.size()); + } +}; + +// base2 +struct Base2 { + static const BaseX& base2() { + static constexpr BaseX encoder(0, "01", "", "", ""); + return encoder; + } + static const BaseX& base2chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "01", "", "", ""); + return encoder; + } +}; + +// base8 +struct Base8 { + static const BaseX& base8() { + static constexpr BaseX encoder(0, "01234567", "", "", ""); + return encoder; + } + static const BaseX& base8chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "01234567", "", "", ""); + return encoder; + } +}; + +// base11 +struct Base11 { + static const BaseX& base11() { + static constexpr BaseX encoder(BaseX::ignore_case, "0123456789a", "", "", ""); + return encoder; + } + static const BaseX& base11chk() { + static constexpr BaseX encoder(BaseX::ignore_case | BaseX::with_checksum, "0123456789a", "", "", ""); + return encoder; + } +}; + +// base16 +struct Base16 { + static const BaseX& base16() { + static constexpr BaseX encoder(BaseX::ignore_case, "0123456789abcdef", "", "", ""); + return encoder; + } + static const BaseX& base16chk() { + static constexpr BaseX encoder(BaseX::ignore_case | BaseX::with_checksum, "0123456789abcdef", "", "", ""); + return encoder; + } + static const BaseX& rfc4648() { + static constexpr BaseX encoder(0, "0123456789ABCDEF", "", "", ""); + return encoder; + } +}; + +// base32 +struct Base32 { + static const BaseX& base32() { + static constexpr BaseX encoder(BaseX::ignore_case, "0123456789abcdefghijklmnopqrstuv", "", "", ""); + return encoder; + } + static const BaseX& base32chk() { + static constexpr BaseX encoder(BaseX::ignore_case | BaseX::with_checksum, "0123456789abcdefghijklmnopqrstuv", "", "", ""); + return encoder; + } + static const BaseX& crockford() { + static constexpr BaseX encoder(BaseX::ignore_case, "0123456789ABCDEFGHJKMNPQRSTVWXYZ", "", "", "-0O1IL"); + return encoder; + } + static const BaseX& crockfordchk() { + static constexpr BaseX encoder(BaseX::ignore_case | BaseX::with_check, "0123456789ABCDEFGHJKMNPQRSTVWXYZ", "*~$=U", "", "-0O1IL"); + return encoder; + } + static const BaseX& rfc4648() { + static constexpr BaseX encoder(BaseX::block_padding, "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "", "========", "\n\r"); + return encoder; + } + static const BaseX& rfc4648hex() { + static constexpr BaseX encoder(BaseX::block_padding, "0123456789ABCDEFGHIJKLMNOPQRSTUV", "", "========", "\n\r"); + return encoder; + } +}; + +// base36 +struct Base36 { + static const BaseX& base36() { + static constexpr BaseX encoder(BaseX::ignore_case, "0123456789abcdefghijklmnopqrstuvwxyz", "", "", ""); + return encoder; + } + static const BaseX& base36chk() { + static constexpr BaseX encoder(BaseX::ignore_case | BaseX::with_checksum, "0123456789abcdefghijklmnopqrstuvwxyz", "", "", ""); + return encoder; + } +}; + +// base58 +struct Base58 { + static const BaseX& base58() { + static constexpr BaseX encoder(0, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv", "", "", ""); + return encoder; + } + static const BaseX& base58chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv", "", "", ""); + return encoder; + } + static const BaseX& bitcoin() { + static constexpr BaseX encoder(0, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", "", "", ""); + return encoder; + } + static const BaseX& bitcoinchk() { + static constexpr BaseX encoder(BaseX::with_checksum, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", "", "", ""); + return encoder; + } + static const BaseX& ripple() { + static constexpr BaseX encoder(0, "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", "", "", ""); + return encoder; + } + static const BaseX& ripplechk() { + static constexpr BaseX encoder(BaseX::with_checksum, "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", "", "", ""); + return encoder; + } + static const BaseX& flickr() { + static constexpr BaseX encoder(0, "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", "", "", ""); + return encoder; + } + static const BaseX& flickrchk() { + static constexpr BaseX encoder(BaseX::with_checksum, "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", "", "", ""); + return encoder; + } +}; + +// base59 +struct Base59 { + static const BaseX& base59() { + static constexpr BaseX encoder(0, "23456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ", "", "", "l1IO0"); + return encoder; + } + static const BaseX& base59chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "23456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ", "", "", "l1IO0"); + return encoder; + } + static const BaseX& dubaluchk() { + static constexpr BaseX encoder(BaseX::with_checksum, "zy9MalDxwpKLdvW2AtmscgbYUq6jhP7E53TiXenZRkVCrouBH4GSQf8FNJO", "", "", "-l1IO0"); + return encoder; + } +}; + +// base62 +struct Base62 { + static const BaseX& base62() { + static constexpr BaseX encoder(0, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "", "", ""); + return encoder; + } + static const BaseX& base62chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "", "", ""); + return encoder; + } + static const BaseX& inverted() { + static constexpr BaseX encoder(0, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "", "", ""); + return encoder; + } + static const BaseX& invertedchk() { + static constexpr BaseX encoder(BaseX::with_checksum, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "", "", ""); + return encoder; + } +}; + +// base64 +struct Base64 { + static const BaseX& base64() { + static constexpr BaseX encoder(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "", "", ""); + return encoder; + } + static const BaseX& base64chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "", "", ""); + return encoder; + } + static const BaseX& url() { + static constexpr BaseX encoder(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", "", "", ""); + return encoder; + } + static const BaseX& urlchk() { + static constexpr BaseX encoder(BaseX::with_checksum, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", "", "", ""); + return encoder; + } + static const BaseX& rfc4648() { + static constexpr BaseX encoder(BaseX::block_padding, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "", "====", "\n\r"); + return encoder; + } + static const BaseX& rfc4648url() { + static constexpr BaseX encoder(BaseX::block_padding, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", "", "====", "\n\r"); + return encoder; + } +}; + +// base66 +struct Base66 { + static const BaseX& base66() { + static constexpr BaseX encoder(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~", "", "", ""); + return encoder; + } + static const BaseX& base66chk() { + static constexpr BaseX encoder(BaseX::with_checksum, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~", "", "", ""); + return encoder; + } +}; + +#endif diff --git a/contrib/base-x/tests/test.cc b/contrib/base-x/tests/test.cc new file mode 100644 index 00000000000..d47d211173e --- /dev/null +++ b/contrib/base-x/tests/test.cc @@ -0,0 +1,30 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +int main(int argc, char * argv[]){ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/contrib/base-x/tests/testcases/tests.cc b/contrib/base-x/tests/testcases/tests.cc new file mode 100644 index 00000000000..c5bebfc8288 --- /dev/null +++ b/contrib/base-x/tests/testcases/tests.cc @@ -0,0 +1,359 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +#include "base_x.hh" + + +static constexpr BaseX test_base2(0, "01", "", "", ""); +static constexpr BaseX test_base16(0, "0123456789abcdef", "", "", ""); +static constexpr BaseX test_base58(0, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", "", "", ""); + + +TEST(UUID, Encode) { + EXPECT_EQ(Base62::base62().encode("\330\105\140\310\23\117\21\346\241\342\64\66\73\322\155\256"), "6a630O1jrtMjCrQDyG3D3O"); + EXPECT_EQ(Base58::bitcoin().encode("\330\105\140\310\23\117\21\346\241\342\64\66\73\322\155\256"), "ThxCy1Ek2q6UhWQhj9CK1o"); + EXPECT_EQ(Base58::base58().encode("\330\105\140\310\23\117\21\346\241\342\64\66\73\322\155\256"), "QetBu0Dh1m5ReTNeg8BI0k"); +} + +TEST(BaseX, checksums) { + EXPECT_EQ(Base64::base64().encode("Hello world!"), "SGVsbG8gd29ybGQh"); + EXPECT_EQ(Base64::base64chk().encode("Hello world!"), "SGVsbG8gd29ybGQhG"); + + EXPECT_EQ(Base64::base64().decode("SGVsbG8gd29ybGQh"), "Hello world!"); + EXPECT_EQ(Base64::base64chk().decode("SGVsbG8gd29ybGQhG"), "Hello world!"); + + EXPECT_EQ(Base62::base62().encode("Hello world!"), "T8dgcjRGuYUueWht"); + EXPECT_EQ(Base62::base62chk().encode("Hello world!"), "T8dgcjRGuYUueWhtE"); + + EXPECT_EQ(Base62::base62().decode("T8dgcjRGuYUueWht"), "Hello world!"); + EXPECT_EQ(Base62::base62chk().decode("T8dgcjRGuYUueWhtE"), "Hello world!"); + + EXPECT_EQ(Base62::base62chk().is_valid("T8dgcjRGuYUueWhtE"), true); + EXPECT_EQ(Base62::base62chk().is_valid("Some random text!"), false); +} + +TEST(base16, Encoder) { + EXPECT_EQ(Base16::base16().encode("A"), "41"); + EXPECT_EQ(Base16::base16().encode("AB"), "4142"); + EXPECT_EQ(Base16::base16().encode("ABC"), "414243"); + EXPECT_EQ(Base16::base16().encode("ABCD"), "41424344"); + EXPECT_EQ(Base16::base16().encode("ABCDE"), "4142434445"); + EXPECT_EQ(Base16::base16().encode("ABCDEF"), "414243444546"); + + EXPECT_EQ(Base16::rfc4648().encode("A"), "41"); + EXPECT_EQ(Base16::rfc4648().encode("AB"), "4142"); + EXPECT_EQ(Base16::rfc4648().encode("ABC"), "414243"); + EXPECT_EQ(Base16::rfc4648().encode("ABCD"), "41424344"); + EXPECT_EQ(Base16::rfc4648().encode("ABCDE"), "4142434445"); + EXPECT_EQ(Base16::rfc4648().encode("ABCDEF"), "414243444546"); +} + +TEST(base16, Decoder) { + EXPECT_EQ(Base16::base16().decode("41"), "A"); + EXPECT_EQ(Base16::base16().decode("4142"), "AB"); + EXPECT_EQ(Base16::base16().decode("414243"), "ABC"); + EXPECT_EQ(Base16::base16().decode("41424344"), "ABCD"); + EXPECT_EQ(Base16::base16().decode("4142434445"), "ABCDE"); + EXPECT_EQ(Base16::base16().decode("414243444546"), "ABCDEF"); + + EXPECT_EQ(Base16::rfc4648().decode("41"), "A"); + EXPECT_EQ(Base16::rfc4648().decode("4142"), "AB"); + EXPECT_EQ(Base16::rfc4648().decode("414243"), "ABC"); + EXPECT_EQ(Base16::rfc4648().decode("41424344"), "ABCD"); + EXPECT_EQ(Base16::rfc4648().decode("4142434445"), "ABCDE"); + EXPECT_EQ(Base16::rfc4648().decode("414243444546"), "ABCDEF"); +} + +TEST(base32, Encoder) { + // Note base64() encoding is NOT the same as the standard (rfc4648) + EXPECT_EQ(Base32::base32().encode("A"), "21"); + EXPECT_EQ(Base32::base32().encode("AB"), "ga2"); + EXPECT_EQ(Base32::base32().encode("ABC"), "42gi3"); + EXPECT_EQ(Base32::base32().encode("ABCD"), "10k4gq4"); + EXPECT_EQ(Base32::base32().encode("ABCDE"), "85146h25"); + EXPECT_EQ(Base32::base32().encode("ABCDEF"), "21891k8ha6"); + EXPECT_EQ(Base32::base32().encode("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), "21891k8ha68t44iiib9h6ksjqga5956l2lapblgmaq"); + + EXPECT_EQ(Base32::rfc4648().encode("A"), "IE======"); + EXPECT_EQ(Base32::rfc4648().encode("AB"), "IFBA===="); + EXPECT_EQ(Base32::rfc4648().encode("ABC"), "IFBEG==="); + EXPECT_EQ(Base32::rfc4648().encode("ABCD"), "IFBEGRA="); + EXPECT_EQ(Base32::rfc4648().encode("ABCDE"), "IFBEGRCF"); + EXPECT_EQ(Base32::rfc4648().encode("ABCDEF"), "IFBEGRCFIY======"); + EXPECT_EQ(Base32::rfc4648().encode("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======"); + + EXPECT_EQ(Base32::crockford().encode(519571), "FVCK"); + EXPECT_EQ(Base32::crockfordchk().encode(1234), "16JD"); + EXPECT_EQ(Base32::crockfordchk().encode("Hello World"), "28CNP6RVS0AXQQ4V348"); +} + +TEST(base32, Decoder) { + // Note base64() encoding is NOT the same as the standard (rfc4648) + EXPECT_EQ(Base32::base32().decode("21"), "A"); + EXPECT_EQ(Base32::base32().decode("ga2"), "AB"); + EXPECT_EQ(Base32::base32().decode("42gi3"), "ABC"); + EXPECT_EQ(Base32::base32().decode("10k4gq4"), "ABCD"); + EXPECT_EQ(Base32::base32().decode("85146h25"), "ABCDE"); + EXPECT_EQ(Base32::base32().decode("21891k8ha6"), "ABCDEF"); + EXPECT_EQ(Base32::base32().decode("21891k8ha68t44iiib9h6ksjqga5956l2lapblgmaq"), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + EXPECT_EQ(Base32::rfc4648().decode("IE======"), "A"); + EXPECT_EQ(Base32::rfc4648().decode("IFBA===="), "AB"); + EXPECT_EQ(Base32::rfc4648().decode("IFBEG==="), "ABC"); + EXPECT_EQ(Base32::rfc4648().decode("IFBEGRA="), "ABCD"); + EXPECT_EQ(Base32::rfc4648().decode("IFBEGRCF"), "ABCDE"); + EXPECT_EQ(Base32::rfc4648().decode("IFBEGRCFIY======"), "ABCDEF"); + EXPECT_EQ(Base32::rfc4648().decode("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======"), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + EXPECT_EQ(Base32::crockford().decode("FVCK"), 519571); + EXPECT_EQ(Base32::crockfordchk().is_valid("16JD"), true); + EXPECT_EQ(Base32::crockfordchk().decode("16JD"), 1234); + + EXPECT_EQ(Base32::crockfordchk().decode("2-8cn-p6r-vso-axq-q4v-348"), "Hello World"); +} + +TEST(base58, Encoder) { + EXPECT_EQ(Base58::base58().decode("1TFvCj"), 987654321); + EXPECT_EQ(Base58::base58().encode(987654321), "1TFvCj"); + EXPECT_EQ(Base58::base58().encode("Hello world!"), "1LDlk6QWOejX6rPrJ"); + EXPECT_EQ(Base58::bitcoin().encode("Hello world!"), "2NEpo7TZRhna7vSvL"); +} + +TEST(base62, Encoder) { + EXPECT_EQ(Base62::base62().decode("14q60P"), 987654321); + EXPECT_EQ(Base62::base62().encode(987654321), "14q60P"); + EXPECT_EQ(Base62::base62().encode("Hello world!"), "T8dgcjRGuYUueWht"); + EXPECT_EQ(Base62::inverted().encode("Hello world!"), "t8DGCJrgUyuUEwHT"); +} + +TEST(base64, Encoder) { + // Note Base64 encoding is NOT the same as the standard (rfc4648) + EXPECT_EQ(Base64::base64().encode("A"), "BB"); + EXPECT_EQ(Base64::base64().encode("AB"), "EFC"); + EXPECT_EQ(Base64::base64().encode("ABC"), "QUJD"); + EXPECT_EQ(Base64::base64().encode("ABCD"), "BBQkNE"); + EXPECT_EQ(Base64::base64().encode("ABCDE"), "EFCQ0RF"); + EXPECT_EQ(Base64::base64().encode("ABCDEF"), "QUJDREVG"); + EXPECT_EQ(Base64::base64().encode("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), "EFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla"); + + EXPECT_EQ(Base64::rfc4648().encode("A"), "QQ=="); + EXPECT_EQ(Base64::rfc4648().encode("AB"), "QUI="); + EXPECT_EQ(Base64::rfc4648().encode("ABC"), "QUJD"); + EXPECT_EQ(Base64::rfc4648().encode("ABCD"), "QUJDRA=="); + EXPECT_EQ(Base64::rfc4648().encode("ABCDE"), "QUJDREU="); + EXPECT_EQ(Base64::rfc4648().encode("ABCDEF"), "QUJDREVG"); + EXPECT_EQ(Base64::rfc4648().encode("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo="); +} + +TEST(base64, Decoder) { + // Note Base64 encoding is NOT the same as the standard (rfc4648) + EXPECT_EQ(Base64::base64().decode("BB"), "A"); + EXPECT_EQ(Base64::base64().decode("EFC"), "AB"); + EXPECT_EQ(Base64::base64().decode("QUJD"), "ABC"); + EXPECT_EQ(Base64::base64().decode("BBQkNE"), "ABCD"); + EXPECT_EQ(Base64::base64().decode("EFCQ0RF"), "ABCDE"); + EXPECT_EQ(Base64::base64().decode("QUJDREVG"), "ABCDEF"); + EXPECT_EQ(Base64::base64().decode("EFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFla"), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + + EXPECT_EQ(Base64::rfc4648().decode("QQ=="), "A"); + EXPECT_EQ(Base64::rfc4648().decode("QUI="), "AB"); + EXPECT_EQ(Base64::rfc4648().decode("QUJD"), "ABC"); + EXPECT_EQ(Base64::rfc4648().decode("QUJDRA=="), "ABCD"); + EXPECT_EQ(Base64::rfc4648().decode("QUJDREU="), "ABCDE"); + EXPECT_EQ(Base64::rfc4648().decode("QUJDREVG"), "ABCDEF"); + EXPECT_EQ(Base64::rfc4648().decode("QUJDREVG\nR0hJSktM\nTU5PUFFS\nU1RVVldY\nWVo="), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +} + +TEST(base58, ShouldEncodeAndDecodeIntegers) { + auto data = 987654321; + + auto gmpEncoded = Base58::base58().encode(data); + auto bitcoinEncoded = Base58::bitcoin().encode(data); + auto rippleEncoded = Base58::ripple().encode(data); + auto flickrEncoded = Base58::flickr().encode(data); + + EXPECT_EQ(gmpEncoded, "1TFvCj"); + EXPECT_EQ(bitcoinEncoded, "2WGzDn"); + EXPECT_EQ(rippleEncoded, "pWGzD8"); + EXPECT_EQ(flickrEncoded, "2vgZdM"); + + auto gmpDecoded = Base58::base58().decode(gmpEncoded); + auto bitcoinDecoded = Base58::bitcoin().decode(bitcoinEncoded); + auto rippleDecoded = Base58::ripple().decode(rippleEncoded); + auto flickrDecoded = Base58::flickr().decode(flickrEncoded); + + EXPECT_EQ(gmpDecoded, data); + EXPECT_EQ(bitcoinDecoded, data); + EXPECT_EQ(rippleDecoded, data); + EXPECT_EQ(flickrDecoded, data); + + auto encoded = Base58::base58().encode(data); + auto decoded = Base58::base58().decode(encoded); + + EXPECT_EQ(decoded, data); +} + +TEST(base58, LongText) { + auto data = "Lorem ipsum dolor consectetur."; + + auto gmpEncoded = Base58::base58().encode(data); + auto bitcoinEncoded = Base58::bitcoin().encode(data); + auto rippleEncoded = Base58::ripple().encode(data); + auto flickrEncoded = Base58::flickr().encode(data); + + EXPECT_EQ(gmpEncoded, "FIHZQEpJ739QdqChX1PkgTBqP1FaDgJWQiGvY92YA"); + EXPECT_EQ(bitcoinEncoded, "GKJcTFtL84ATguDka2SojWCuS2GdEjLZTmHzbA3bB"); + EXPECT_EQ(rippleEncoded, "GKJcTEtL3hwTguDk2pSojWUuSpGdNjLZTmHzbwsbB"); + EXPECT_EQ(flickrEncoded, "gjiBsfTk84asFUdKz2rNJvcUr2gCeJkysLhZAa3Ab"); + + auto gmpDecoded = Base58::base58().decode(gmpEncoded); + auto bitcoinDecoded = Base58::bitcoin().decode(bitcoinEncoded); + auto rippleDecoded = Base58::ripple().decode(rippleEncoded); + auto flickrDecoded = Base58::flickr().decode(flickrEncoded); + + EXPECT_EQ(gmpDecoded, data); + EXPECT_EQ(bitcoinDecoded, data); + EXPECT_EQ(rippleDecoded, data); + EXPECT_EQ(flickrDecoded, data); +} + +TEST(base58, Tests) { + EXPECT_EQ(test_base2.encode(uinteger_t("000f", 16)), "1111"); + // EXPECT_EQ(test_base2.encode(uinteger_t("00ff", 16)), "011111111"); // ->> + EXPECT_EQ(test_base2.encode(uinteger_t("00ff", 16)), "11111111"); + EXPECT_EQ(test_base2.encode(uinteger_t("0fff", 16)), "111111111111"); + EXPECT_EQ(test_base2.encode(uinteger_t("ff00ff00", 16)), "11111111000000001111111100000000"); + // EXPECT_EQ(test_base16.encode(uinteger_t("0000000f", 16)), "000f"); // ->> + EXPECT_EQ(test_base16.encode(uinteger_t("0000000f", 16)), "f"); + // EXPECT_EQ(test_base16.encode(uinteger_t("000fff", 16)), "0fff"); // ->> + EXPECT_EQ(test_base16.encode(uinteger_t("000fff", 16)), "fff"); + EXPECT_EQ(test_base16.encode(uinteger_t("ffff", 16)), "ffff"); + // EXPECT_EQ(test_base58.encode(uinteger_t("", 16)), ""); // ->> + EXPECT_EQ(test_base58.encode(uinteger_t("", 16)), "1"); + EXPECT_EQ(test_base58.encode(uinteger_t("61", 16)), "2g"); + EXPECT_EQ(test_base58.encode(uinteger_t("626262", 16)), "a3gV"); + EXPECT_EQ(test_base58.encode(uinteger_t("636363", 16)), "aPEr"); + EXPECT_EQ(test_base58.encode(uinteger_t("73696d706c792061206c6f6e6720737472696e67", 16)), "2cFupjhnEsSn59qHXstmK2ffpLv2"); + // EXPECT_EQ(test_base58.encode(uinteger_t("00eb15231dfceb60925886b67d065299925915aeb172c06647", 16)), "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"); // ->> + EXPECT_EQ(test_base58.encode(uinteger_t("00eb15231dfceb60925886b67d065299925915aeb172c06647", 16)), "NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"); + EXPECT_EQ(test_base58.encode(uinteger_t("516b6fcd0f", 16)), "ABnLTmg"); + EXPECT_EQ(test_base58.encode(uinteger_t("bf4f89001e670274dd", 16)), "3SEo3LWLoPntC"); + EXPECT_EQ(test_base58.encode(uinteger_t("572e4794", 16)), "3EFU7m"); + EXPECT_EQ(test_base58.encode(uinteger_t("ecac89cad93923c02321", 16)), "EJDM8drfXA6uyA"); + EXPECT_EQ(test_base58.encode(uinteger_t("10c8511e", 16)), "Rt5zm"); + // EXPECT_EQ(test_base58.encode(uinteger_t("00000000000000000000", 16)), "1111111111"); // ->> + EXPECT_EQ(test_base58.encode(uinteger_t("00000000000000000000", 16)), "1"); + EXPECT_EQ(test_base58.encode(uinteger_t("801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e", 16)), "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD"); + // EXPECT_EQ(test_base58.encode(uinteger_t("003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187", 16)), "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS"); // ->> + EXPECT_EQ(test_base58.encode(uinteger_t("003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187", 16)), "6UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS"); + EXPECT_EQ(test_base58.encode(uinteger_t("ffffffffffffffffffff", 16)), "FPBt6CHo3fovdL"); + EXPECT_EQ(test_base58.encode(uinteger_t("ffffffffffffffffffffffffff", 16)), "NKioeUVktgzXLJ1B3t"); + EXPECT_EQ(test_base58.encode(uinteger_t("ffffffffffffffffffffffffffffffff", 16)), "YcVfxkQb6JRzqk5kF2tNLv"); + EXPECT_EQ(test_base2.encode(uinteger_t("fb6f9ac3", 16)), "11111011011011111001101011000011"); + EXPECT_EQ(test_base2.encode(uinteger_t("179eea7a", 16)), "10111100111101110101001111010"); + EXPECT_EQ(test_base2.encode(uinteger_t("6db825db", 16)), "1101101101110000010010111011011"); + EXPECT_EQ(test_base2.encode(uinteger_t("93976aa7", 16)), "10010011100101110110101010100111"); + EXPECT_EQ(test_base58.encode(uinteger_t("ef41b9ce7e830af7", 16)), "h26E62FyLQN"); + EXPECT_EQ(test_base58.encode(uinteger_t("606cbc791036d2e9", 16)), "H8Sa62HVULG"); + EXPECT_EQ(test_base58.encode(uinteger_t("bdcb0ea69c2c8ec8", 16)), "YkESUPpnfoD"); + EXPECT_EQ(test_base58.encode(uinteger_t("1a2358ba67fb71d5", 16)), "5NaBN89ajtQ"); + EXPECT_EQ(test_base58.encode(uinteger_t("e6173f0f4d5fb5d7", 16)), "fVAoezT1ZkS"); + EXPECT_EQ(test_base58.encode(uinteger_t("91c81cbfdd58bbd2", 16)), "RPGNSU3bqTX"); + EXPECT_EQ(test_base58.encode(uinteger_t("329e0bf0e388dbfe", 16)), "9U41ZkwwysT"); + EXPECT_EQ(test_base58.encode(uinteger_t("30b10393210fa65b", 16)), "99NMW3WHjjY"); + EXPECT_EQ(test_base58.encode(uinteger_t("ab3bdd18e3623654", 16)), "VeBbqBb4rCT"); + EXPECT_EQ(test_base58.encode(uinteger_t("fe29d1751ec4af8a", 16)), "jWhmYLN9dUm"); + EXPECT_EQ(test_base58.encode(uinteger_t("c1273ab5488769807d", 16)), "3Tbh4kL3WKW6g"); + EXPECT_EQ(test_base58.encode(uinteger_t("6c7907904de934f852", 16)), "2P5jNYhfpTJxy"); + EXPECT_EQ(test_base58.encode(uinteger_t("05f0be055db47a0dc9", 16)), "5PN768Kr5oEp"); + EXPECT_EQ(test_base58.encode(uinteger_t("3511e6206829b35b12", 16)), "gBREojGaJ6DF"); + EXPECT_EQ(test_base58.encode(uinteger_t("d1c7c2ddc4a459d503", 16)), "3fsekq5Esq2KC"); + EXPECT_EQ(test_base58.encode(uinteger_t("1f88efd17ab073e9a1", 16)), "QHJbmW9ZY7jn"); + EXPECT_EQ(test_base58.encode(uinteger_t("0f45dadf4e64c5d5c2", 16)), "CGyVUMmCKLRf"); + EXPECT_EQ(test_base58.encode(uinteger_t("de1e5c5f718bb7fafa", 16)), "3pyy8U7w3KUa5"); + EXPECT_EQ(test_base58.encode(uinteger_t("123190b93e9a49a46c", 16)), "ES3DeFrG1zbd"); + EXPECT_EQ(test_base58.encode(uinteger_t("8bee94a543e7242e5a", 16)), "2nJnuWyLpGf6y"); + EXPECT_EQ(test_base58.encode(uinteger_t("9fd5f2285362f5cfd834", 16)), "9yqFhqeewcW3pF"); + EXPECT_EQ(test_base58.encode(uinteger_t("6987bac63ad23828bb31", 16)), "6vskE5Y1LhS3U4"); + EXPECT_EQ(test_base58.encode(uinteger_t("19d4a0f9d459cc2a08b0", 16)), "2TAsHPuaLhh5Aw"); + EXPECT_EQ(test_base58.encode(uinteger_t("a1e47ffdbea5a807ab26", 16)), "A6XzPgSUJDf1W5"); + EXPECT_EQ(test_base58.encode(uinteger_t("35c231e5b3a86a9b83db", 16)), "42B8reRwPAAoAa"); + EXPECT_EQ(test_base58.encode(uinteger_t("b2351012a48b8347c351", 16)), "B1hPyomGx4Vhqa"); + EXPECT_EQ(test_base58.encode(uinteger_t("71d402694dd9517ea653", 16)), "7Pv2SyAQx2Upu8"); + EXPECT_EQ(test_base58.encode(uinteger_t("55227c0ec7955c2bd6e8", 16)), "5nR64BkskyjHMq"); + EXPECT_EQ(test_base58.encode(uinteger_t("17b3d8ee7907c1be34df", 16)), "2LEg7TxosoxTGS"); + EXPECT_EQ(test_base58.encode(uinteger_t("7e7bba7b68bb8e95827f", 16)), "879o2ATGnmYyAW"); + EXPECT_EQ(test_base58.encode(uinteger_t("db9c13f5ba7654b01407fb", 16)), "wTYfxjDVbiks874"); + EXPECT_EQ(test_base58.encode(uinteger_t("6186449d20f5fd1e6c4393", 16)), "RBeiWhzZNL6VtMG"); + EXPECT_EQ(test_base58.encode(uinteger_t("5248751cebf4ad1c1a83c3", 16)), "MQSVNnc8ehFCqtW"); + EXPECT_EQ(test_base58.encode(uinteger_t("32090ef18cd479fc376a74", 16)), "DQdu351ExDaeYeX"); + EXPECT_EQ(test_base58.encode(uinteger_t("7cfa5d6ed1e467d986c426", 16)), "XzW67T5qfEnFcaZ"); + EXPECT_EQ(test_base58.encode(uinteger_t("9d8707723c7ede51103b6d", 16)), "g4eTCg6QJnB1UU4"); + EXPECT_EQ(test_base58.encode(uinteger_t("6f4d1e392d6a9b4ed8b223", 16)), "Ubo7kZY5aDpAJp2"); + EXPECT_EQ(test_base58.encode(uinteger_t("38057d98797cd39f80a0c9", 16)), "EtjQ2feamJvuqse"); + EXPECT_EQ(test_base58.encode(uinteger_t("de7e59903177e20880e915", 16)), "xB2N7yRBnDYEoT2"); + EXPECT_EQ(test_base58.encode(uinteger_t("b2ea24a28bc4a60b5c4b8d", 16)), "mNFMpJ2P3TGYqhv"); + EXPECT_EQ(test_base58.encode(uinteger_t("cf84938958589b6ffba6114d", 16)), "4v8ZbsGh2ePz5sipt"); + EXPECT_EQ(test_base58.encode(uinteger_t("dee13be7b8d8a08c94a3c02a", 16)), "5CwmE9jQqwtHkTF45"); + EXPECT_EQ(test_base58.encode(uinteger_t("14cb9c6b3f8cd2e02710f569", 16)), "Pm85JHVAAdeUdxtp"); + EXPECT_EQ(test_base58.encode(uinteger_t("ca3f2d558266bdcc44c79cb5", 16)), "4pMwomBAQHuUnoLUC"); + EXPECT_EQ(test_base58.encode(uinteger_t("c031215be44cbad745f38982", 16)), "4dMeTrcxiVw9RWvj3"); + EXPECT_EQ(test_base58.encode(uinteger_t("1435ab1dbc403111946270a5", 16)), "P7wX3sCWNrbqhBEC"); + EXPECT_EQ(test_base58.encode(uinteger_t("d8c6e4d775e7a66a0d0f9f41", 16)), "56GLoRDGWGuGJJwPN"); + EXPECT_EQ(test_base58.encode(uinteger_t("dcee35e74f0fd74176fce2f4", 16)), "5Ap1zyuYiJJFwWcMR"); + EXPECT_EQ(test_base58.encode(uinteger_t("bfcc0ca4b4855d1cf8993fc0", 16)), "4cvafQW4PEhARKv9D"); + EXPECT_EQ(test_base58.encode(uinteger_t("e02a3ac25ece7b54584b670a", 16)), "5EMM28xkpxZ1kkVUM"); + EXPECT_EQ(test_base58.encode(uinteger_t("fe4d938fc3719f064cabb4bfff", 16)), "NBXKkbHwrAsiWTLAk6"); + EXPECT_EQ(test_base58.encode(uinteger_t("9289cb4f6b15c57e6086b87ea5", 16)), "DCvDpjEXEbHjZqskKv"); + EXPECT_EQ(test_base58.encode(uinteger_t("fc266f35626b3612bfe978537b", 16)), "N186PVoBWrNre35BGE"); + EXPECT_EQ(test_base58.encode(uinteger_t("33ff08c06d92502bf258c07166", 16)), "5LC4SoW6jmTtbkbePw"); + EXPECT_EQ(test_base58.encode(uinteger_t("6a81cac1f3666bc59dc67b1c3c", 16)), "9sXgUySUzwiqDU5WHy"); + EXPECT_EQ(test_base58.encode(uinteger_t("9dfb8e7e744c544c0f323ea729", 16)), "EACsmGmkgcwsrPFzLg"); + EXPECT_EQ(test_base58.encode(uinteger_t("1e7a1e284f70838b38442b682b", 16)), "3YEVk9bE7rw5qExMkv"); + EXPECT_EQ(test_base58.encode(uinteger_t("2a862ad57901a8235f5dc74eaf", 16)), "4YS259nuTLfeXa5Wuc"); + EXPECT_EQ(test_base58.encode(uinteger_t("74c82096baef21f9d3089e5462", 16)), "AjAcKEhUfrqm8smvM7"); + EXPECT_EQ(test_base58.encode(uinteger_t("7a3edbc23d7b600263920261cc", 16)), "BBZXyRgey5S5DDZkcK"); + EXPECT_EQ(test_base58.encode(uinteger_t("20435664c357d25a9c8df751cf4f", 16)), "CrwNL6Fbv4pbRx1zd9g"); + EXPECT_EQ(test_base58.encode(uinteger_t("51a7aa87cf5cb1c12d045ec3422d", 16)), "X27NHGgKXmGzzQvDtpC"); + EXPECT_EQ(test_base58.encode(uinteger_t("344d2e116aa26f1062a2cb6ebbef", 16)), "LEDLDvL1Hg4qt1efVXt"); + EXPECT_EQ(test_base58.encode(uinteger_t("6941add7be4c0b5c7163e4928f8e", 16)), "fhMyN6gwoxE3uYraVzV"); + EXPECT_EQ(test_base58.encode(uinteger_t("10938fcbb7c4ab991649734a14bf", 16)), "76TPrSDxzGQfSzMu974"); + EXPECT_EQ(test_base58.encode(uinteger_t("eafe04d944ba504e9af9117b07de", 16)), "2VPgov563ryfe4L2Bj6M"); + EXPECT_EQ(test_base58.encode(uinteger_t("58d0aeed4d35da20b6f052127edf", 16)), "ZenZhXF9YwP8nQvNtNz"); + EXPECT_EQ(test_base58.encode(uinteger_t("d734984e2f5aecf25f7a3e353f8a", 16)), "2N7n3jFsTdyN49Faoq6h"); + EXPECT_EQ(test_base58.encode(uinteger_t("57d873fdb405b7daf4bafa62068a", 16)), "ZJ7NwoP4wHvwyZg3Wjs"); + EXPECT_EQ(test_base58.encode(uinteger_t("bda4ec7b40d0d65ca95dec4c4d3b", 16)), "2CijxjsNyvqTwPCfDcpA"); + EXPECT_EQ(test_base58.encode(uinteger_t("826c4abdceb1b91f0d4ad665f86d2e", 16)), "4edfvuDQu9KzVxLuXHfMo"); + EXPECT_EQ(test_base58.encode(uinteger_t("e7ecb35d07e65b960cb10574a4f51a", 16)), "7VLRYdB4cToipp2J2p3v9"); + EXPECT_EQ(test_base58.encode(uinteger_t("4f2d72ead87b31d6869fba39eac6dc", 16)), "3DUjqJRcfdWhpsrLrGcQs"); + EXPECT_EQ(test_base58.encode(uinteger_t("8b4f5788d60030950d5dfbf94c585d", 16)), "4u44JSRH5jP5X39YhPsmE"); + EXPECT_EQ(test_base58.encode(uinteger_t("ee4c0a0025d1a74ace9fe349355cc5", 16)), "7fgACjABRQUGUEpN6VBBA"); + EXPECT_EQ(test_base58.encode(uinteger_t("58ac05b9a0b4b66083ff1d489b8d84", 16)), "3UtJPyTwGXapcxHx8Rom5"); + EXPECT_EQ(test_base58.encode(uinteger_t("1aa35c05e1132e8e049aafaef035d8", 16)), "kE2eSU7gM2619pT82iGP"); + EXPECT_EQ(test_base58.encode(uinteger_t("771b0c28608484562a292e5d5d2b30", 16)), "4LGYeWhyfrjUByibUqdVR"); + EXPECT_EQ(test_base58.encode(uinteger_t("78ff9a0e56f9e88dc1cd654b40d019", 16)), "4PLggs66qAdbmZgkaPihe"); + EXPECT_EQ(test_base58.encode(uinteger_t("6d691bdd736346aa5a0a95b373b2ab", 16)), "44Y6qTgSvRMkdqpQ5ufkN"); +} diff --git a/contrib/base-x/uinteger_t.hh b/contrib/base-x/uinteger_t.hh new file mode 100644 index 00000000000..901460f75c4 --- /dev/null +++ b/contrib/base-x/uinteger_t.hh @@ -0,0 +1,2546 @@ +/* +uinteger_t.hh +An arbitrary precision unsigned integer type for C++ + +Copyright (c) 2017 German Mendez Bravo (Kronuz) @ german dot mb at gmail.com +Copyright (c) 2013 - 2017 Jason Lee @ calccrypto at gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +With much help from Auston Sterling + +Thanks to Stefan Deigmüller for finding +a bug in operator*. + +Thanks to François Dessenne for convincing me +to do a general rewrite of this class. + +Germán Mández Bravo (Kronuz) converted Jason Lee's uint128_t +to header-only and extended to arbitrary bit length. +*/ + +#ifndef __uint_t__ +#define __uint_t__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT assert + +// Compatibility inlines +#ifndef __has_builtin // Optional of course +#define __has_builtin(x) 0 // Compatibility with non-clang compilers +#endif + +#if defined _MSC_VER +# define HAVE___ADDCARRY_U64 +# define HAVE___SUBBORROW_U64 +# define HAVE___ADDCARRY_U32 +# define HAVE___SUBBORROW_U32 +# define HAVE___ADDCARRY_U16 +# define HAVE___SUBBORROW_U16 +# define HAVE___UMUL128 +# define HAVE___UMUL64 +# define HAVE___UMUL32 +# include +#endif + +#if (defined(__clang__) && __has_builtin(__builtin_clzll)) || (defined(__GNUC__ ) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) +# define HAVE____BUILTIN_CLZLL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_clzl)) || (defined(__GNUC__ ) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) +# define HAVE____BUILTIN_CLZL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_clz)) || (defined(__GNUC__ ) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))) +# define HAVE____BUILTIN_CLZ +#endif +#if (defined(__clang__) && __has_builtin(__builtin_addcll)) +# define HAVE____BUILTIN_ADDCLL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_addcl)) +# define HAVE____BUILTIN_ADDCL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_addc)) +# define HAVE____BUILTIN_ADDC +#endif +#if (defined(__clang__) && __has_builtin(__builtin_subcll)) +# define HAVE____BUILTIN_SUBCLL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_subcl)) +# define HAVE____BUILTIN_SUBCL +#endif +#if (defined(__clang__) && __has_builtin(__builtin_subc)) +# define HAVE____BUILTIN_SUBC +#endif + +#if defined __SIZEOF_INT128__ +#define HAVE____INT128_T +#endif + + +#ifndef DIGIT_T +#define DIGIT_T std::uint64_t +#endif + +#ifndef HALF_DIGIT_T +#define HALF_DIGIT_T std::uint32_t +#endif + +class uinteger_t; + +namespace std { // This is probably not a good idea + // Give uinteger_t type traits + template <> struct is_arithmetic : std::true_type {}; + template <> struct is_integral : std::true_type {}; + template <> struct is_unsigned : std::true_type {}; +} + +class uinteger_t { +public: + using digit = DIGIT_T; + using half_digit = HALF_DIGIT_T; + + static constexpr std::size_t digit_octets = sizeof(digit); // number of octets per digit + static constexpr std::size_t digit_bits = digit_octets * 8; // number of bits per digit + static constexpr std::size_t half_digit_octets = sizeof(half_digit); // number of octets per half_digit + static constexpr std::size_t half_digit_bits = half_digit_octets * 8; // number of bits per half_digit + + using container = std::vector; + + template + struct is_result { + static const bool value = false; + }; + + template + struct is_result> { + static const bool value = true; + }; + + template + struct is_result> { + static const bool value = true; + }; + +private: + static_assert(digit_octets == half_digit_octets * 2, "half_digit must be exactly half the size of digit"); + + static constexpr std::size_t karatsuba_cutoff = 1024 / digit_bits; + static constexpr double growth_factor = 1.5; + + std::size_t _begin; + std::size_t _end; + container _value_instance; + container& _value; + bool _carry; + +public: + // Window to vector (uses _begin and _end) + + void reserve(std::size_t sz) { + _value.reserve(sz + _begin); + } + + std::size_t grow(std::size_t n) { + // expands the vector using a growth factor + // and returns the new capacity. + auto cc = _value.capacity(); + if (n >= cc) { + cc = n * growth_factor; + _value.reserve(cc); + } + return cc; + } + + void resize(std::size_t sz) { + grow(sz + _begin); + _value.resize(sz + _begin); + } + + void resize(std::size_t sz, const digit& c) { + grow(sz + _begin); + _value.resize(sz + _begin, c); + } + + void clear() { + _value.clear(); + _begin = 0; + _end = 0; + _carry = false; + } + + digit* data() noexcept { + return _value.data() + _begin; + } + + const digit* data() const noexcept { + return _value.data() + _begin; + } + + std::size_t size() const noexcept { + return _end ? _end - _begin : _value.size() - _begin; + } + + void prepend(std::size_t sz, const digit& c) { + // Efficiently prepend by growing backwards by growth factor + auto min = std::min(_begin, sz); + if (min) { + // If there is some space before `_begin`, we try using it first: + _begin -= min; + std::fill_n(_value.begin() + _begin, min, c); + sz -= min; + } + if (sz) { + ASSERT(_begin == 0); // _begin should be 0 in here + // If there's still more room needed, we grow the vector: + // Ex.: grow using prepend(3, y) + // sz = 3 + // _begin = 0 (B) + // _end = 1 (E) + // initially (capacity == 12): + // |xxxxxxxxxx- | + // B E + // after reclaiming space after `_end` (same capacity == 12): + // |xxxxxxxxxx | + // B + // _end = 0 + // csz = 10 + // grow returns the new capacity (22) + // isz = 12 (22 - 10) + // _begin = 9 (12 - 3) + // after (capacity == (12 + 3) * 1.5 == 22): + // |---------yyyxxxxxxxxxx| + // B + if (_end) { + // reclaim space after `_end` + _value.resize(_end); + _end = 0; + } + auto csz = _value.size(); + auto isz = grow(csz + sz) - csz; + _value.insert(_value.begin(), isz, c); + _begin = isz - sz; + } + } + + void prepend(const digit& c) { + prepend(1, c); + } + + void prepend(const uinteger_t& num) { + prepend(num.size(), 0); + std::copy(num.begin(), num.end(), begin()); + } + + void append(std::size_t sz, const digit& c) { + // Efficiently append by growing by growth factor + if (_end) { + // reclaim space after `_end` + _value.resize(_end); + _end = 0; + } + auto nsz = _value.size() + sz; + grow(nsz); + _value.resize(nsz, c); + } + + void append(const digit& c) { + append(1, c); + } + + void append(const uinteger_t& num) { + auto sz = num.size(); + append(sz, 0); + std::copy(num.begin(), num.end(), end() - sz); + } + + container::iterator begin() noexcept { + return _value.begin() + _begin; + } + + container::const_iterator begin() const noexcept { + return _value.cbegin() + _begin; + } + + container::iterator end() noexcept { + return _end ? _value.begin() + _end : _value.end(); + } + + container::const_iterator end() const noexcept { + return _end ? _value.cbegin() + _end : _value.cend(); + } + + container::reverse_iterator rbegin() noexcept { + return _end ? container::reverse_iterator(_value.begin() + _end) : _value.rbegin(); + } + + container::const_reverse_iterator rbegin() const noexcept { + return _end ? container::const_reverse_iterator(_value.cbegin() + _end) : _value.crbegin(); + } + + container::reverse_iterator rend() noexcept { + return container::reverse_iterator(_value.begin() + _begin); + } + + container::const_reverse_iterator rend() const noexcept { + return container::const_reverse_iterator(_value.cbegin() + _begin); + } + + container::reference front() { + return *begin(); + } + + container::const_reference front() const { + return *begin(); + } + + container::reference back() { + return *rbegin(); + } + + container::const_reference back() const { + return *rbegin(); + } + +private: + // Optimized primitives for operations + + static digit _bits(digit x) { + #if defined HAVE____BUILTIN_CLZLL + if (digit_octets == sizeof(unsigned long long)) { + return x ? digit_bits - __builtin_clzll(x) : 1; + } + #endif + #if defined HAVE____BUILTIN_CLZL + if (digit_octets == sizeof(unsigned long)) { + return x ? digit_bits - __builtin_clzl(x) : 1; + } + #endif + #if defined HAVE____BUILTIN_CLZ + if (digit_octets == sizeof(unsigned)) { + return x ? digit_bits - __builtin_clz(x) : 1; + } + #endif + { + digit c = x ? 0 : 1; + while (x) { + x >>= 1; + ++c; + } + return c; + } + } + + static digit _mult(digit x, digit y, digit* lo) { + #if defined HAVE___UMUL128 + if (digit_bits == 64) { + digit h; + digit l = _umul128(x, y, &h); // _umul128(x, y, *hi) -> lo + return h; + } + #endif + #if defined HAVE___UMUL64 + if (digit_bits == 32) { + digit h; + digit l = _umul64(x, y, &h); // _umul64(x, y, *hi) -> lo + return h; + } + #endif + #if defined HAVE___UMUL32 + if (digit_bits == 16) { + digit h; + digit l = _umul32(x, y, &h); // _umul32(x, y, *hi) -> lo + return h; + } + #endif + #if defined HAVE____INT128_T + if (digit_bits == 64) { + auto r = static_cast<__uint128_t>(x) * static_cast<__uint128_t>(y); + *lo = r; + return r >> digit_bits; + } + #endif + if (digit_bits == 64) { + digit x0 = x & 0xffffffffUL; + digit x1 = x >> 32; + digit y0 = y & 0xffffffffUL; + digit y1 = y >> 32; + + digit u = (x0 * y0); + digit v = (x1 * y0) + (u >> 32); + digit w = (x0 * y1) + (v & 0xffffffffUL); + + *lo = (w << 32) + (u & 0xffffffffUL); // low + return (x1 * y1) + (v >> 32) + (w >> 32); // high + } if (digit_bits == 32) { + auto r = static_cast(x) * static_cast(y); + *lo = r; + return r >> 32; + } if (digit_bits == 16) { + auto r = static_cast(x) * static_cast(y); + *lo = r; + return r >> 16; + } if (digit_bits == 8) { + auto r = static_cast(x) * static_cast(y); + *lo = r; + return r >> 8; + } + } + + static digit _multadd(digit x, digit y, digit a, digit c, digit* lo) { + #if defined HAVE___UMUL128 && defined HAVE___ADDCARRY_U64 + if (digit_bits == 64) { + digit h; + digit l = _umul128(x, y, &h); // _umul128(x, y, *hi) -> lo + return h + _addcarry_u64(c, l, a, lo); // _addcarry_u64(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___UMUL64 && defined HAVE___ADDCARRY_U32 + if (digit_bits == 32) { + digit h; + digit l = _umul64(x, y, &h); // _umul64(x, y, *hi) -> lo + return h + _addcarry_u32(c, l, a, lo); // _addcarry_u32(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___UMUL32 && defined HAVE___ADDCARRY_U16 + if (digit_bits == 16) { + digit h; + digit l = _umul32(x, y, &h); // _umul32(x, y, *hi) -> lo + return h + _addcarry_u16(c, l, a, lo); // _addcarry_u16(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE____INT128_T + if (digit_bits == 64) { + auto r = static_cast<__uint128_t>(x) * static_cast<__uint128_t>(y) + static_cast<__uint128_t>(a) + static_cast<__uint128_t>(c); + *lo = r; + return r >> digit_bits; + } + #endif + if (digit_bits == 64) { + digit x0 = x & 0xffffffffUL; + digit x1 = x >> 32; + digit y0 = y & 0xffffffffUL; + digit y1 = y >> 32; + + digit u = (x0 * y0) + (a & 0xffffffffUL) + (c & 0xffffffffUL); + digit v = (x1 * y0) + (u >> 32) + (a >> 32) + (c >> 32); + digit w = (x0 * y1) + (v & 0xffffffffUL); + + *lo = (w << 32) + (u & 0xffffffffUL); // low + return (x1 * y1) + (v >> 32) + (w >> 32); // high + } + if (digit_bits == 32) { + auto r = static_cast(x) * static_cast(y) + static_cast(a) + static_cast(c); + *lo = r; + return r >> 32; + } + if (digit_bits == 16) { + auto r = static_cast(x) * static_cast(y) + static_cast(a) + static_cast(c); + *lo = r; + return r >> 16; + } + if (digit_bits == 8) { + auto r = static_cast(x) * static_cast(y) + static_cast(a) + static_cast(c); + *lo = r; + return r >> 8; + } + } + + static digit _divmod(digit x_hi, digit x_lo, digit y, digit* result) { + #if defined HAVE____INT128_T + if (digit_bits == 64) { + auto x = static_cast<__uint128_t>(x_hi) << digit_bits | static_cast<__uint128_t>(x_lo); + digit q = x / y; + digit r = x % y; + + *result = q; + return r; + } + #endif + if (digit_bits == 64) { + // quotient + digit q = x_lo << 1; + + // remainder + digit r = x_hi; + + digit carry = x_lo >> 63; + int i; + + for (i = 0; i < 64; i++) { + auto tmp = r >> 63; + r <<= 1; + r |= carry; + carry = tmp; + + if (carry == 0) { + if (r >= y) { + carry = 1; + } else { + tmp = q >> 63; + q <<= 1; + q |= carry; + carry = tmp; + continue; + } + } + + r -= y; + r -= (1 - carry); + carry = 1; + tmp = q >> 63; + q <<= 1; + q |= carry; + carry = tmp; + } + + *result = q; + return r; + } + if (digit_bits == 32) { + auto x = static_cast(x_hi) << 32 | static_cast(x_lo); + digit q = x / y; + digit r = x % y; + + *result = q; + return r; + } + if (digit_bits == 16) { + auto x = static_cast(x_hi) << 16 | static_cast(x_lo); + digit q = x / y; + digit r = x % y; + + *result = q; + return r; + } + if (digit_bits == 8) { + auto x = static_cast(x_hi) << 8 | static_cast(x_lo); + digit q = x / y; + digit r = x % y; + + *result = q; + return r; + } + } + + static digit _addcarry(digit x, digit y, digit c, digit* result) { + #if defined HAVE___ADDCARRY_U64 + if (digit_bits == 64) { + return _addcarry_u64(c, x, y, result); // _addcarry_u64(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___ADDCARRY_U32 + if (digit_bits == 32) { + return _addcarry_u32(c, x, y, result); // _addcarry_u32(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___ADDCARRY_U16 + if (digit_bits == 16) { + return _addcarry_u16(c, x, y, result); // _addcarry_u16(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE____BUILTIN_ADDCLL + if (digit_octets == sizeof(unsigned long long)) { + unsigned long long carryout; + *result = __builtin_addcll(x, y, c, &carryout); // __builtin_addcll(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____BUILTIN_ADDCL + if (digit_octets == sizeof(unsigned long)) { + unsigned long carryout; + *result = __builtin_addcl(x, y, c, &carryout); // __builtin_addcl(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____BUILTIN_ADDC + if (digit_octets == sizeof(unsigned)) { + unsigned carryout; + *result = __builtin_addc(x, y, c, &carryout); // __builtin_addc(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____INT128_T + if (digit_bits == 64) { + auto r = static_cast<__uint128_t>(x) + static_cast<__uint128_t>(y) + static_cast<__uint128_t>(c); + *result = r; + return static_cast(r >> digit_bits); + } + #endif + if (digit_bits == 64) { + digit x0 = x & 0xffffffffUL; + digit x1 = x >> 32; + digit y0 = y & 0xffffffffUL; + digit y1 = y >> 32; + + auto u = x0 + y0 + c; + auto v = x1 + y1 + static_cast(u >> 32); + *result = (v << 32) + (u & 0xffffffffUL); + return static_cast(v >> 32); + } + if (digit_bits == 32) { + auto r = static_cast(x) + static_cast(y) + static_cast(c); + *result = r; + return static_cast(r >> 32); + } + if (digit_bits == 16) { + auto r = static_cast(x) + static_cast(y) + static_cast(c); + *result = r; + return static_cast(r >> 16); + } + if (digit_bits == 8) { + auto r = static_cast(x) + static_cast(y) + static_cast(c); + *result = r; + return static_cast(r >> 8); + } + } + + static digit _subborrow(digit x, digit y, digit c, digit* result) { + #if defined HAVE___SUBBORROW_U64 + if (digit_bits == 64) { + return _subborrow_u64(c, x, y, result); // _subborrow_u64(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___SUBBORROW_U32 + if (digit_bits == 64) { + return _subborrow_u32(c, x, y, result); // _subborrow_u32(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE___SUBBORROW_U16 + if (digit_bits == 64) { + return _subborrow_u16(c, x, y, result); // _subborrow_u16(carryin, x, y, *sum) -> carryout + } + #endif + #if defined HAVE____BUILTIN_SUBCLL + if (digit_octets == sizeof(unsigned long long)) { + unsigned long long carryout; + *result = __builtin_subcll(x, y, c, &carryout); // __builtin_subcll(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____BUILTIN_SUBCL + if (digit_octets == sizeof(unsigned long)) { + unsigned long carryout; + *result = __builtin_subcl(x, y, c, &carryout); // __builtin_subcl(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____BUILTIN_SUBC + if (digit_octets == sizeof(unsigned)) { + unsigned carryout; + *result = __builtin_subc(x, y, c, &carryout); // __builtin_subc(x, y, carryin, *carryout) -> sum + return carryout; + } + #endif + #if defined HAVE____INT128_T + if (digit_bits == 64) { + auto r = static_cast<__uint128_t>(x) - static_cast<__uint128_t>(y) - static_cast<__uint128_t>(c); + *result = r; + return static_cast(r >> 64); + } + #endif + if (digit_bits == 64) { + digit x0 = x & 0xffffffffUL; + digit x1 = x >> 32; + digit y0 = y & 0xffffffffUL; + digit y1 = y >> 32; + + auto u = x0 - y0 - c; + auto v = x1 - y1 - static_cast(u >> 32); + *result = (v << 32) + (u & 0xffffffffUL); + return static_cast(v >> 32); + } + if (digit_bits == 32) { + auto r = static_cast(x) - static_cast(y) - static_cast(c); + *result = r; + return static_cast(r >> 32); + } + if (digit_bits == 16) { + auto r = static_cast(x) - static_cast(y) - static_cast(c); + *result = r; + return static_cast(r >> 16); + } + if (digit_bits == 8) { + auto r = static_cast(x) - static_cast(y) - static_cast(c); + *result = r; + return static_cast(r >> 8); + } + } + + // Helper functions + + void trim(digit mask = 0) { + auto rit = rbegin(); + auto rit_e = rend(); + + // Masks the last value of internal vector + mask &= (digit_bits - 1); + if (mask && rit != rit_e) { + *rit &= (static_cast(1) << mask) - 1; + } + + // Removes all unused zeros from the internal vector + auto rit_f = std::find_if(rit, rit_e, [](const digit& c) { return c; }); + resize(rit_e - rit_f); // shrink + } + + static constexpr char chr(int ord) { + constexpr const char _[256] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + }; + return _[ord]; + } + + static constexpr int ord(int chr) { + constexpr const int _[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + return _[chr]; + } + +public: + static constexpr unsigned base_bits(int base) { + constexpr const unsigned _[256] = { + 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + }; + return _[base - 1]; + } + + static constexpr unsigned base_size(int base) { + constexpr const unsigned _[256] = { + 0, 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, + 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, + + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, + }; + return _[base - 1]; + } + + static const uinteger_t uint_0() { + static uinteger_t uint_0(0); + return uint_0; + } + + static const uinteger_t uint_1() { + static uinteger_t uint_1(1); + return uint_1; + } + +private: + // Public Implementation +#ifdef UINT_T_PUBLIC_IMPLEMENTATION +public: +#endif + static uinteger_t& bitwise_and(uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz > rhs_sz) { + lhs.resize(rhs_sz); // shrink + } + + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs.end(); + + auto rhs_it = rhs.begin(); + + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it) { + *lhs_it &= *rhs_it; + } + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& bitwise_and(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + auto result_sz = std::max(lhs_sz, rhs_sz); + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` or `rhs` if `result` is also either `rhs` or `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + auto it = result.begin(); + + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it & *rhs_it; + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++it) { + *it = 0; + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it & *rhs_it; + } + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = 0; + } + } + + // Finish up + result.trim(); + return result; + } + + static uinteger_t bitwise_and(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + bitwise_and(result, lhs, rhs); + return result; + } + + static uinteger_t& bitwise_or(uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz < rhs_sz) { + lhs.resize(rhs_sz, 0); // grow + } + + auto lhs_it = lhs.begin(); + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs.end(); + + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it) { + *lhs_it |= *rhs_it; + } + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& bitwise_or(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + auto result_sz = std::max(lhs_sz, rhs_sz); + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` or `rhs` if `result` is also either `rhs` or `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + auto it = result.begin(); + + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it | *rhs_it; + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++it) { + *it = *rhs_it; + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it | *rhs_it; + } + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = *lhs_it; + } + } + + // Finish up + result.trim(); + return result; + } + static uinteger_t bitwise_or(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + bitwise_or(result, lhs, rhs); + return result; + } + + static uinteger_t& bitwise_xor(uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz < rhs_sz) { + lhs.resize(rhs_sz, 0); // grow + } + + auto lhs_it = lhs.begin(); + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs.end(); + + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it) { + *lhs_it ^= *rhs_it; + } + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& bitwise_xor(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + auto result_sz = std::max(lhs_sz, rhs_sz); + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` or `rhs` if `result` is also either `rhs` or `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + auto it = result.begin(); + + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it ^ *rhs_it; + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++it) { + *it = *rhs_it; + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it, ++it) { + *it = *lhs_it ^ *rhs_it; + } + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = *lhs_it; + } + } + + // Finish up + result.trim(); + return result; + } + + static uinteger_t bitwise_xor(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + bitwise_xor(result, lhs, rhs); + return result; + } + + static uinteger_t& bitwise_inv(uinteger_t& lhs) { + auto lhs_sz = lhs.size(); + + auto b = lhs.bits(); + + if (!lhs_sz) { + lhs.append(0); + } + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` if `result` is also `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + for (; lhs_it != lhs_it_e; ++lhs_it) { + *lhs_it = ~*lhs_it; + } + + // Finish up + lhs.trim(b ? b : 1); + return lhs; + } + + static uinteger_t& bitwise_inv(uinteger_t& result, const uinteger_t& lhs) { + auto lhs_sz = lhs.size(); + + auto b = lhs.bits(); + + auto result_sz = lhs_sz ? lhs_sz : 1; + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` if `result` is also `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto it = result.begin(); + auto it_e = it + result_sz; + + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = ~*lhs_it; + } + for (; it != it_e; ++it) { + *it = ~static_cast(0); + } + + // Finish up + result.trim(b ? b : 1); + return result; + } + + static uinteger_t bitwise_inv(const uinteger_t& lhs) { + uinteger_t result; + bitwise_inv(result, lhs); + return result; + } + + static uinteger_t& bitwise_lshift(uinteger_t& lhs, const uinteger_t& rhs) { + if (!rhs) { + return lhs; + } + + uinteger_t shifts_q; + uinteger_t shifts_r; + auto _digit_bits = digit_bits; + auto uint_digit_bits = uinteger_t(_digit_bits); + divmod(shifts_q, shifts_r, rhs, uint_digit_bits); + std::size_t shifts = static_cast(shifts_q); + std::size_t shift = static_cast(shifts_r); + + if (shifts) { + lhs.prepend(shifts, 0); + } + if (shift) { + digit shifted = 0; + auto lhs_it = lhs.begin() + shifts; + auto lhs_it_e = lhs.end(); + for (; lhs_it != lhs_it_e; ++lhs_it) { + auto v = (*lhs_it << shift) | shifted; + shifted = *lhs_it >> (_digit_bits - shift); + *lhs_it = v; + } + if (shifted) { + lhs.append(shifted); + } + } + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& bitwise_lshift(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + if (&result._value == &lhs._value) { + bitwise_lshift(result, rhs); + return result; + } + if (!rhs) { + result = lhs; + return result; + } + + auto lhs_sz = lhs.size(); + + uinteger_t shifts_q; + uinteger_t shifts_r; + auto _digit_bits = digit_bits; + auto uint_digit_bits = uinteger_t(_digit_bits); + divmod(shifts_q, shifts_r, rhs, uint_digit_bits); + std::size_t shifts = static_cast(shifts_q); + std::size_t shift = static_cast(shifts_r); + + auto result_sz = lhs_sz + shifts; + result.grow(result_sz + 1); + result.resize(shifts, 0); + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` if `result` is also `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto it = result.begin() + shifts; + + if (shift) { + digit shifted = 0; + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + auto v = (*lhs_it << shift) | shifted; + shifted = *lhs_it >> (_digit_bits - shift); + *it = v; + } + if (shifted) { + result.append(shifted); + } + } else { + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = *lhs_it; + } + } + + // Finish up + result.trim(); + return result; + } + + static uinteger_t bitwise_lshift(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + bitwise_lshift(result, lhs, rhs); + return result; + } + + static uinteger_t& bitwise_rshift(uinteger_t& lhs, const uinteger_t& rhs) { + if (!rhs) { + return lhs; + } + + auto lhs_sz = lhs.size(); + + auto _digit_bits = digit_bits; + if (compare(rhs, uinteger_t(lhs_sz * _digit_bits)) >= 0) { + lhs = uint_0(); + return lhs; + } + + uinteger_t shifts_q; + uinteger_t shifts_r; + auto uint_digit_bits = uinteger_t(_digit_bits); + divmod(shifts_q, shifts_r, rhs, uint_digit_bits); + std::size_t shifts = static_cast(shifts_q); + std::size_t shift = static_cast(shifts_r); + + if (shifts) { + lhs._begin += shifts; + } + if (shift) { + digit shifted = 0; + auto lhs_rit = lhs.rbegin(); + auto lhs_rit_e = lhs.rend(); + for (; lhs_rit != lhs_rit_e; ++lhs_rit) { + auto v = (*lhs_rit >> shift) | shifted; + shifted = *lhs_rit << (_digit_bits - shift); + *lhs_rit = v; + } + lhs.trim(); + } + + return lhs; + } + + static uinteger_t& bitwise_rshift(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + if (&result._value == &lhs._value) { + bitwise_lshift(result, rhs); + return result; + } + if (!rhs) { + result = lhs; + return result; + } + + auto lhs_sz = lhs.size(); + + auto _digit_bits = digit_bits; + if (compare(rhs, uinteger_t(lhs_sz * _digit_bits)) >= 0) { + result = uint_0(); + return result; + } + + uinteger_t shifts_q; + uinteger_t shifts_r; + auto uint_digit_bits = uinteger_t(_digit_bits); + divmod(shifts_q, shifts_r, rhs, uint_digit_bits); + std::size_t shifts = static_cast(shifts_q); + std::size_t shift = static_cast(shifts_r); + + auto result_sz = lhs_sz - shifts; + result.resize(result_sz); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` if `result` is also `lhs`. + auto lhs_rit = lhs.rbegin(); + auto lhs_rit_e = lhs_rit + lhs_sz - shifts; + + auto rit = result.rbegin(); + auto rit_e = rit + result_sz; + + if (shift) { + digit shifted = 0; + for (; lhs_rit != lhs_rit_e; ++lhs_rit, ++rit) { + ASSERT(rit != rit_e); (void)(rit_e); + auto v = (*lhs_rit >> shift) | shifted; + shifted = *lhs_rit << (_digit_bits - shift); + *rit = v; + } + } else { + for (; lhs_rit != lhs_rit_e; ++lhs_rit, ++rit) { + ASSERT(rit != rit_e); (void)(rit_e); + *rit = *lhs_rit; + } + } + + // Finish up + result.trim(); + return result; + } + + static uinteger_t bitwise_rshift(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + bitwise_rshift(result, lhs, rhs); + return result; + } + + static int compare(const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz > rhs_sz) return 1; + if (lhs_sz < rhs_sz) return -1; + + auto lhs_rit = lhs.rbegin(); + auto lhs_rit_e = lhs.rend(); + + auto rhs_rit = rhs.rbegin(); + + for (; lhs_rit != lhs_rit_e && *lhs_rit == *rhs_rit; ++lhs_rit, ++rhs_rit); + + if (lhs_rit != lhs_rit_e) { + if (*lhs_rit > *rhs_rit) return 1; + if (*lhs_rit < *rhs_rit) return -1; + } + + return 0; + } + + static uinteger_t& long_add(uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz < rhs_sz) { + lhs.reserve(rhs_sz + 1); + lhs.resize(rhs_sz, 0); // grow + } + + // not using `end()` because resize of `lhs.resize()` could have + // resized `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + digit carry = 0; + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++rhs_it, ++lhs_it) { + carry = _addcarry(*lhs_it, *rhs_it, carry, &*lhs_it); + } + for (; carry && rhs_it != rhs_it_e; ++rhs_it, ++lhs_it) { + carry = _addcarry(0, *rhs_it, carry, &*lhs_it); + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++lhs_it) { + *lhs_it = *rhs_it; + } + } else { + for (; rhs_it != rhs_it_e; ++rhs_it, ++lhs_it) { + carry = _addcarry(*lhs_it, *rhs_it, carry, &*lhs_it); + } + for (; carry && lhs_it != lhs_it_e; ++lhs_it) { + carry = _addcarry(*lhs_it, 0, carry, &*lhs_it); + } + } + + if (carry) { + lhs.append(1); + } + + lhs._carry = false; + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& long_add(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + auto result_sz = std::max(lhs_sz, rhs_sz); + result.reserve(result_sz + 1); + result.resize(result_sz, 0); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` or `rhs` if `result` is also either `rhs` or `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + auto it = result.begin(); + + digit carry = 0; + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it, ++it) { + carry = _addcarry(*lhs_it, *rhs_it, carry, &*it); + } + for (; carry && rhs_it != rhs_it_e; ++rhs_it, ++it) { + carry = _addcarry(0, *rhs_it, carry, &*it); + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++it) { + *it = *rhs_it; + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it, ++it) { + carry = _addcarry(*lhs_it, *rhs_it, carry, &*it); + } + for (; carry && lhs_it != lhs_it_e; ++lhs_it, ++it) { + carry = _addcarry(*lhs_it, 0, carry, &*it); + } + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = *lhs_it; + } + } + + if (carry) { + result.append(1); + } + result._carry = false; + + // Finish up + result.trim(); + return result; + } + + static uinteger_t& add(uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!rhs) { + return lhs; + } + if (!lhs) { + lhs = rhs; + return lhs; + } + + return long_add(lhs, rhs); + } + + static uinteger_t& add(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!rhs) { + result = lhs; + return result; + } + if (!lhs) { + result = rhs; + return result; + } + + return long_add(result, lhs, rhs); + } + + static uinteger_t add(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + add(result, lhs, rhs); + return result; + } + + static uinteger_t& long_sub(uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz < rhs_sz) { + lhs.resize(rhs_sz, 0); // grow + } + + // not using `end()` because resize of `lhs.resize()` could have + // resized `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + digit borrow = 0; + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it) { + borrow = _subborrow(*lhs_it, *rhs_it, borrow, &*lhs_it); + } + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it) { + borrow = _subborrow(0, *rhs_it, borrow, &*lhs_it); + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it) { + borrow = _subborrow(*lhs_it, *rhs_it, borrow, &*lhs_it); + } + for (; borrow && lhs_it != lhs_it_e; ++lhs_it) { + borrow = _subborrow(*lhs_it, 0, borrow, &*lhs_it); + } + } + + lhs._carry = borrow; + + // Finish up + lhs.trim(); + return lhs; + } + + static uinteger_t& long_sub(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + auto result_sz = std::max(lhs_sz, rhs_sz); + result.resize(result_sz, 0); + + // not using `end()` because resize of `result.resize()` could have + // resized `lhs` or `rhs` if `result` is also either `rhs` or `lhs`. + auto lhs_it = lhs.begin(); + auto lhs_it_e = lhs_it + lhs_sz; + + auto rhs_it = rhs.begin(); + auto rhs_it_e = rhs_it + rhs_sz; + + auto it = result.begin(); + + digit borrow = 0; + if (lhs_sz < rhs_sz) { + for (; lhs_it != lhs_it_e; ++lhs_it, ++rhs_it, ++it) { + borrow = _subborrow(*lhs_it, *rhs_it, borrow, &*it); + } + for (; rhs_it != rhs_it_e; ++rhs_it, ++it) { + borrow = _subborrow(0, *rhs_it, borrow, &*it); + } + } else { + for (; rhs_it != rhs_it_e; ++lhs_it, ++rhs_it, ++it) { + borrow = _subborrow(*lhs_it, *rhs_it, borrow, &*it); + } + for (; borrow && lhs_it != lhs_it_e; ++lhs_it, ++it) { + borrow = _subborrow(*lhs_it, 0, borrow, &*it); + } + for (; lhs_it != lhs_it_e; ++lhs_it, ++it) { + *it = *lhs_it; + } + } + + result._carry = borrow; + + // Finish up + result.trim(); + return result; + } + + static uinteger_t& sub(uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!rhs) { + return lhs; + } + + return long_sub(lhs, rhs); + } + + static uinteger_t& sub(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!rhs) { + result = lhs; + return result; + } + + return long_sub(result, lhs, rhs); + } + + static uinteger_t sub(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + sub(result, lhs, rhs); + return result; + } + + // Single word long multiplication + // Fastests, but ONLY for single sized rhs + static uinteger_t& single_mult(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + ASSERT(rhs_sz == 1); (void)(rhs_sz); + auto n = rhs.front(); + + uinteger_t tmp; + tmp.resize(lhs_sz + 1, 0); + + auto it_lhs = lhs.begin(); + auto it_lhs_e = lhs.end(); + + auto it_result = tmp.begin(); + + digit carry = 0; + for (; it_lhs != it_lhs_e; ++it_lhs, ++it_result) { + carry = _multadd(*it_lhs, n, 0, carry, &*it_result); + } + if (carry) { + *it_result = carry; + } + + result = std::move(tmp); + + // Finish up + result.trim(); + return result; + } + + static uinteger_t& long_mult(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz > rhs_sz) { + // rhs should be the largest: + return long_mult(result, rhs, lhs); + } + + if (lhs_sz == 1) { + return single_mult(result, rhs, lhs); + } + + uinteger_t tmp; + tmp.resize(lhs_sz + rhs_sz, 0); + + auto it_lhs = lhs.begin(); + auto it_lhs_e = lhs.end(); + + auto it_rhs = rhs.begin(); + auto it_rhs_e = rhs.end(); + + auto it_result = tmp.begin(); + auto it_result_s = it_result; + auto it_result_l = it_result; + + for (; it_lhs != it_lhs_e; ++it_lhs, ++it_result) { + if (auto lhs_it_val = *it_lhs) { + auto _it_rhs = it_rhs; + auto _it_result = it_result; + digit carry = 0; + for (; _it_rhs != it_rhs_e; ++_it_rhs, ++_it_result) { + carry = _multadd(*_it_rhs, lhs_it_val, *_it_result, carry, &*_it_result); + } + if (carry) { + *_it_result++ = carry; + } + if (it_result_l < _it_result) { + it_result_l = _it_result; + } + } + } + + tmp.resize(it_result_l - it_result_s); // shrink + + result = std::move(tmp); + + // Finish up + result.trim(); + return result; + } + + // A helper for Karatsuba multiplication to split a number in two, at n. + static std::pair karatsuba_mult_split(const uinteger_t& num, std::size_t n) { + const uinteger_t a(num, num._begin, num._begin + n); + const uinteger_t b(num, num._begin + n, num._end); + return std::make_pair(std::move(a), std::move(b)); + } + + // If rhs has at least twice the digits of lhs, and lhs is big enough that + // Karatsuba would pay off *if* the inputs had balanced sizes. + // View rhs as a sequence of slices, each with lhs.size() digits, + // and multiply the slices by lhs, one at a time. + static uinteger_t& karatsuba_lopsided_mult(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs, std::size_t cutoff) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + ASSERT(lhs_sz > cutoff); + ASSERT(2 * lhs_sz <= rhs_sz); + + auto rhs_begin = rhs._begin; + std::size_t shift = 0; + + uinteger_t r; + while (rhs_sz > 0) { + // Multiply the next slice of rhs by lhs and add into result: + auto slice_size = std::min(lhs_sz, rhs_sz); + const uinteger_t rhs_slice(rhs, rhs_begin, rhs_begin + slice_size); + uinteger_t p; + karatsuba_mult(p, lhs, rhs_slice, cutoff); + uinteger_t rs(r, shift, 0); + add(rs, rs, p); + shift += slice_size; + rhs_sz -= slice_size; + rhs_begin += slice_size; + } + + result = std::move(r); + return result; + } + + // Karatsuba multiplication + static uinteger_t& karatsuba_mult(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs, std::size_t cutoff = 1) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + if (lhs_sz > rhs_sz) { + // rhs should be the largest: + return karatsuba_mult(result, rhs, lhs, cutoff); + } + + if (lhs_sz <= cutoff) { + return long_mult(result, lhs, rhs); + } + + // If a is too small compared to b, splitting on b gives a degenerate case + // in which Karatsuba may be (even much) less efficient than long multiplication. + if (2 * lhs_sz <= rhs_sz) { + return karatsuba_lopsided_mult(result, lhs, rhs, cutoff); + } + + // Karatsuba: + // + // A B + // x C D + // --------------------- + // AD BD + // AC BC + // --------------------- + // AC AD + BC BD + // + // AD + BC = + // AC + AD + BC + BD - AC - BD + // (A + B) (C + D) - AC - BD + + // Calculate the split point near the middle of the largest (rhs). + auto shift = rhs_sz >> 1; + + // Split to get A and B: + const auto lhs_pair = karatsuba_mult_split(lhs, shift); + const auto& A = lhs_pair.second; // hi + const auto& B = lhs_pair.first; // lo + + // Split to get C and D: + const auto rhs_pair = karatsuba_mult_split(rhs, shift); + const auto& C = rhs_pair.second; // hi + const auto& D = rhs_pair.first; // lo + + // Get the pieces: + uinteger_t AC; + karatsuba_mult(AC, A, C, cutoff); + + uinteger_t BD; + karatsuba_mult(BD, B, D, cutoff); + uinteger_t AD_BC, AB, CD; + karatsuba_mult(AD_BC, A + B, C + D, cutoff); + AD_BC -= AC; + AD_BC -= BD; + + // Join the pieces, AC and BD (can't overlap) into BD: + BD.reserve(shift * 2 + AC.size()); + BD.resize(shift * 2, 0); + BD.append(AC); + + // And add AD_BC to the middle: (AC BD) + ( AD + BC ): + uinteger_t BDs(BD, shift, 0); + add(BDs, BDs, AD_BC); + + result = std::move(BD); + + // Finish up + result.trim(); + return result; + } + + static uinteger_t& mult(uinteger_t& lhs, const uinteger_t& rhs) { + // Hard to see how this could have a further optimized implementation. + return mult(lhs, lhs, rhs); + } + + static uinteger_t& mult(uinteger_t& result, const uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!lhs || !rhs) { + result = uint_0(); + return result; + } + if (compare(lhs, uint_1()) == 0) { + result = rhs; + return result; + } + if (compare(rhs, uint_1()) == 0) { + result = lhs; + return result; + } + + return karatsuba_mult(result, lhs, rhs, karatsuba_cutoff); + } + + static uinteger_t mult(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t result; + mult(result, lhs, rhs); + return result; + } + + // Single word long division + // Fastests, but ONLY for single sized rhs + static std::pair, std::reference_wrapper> single_divmod(uinteger_t& quotient, uinteger_t& remainder, const uinteger_t& lhs, const uinteger_t& rhs) { + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + + ASSERT(rhs_sz == 1); (void)(rhs_sz); + auto n = rhs.front(); + + auto rit_lhs = lhs.rbegin(); + auto rit_lhs_e = lhs.rend(); + + auto q = uint_0(); + q.resize(lhs_sz, 0); + auto rit_q = q.rbegin(); + + digit r = 0; + for (; rit_lhs != rit_lhs_e; ++rit_lhs, ++rit_q) { + r = _divmod(r, *rit_lhs, n, &*rit_q); + } + + q.trim(); + + quotient = std::move(q); + remainder = r; + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + + // Implementation of Knuth's Algorithm D + static std::pair, std::reference_wrapper> knuth_divmod(uinteger_t& quotient, uinteger_t& remainder, const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t v(lhs); + uinteger_t w(rhs); + + auto v_size = v.size(); + auto w_size = w.size(); + ASSERT(v_size >= w_size && w_size >= 2); + + // D1. normalize: shift rhs left so that its top digit is >= 63 bits. + // shift lhs left by the same amount. Results go into w and v. + auto d = uinteger_t(digit_bits - _bits(w.back())); + v <<= d; + w <<= d; + + if (*v.rbegin() >= *w.rbegin()) { + v.append(0); + } + v_size = v.size(); + v.append(0); + + // Now *v.rbegin() < *w.rbegin() so quotient has at most + // (and usually exactly) k = v.size() - w.size() digits. + auto k = v_size - w_size; + auto q = uint_0(); + q.resize(k + 1, 0); + + auto rit_q = q.rend() - (k + 1); + + auto it_v_b = v.begin(); + auto it_v_k = it_v_b + k; + + auto it_w = w.begin(); + auto it_w_e = w.end(); + + auto rit_w = w.rbegin(); + auto wm1 = *rit_w++; + auto wm2 = *rit_w; + + // D2. inner loop: divide v[k+0..k+n] by w[0..n] + for (; it_v_k >= it_v_b; --it_v_k, ++rit_q) { + // D3. Compute estimate quotient digit q; may overestimate by 1 (rare) + digit _q; + auto _r = _divmod(*(it_v_k + w_size), *(it_v_k + w_size - 1), wm1, &_q); + digit mullo = 0; + auto mulhi = _mult(_q, wm2, &mullo); + auto rlo = *(it_v_k + w_size - 2); + while (mulhi > _r || (mulhi == _r && mullo > rlo)) { + --_q; + if (_addcarry(_r, wm1, 0, &_r)) { + break; + } + mulhi = _mult(_q, wm2, &mullo); + } + + // D4. Multiply and subtract _q * w0[0:size_w] from vk[0:size_w+1] + auto _it_v = it_v_k; + auto _it_w = it_w; + mulhi = 0; + digit carry = 0; + for (; _it_w != it_w_e; ++_it_v, ++_it_w) { + mullo = 0; + mulhi = _multadd(*_it_w, _q, 0, mulhi, &mullo); + carry = _subborrow(*_it_v, mullo, carry, &*_it_v); + } + carry = _subborrow(*_it_v, 0, carry, &*_it_v); + + if (carry) { + // D6. Add w back if q was too large (this branch taken rarely) + --_q; + + _it_v = it_v_k; + _it_w = it_w; + carry = 0; + for (; _it_w != it_w_e; ++_it_v, ++_it_w) { + carry = _addcarry(*_it_v, *_it_w, carry, &*_it_v); + } + carry = _addcarry(*_it_v, 0, carry, &*_it_v); + } + + /* store quotient digit */ + *rit_q = _q; + } + + // D8. unnormalize: unshift remainder. + v.resize(w_size); + v >>= d; + + q.trim(); + v.trim(); + + quotient = std::move(q); + remainder = std::move(v); + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + + static std::pair, std::reference_wrapper> divmod(uinteger_t& quotient, uinteger_t& remainder, const uinteger_t& lhs, const uinteger_t& rhs) { + // First try saving some calculations: + if (!rhs) { + throw std::domain_error("Error: division or modulus by 0"); + } + auto lhs_sz = lhs.size(); + auto rhs_sz = rhs.size(); + if (lhs_sz == 1 && rhs_sz == 1) { + // Fast division and modulo for single value + auto a = *lhs.begin(); + auto b = *rhs.begin(); + quotient = a / b; + remainder = a % b; + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + if (compare(rhs, uint_1()) == 0) { + quotient = lhs; + remainder = uint_0(); + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + auto compared = compare(lhs, rhs); + if (compared == 0) { + quotient = uint_1(); + remainder = uint_0(); + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + if (!lhs || compared < 0) { + quotient = uint_0(); + remainder = lhs; + return std::make_pair(std::ref(quotient), std::ref(remainder)); + } + if (rhs_sz == 1) { + return single_divmod(quotient, remainder, lhs, rhs); + } + + return knuth_divmod(quotient, remainder, lhs, rhs); + } + + static std::pair divmod(const uinteger_t& lhs, const uinteger_t& rhs) { + uinteger_t quotient; + uinteger_t remainder; + divmod(quotient, remainder, lhs, rhs); + return std::make_pair(std::move(quotient), std::move(remainder)); + } + +private: + // Constructors + + template ::value and not std::is_same>::value>> + void _uint_t(const T& value) { + append(static_cast(value)); + } + + template ::value and not std::is_same>::value>> + void _uint_t(const T& value, Args... args) { + _uint_t(args...); + append(static_cast(value)); + } + + // This constructor creates a window view of the _value + uinteger_t(const uinteger_t& o, std::size_t begin, std::size_t end) : + _begin(begin), + _end(end), + _value(o._value), + _carry(o._carry) { } + +public: + uinteger_t() : + _begin(0), + _end(0), + _value(_value_instance), + _carry(false) { } + + uinteger_t(const uinteger_t& o) : + _begin(0), + _end(0), + _value_instance(o.begin(), o.end()), + _value(_value_instance), + _carry(o._carry) { } + + uinteger_t(uinteger_t&& o) : + _begin(std::move(o._begin)), + _end(std::move(o._end)), + _value_instance(std::move(o._value_instance)), + _value(_value_instance), + _carry(std::move(o._carry)) { } + + template ::value and not std::is_same>::value>> + uinteger_t(const T& value) : + _begin(0), + _end(0), + _value(_value_instance), + _carry(false) { + if (value) { + append(static_cast(value)); + } + } + + template ::value and not std::is_same>::value>> + uinteger_t(const T& value, Args... args) : + _begin(0), + _end(0), + _value(_value_instance), + _carry(false) { + _uint_t(args...); + append(static_cast(value)); + trim(); + } + + template ::value and not std::is_same>::value>> + uinteger_t(std::initializer_list list) : + _begin(0), + _end(0), + _value(_value_instance), + _carry(false) { + reserve(list.size()); + for (const auto& value : list) { + append(static_cast(value)); + } + trim(); + } + + template + explicit uinteger_t(T (&s)[N], int base=10) : + uinteger_t(s, N - 1, base) { } + + explicit uinteger_t(const unsigned char* bytes, std::size_t sz, int base) : + uinteger_t(strtouint(bytes, sz, base)) { } + + explicit uinteger_t(const char* bytes, std::size_t sz, int base) : + uinteger_t(strtouint(bytes, sz, base)) { } + + template + explicit uinteger_t(const std::vector& bytes, int base=10) : + uinteger_t(bytes.data(), bytes.size(), base) { } + + explicit uinteger_t(const std::string& bytes, int base=10) : + uinteger_t(bytes.data(), bytes.size(), base) { } + + // Assignment Operator + uinteger_t& operator=(const uinteger_t& o) { + _begin = 0; + _end = 0; + _value = container(o.begin(), o.end()); + _carry = o._carry; + return *this; + } + uinteger_t& operator=(uinteger_t&& o) { + _begin = std::move(o._begin); + _end = std::move(o._end); + _value_instance = std::move(o._value_instance); + _carry = std::move(o._carry); + return *this; + } + + // Typecast Operators + explicit operator bool() const { + return static_cast(size()); + } + explicit operator unsigned char() const { + return static_cast(size() ? front() : 0); + } + explicit operator unsigned short() const { + return static_cast(size() ? front() : 0); + } + explicit operator unsigned int() const { + return static_cast(size() ? front() : 0); + } + explicit operator unsigned long() const { + return static_cast(size() ? front() : 0); + } + explicit operator unsigned long long() const { + return static_cast(size() ? front() : 0); + } + explicit operator char() const { + return static_cast(size() ? front() : 0); + } + explicit operator short() const { + return static_cast(size() ? front() : 0); + } + explicit operator int() const { + return static_cast(size() ? front() : 0); + } + explicit operator long() const { + return static_cast(size() ? front() : 0); + } + explicit operator long long() const { + return static_cast(size() ? front() : 0); + } + + // Bitwise Operators + uinteger_t operator&(const uinteger_t& rhs) const { + return bitwise_and(*this, rhs); + } + + uinteger_t& operator&=(const uinteger_t& rhs) { + return bitwise_and(*this, rhs); + } + + uinteger_t operator|(const uinteger_t& rhs) const { + return bitwise_or(*this, rhs); + } + + uinteger_t& operator|=(const uinteger_t& rhs) { + return bitwise_or(*this, rhs); + } + + uinteger_t operator^(const uinteger_t& rhs) const { + return bitwise_xor(*this, rhs); + } + + uinteger_t& operator^=(const uinteger_t& rhs) { + return bitwise_xor(*this, rhs); + } + + uinteger_t operator~() const { + return bitwise_inv(*this); + } + + uinteger_t inv() { + return bitwise_inv(*this); + } + + // Bit Shift Operators + uinteger_t operator<<(const uinteger_t& rhs) const { + return bitwise_lshift(*this, rhs); + } + + uinteger_t& operator<<=(const uinteger_t& rhs) { + return bitwise_lshift(*this, rhs); + } + + uinteger_t operator>>(const uinteger_t& rhs) const { + return bitwise_rshift(*this, rhs); + } + + uinteger_t& operator>>=(const uinteger_t& rhs) { + return bitwise_rshift(*this, rhs); + } + + // Logical Operators + bool operator!() const { + return !static_cast(*this); + } + + bool operator&&(const uinteger_t& rhs) const { + return static_cast(*this) && rhs; + } + + bool operator||(const uinteger_t& rhs) const { + return static_cast(*this) || rhs; + } + + // Comparison Operators + bool operator==(const uinteger_t& rhs) const { + return compare(*this, rhs) == 0; + } + + bool operator!=(const uinteger_t& rhs) const { + return compare(*this, rhs) != 0; + } + + bool operator>(const uinteger_t& rhs) const { + return compare(*this, rhs) > 0; + } + + bool operator<(const uinteger_t& rhs) const { + return compare(*this, rhs) < 0; + } + + bool operator>=(const uinteger_t& rhs) const { + return compare(*this, rhs) >= 0; + } + + bool operator<=(const uinteger_t& rhs) const { + return compare(*this, rhs) <= 0; + } + + // Arithmetic Operators + uinteger_t operator+(const uinteger_t& rhs) const { + return add(*this, rhs); + } + + uinteger_t& operator+=(const uinteger_t& rhs) { + return add(*this, rhs); + } + + uinteger_t operator-(const uinteger_t& rhs) const { + return sub(*this, rhs); + } + + uinteger_t& operator-=(const uinteger_t& rhs) { + return sub(*this, rhs); + } + + uinteger_t operator*(const uinteger_t& rhs) const { + return mult(*this, rhs); + } + + uinteger_t& operator*=(const uinteger_t& rhs) { + return mult(*this, rhs); + } + + std::pair divmod(const uinteger_t& rhs) const { + return divmod(*this, rhs); + } + + uinteger_t operator/(const uinteger_t& rhs) const { + return divmod(*this, rhs).first; + } + + uinteger_t& operator/=(const uinteger_t& rhs) { + uinteger_t quotient; + uinteger_t remainder; + divmod(quotient, remainder, *this, rhs); + *this = std::move(quotient); + return *this; + } + + uinteger_t operator%(const uinteger_t& rhs) const { + return divmod(*this, rhs).second; + } + + uinteger_t& operator%=(const uinteger_t& rhs) { + uinteger_t quotient; + uinteger_t remainder; + divmod(quotient, remainder, *this, rhs); + *this = std::move(remainder); + return *this; + } + + // Increment Operator + uinteger_t& operator++() { + return *this += uint_1(); + } + uinteger_t operator++(int) { + uinteger_t temp(*this); + ++*this; + return temp; + } + + // Decrement Operator + uinteger_t& operator--() { + return *this -= uint_1(); + } + uinteger_t operator--(int) { + uinteger_t temp(*this); + --*this; + return temp; + } + + // Nothing done since promotion doesn't work here + uinteger_t operator+() const { + return *this; + } + + // two's complement + uinteger_t operator-() const { + return uint_0() - *this; + } + + // Get private value at index + const digit& value(std::size_t idx) const { + static const digit zero = 0; + return idx < size() ? *(begin() + idx) : zero; + } + + // Get value of bit N + bool operator[](std::size_t n) const { + auto nd = n / digit_bits; + auto nm = n % digit_bits; + return nd < size() ? (*(begin() + nd) >> nm) & 1 : 0; + } + + // Get bitsize of value + std::size_t bits() const { + auto sz = size(); + if (sz) { + return _bits(back()) + (sz - 1) * digit_bits; + } + return 0; + } + + // Get string representation of value + template ::value>> + Result str(int alphabet_base = 10) const { + auto num_sz = size(); + if (alphabet_base >= 2 && alphabet_base <= 36) { + Result result; + if (num_sz) { + auto alphabet_base_bits = base_bits(alphabet_base); + result.reserve(num_sz * base_size(alphabet_base)); + if (alphabet_base_bits) { + digit alphabet_base_mask = alphabet_base - 1; + std::size_t shift = 0; + auto ptr = reinterpret_cast(data()); + digit v = *ptr++; + v <<= half_digit_bits; + for (auto i = num_sz * 2 - 1; i; --i) { + v >>= half_digit_bits; + v |= (static_cast(*ptr++) << half_digit_bits); + do { + auto d = static_cast((v >> shift) & alphabet_base_mask); + result.push_back(chr(d)); + shift += alphabet_base_bits; + } while (shift <= half_digit_bits); + shift -= half_digit_bits; + } + v >>= (shift + half_digit_bits); + while (v) { + auto d = static_cast(v & alphabet_base_mask); + result.push_back(chr(d)); + v >>= alphabet_base_bits; + } + auto s = chr(0); + auto rit_f = std::find_if(result.rbegin(), result.rend(), [s](const char& c) { return c != s; }); + result.resize(result.rend() - rit_f); // shrink + } else { + uinteger_t uint_base = alphabet_base; + uinteger_t quotient = *this; + do { + auto r = quotient.divmod(uint_base); + auto d = static_cast(r.second); + result.push_back(chr(d)); + quotient = std::move(r.first); + } while (quotient); + } + std::reverse(result.begin(), result.end()); + } else { + result.push_back(chr(0)); + } + return result; + } else if (alphabet_base == 256) { + if (num_sz) { + auto ptr = reinterpret_cast(data()); + Result result(ptr, ptr + num_sz * digit_octets); + auto rit_f = std::find_if(result.rbegin(), result.rend(), [](const char& c) { return c; }); + result.resize(result.rend() - rit_f); // shrink + std::reverse(result.begin(), result.end()); + return result; + } else { + Result result; + result.push_back('\x00'); + return result; + } + } else { + throw std::invalid_argument("Base must be in the range [2, 36]"); + } + } + + static uinteger_t strtouint(const void* encoded, std::size_t encoded_size, int alphabet_base) { + const char* data = (const char *)encoded; + uinteger_t result; + + if (alphabet_base >= 2 && alphabet_base <= 36) { + uinteger_t alphabet_base_bits = base_bits(alphabet_base); + uinteger_t uint_base = alphabet_base; + if (alphabet_base_bits) { + for (; encoded_size; --encoded_size, ++data) { + auto d = ord(static_cast(*data)); + if (d < 0) { + throw std::invalid_argument("Error: Not a digit in base " + std::to_string(alphabet_base) + ": '" + std::string(1, *data) + "' at " + std::to_string(encoded_size)); + } + result = (result << alphabet_base_bits) | d; + } + } else { + for (; encoded_size; --encoded_size, ++data) { + auto d = ord(static_cast(*data)); + if (d < 0) { + throw std::invalid_argument("Error: Not a digit in base " + std::to_string(alphabet_base) + ": '" + std::string(1, *data) + "' at " + std::to_string(encoded_size)); + } + result = (result * uint_base) + d; + } + } + } else if (encoded_size && alphabet_base == 256) { + auto value_size = encoded_size / digit_octets; + auto value_padding = encoded_size % digit_octets; + if (value_padding) { + value_padding = digit_octets - value_padding; + ++value_size; + } + result.resize(value_size); // grow (no initialization) + *result.begin() = 0; // initialize value + auto ptr = reinterpret_cast(result.data()); + std::copy(data, data + encoded_size, ptr + value_padding); + std::reverse(ptr, ptr + value_size * digit_octets); + } else { + throw std::invalid_argument("Error: Cannot convert from base " + std::to_string(alphabet_base)); + } + + return result; + } + + template ::value>> + Result bin() const { + return str(2); + } + + template ::value>> + Result oct() const { + return str(8); + } + + template ::value>> + Result hex() const { + return str(16); + } + + template ::value>> + Result raw() const { + return str(256); + } +}; + +namespace std { // This is probably not a good idea + // Make it work with std::string() + inline std::string to_string(uinteger_t& num) { + return num.str(); + } + inline const std::string to_string(const uinteger_t& num) { + return num.str(); + } +} + +// lhs type T as first arguemnt +// If the output is not a bool, casts to type T + +// Bitwise Operators +template ::value and not std::is_same>::value>> +uinteger_t operator&(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) & rhs; +} + +template ::value and not std::is_same>::value>> +T& operator&=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(rhs & lhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator|(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) | rhs; +} + +template ::value and not std::is_same>::value>> +T& operator|=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(rhs | lhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator^(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) ^ rhs; +} + +template ::value and not std::is_same>::value>> +T& operator^=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(rhs ^ lhs); +} + +// Bitshift operators +template ::value and not std::is_same>::value>> +inline uinteger_t operator<<(T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) << rhs; +} + +template ::value and not std::is_same>::value>> +T& operator<<=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(lhs << rhs); +} + +template ::value and not std::is_same>::value>> +inline uinteger_t operator>>(T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) >> rhs; +} + +template ::value and not std::is_same>::value>> +T& operator>>=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(lhs >> rhs); +} + +// Comparison Operators +template ::value and not std::is_same>::value>> +bool operator==(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) == rhs; +} + +template ::value and not std::is_same>::value>> +bool operator!=(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) != rhs; +} + +template ::value and not std::is_same>::value>> +bool operator>(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) > rhs; +} + +template ::value and not std::is_same>::value>> +bool operator<(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) < rhs; +} + +template ::value and not std::is_same>::value>> +bool operator>=(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) >= rhs; +} + +template ::value and not std::is_same>::value>> +bool operator<=(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) <= rhs; +} + +// Arithmetic Operators +template ::value and not std::is_same>::value>> +uinteger_t operator+(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) + rhs; +} + +template ::value and not std::is_same>::value>> +T& operator+=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(rhs + lhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator-(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) - rhs; +} + +template ::value and not std::is_same>::value>> +T& operator-=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(lhs - rhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator*(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) * rhs; +} + +template ::value and not std::is_same>::value>> +T& operator*=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(rhs * lhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator/(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) / rhs; +} + +template ::value and not std::is_same>::value>> +T& operator/=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(lhs / rhs); +} + +template ::value and not std::is_same>::value>> +uinteger_t operator%(const T& lhs, const uinteger_t& rhs) { + return uinteger_t(lhs) % rhs; +} + +template ::value and not std::is_same>::value>> +T& operator%=(T& lhs, const uinteger_t& rhs) { + return lhs = static_cast(lhs % rhs); +} + +// IO Operator +inline std::ostream& operator<<(std::ostream& stream, const uinteger_t& rhs) { + if (stream.flags() & stream.oct) { + stream << rhs.str(8); + } else if (stream.flags() & stream.dec) { + stream << rhs.str(10); + } else if (stream.flags() & stream.hex) { + stream << rhs.str(16); + } + return stream; +} + +#endif diff --git a/contrib/base58 b/contrib/base58 deleted file mode 160000 index a85f98fb4ed..00000000000 --- a/contrib/base58 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a85f98fb4ed52c2f4029a4b6ac1ef0bafdfc56f5 diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index bf72795aae0..32f7952961c 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -72,8 +72,8 @@ if (TARGET ch_contrib::llvm) target_link_libraries(clickhouse_functions PRIVATE ch_contrib::llvm) endif () -if (TARGET ch_contrib::base58) - target_link_libraries(clickhouse_functions PRIVATE ch_contrib::base58) +if (TARGET ch_contrib::base-x) + target_link_libraries(clickhouse_functions PRIVATE ch_contrib::base-x) endif() if (TARGET ch_contrib::base64) diff --git a/src/Functions/FunctionBase58Conversion.h b/src/Functions/FunctionBase58Conversion.h index a0431ca47df..c0c54fd0ba9 100644 --- a/src/Functions/FunctionBase58Conversion.h +++ b/src/Functions/FunctionBase58Conversion.h @@ -26,19 +26,19 @@ struct Base58Encode { static constexpr auto name = "base58Encode"; - static void process(const ColumnString * input, ColumnString::MutablePtr& dst_column, std::string& alphabet, size_t input_rows_count) + static void process(const ColumnString& input, ColumnString::MutablePtr& dst_column, std::string& alphabet, size_t input_rows_count) { auto & dst_data = dst_column->getChars(); auto & dst_offsets = dst_column->getOffsets(); - size_t current_allocated_size = input->getChars().size(); + size_t current_allocated_size = input.getChars().size(); dst_data.resize(current_allocated_size); dst_offsets.resize(input_rows_count); - const ColumnString::Offsets & src_offsets = input->getOffsets(); + const ColumnString::Offsets & src_offsets = input.getOffsets(); - const auto * source = input->getChars().raw_data(); + const auto * source = input.getChars().raw_data(); auto * dst = dst_data.data(); auto * dst_pos = dst; @@ -48,8 +48,9 @@ struct Base58Encode const auto& encoder = (alphabet == "bitcoin") ? Base58::bitcoin() : ((alphabet == "flickr") ? Base58::flickr() : ((alphabet == "ripple") ? Base58::ripple() : - Base58::base58())); + Base58::base58())); //GMP + std::string encoded; for (size_t row = 0; row < input_rows_count; ++row) { size_t srclen = src_offsets[row] - src_offset_prev - 1; @@ -57,7 +58,7 @@ struct Base58Encode /// We don't know the size of the result string beforehand (it's not byte-to-byte encoding), /// so we may need to do many resizes (the worst case -- we'll do it for each row) /// This way we do exponential resizes and one final resize after whole operation is complete - std::string encoded; + encoded.clear(); if (srclen) encoder.encode(encoded, source, srclen); size_t outlen = encoded.size(); @@ -66,14 +67,15 @@ struct Base58Encode { current_allocated_size += current_allocated_size; dst_data.resize(current_allocated_size); + auto processed_offset = dst_pos - dst; + dst = dst_data.data(); + dst_pos = dst; + dst_pos += processed_offset; } - if (srclen) - std::strcpy(reinterpret_cast(dst_pos), encoded.c_str()); + std::memcpy(dst_pos, encoded.c_str(), ++outlen); source += srclen + 1; dst_pos += outlen; - *dst_pos = '\0'; - dst_pos += 1; dst_offsets[row] = dst_pos - dst; src_offset_prev = src_offsets[row]; @@ -88,19 +90,19 @@ struct Base58Decode { static constexpr auto name = "base58Decode"; - static void process(const ColumnString * input, ColumnString::MutablePtr& dst_column, std::string& alphabet, size_t input_rows_count) + static void process(const ColumnString& input, ColumnString::MutablePtr& dst_column, std::string& alphabet, size_t input_rows_count) { auto & dst_data = dst_column->getChars(); auto & dst_offsets = dst_column->getOffsets(); - size_t current_allocated_size = input->getChars().size(); + size_t current_allocated_size = input.getChars().size(); dst_data.resize(current_allocated_size); dst_offsets.resize(input_rows_count); - const ColumnString::Offsets & src_offsets = input->getOffsets(); + const ColumnString::Offsets & src_offsets = input.getOffsets(); - const auto * source = input->getChars().raw_data(); + const auto * source = input.getChars().raw_data(); auto * dst = dst_data.data(); auto * dst_pos = dst; @@ -112,6 +114,7 @@ struct Base58Decode ((alphabet == "ripple") ? Base58::ripple() : Base58::base58())); + std::string decoded; for (size_t row = 0; row < input_rows_count; ++row) { size_t srclen = src_offsets[row] - src_offset_prev - 1; @@ -119,21 +122,24 @@ struct Base58Decode /// We don't know the size of the result string beforehand (it's not byte-to-byte encoding), /// so we may need to do many resizes (the worst case -- we'll do it for each row) /// This way we do exponential resizes and one final resize after whole operation is complete - std::string decoded; - decoder.decode(decoded, source, srclen); + decoded.clear(); + if (srclen) + decoder.decode(decoded, source, srclen); size_t outlen = decoded.size(); if (processed_size + outlen >= current_allocated_size) { current_allocated_size += current_allocated_size; dst_data.resize(current_allocated_size); + auto processed_offset = dst_pos - dst; + dst = dst_data.data(); + dst_pos = dst; + dst_pos += processed_offset; } - std::strcpy(reinterpret_cast(dst_pos), decoded.c_str()); + std::memcpy(dst_pos, decoded.c_str(), ++outlen); source += srclen + 1; dst_pos += outlen; - *dst_pos = '\0'; - dst_pos += 1; dst_offsets[row] = dst_pos - dst; src_offset_prev = src_offsets[row]; @@ -216,7 +222,7 @@ public: auto dst_column = ColumnString::create(); - Func::process(input, dst_column, alphabet, input_rows_count); + Func::process(*input, dst_column, alphabet, input_rows_count); return dst_column; } diff --git a/src/Functions/configure_config.cmake b/src/Functions/configure_config.cmake index 776996d7e17..856d9a5682a 100644 --- a/src/Functions/configure_config.cmake +++ b/src/Functions/configure_config.cmake @@ -1,7 +1,7 @@ if (TARGET ch_contrib::fastops) set(USE_FASTOPS 1) endif() -if (TARGET ch_contrib::base58) +if (TARGET ch_contrib::base-x) set(USE_BASE58 1) endif() if (TARGET ch_contrib::base64) diff --git a/tests/queries/0_stateless/02337_base58.reference b/tests/queries/0_stateless/02337_base58.reference new file mode 100644 index 00000000000..718dfeb4a34 --- /dev/null +++ b/tests/queries/0_stateless/02337_base58.reference @@ -0,0 +1,48 @@ +32YCBjgZhV4AdCWHaCDNu + +f +fo +foo +foob +fooba +foobar +Hello world! + +f +fo +foo +foob +fooba +foobar +Hello world! + +f +fo +foo +foob +fooba +foobar +Hello world! + +f +fo +foo +foob +fooba +foobar +Hello world! + +2m +8o8 +bQbp +3csAg9 +CZJRhmz +t1Zv2yaZ + +f +fo +foo +foob +fooba +foobar +1 1 diff --git a/tests/queries/0_stateless/02337_base58.sql b/tests/queries/0_stateless/02337_base58.sql new file mode 100644 index 00000000000..b67993d2cc9 --- /dev/null +++ b/tests/queries/0_stateless/02337_base58.sql @@ -0,0 +1,17 @@ +-- Tags: no-fasttest + +SET send_logs_level = 'fatal'; + +SELECT base58Encode('Hold my beer...'); + +SELECT base58Decode(encoded, 'gmp') FROM (SELECT base58Encode(val, 'gmp') as encoded FROM (select arrayJoin(['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar', 'Hello world!']) val)); +SELECT base58Decode(encoded, 'ripple') FROM (SELECT base58Encode(val, 'ripple') as encoded FROM (select arrayJoin(['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar', 'Hello world!']) val)); +SELECT base58Decode(encoded, 'flickr') FROM (SELECT base58Encode(val, 'flickr') as encoded FROM (select arrayJoin(['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar', 'Hello world!']) val)); +SELECT base58Decode(encoded, 'bitcoin') FROM (SELECT base58Encode(val, 'bitcoin') as encoded FROM (select arrayJoin(['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar', 'Hello world!']) val)); + +SELECT base58Encode(val) FROM (select arrayJoin(['', 'f', 'fo', 'foo', 'foob', 'fooba', 'foobar']) val); +SELECT base58Decode(val) FROM (select arrayJoin(['', '2m', '8o8', 'bQbp', '3csAg9', 'CZJRhmz', 't1Zv2yaZ']) val); + +SELECT base58Decode(base58Encode('foo')) = 'foo', base58Encode(base58Decode('bQbp')) == 'bQbp'; + +SELECT base58Decode('Why_not?'); -- { serverError 1001 } \ No newline at end of file