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