Examples

All the following examples can be found in the examples/ folder of this library. We will first start with the basics and then move on to more advanced examples.

Foundational Examples

Construction

Example 1. This example demonstrates the many ways to create a beman::big_int: from built-in integers, from floating-point values, from a user-defined literal, by copy and move, and from a range of limbs
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <array>
#include <iostream>
#include <utility>
#include <version>

auto main() -> int {
    using beman::big_int::big_int;
    using beman::big_int::uint_multiprecision_t;

    // 1. A default-constructed big_int holds the value zero. Small values live
    //    inside the object itself, so no allocation is performed.
    const big_int zero;

    // 2. Construction from a built-in integer is implicit, so a big_int may be
    //    initialized or assigned from one directly. Both the sign and the full
    //    width of the source type are preserved.
    const big_int from_int = -123456789;
    const big_int from_ull = 18446744073709551615ULL; // 2^64 - 1, the widest built-in magnitude

    // 3. Construction from a floating-point value is explicit and truncates
    //    toward zero, exactly like a narrowing conversion to a built-in integer.
    const big_int from_double{1e15}; // 1000000000000000
    const big_int truncated{-42.9};  // -42

    // 4. A user-defined literal builds a value of any size directly in source.
    using namespace beman::big_int::literals;
    const big_int from_literal = 170141183460469231731687303715884105728_n; // 2^127

    // 5. Copies and moves behave as expected. A move transfers the internal
    //    storage, leaving the source in a valid state ready for reuse.
    big_int       source = from_literal;
    const big_int copied{source};
    const big_int moved{std::move(source)};

    // 6. A big_int can also be built from a range of unsigned limbs given in
    //    little-endian order (least significant limb first).
    bool from_limbs_ok = true;
#if defined(__cpp_lib_containers_ranges) && __cpp_lib_containers_ranges >= 202202L
    const std::array<uint_multiprecision_t, 2> limbs{0U, 1U}; // 0 + 1 * 2^64
    const big_int                              from_range{std::from_range, limbs};
    from_limbs_ok = from_range == (big_int{1} << 64);
#endif

    const bool result_is_ok = zero == 0 && from_int == -123456789 && from_ull == (big_int{1} << 64) - 1 &&
                              from_double == 1000000000000000 && truncated == -42 &&
                              from_literal == (big_int{1} << 127) && copied == from_literal && moved == from_literal &&
                              from_limbs_ok;

    std::cout << "zero:         " << to_string(zero) << "\n"
              << "from_int:     " << to_string(from_int) << "\n"
              << "from_ull:     " << to_string(from_ull) << "\n"
              << "from_double:  " << to_string(from_double) << "\n"
              << "truncated:    " << to_string(truncated) << "\n"
              << "from_literal: " << to_string(from_literal) << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
zero:         0
from_int:     -123456789
from_ull:     18446744073709551615
from_double:  1000000000000000
truncated:    -42
from_literal: 170141183460469231731687303715884105728

result_is_ok: true

Fundamental operators

Example 2. This example demonstrates the fundamental arithmetic, comparison, bitwise, shift, increment, and compound-assignment operators of beman::big_int
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <compare>
#include <iostream>

auto main() -> int {
    using namespace beman::big_int::literals;
    using beman::big_int::big_int;

    // 1. The usual binary arithmetic operators are available and never overflow;
    //    the result simply grows to as many limbs as it needs.
    const big_int a          = 1'000'000'000'000_n; // 10^12
    const big_int b          = 7_n;
    const big_int sum        = a + b;
    const big_int difference = a - b;
    const big_int product    = a * b;
    const big_int quotient   = a / b; // division truncates toward zero
    const big_int remainder  = a % b;

    // 2. Mixing a big_int with a built-in integer works too; the built-in
    //    operand is promoted to big_int automatically.
    const big_int mixed = product * 2 + 5;

    // 3. Unary operators: negation, unary plus, and bitwise NOT. On a
    //    two's-complement value, ~x is mathematically -(x + 1).
    const big_int negated    = -a;
    const big_int complement = ~a;

    // 4. Pre- and post-increment / decrement behave as for the built-in types.
    big_int counter = 10_n;
    ++counter;                       // 11
    --counter;                       // 10
    const big_int after = counter++; // after == 10, counter == 11

    // 5. Comparisons give the natural ordering; operator<=> yields a
    //    std::strong_ordering directly.
    const bool ordered   = a > b && b < a && a >= a && a != b;
    const bool spaceship = (a <=> b) == std::strong_ordering::greater;

    // 6. Bitwise and shift operators treat the value as an arbitrarily wide
    //    two's-complement integer.
    const big_int shifted = 1_n << 100;           // 2^100
    const big_int masked  = (shifted - 1) & 0xFF; // low eight bits
    const big_int xored   = shifted ^ shifted;    // 0

    // 7. Every binary operator has a compound-assignment form.
    big_int acc = 2_n;
    acc <<= 10; // 2048
    acc += 1;   // 2049
    acc /= 3;   // 683

    const bool result_is_ok = sum == 1'000'000'000'007_n && difference == 999'999'999'993_n &&
                              product == 7'000'000'000'000_n && quotient == 142'857'142'857_n && remainder == 1_n &&
                              mixed == 14'000'000'000'005_n && negated == -1'000'000'000'000_n &&
                              complement == -1'000'000'000'001_n && after == 10_n && counter == 11_n && ordered &&
                              spaceship && shifted == (1_n << 100) && masked == 255_n && xored == 0_n && acc == 683_n;

    std::cout << "sum:        " << to_string(sum) << "\n"
              << "difference: " << to_string(difference) << "\n"
              << "product:    " << to_string(product) << "\n"
              << "quotient:   " << to_string(quotient) << " remainder " << to_string(remainder) << "\n"
              << "complement: " << to_string(complement) << "\n"
              << "2^100:      " << to_string(shifted) << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
sum:        1000000000007
difference: 999999999993
product:    7000000000000
quotient:   142857142857 remainder 1
complement: -1000000000001
2^100:      1267650600228229401496703205376

result_is_ok: true

Factorial

Example 3. This example demonstrates computing the 100th factorial with beman::big_int using ordinary arithmetic operators in a recursive function
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iomanip>
#include <iostream>

template <class BigIntType>
constexpr auto factorial(unsigned int n) -> BigIntType {
    return (n <= 1) ? 1 : n * factorial<BigIntType>(n - 1);
}

auto main() -> int {
    using beman::big_int::big_int;

    // Compute the 100th Factorial number.
    const big_int fact_100{factorial<big_int>(100)};

    using namespace beman::big_int::literals;

    const big_int bn_ctrl{
        93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000_n};

    const bool result_is_ok{fact_100 == bn_ctrl};

    std::cout << "fact_100:\n"
              << to_string(fact_100) << "\n\nresult_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
fact_100:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

result_is_ok: true

User-defined literals

Example 4. This example demonstrates writing beman::big_int constants directly in source code with the _n literal suffix, including non-decimal bases, digit separators, and negative values
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iostream>

auto main() -> int {
    // The user-defined literals live in an inline namespace; a single
    // using-directive makes the _n suffix (and the n suffix where the compiler
    // supports it) visible.
    using namespace beman::big_int::literals;

    // 1. A decimal literal far larger than any built-in integer type. Digit
    //    separators (') may be placed between digits and are ignored.
    const auto big = 340'282'366'920'938'463'463'374'607'431'768'211'457_n; // 2^128 + 1

    // 2. The base follows the usual integer-literal prefixes: 0x for
    //    hexadecimal, 0b for binary, and a leading 0 for octal. Each of the
    //    three literals below names the value 255.
    const auto from_hex = 0xFF_n;
    const auto from_bin = 0b1111'1111_n;
    const auto from_oct = 0377_n;

    // 3. A literal is never negative; apply unary minus to obtain a negative
    //    value.
    const auto negative = -1'000'000'000'000'000'000'000_n;

    const bool big_is_ok    = big == (1_n << 128) + 1_n;
    const bool bases_agree  = from_hex == 255_n && from_bin == 255_n && from_oct == 255_n;
    const bool sign_is_ok   = negative < 0_n;
    const bool result_is_ok = big_is_ok && bases_agree && sign_is_ok;

    std::cout << "big:      " << to_string(big) << "\n"
              << "from_hex: " << to_string(from_hex) << "\n"
              << "negative: " << to_string(negative) << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
big:      340282366920938463463374607431768211457
from_hex: 255
negative: -1000000000000000000000

result_is_ok: true

Numeric conversions

Example 5. This example demonstrates parsing a beman::big_int from text with from_chars and rendering it back with to_chars
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <array>
#include <charconv>
#include <iostream>
#include <string_view>
#include <system_error>

auto main() -> int {
    using beman::big_int::big_int;

    // 1. Parse a value far too large for any built-in integer type from its
    //    decimal text using from_chars. The default base is 10.
    constexpr std::string_view decimal{"340282366920938463463374607431768211457"}; // 2^128 + 1
    big_int                    value;
    const auto                 parse     = from_chars(decimal.data(), decimal.data() + decimal.size(), value);
    const bool                 parsed_ok = parse.ec == std::errc{} && parse.ptr == decimal.data() + decimal.size();

    // 2. Render the value back to text in base 16 with to_chars. The buffer
    //    must be large enough; otherwise to_chars reports value_too_large.
    std::array<char, 64>   buffer{};
    const auto             print = to_chars(buffer.data(), buffer.data() + buffer.size(), value, 16);
    const std::string_view hex{buffer.data(), static_cast<std::size_t>(print.ptr - buffer.data())};
    const bool             printed_ok = print.ec == std::errc{};

    // 3. Round-trip: parse the hexadecimal text back and confirm the value is
    //    recovered exactly.
    big_int    recovered;
    const auto reparse       = from_chars(hex.data(), hex.data() + hex.size(), recovered, 16);
    const bool round_trip_ok = reparse.ec == std::errc{} && recovered == value;

    const bool result_is_ok = parsed_ok && printed_ok && round_trip_ok;

    std::cout << "decimal: " << decimal << "\n"
              << "hex:     " << hex << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
decimal: 340282366920938463463374607431768211457
hex:     100000000000000000000000000000001

result_is_ok: true

String conversions

Example 6. This example demonstrates rendering a beman::big_int to a std::string with to_string and to a std::wstring with to_wstring
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iostream>
#include <string>

auto main() -> int {
    using namespace beman::big_int::literals;

    // A value far larger than any built-in integer type.
    const auto value = 1_n << 128; // 2^128

    // 1. to_string returns a std::string holding the value in a chosen base.
    //    The default is base 10; digit values of ten or more use the lowercase
    //    letters a-z. Unlike to_chars, it allocates the result for you.
    const std::string decimal = to_string(value);
    const std::string hex     = to_string(value, 16);

    // 2. A negative value is rendered with a single leading '-'.
    const std::string negative = to_string(-value, 16);

    // 3. to_wstring is the wchar_t twin of to_string: the same digits, returned
    //    as a std::wstring for use with wide-character APIs. Widening the narrow
    //    result confirms the two agree character for character.
    const std::wstring wide_hex = to_wstring(value, 16);
    const bool         wide_ok  = wide_hex == std::wstring(hex.begin(), hex.end());

    const bool result_is_ok = decimal == "340282366920938463463374607431768211456" && // 2^128
                              hex == "1" + std::string(32, '0') &&                    // 2^128 == 16^32
                              negative == "-" + hex && wide_ok;

    std::cout << "decimal:  " << decimal << "\n"
              << "hex:      " << hex << "\n"
              << "negative: " << negative << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
decimal:  340282366920938463463374607431768211456
hex:      100000000000000000000000000000000
negative: -100000000000000000000000000000000

result_is_ok: true

Numeric functions

Example 7. This example demonstrates the free numeric helpers in <beman/big_int/numeric.hpp>: abs for the magnitude of a value, and saturating_cast for range-clamped narrowing to a built-in integer
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iostream>
#include <limits>

auto main() -> int {
    using namespace beman::big_int::literals;
    using beman::big_int::abs;
    using beman::big_int::big_int;
    using beman::big_int::saturating_cast;

    // The free numeric helpers live in <beman/big_int/numeric.hpp>.

    // 1. abs returns the magnitude of a value. Because big_int is unbounded it
    //    can never overflow, unlike std::abs on the most negative built-in.
    const big_int negative  = -170141183460469231731687303715884105728_n; // -2^127
    const big_int magnitude = abs(negative);

    // 2. saturating_cast narrows a big_int to a built-in integer, clamping to
    //    that type's range instead of wrapping or invoking undefined behavior.
    const big_int huge = 1_n << 200; // far larger than any built-in integer

    const int      clamped_hi  = saturating_cast<int>(huge);      // INT_MAX
    const int      clamped_lo  = saturating_cast<int>(-huge);     // INT_MIN
    const unsigned clamped_neg = saturating_cast<unsigned>(-1_n); // 0 (below the minimum)

    // 3. A value that already fits the destination type is returned exactly.
    const int exact = saturating_cast<int>(12345_n);

    const bool result_is_ok = magnitude == (1_n << 127) && clamped_hi == std::numeric_limits<int>::max() &&
                              clamped_lo == std::numeric_limits<int>::min() && clamped_neg == 0U && exact == 12345;

    std::cout << "magnitude:   " << to_string(magnitude) << "\n"
              << "clamped_hi:  " << clamped_hi << "\n"
              << "clamped_lo:  " << clamped_lo << "\n"
              << "clamped_neg: " << clamped_neg << "\n"
              << "exact:       " << exact << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
magnitude:   170141183460469231731687303715884105728
clamped_hi:  2147483647
clamped_lo:  -2147483648
clamped_neg: 0
exact:       12345

result_is_ok: true

Integer division

Example 8. This example demonstrates the div_rem_to_zero free function, which computes the quotient and remainder together in a single division
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iostream>

auto main() -> int {
    using namespace beman::big_int::literals;
    using beman::big_int::big_int;
    using beman::big_int::div_rem_to_zero;

    // div_rem_to_zero (declared in <beman/big_int/big_int.hpp>) computes the
    // quotient and remainder together in a single division, which is cheaper
    // than evaluating operator/ and operator% separately.

    const big_int dividend = 100'000'000'000'000'000'000'000'000'000'000_n; // 10^32
    const big_int divisor  = 1'000'000'007_n;

    // 1. The result is a div_result aggregate exposing .quotient and .remainder.
    //    Division rounds (truncates) toward zero, so the identity
    //    dividend == quotient * divisor + remainder always holds.
    const auto [quotient, remainder] = div_rem_to_zero(dividend, divisor);

    // 2. For a negative dividend the quotient is negated and the remainder takes
    //    the sign of the dividend, matching the built-in integer operators.
    const auto negative = div_rem_to_zero(-dividend, divisor);

    const bool result_is_ok = quotient * divisor + remainder == dividend && quotient == dividend / divisor &&
                              remainder == dividend % divisor && negative.quotient == -quotient &&
                              negative.remainder == -remainder;

    std::cout << "dividend:  " << to_string(dividend) << "\n"
              << "divisor:   " << to_string(divisor) << "\n"
              << "quotient:  " << to_string(quotient) << "\n"
              << "remainder: " << to_string(remainder) << "\n\n"
              << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
dividend:  100000000000000000000000000000000
divisor:   1000000007
quotient:  99999999300000004899999
remainder: 965700007

result_is_ok: true

Formatting and output

Example 9. This example demonstrates displaying a beman::big_int by converting it to text with to_string and by formatting it with std::format
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iostream>
#include <string>
#include <version>

#if __has_include(<format>) && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L
    #include <format>
#endif

auto main() -> int {
    using namespace beman::big_int::literals;
    using beman::big_int::big_int;

    const big_int value = 1_n << 128; // 2^128

    // 1. big_int has no stream inserter of its own. To display a value, convert
    //    it to text with to_string (base 10 by default, or any base up to 36)
    //    and write the result to any output stream.
    const std::string decimal = to_string(value);
    const std::string hex     = to_string(value, 16);

    std::cout << "decimal: " << decimal << "\n"
              << "hex:     " << hex << "\n";

    bool formatted_ok = true;

    // 2. When <format> is available, big_int plugs into std::format just like a
    //    built-in integer: the b/o/x/X bases, the '#' alternate form, the sign
    //    option, and fill / align / width all behave as the standard specifies.
#if __has_include(<format>) && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L
    const std::string as_hex    = std::format("{:#x}", value);  // 0x1000...0
    const std::string as_binary = std::format("{:b}", 42_n);    // 101010
    const std::string padded    = std::format("{:>12}", 255_n); // right-justified in a width of 12
    const std::string with_sign = std::format("{:+}", 7_n);     // a leading + on a positive value

    std::cout << "format {:#x}:  " << as_hex << "\n"
              << "format {:b}:   " << as_binary << "\n"
              << "format {:>12}: '" << padded << "'\n"
              << "format {:+}:   " << with_sign << "\n";

    formatted_ok =
        as_hex == "0x" + hex && as_binary == "101010" && padded == std::string(9, ' ') + "255" && with_sign == "+7";
#else
    std::cout << "(std::format is unavailable in this configuration)\n";
#endif

    const bool result_is_ok = decimal == "340282366920938463463374607431768211456" && // 2^128
                              hex == "1" + std::string(32, '0') &&                    // 2^128 == 16^32
                              formatted_ok;

    std::cout << "\nresult_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
decimal: 340282366920938463463374607431768211456
hex:     100000000000000000000000000000000
format {:#x}:  0x100000000000000000000000000000000
format {:b}:   101010
format {:>12}: '         255'
format {:+}:   +7

result_is_ok: true

Advanced Examples

Fibonacci

Example 10. This example demonstrates computing the 10,000th Fibonacci number with beman::big_int using fast matrix exponentiation by squaring
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iomanip>
#include <iostream>

template <class BigIntType>
struct Matrix2x2 {
    BigIntType a{}, b{}, c{}, d{};
};

template <class BigIntType>
Matrix2x2<BigIntType> multiply(const Matrix2x2<BigIntType>& x, const Matrix2x2<BigIntType>& y) {
    // 2x2 matrix multiplication.

    return {x.a * y.a + x.b * y.c, x.a * y.b + x.b * y.d, x.c * y.a + x.d * y.c, x.c * y.b + x.d * y.d};
}

template <class BigIntType>
Matrix2x2<BigIntType> power(Matrix2x2<BigIntType> base, unsigned n) {
    // Implement fast exponentiation by squaring.

    // Start with the identity matrix.
    Matrix2x2<BigIntType> result{BigIntType{1}, BigIntType{0}, BigIntType{0}, BigIntType{1}};

    while (n > 0U) {
        if ((n & 1U) != 0U) {
            result = multiply(result, base);
        }

        base = multiply(base, base);

        n >>= 1;
    }

    return result;
}

template <class BigIntType>
BigIntType fibonacci(unsigned n) {
    // Calculate the n'th Fibonacci number using matrix exponentiation.

    if (n == 0U) {
        return BigIntType{0};
    }

    Matrix2x2<BigIntType> fibMatrix{BigIntType{1}, BigIntType{1}, BigIntType{1}, BigIntType{0}};

    Matrix2x2<BigIntType> result = power(fibMatrix, n - 1U);

    return result.a;
}

auto main() -> int {
    using beman::big_int::big_int;

    // Compute the 10,000th Fibonacci number.
    const big_int fib_10000{fibonacci<big_int>(10000U)};

    using namespace beman::big_int::literals;

    // The result is very long. Literals may exceed compiler limits
    // for some implementations. So we work with a control string here.

    const std::string str_ctrl{"3364476487643178326662161200510754331030214846068006390656476997"
                               "4680081442166662368155595513633734025582065332680836159373734790"
                               "4838652682630408924630564318873545443695598274916066020998841839"
                               "3386465273130008883026923567361313511757929743785441375213052050"
                               "4347701602264758318906527890855154366159582987279682987510631200"
                               "5754287834532155151038708182989697916131278562650331954871402142"
                               "8753269818796204693609787990035096230229102636813149319527563022"
                               "7837628441540360584402572114334961180023091208287046088923962328"
                               "8354615057765832712525460935911282039252853934346209042452489294"
                               "0390170623388899108584106518317336043747073790855263176432573399"
                               "3712871937587746897479926305837065742830161637408969178426378624"
                               "2128352581128205163702980893320999057079200643674262023897831114"
                               "7005407499845925036063356093388383192338678305613643535189213327"
                               "9732908133732642652633989763922723407882928177953580570993691049"
                               "1754708089318410561463223382174656373212482263830921032977016480"
                               "5472624384237486241145309381220656491403275108664339451751216152"
                               "6545361333111314042436854805106765843493523836959653428071768775"
                               "3283482343455573667197313927462736291082106792807847180353291311"
                               "7677892465908993863545932789452377767440619224033763867400402133"
                               "0343297496902028328145933418826817683893072003634795623117103101"
                               "2919531697946076327375892535307725523759437884345040677155557790"
                               "5645044301664011946258097221672975861502696844314695203461493229"
                               "1105970676243268515992834709891284706740862008587135016260312071"
                               "9031720860940812983215810772820763531866246112782455372085323653"
                               "0577595643007251774431505153960090516860322034916322264088524885"
                               "2433158051534849622434848299380905070483482449327453732624567755"
                               "8790891871908036620580095947431500524025327097469953187707243768"
                               "2590741993963226598414749819360928522394503970716544315642132815"
                               "7688908058783183404917434556270520223564846495196112460268313970"
                               "9750693826487066132645076650746115126775227486215986425307112984"
                               "4118262266105716351506926002986170494542504749137811515413994155"
                               "0671256271197133252763631939606902895650288268608362241082050562"
                               "430701794976171121233066073310059947366875"};

    // The to_string() function gives us a very convenient way to verify
    // the numerical correctness.

    const std::string str_fib_10000{to_string(fib_10000)};

    const bool result_is_ok{str_fib_10000 == str_ctrl};

    std::cout << "fib_10000:\n"
              << to_string(fib_10000) << "\n\nresult_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
fib_10000:
33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875

result_is_ok: true

Roots

Example 11. This example demonstrates computing the exact integer square root and cube root of a 1,001-digit integer with beman::big_int
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iomanip>
#include <iostream>
#include <span>
#include <string>

using beman::big_int::big_int;

auto sqrt(big_int m) -> big_int {
    // Calculate the square root.

    big_int s{};

    if (m <= 0) {
        s = 0;
    } else {
        // Obtain the initial guess via algorithms
        // involving the position of the msb.
        const std::size_t msb_pos{m.size() - 1U};

        const auto msb_pos_mod_2 = msb_pos % 2;

        // Obtain the initial value.
        const auto left_shift_amount = 1 + ((msb_pos_mod_2 == 0) ? msb_pos / 2 : (msb_pos + 1) / 2);

        big_int u{big_int{1} << left_shift_amount};

        // Perform the iteration for the square root.
        // See Algorithm 1.13 SqrtInt, Sect. 1.5.1
        // in R.P. Brent and Paul Zimmermann, "Modern Computer Arithmetic",
        // Cambridge University Press, 2011.

        for (auto i{0}; i < 64; ++i) {
            static_cast<void>(i);

            s = u;

            u = (s + (m / s)) / 2;

            if (u >= s) {
                break;
            }
        }
    }

    return s;
}

auto cbrt(big_int m) -> big_int {
    // Calculate the cube root.

    big_int s{};

    if (m < 0) {
        s = -cbrt(-m);
    } else if (m == 0) {
        s = 0;
    } else {
        // Obtain the initial guess via algorithms
        // involving the position of the msb.
        const std::size_t msb_pos{m.size() - 1U};

        // Obtain the initial value.
        const auto msb_pos_mod_3 = msb_pos % 3;

        const auto left_shift_amount =
            1 + ((msb_pos_mod_3 == 0) ? 1 + msb_pos / 3 : (msb_pos + (3 - msb_pos_mod_3)) / 3);

        big_int u{big_int{1} << left_shift_amount};

        // Perform the iteration for the k'th root (applied for k = 3).
        // See Algorithm 1.14 RootInt, Sect. 1.5.2
        // in R.P. Brent and Paul Zimmermann, "Modern Computer Arithmetic",
        // Cambridge University Press, 2011.

        for (auto i{0}; i < 64; ++i) {
            static_cast<void>(i);

            s = u;

            u = ((s * 2) + (m / (s * s))) / 3;

            if (u >= s) {
                break;
            }
        }
    }

    return s;
}

auto main() -> int {
    using namespace beman::big_int::literals;

    const big_int arg_x{
        31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989_n};

    const big_int ctrl_sqrt{
        177245385090551602729816748334114518279754945612238712821380778985291128459103218137495065673854466541622682362428257066623615286572442260252509370960278706846203769865310512284992517302895082622893209537926796280017463901535147972051670019018523401858544697449491264031392177552590621640541933250090639840761373347747515343366798978936585183640879545116516173876005906739343179133280985484624818490205465485219561325156164746751504273876105610799612710721006037204448367236529661370809432349883166842_n};

    const big_int ctrl_cbrt{
        3155367569301821867326519405336421207498251961314999997901193388809739079012897744887631254739292007907947433618484758481627548989501719089220482459948775432816342410555282540612960501433021296640960423450227920209938211887719077129428385543361947758023983184697480787087966223432486682721792221472648796720202642738747343051078071241_n};

    const big_int result_sqrt{sqrt(arg_x)};
    const big_int result_cbrt{cbrt(arg_x)};

    const bool result_is_ok{(result_sqrt == ctrl_sqrt) && (result_cbrt == ctrl_cbrt)};

    std::cout << "result_sqrt:\n" << to_string(result_sqrt) << std::endl;
    std::cout << "\nresult_cbrt:\n" << to_string(result_cbrt) << std::endl;

    std::cout << "\nresult_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
result_sqrt:
177245385090551602729816748334114518279754945612238712821380778985291128459103218137495065673854466541622682362428257066623615286572442260252509370960278706846203769865310512284992517302895082622893209537926796280017463901535147972051670019018523401858544697449491264031392177552590621640541933250090639840761373347747515343366798978936585183640879545116516173876005906739343179133280985484624818490205465485219561325156164746751504273876105610799612710721006037204448367236529661370809432349883166842

result_cbrt:
3155367569301821867326519405336421207498251961314999997901193388809739079012897744887631254739292007907947433618484758481627548989501719089220482459948775432816342410555282540612960501433021296640960423450227920209938211887719077129428385543361947758023983184697480787087966223432486682721792221472648796720202642738747343051078071241

result_is_ok: true

Primes

Example 12. This example demonstrates generating pseudo-random 512-bit prime numbers with beman::big_int using the Miller-Rabin primality test
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include <string>

// #define BEMAN_BIG_INT_EXAMPLE_PRIMES_USE_ENTROPY

namespace rnd_gens {
using gen_type = std::mt19937_64;

auto eng1() -> gen_type& {
    static gen_type instance{};

    return instance;
};

auto eng2() -> gen_type& {
    static gen_type instance{};

    return instance;
};

} // namespace rnd_gens

template <class BigIntType, class RndEngineType>
[[nodiscard]] auto get_pseudo_random_integer(RndEngineType& eng, const BigIntType& max_val) -> BigIntType {
    BigIntType value_to_get{};

    for (int bit_index{0}; value_to_get < max_val; bit_index += 64) {
        if (bit_index != 0U) {
            value_to_get <<= 64U;
        }

        // Get the next 64-bit chunk. Also ensure each chunk retrieved
        // is non-zero and also that the resulting candidate will, after
        // piecing the chunks together, have the full bit width.
        value_to_get += (eng() | std::uint64_t{0x8000000000000000});
    }

    return value_to_get % max_val;
}

template <class BigIntType>
auto powm(BigIntType b, BigIntType p, const BigIntType& m) -> BigIntType {

    // Calculate (b ^ p) % m.

    BigIntType x{1};

    unsigned p0{};

    while (((p0 = static_cast<unsigned>(p)) != 0U) || (p != 0U)) {
        if ((p0 & 1U) != 0U) {
            x *= b;
            x %= m;
        }

        b *= b;
        b %= m;

        p >>= 1U;
    }

    return x;
}

constexpr auto lsb_position(std::uint64_t x) noexcept -> unsigned {
    // We use tricky, enhanced knowledge here. Because of the way the prime
    // candidates are created, each 64-bit chunk is "rigged" to have a non-zero
    // value. So here, we can safely calculate the LSB of the entire big_int
    // based on the single lowest chunk.

    unsigned pos{};

    while ((static_cast<unsigned>(x) & 1U) == 0U) {
        x >>= 1U;
        ++pos;
    }

    return pos;
}

template <class BigIntType>
auto miller_rabin(const BigIntType& np, const int trials) -> bool {
    // Perform the Miller-Rabin primality test on the prime candidate np.
    // This subroutine returns true if the prime candidate is prime within
    // the limits of Miller-Rabin testing for the given input of trials.

    // Table[Prime[i], {i, 2, 49, 1}]
    constexpr std::array<unsigned, 48> small_primes = {
        3U,   5U,   7U,   11U,  13U,  17U,  19U,  23U,  29U,  31U,  37U,  41U,  43U,  47U,  53U,  59U,
        61U,  67U,  71U,  73U,  79U,  83U,  89U,  97U,  101U, 103U, 107U, 109U, 113U, 127U, 131U, 137U,
        139U, 149U, 151U, 157U, 163U, 167U, 173U, 179U, 181U, 191U, 193U, 197U, 199U, 211U, 223U, 227U};

    {
        // Handle even numbers.
        const auto n0{static_cast<unsigned>(np)};

        const bool n_is_even{(n0 & 1U) == 0U};

        if (n_is_even) {
            // If true:
            // Handle the trivial special case of 2, which is prime.

            // If false:
            // The prime candidate is not prime because it is either
            // even and larger than 2 or equal to zero. Herewith, we
            // handle non-prime even numbers and the non-primality of 0.
            const bool is_prime_two_or_is_non_prime_even{(n0 == 2U) && (np == 2U)};

            return is_prime_two_or_is_non_prime_even;
        }

        if ((n0 <= small_primes.back()) && (np <= small_primes.back())) {
            // This handles the trivial special case of the (non-primality) of 1.
            if (n0 == 1U) {
                return false;
            }

            // Exclude pure small primes from the small_primes table.
            // We are already restricted to np <= small_primes.back()
            // via the query above. So it is sufficient to test only
            // the lowest unsigned-casted value, n0.
            const bool is_small_prime{std::ranges::contains(small_primes, n0)};

            if (is_small_prime) {
                return true;
            }
        }

        // Handle numbers divisible by small primes in the small_primes table.
        const bool is_small_prime_divisible{
            std::ranges::any_of(small_primes, [&np](unsigned p) { return (np % p) == 0U; })};

        if (is_small_prime_divisible) {
            return false;
        }
    }

    const BigIntType nm1{np - 1U};

    auto local_functor_isone{[](const BigIntType& t1) { return ((static_cast<unsigned>(t1) == 1U) && (t1 == 1U)); }};

    {
        // Perform a single Fermat test which will exclude many non-prime candidates.
        // If this fails, np is definitely composite. If it passes, np might still
        // be composite (Carmichael numbers are the classic troublemakers).
        // But this simple test weeds out many non-prime candidates. The value
        // 228 (which is small_primes.back + 1) is not a correctness requirement.
        // Rather, it is just a performance tradeoff in this interpretation
        // of Miller-Rabin primality testing.

        const BigIntType fn{powm(BigIntType{small_primes.back() + 1U}, nm1, np)};

        if (!local_functor_isone(fn)) {
            return false;
        }
    }

    const unsigned k{lsb_position(static_cast<std::uint64_t>(nm1))};

    const BigIntType q{nm1 >> k};

    // Assume the test will pass, even though it usually does not pass.
    bool result_candidate_is_prime{true};

    const BigIntType nm2{np - 2};

    // We will now run the trials.
    for (int trial{0}; ((trial < trials) && result_candidate_is_prime); ++trial) {
        static_cast<void>(trial);

        BigIntType next_rnd{get_pseudo_random_integer(rnd_gens::eng1(), nm2)};

        BigIntType y{powm(next_rnd, q, np)};

        for (auto j{0U}; ((j < k) && result_candidate_is_prime); ++j) {
            if (y == nm1) {
                // This trial passes and the candidate is very probably prime
                // within the limits of Miller-Rabin primality testing.

                break;
            }

            if (local_functor_isone(y)) {
                // Failure and the candidate is not prime, but only if this is
                // not the first step.

                if (j != 0U) {
                    result_candidate_is_prime = false;
                }

                break;
            }

            // Compute y = y^2 mod np.
            y = (y * y) % np;

            // If we reach the final iteration without hitting nm1,
            // then the candidate is not prime within the limits of
            // Miller-Rabin primality testing.

            if ((j + 1U) == k) {
                result_candidate_is_prime = false;
            }
        }
    }

    return result_candidate_is_prime;
}

[[nodiscard]] auto str_prime_density(std::uint64_t trial_count, std::uint64_t prime_count) -> std::string {
    std::stringstream strm{};

    strm << "prime density: 1/" << std::fixed << std::setprecision(1)
         << static_cast<float>(static_cast<double>(trial_count) / static_cast<double>(prime_count));

    return strm.str();
}

auto main() -> int {

    using beman::big_int::big_int;

    constexpr unsigned prime_candidate_bits{512U};

    const big_int max_val{(big_int{1} << prime_candidate_bits) - 1};

    std::uint64_t prime_count{};
    std::uint64_t trial_count{};

    constexpr std::uint64_t max_prime_count{32};

    big_int next_prime{};

    while (prime_count < max_prime_count) {

#if defined(BEMAN_BIG_INT_EXAMPLE_PRIMES_USE_ENTROPY)
        static unsigned seed_prescaler{};
        if ((seed_prescaler % 128U) == 0U) {
            rnd_gens::eng1().seed(static_cast<typename rnd_gens::gen_type::result_type>(std::random_device{}()));
            rnd_gens::eng2().seed(static_cast<typename rnd_gens::gen_type::result_type>(std::random_device{}()));
        }
        ++seed_prescaler;
#endif

        const big_int prime_candidate{get_pseudo_random_integer(rnd_gens::eng2(), max_val)};
        ++trial_count;

        const bool is_prime{miller_rabin(prime_candidate, 25)};

        if (is_prime) {
            next_prime = prime_candidate;

            ++prime_count;

            std::cout << "prime" << prime_count << "/" << trial_count << ", "
                      << str_prime_density(trial_count, prime_count) << ": " << to_string(next_prime) << std::endl;
        }
    }

    std::cout << str_prime_density(trial_count, prime_count) << std::endl;

    int ret_val{};

#if !defined(BEMAN_BIG_INT_EXAMPLE_PRIMES_USE_ENTROPY)
    if constexpr ((max_prime_count == 32U) && (prime_candidate_bits == 512U)) {
        using namespace beman::big_int::literals;
        ret_val =
            (next_prime ==
             10143171719460317030776606042161455743692462665971704190268979952647685451042758582438652518606262043864611102965422197344907990728961691317186137657753121_n)
                ? 0
                : -1;
    }
#endif

    return ret_val;
}
Expected Output
prime1/389, prime density: 1/389.0: 10820050589452043375129359968044848823653108826102038432756084259814539871824595985135514160240839677587997096082728855057283136747718736835174028301439811
prime2/884, prime density: 1/442.0: 13053002634051457153269563505294787586530781045600655384239350974464030985322274634125948380308613275252152709114371864175536321306020483092373490038537513
prime3/1303, prime density: 1/434.3: 9113408390339776939937322533531368418638443465573228668211431065150845249377352845613968527317499019011801084926347359849041995335674454623951889187698963
prime4/1840, prime density: 1/460.0: 7439143610847456537936719324555893940192966903384041376930051045330542149889003496304942761542867062031204090967391533149782780518009090886735700492732283
prime5/2014, prime density: 1/402.8: 12716894641740749251119761437951387904058516736910649781390037489145551947806519423222547226541093019610728346575724117851151898056577600921478324875159091
prime6/2035, prime density: 1/339.2: 9222349343967327287849286452525376834894709454254257707095388110623878080054920908058430746322933307455260345459876279076202283538100182059186936603713831
prime7/2090, prime density: 1/298.6: 12831229148477418253106043204282071827093809131256641162444751486066729672628041405514939852612750312903917843462574513604301240822605177167792435313902273
prime8/2260, prime density: 1/282.5: 8746861626218053409709956923357170144985454354160030369716624107293502586623794341935616865752118681593544469082074465648793189924983198232012802359924671
prime9/2427, prime density: 1/269.7: 7633482601685949173720622865323608143520580275267751210246406360413144970863887803319385531589554761187652287055888183448600828422544165180120260851374887
prime10/2430, prime density: 1/243.0: 11847467563413066610675951659714131742354648916442529948482019931037355875049950280667788699722528296767926588622021234107461625108618228388499946114047489
prime11/2440, prime density: 1/221.8: 9260260689118454167562804827539120300709545886873167989694236269500813506636282361484799393469979752085123674682219995002541878470688102203616982511678681
prime12/2879, prime density: 1/239.9: 9947390421477912169086605427758117723193021243891519173545352862610804567575613865392948307284734248478560954639192558358682900522186541125892993776955791
prime13/2921, prime density: 1/224.7: 12667622498107762486829970578318341990240814882592474638713636501273992769924875565958228511678297638159528003594525619112565957578700616223753614474148447
prime14/3078, prime density: 1/219.9: 10797547412202246169768018915713590083135823909856461567086379718243508972646913260374270982064741872203117015246494506284607470618363171128229557279865727
prime15/3478, prime density: 1/231.9: 10109814943513084082145355946535754518619368198794387527064174803949822721764518392485810707371581666174165604169260407223826501883471688923117188147625351
prime16/4041, prime density: 1/252.6: 11359263851967576533042750683870312868759042157749867146230538637081169564115048523755571459791556173166078172912039805305906136288439639499875743428477737
prime17/4849, prime density: 1/285.2: 8461051509892530786979679056211698753782107742553540426258021916202140206965742021144126203264415010913517143944409074654903819493490749444825330879616357
prime18/5663, prime density: 1/314.6: 8798943523586288157317488262872677633422830393867026395476330026298298130174852196478117848982376460505825997616597071387697692048742108885039127508941669
prime19/5811, prime density: 1/305.8: 12142376573463633134771248401253342069939903932772656554476370380491941464116577564057621451229017374183519865007466149021291031712609065533242288612297123
prime20/6026, prime density: 1/301.3: 10833914456441612583329053643841287939248242435526212997116787406336770351502613608810904575750699037742699151076810850343780897160682757987971583735320783
prime21/6198, prime density: 1/295.1: 8048051182610579332957997827952776258642337252694389587976645411708847932029564702605733381978721208630985420286887205954012096273310133784813475965012477
prime22/6962, prime density: 1/316.5: 9384518468574604480630427223156889185710482619610196839275096447458488568940640966162732764609921716507857940156492047176435129239659346365135566148723127
prime23/6991, prime density: 1/304.0: 9285658550344564101453546211604055202400750270678363747655373035150324441737798020241194457929714620002677959732879370099083676684017203461899951948178201
prime24/7140, prime density: 1/297.5: 12836875527205276778139063595485347659259905477351905427887403530435809228753573582346088326686244728783962715150651285174201587922705627372938203777267787
prime25/7373, prime density: 1/294.9: 8355670652373978890192305192182307715194591539020798723668012496334499144494586403192275462251369733479436640199098873362601364155564950218868692507791839
prime26/7873, prime density: 1/302.8: 7541013783402999794495719564796750862444069778205079802006243807238942901583976883985678242785257683500141162737734314807623731033896225272722911066336977
prime27/8867, prime density: 1/328.4: 8208013964257468941839554241439095007725773008325977189846894465009028652854527234465344193234627649070425728601595894508112507041359920508276203964603017
prime28/9007, prime density: 1/321.7: 6998765494046967697613728020448491776532542830654947729913105548726740547544121571059615476131564644433480539087334880950793501576430928664517813474485331
prime29/9191, prime density: 1/316.9: 9552177201174725333070741944061774778105130380265714691348303928884424855666399637567129856001335968797392019708266336845577332739474030289856373149159801
prime30/9321, prime density: 1/310.7: 11661441967855402899798260522692264827618083091945043221563825809558182035673564435207562963016403058101339737837930425104472812000810009560679545747037689
prime31/9913, prime density: 1/319.8: 11993494054317078291390100787343155651165162864863058699200749619856941724208059268904427836286926786544961764290994981551477478044756034799953931767671699
prime32/10371, prime density: 1/324.1: 10143171719460317030776606042161455743692462665971704190268979952647685451042758582438652518606262043864611102965422197344907990728961691317186137657753121
prime density: 1/324.1

RSA

Example 13. This example demonstrates the usage of beman::big_int in the RSA algorithm
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-License-Identifier: BSL-1.0

#include <beman/big_int.hpp>

#include <array>
#include <charconv>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>

template <class BigIntType>
auto powm(BigIntType b, BigIntType p, const BigIntType& m) -> BigIntType {

    // Calculate (b ^ p) % m.

    BigIntType x{1};

    unsigned p0{};

    while (((p0 = static_cast<unsigned>(p)) != 0U) || (p != 0U)) {
        if ((p0 & 1U) != 0U) {
            x *= b;
            x %= m;
        }

        b *= b;
        b %= m;

        p >>= 1U;
    }

    return x;
}

using beman::big_int::big_int;

auto from_hex_string(const std::string_view hex_str) -> big_int {
    big_int value_to_get{};

    // big_int supports from_chars, found in this example by ADL.
    const auto fc_result{from_chars(hex_str.data(), hex_str.data() + hex_str.size(), value_to_get, 16)};

    static_cast<void>(fc_result);

    return value_to_get;
}

auto to_hex_string(big_int value_to_convert) -> std::string {
    // Calculate the hex-expected string length and also align to 16.
    const std::size_t buf_size{(((value_to_convert.size() + 4U) / 4U) / 16U + 1U) * 16U};

    std::string result(buf_size, '\0');

    // big_int supports to_chars, found in this example by ADL.
    const auto [ptr, ec]{to_chars(result.data(), result.data() + result.size(), value_to_convert, 16)};

    static_cast<void>(ec);

    result.resize(static_cast<std::string::size_type>(std::distance(result.data(), ptr)));

    return result;
}

auto ascii_to_hex(std::string_view input) -> std::string {
    constexpr std::array hex{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    std::string out{};
    out.reserve(input.size() * 2U);

    for (char c : input) {
        out.push_back(hex[static_cast<std::size_t>(c) >> 4]);
        out.push_back(hex[static_cast<std::size_t>(c) & 0x0F]);
    }

    return out;
}

auto hex_to_ascii(std::string_view hex) -> std::string {
    std::string out{};
    out.reserve(hex.size() / 2);

    for (std::size_t i = 0; i < hex.size(); i += 2) {
        unsigned int value{};

        std::from_chars(hex.data() + i, hex.data() + i + 2, value, 16);

        out.push_back(static_cast<char>(value));
    }

    return out;
}

auto main() -> int {
    using namespace beman::big_int::literals;

    // Define the RSA parameters. See also lines 25-30 in the
    // traditional NIST CAVS file "KeyGen_186-3.rsp".

    big_int n{
        0xd9f3094b36634c05a02ae1a5569035107a48029e39b3c6a1853817f063e18e761c0c538e55ff2c7e53d603bb35cabb3b8d07f82aa0afdeaf7441fcf6746c5bcaaa2cde398ad73edb9c340c3ffca559132581eaf8f65c13d02f3445a932a3e1fadb5912f7553edec5047e4d0ed06ee87effc549e194d38e06b73a971c961688ba2d4aa4f450d2523372f317d41d06f9f0360e962ce953a69f36c53c370799fcfba195e8f691ebe862f84ae4bbd7747bc14499bd0efffcdc7154325908355c2ffc5b3948b8102b33aa2420381470e4ee858380ff0eea58288516c263f6d51dbbd0e477d1393a0a3ee60e1fde4330856665bf522006608a6104c138c0f39e09c4c5_n};
    big_int d{
        0x1bf009caddc664b4404d59711fde16d7c55822449de1c5a084d22ed5791fdaa37ea538867fc91a17e6856e277c2dedd70ca8bf6ec44b0e729917a88e5988cc561d948ddeea46e21fd8ff46cce7657c94bfb1bdf40b3b30d4595a8bc3a15f1d4ad4c665c09b3b265ba19cdb0b89cbaadd0097ff52e9f6e594f86829c5bb4e9ba0200f12fa6dc60fd28dec0d194f08deb50f5a7749540160d6e8338e75b11165b76f4650c2fcce08f979ad9941daedaa5e328473bf712f8f549c36967f5e15477dc643d1f48d563139134e5cdc4bb84f9782cd5125e864e067cb980290f215cb41090e297bac2714efba61115d85613851c2de50a82f4ab526b88c61b7c9a0b589_n};
    big_int e{0x100000001_n};

    constexpr char str_message[] = "Hello std-big-int RSA";

    // Convert the input ASCII text message to a hex string
    // and subsequently a big_int.
    big_int message{::from_hex_string(::ascii_to_hex(str_message))};

    // Perform RSA encryption and decryption.
    big_int cipher_text{::powm(message, e, n)};
    big_int recover{::powm(cipher_text, d, n)};

    // Represent the recovered big_int message (recover) as
    // a hex string and subsequently convert it back to its
    // ASCII representation.

    const std::string str_message_recover{::hex_to_ascii(::to_hex_string(recover))};

    std::cout << "message:\n" << str_message << std::endl;
    std::cout << "\nrecover:\n" << str_message_recover << std::endl;

    const bool result_is_ok{str_message == str_message_recover};

    std::cout << "\nresult_is_ok: " << std::boolalpha << result_is_ok << std::endl;

    return result_is_ok ? 0 : -1;
}
Expected Output
message:
Hello std-big-int RSA

recover:
Hello std-big-int RSA

result_is_ok: true