template<int digit, class Tail> struct BigInteger { };
, operations on it and so on. But to the glory of the new standard, we will go the other way.
template<typename... Types> class tuple { // ... };
Note the three dots in the template arguments. Now Types
is not a type name, but a parameter pack is a collection of zero or more arbitrary types. How to use it? Cleverly.
template<typename... Types> void just_do_nothing_for_no_reason(Types... args) { // indeed }
Here Types... args
is another special syntactic construction ( function parameter pack ), which expands into the corresponding parameter pack of the function argument chain. Since C ++ supports autodetection of template function arguments, this function can now be called with any number of arguments of any type.
template<typename... Types> tuple<Types...> make_tuple(Types... args) { tuple<Types...> result(args...); return result; }
Types... args
we already know. args...
is another special construction ( parameter pack expansion ), which in this case will be expanded into a list of function arguments separated by a comma. So, if you make a call to make_tuple(1, 1.f, '1')
somewhere in the code, then a function of the make_tuple(1, 1.f, '1')
will be created and called
tuple<int, float, char> make_tuple(int a1, float a2, char a3) { tuple<int, float, char> result(a1, a2, a3); return result; }
template<typename... Types> std::tuple<Types...> just_double_everything(Types... args) { std::tuple<Types...> result((args * 2)...); // OMG return result; }
Iiii - yes, you guessed it! The construction ((args * 2)...)
will unfold in (a1 * 2, a2 * 2, a3 * 2)
.
template<typename T> std::ostream& print(std::ostream& where, const T& what) { return where << what; } template<typename T, typename... Types> std::ostream& print(std::ostream& where, const T& what, const Types& ... other) { return print(where << what << ' ', other...); }
The output is type-safe printf - definitely worth it!
template<int ...> struct seq { }; // template<int N, int... S> // Python' range() struct make_range : make_range<N-1, N-1, S...> { }; template<int ...S> struct make_range<0, S...> { typedef seq<S...> result; }; template<typename... Types, int... range> std::ostream& operator_shl_impl( std::ostream& out, const std::tuple<Types...>& what, const seq<range...> /* a dummy argument */ ) { return print(out, std::get<range>(what)...); } template<typename... Types> std::ostream& operator<<(std::ostream& out, const std::tuple<Types...>& what) { using range = typename make_range<sizeof...(Types)>::result; return operator_shl_impl(out, what, range()); }
sizeof...(Types)
- it returns the number of types in the parameter pack. By the way, it can be implemented independently.
print(out, std::get<range>(what)...);
It does nothing but calls a function with arguments from the tuple . Just think! It is for this focus that we needed a list of numbers from 0 to (n - 1) - it is thanks to him that this line unfolds into something like
print(out, std::get<0>(what), std::get<1>(what), std::get<2>(what));
Write the list generator from (n - 1) to 0 - and you will be able to expand the tuples in one line:
auto reversed_tuple = std::make_tuple(std::get<rev_range>(my_tuple)...);
template<uint32_t... digits> struct BigUnsigned { static const size_t length = sizeof...(digits); // }; using Zero = BigUnsigned< >; using One = BigUnsigned<1>;
template<class A, class B> struct Concatenate { using Result = void; };
We mean that these two types will be BigUnsigned implementations. We implement the operation for them:
template<uint32_t... a, uint32_t... b> struct Concatenate<BigUnsigned<a...>, BigUnsigned<b...>> { // >> - C++11 using Result = BigUnsigned<a..., b...>; // ! ! };
template<class A, class B> struct Xor; template<uint32_t... a, uint32_t... b> struct Xor<BigUnsigned<a...>, BigUnsigned<b...>> { using Result = BigUnsigned< (a^b)... >; // , a b };
template<class A, class B> struct Sum; template<class A> struct Sum<Zero, A> { using Result = A; }; template<class A> struct Sum< A, Zero> { using Result = A; }; // , : template< > struct Sum<Zero, Zero> { using Result = Zero; };
template<uint32_t a_0, uint32_t... a_tail, uint32_t b_0, uint32_t... b_tail> struct Sum<BigUnsigned<a_0, a_tail...>, BigUnsigned<b_0, b_tail...>> { static const uint32_t carry = b_0 > UINT32_MAX - a_0 ? 1 : 0; using Result = typename Concatenate< BigUnsigned<a_0 + b_0>, typename Sum< typename Sum< BigUnsigned<a_tail...>, BigUnsigned<b_tail...> >::Result, BigUnsigned<carry> >::Result >::Result; };
template<uint32_t a_0, uint32_t... a_tail, uint32_t b_0, uint32_t... b_tail> struct Sum<BigUnsigned<a_0, a_tail...>, BigUnsigned<b_0, b_tail...>> {
we need to separate the least significant digits of long numbers from all the older ones. If we used a more general pattern
template<uint32_t a..., uint32_t b...> struct Sum<BigUnsigned<a...>, BigUnsigned<b...>> {
, it would not be so easy to do this, and we would need some more supporting structures. (But one could write them wisely and subsequently realize the multiplication of Karatsuba, nya!)
static const uint32_t carry = b_0 > UINT32_MAX - a_0 ? 1 : 0;
calculates a so-called carry bit, indicating whether an overflow occurred in the current bit or not. (Instead of the UINT32_MAX
constant, UINT32_MAX
can and should use std :: numeric_limits .)
using A = BigUnsigned<0xFFFFFFFFFFFFFFFFULL>; using B = One; using C = Sum<A, B>::Result; int main(int argc, char** argv) { // ... }
int main(int argc, char** argv) { C::entertain_me(); }
static_bignum.cpp: «int main(int, char**)»: static_bignum.cpp:32:5: : «entertain_me» «C {aka static_bignum::BigUnsigned<0, 1>}» C::entertain_me(); ^
However, this whining about the g ++ error gave us the main secret - now we see that C is equal to static_bignum::BigUnsigned<0, 1>
, that is, 2 32 - everything came together!
template<class A> struct BinaryRepresentation; template<uint32_t a_0, uint32_t... a> struct BinaryRepresentation<BigUnsigned<a_0, a...>> { static std::string str(void) { // ... } };
We use the standard object std :: bitset to print the current 32 bits at each stage:
template<class A> struct BinaryRepresentation; template<uint32_t a_0, uint32_t... a> struct BinaryRepresentation<BigUnsigned<a_0, a...>> { static std::string str(void) { std::bitset<32> bset(a_0); return BinaryRepresentation<BigUnsigned<a...>>::str() + bset.to_string(); } };
It remains only to specify the base recursion:
template<> struct BinaryRepresentation<Zero> { static std::string str(void) { return "0b"; // Oppa Python Style } };
using A = BigUnsigned<0xFFFFFFFFFFFFFFFFULL>; using B = One; using C = Sum<A, B>::Result; int main(int argc, char** argv) { std::cout << BinaryRepresentation<C>::str() << std::endl; }
and give us just a stunning readability response
0b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000
However, a careful calculation of the number of zeros can be sure that it is still 2 32 !
template<class A, class B> struct Difference; struct OverflowError {}; // - ( ) // -- : template<> struct Difference<Zero, Zero> { using Result = Zero; }; template<uint32_t n, uint32_t... tail> // -- struct Difference<BigUnsigned<n, tail...>, Zero> { using Result = BigUnsigned<n, tail...>; }; template<uint32_t n, uint32_t... tail> // -- struct Difference<Zero, BigUnsigned<n, tail...>> { using Result = OverflowError; }; template<class A> struct Difference<OverflowError, A> { using Result = OverflowError; }; template<class A> struct Difference<A, OverflowError> { using Result = OverflowError; }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct Difference<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...> > { using A = BigUnsigned<a_n, a_tail...>; // using B = BigUnsigned<b_n, b_tail...>; using C = typename Difference<BigUnsigned<a_tail...>, BigUnsigned<b_tail...>>::Result; using Result_T = typename std::conditional< // ? a_n >= b_n, C, typename Difference<C, One>::Result >::type; using Result = typename std::conditional< // a_n == b_n && std::is_same<Result_T, Zero>::value, Zero, typename Concatenate< BigUnsigned<a_n - b_n>, Result_T >::Result >::type; };
template<class A, size_t shift> struct ShiftLeft; template<class A, size_t shift> struct ShiftRight; template<class A, size_t shift> struct BigShiftLeft; template<class A, size_t shift> struct BigShiftRight; template<class A, size_t shift> struct SmallShiftLeft; template<class A, size_t shift> struct SmallShiftRight; template<size_t shift> struct BigShiftLeft <Zero, shift> { using Result = Zero; }; template<size_t shift> struct BigShiftRight <Zero, shift> { using Result = Zero; }; template<size_t shift> struct SmallShiftLeft <Zero, shift> { using Result = Zero; }; template<size_t shift> struct SmallShiftRight<Zero, shift> { using Result = Zero; static const uint32_t carry = 0; }; template<uint32_t... a> struct BigShiftLeft <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct BigShiftRight <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct SmallShiftLeft <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct SmallShiftRight<BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; static const uint32_t carry = 0; };
I decided that it is logical to divide any bit shift into the alternate use of small (shift less than 32 bits) and large (shift is a multiple of 32 bits), since their implementations differ significantly. So a big shift:
template<uint32_t... a, size_t shift> struct BigShiftLeft<BigUnsigned<a...>, shift> { using Result = typename Concatenate< BigUnsigned<0>, typename BigShiftLeft< BigUnsigned<a...>, shift - 1 >::Result >::Result; }; template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct BigShiftRight<BigUnsigned<a_0, a_tail...>, shift> { using Result = typename BigShiftRight< BigUnsigned<a_tail...>, shift - 1 >::Result; };
Here, shift denotes a shift of 32 ⋅ shift bits, and the operation itself simply “eats” or adds in turn entire 32-bit words.
template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct SmallShiftLeft<BigUnsigned<a_0, a_tail...>, shift> { static_assert(shift < 32, "shift in SmallShiftLeft must be less than 32 bits"); static const uint32_t carry = a_0 >> (32 - shift); using Result = typename Concatenate< BigUnsigned<(a_0 << shift)>, typename Sum< // Or Xor, Sum typename SmallShiftLeft<BigUnsigned<a_tail...>, shift>::Result, BigUnsigned<carry> >::Result >::Result; }; template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct SmallShiftRight<BigUnsigned<a_0, a_tail...>, shift> { static_assert(shift < 32, "shift in SmallShiftRight must be less than 32 bits"); static const uint32_t carry = a_0 << (32 - shift); using Result = typename Concatenate< BigUnsigned<(a_0 >> shift) | SmallShiftRight<BigUnsigned<a_tail...>, shift>::carry>, typename SmallShiftRight<BigUnsigned<a_tail...>, shift>::Result >::Result; };
Here again you have to take care of accurate bit shifting.
template<class A, size_t shift> struct ShiftLeft { using Result = typename BigShiftLeft< typename SmallShiftLeft<A, shift % 32>::Result, shift / 32 >::Result; }; template<class A, size_t shift> struct ShiftRight { using Result = typename SmallShiftRight< typename BigShiftRight<A, shift / 32>::Result, shift % 32 >::Result; };
As promised, he just correctly uses the big and small shifts.
template<class A, class B> struct GreaterThan; template<class A, class B> struct GreaterThanOrEqualTo; template<class A> struct GreaterThan<Zero, A> { static const bool value = false; }; template<class A> struct GreaterThanOrEqualTo<Zero, A> { static const bool value = false; }; template< > struct GreaterThanOrEqualTo<Zero, Zero> { static const bool value = true; }; template<uint32_t n, uint32_t... tail> struct GreaterThanOrEqualTo<BigUnsigned<n, tail...>, Zero> { static const bool value = true; }; template<uint32_t n, uint32_t... tail> struct GreaterThan<BigUnsigned<n, tail...>, Zero> { static const bool value = n > 0 || GreaterThan<BigUnsigned<tail...>, Zero>::value; }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct GreaterThan<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { using A_tail = BigUnsigned<a_tail...>; using B_tail = BigUnsigned<b_tail...>; static const bool value = GreaterThan<A_tail, B_tail>::value || (GreaterThanOrEqualTo<A_tail, B_tail>::value && a_n > b_n); }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct GreaterThanOrEqualTo<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { using A_tail = BigUnsigned<a_tail...>; using B_tail = BigUnsigned<b_tail...>; static const bool value = GreaterThan<A_tail, B_tail>::value || (GreaterThanOrEqualTo<A_tail, B_tail>::value && a_n >= b_n); }; template<class A, class B> struct LessThan { static const bool value = !GreaterThanOrEqualTo<A, B>::value; }; template<class A, class B> struct LessThanOrEqualTo { static const bool value = !GreaterThan<A, B>::value; };
template<class A, class B> struct Division; template<class A> struct Division<A, Zero> { using Quotient = DivisionByZeroError; using Residue = DivisionByZeroError; }; template<uint32_t n, uint32_t... tail> struct Division<BigUnsigned<n, tail...>, One> { using Quotient = BigUnsigned<n, tail...>; using Residue = Zero; }; template<class A, class B> struct DummyDivision { using Quotient = Zero; using Residue = A; };
struct DivisionByZeroError {}
,
int main(...) { ... std::cout << BinaryRepresentation<typename Division<One, Zero>::Quotient>::str() << std::endl; ... }
clang will give us a warning like static_bignum.cpp:229:18: error: implicit instantiation of undefined template 'BinaryRepresentation<DivisionByZeroError>'
Why we need a mysterious class DummyDivision
, we will see.
DummyDivision
.) Otherwise, let Q be the result of dividing A by 2B, R is the remainder of this division, that is, A = 2BQ + R
Then, obviously, A / B = 2Q + R / B
; however, since R is guaranteed to be less than 2B, then R / B
is either 0 or 1. In turn, A % B = R % B
, and R % B
is equal to either R or R - B
Actually, the code:
template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct Division<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { private: using A = BigUnsigned<a_n, a_tail...>; // .. .. using B = BigUnsigned<b_n, b_tail...>; using D = typename std::conditional< // : 2B GreaterThanOrEqualTo<A, B>::value, Division<A, typename SmallShiftLeft<B, 1>::Result>, DummyDivision<A, B> // ( ) >::type; using Q = typename D::Quotient; // using R = typename D::Residue; public: using Quotient = typename Sum< // 2Q (, , Q << 1) R / B typename SmallShiftLeft<Q, 1>::Result, typename std::conditional<GreaterThanOrEqualTo<R, B>::value, One, Zero>::type // <type_traits> std::conditional, // , >::Result; using Residue = typename std::conditional< GreaterThanOrEqualTo<R, B>::value, typename Difference<R, B>::Result, R >::type; };
uint32_t
, namely, by 10 9 .
template<class A> struct DecimalRepresentation; template<> struct DecimalRepresentation<Zero> { static inline std::string str(void) { return "0"; } }; template<class A> struct Digit; // , template<> struct Digit<Zero> { static const uint32_t value = 0; }; template<uint32_t digit, uint32_t... tail> struct Digit<BigUnsigned<digit, tail...>> { static const uint32_t value = digit; }; template<uint32_t n, uint32_t... tail> struct DecimalRepresentation<BigUnsigned<n, tail...>> { private: static const uint32_t modulo = 1000000000UL; static const uint32_t modulo_log = 9; using D = Division<BigUnsigned<n, tail...>, BigUnsigned<modulo>>; using Q = typename D::Quotient; using R = typename D::Residue; static_assert(Digit<R>::value < modulo, "invalid division by power of 10"); public: static std::string str(void) { // C++ , . // , . std::string stail = DecimalRepresentation<Q>::str(); // if(stail == "0") stail = ""; std::string curr = std::to_string(Digit<R>::value); // if(stail != "") while(curr.size() < modulo_log) curr = "0" + curr; return stail + curr; } };
using A = BigUnsigned<0xFFFFFFFFULL>; using B = One; using C = Sum<A, B>::Result; int main(int argc, char** argv) { std::cout << DecimalRepresentation<C>::str() << std::endl; }
4294967296
, which is exactly equal to 2 32 !
#include <cstdint> #include <bitset> #include <iostream> #include <algorithm> #include <type_traits> template<uint32_t... digits> struct BigUnsigned { static const size_t length = sizeof...(digits); }; using Zero = BigUnsigned< >; using One = BigUnsigned<1>; template<class A, class B> struct Concatenate { using Result = void; }; template<uint32_t... a, uint32_t... b> struct Concatenate<BigUnsigned<a...>, BigUnsigned<b...>> { // >> - C++11 using Result = BigUnsigned<a..., b...>; }; template<class A, class B> struct Sum; template<class A> struct Sum<Zero, A> { using Result = A; }; template<class A> struct Sum< A, Zero> { using Result = A; }; // , : template< > struct Sum<Zero, Zero> { using Result = Zero; }; template<uint32_t a_0, uint32_t... a_tail, uint32_t b_0, uint32_t... b_tail> struct Sum<BigUnsigned<a_0, a_tail...>, BigUnsigned<b_0, b_tail...>> { static const uint32_t carry = b_0 > UINT32_MAX - a_0 ? 1 : 0; using Result = typename Concatenate< BigUnsigned<a_0 + b_0>, typename Sum< typename Sum< BigUnsigned<a_tail...>, BigUnsigned<b_tail...> >::Result, BigUnsigned<carry> >::Result >::Result; }; template<class A> struct BinaryRepresentation; template<uint32_t a_0, uint32_t... a> struct BinaryRepresentation<BigUnsigned<a_0, a...>> { static std::string str(void) { std::bitset<32> bset(a_0); return BinaryRepresentation<BigUnsigned<a...>>::str() + bset.to_string(); } }; template<> struct BinaryRepresentation<Zero> { static std::string str(void) { return "0b"; // Oppa Python Style } }; template<class A, class B> struct Difference; struct OverflowError {}; template<> struct Difference<Zero, Zero> { using Result = Zero; }; template<uint32_t n, uint32_t... tail> struct Difference<BigUnsigned<n, tail...>, Zero> { using Result = BigUnsigned<n, tail...>; }; template<uint32_t n, uint32_t... tail> struct Difference<Zero, BigUnsigned<n, tail...>> { using Result = OverflowError; }; template<class A> struct Difference<OverflowError, A> { using Result = OverflowError; }; template<class A> struct Difference<A, OverflowError> { using Result = OverflowError; }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct Difference<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...> > { using A = BigUnsigned<a_n, a_tail...>; // using B = BigUnsigned<b_n, b_tail...>; using C = typename Difference<BigUnsigned<a_tail...>, BigUnsigned<b_tail...>>::Result; using Result_T = typename std::conditional< // ? a_n >= b_n, C, typename Difference<C, One>::Result >::type; using Result = typename std::conditional< // a_n == b_n && std::is_same<Result_T, Zero>::value, Zero, typename Concatenate< BigUnsigned<a_n - b_n>, Result_T >::Result >::type; }; template<class A, size_t shift> struct ShiftLeft; template<class A, size_t shift> struct ShiftRight; template<class A, size_t shift> struct BigShiftLeft; template<class A, size_t shift> struct BigShiftRight; template<class A, size_t shift> struct SmallShiftLeft; template<class A, size_t shift> struct SmallShiftRight; template<size_t shift> struct BigShiftLeft <Zero, shift> { using Result = Zero; }; template<size_t shift> struct BigShiftRight <Zero, shift> { using Result = Zero; }; template<size_t shift> struct SmallShiftLeft <Zero, shift> { using Result = Zero; }; template<size_t shift> struct SmallShiftRight<Zero, shift> { using Result = Zero; static const uint32_t carry = 0; }; template<uint32_t... a> struct BigShiftLeft <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct BigShiftRight <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct SmallShiftLeft <BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; }; template<uint32_t... a> struct SmallShiftRight<BigUnsigned<a...>, 0> { using Result = BigUnsigned<a...>; static const uint32_t carry = 0; }; template<uint32_t... a, size_t shift> struct BigShiftLeft<BigUnsigned<a...>, shift> { using Result = typename Concatenate< BigUnsigned<0>, typename BigShiftLeft< BigUnsigned<a...>, shift - 1 >::Result >::Result; }; template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct BigShiftRight<BigUnsigned<a_0, a_tail...>, shift> { using Result = typename BigShiftRight< BigUnsigned<a_tail...>, shift - 1 >::Result; }; template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct SmallShiftLeft<BigUnsigned<a_0, a_tail...>, shift> { static_assert(shift < 32, "shift in SmallShiftLeft must be less than 32 bits"); static const uint32_t carry = a_0 >> (32 - shift); using Tail = typename Sum< // Or Xor, Sum typename SmallShiftLeft<BigUnsigned<a_tail...>, shift>::Result, BigUnsigned<carry> >::Result; using Result = typename std::conditional< std::is_same<Tail, BigUnsigned<0>>::value, // , (!) BigUnsigned<(a_0 << shift)>, typename Concatenate< BigUnsigned<(a_0 << shift)>, Tail >::Result >::type; }; template<uint32_t a_0, uint32_t... a_tail, size_t shift> struct SmallShiftRight<BigUnsigned<a_0, a_tail...>, shift> { static_assert(shift < 32, "shift in SmallShiftRight must be less than 32 bits"); static const uint32_t carry = a_0 << (32 - shift); using Result = typename Concatenate< BigUnsigned<(a_0 >> shift) | SmallShiftRight<BigUnsigned<a_tail...>, shift>::carry>, typename SmallShiftRight<BigUnsigned<a_tail...>, shift>::Result >::Result; }; template<class A, size_t shift> struct ShiftLeft { using Result = typename BigShiftLeft< typename SmallShiftLeft<A, shift % 32>::Result, shift / 32 >::Result; }; template<class A, size_t shift> struct ShiftRight { using Result = typename SmallShiftRight< typename BigShiftRight<A, shift / 32>::Result, shift % 32 >::Result; }; template<class A, class B> struct GreaterThan; template<class A, class B> struct GreaterThanOrEqualTo; template<class A> struct GreaterThan<Zero, A> { static const bool value = false; }; template<class A> struct GreaterThanOrEqualTo<Zero, A> { static const bool value = false; }; template< > struct GreaterThanOrEqualTo<Zero, Zero> { static const bool value = true; }; template<uint32_t n, uint32_t... tail> struct GreaterThanOrEqualTo<BigUnsigned<n, tail...>, Zero> { static const bool value = true; }; template<uint32_t n, uint32_t... tail> struct GreaterThan<BigUnsigned<n, tail...>, Zero> { static const bool value = n > 0 || GreaterThan<BigUnsigned<tail...>, Zero>::value; }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct GreaterThan<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { using A_tail = BigUnsigned<a_tail...>; using B_tail = BigUnsigned<b_tail...>; static const bool value = GreaterThan<A_tail, B_tail>::value || (GreaterThanOrEqualTo<A_tail, B_tail>::value && a_n > b_n); }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct GreaterThanOrEqualTo<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { using A_tail = BigUnsigned<a_tail...>; using B_tail = BigUnsigned<b_tail...>; static const bool value = GreaterThan<A_tail, B_tail>::value || (GreaterThanOrEqualTo<A_tail, B_tail>::value && a_n >= b_n); }; template<class A, class B> struct LessThan { static const bool value = !GreaterThanOrEqualTo<A, B>::value; }; template<class A, class B> struct LessThanOrEqualTo { static const bool value = !GreaterThan<A, B>::value; }; struct DivisionByZeroError { }; template<class A, class B> struct Division; template<class A> struct Division<A, Zero> { using Quotient = DivisionByZeroError; using Residue = DivisionByZeroError; }; template<uint32_t n, uint32_t... tail> struct Division<BigUnsigned<n, tail...>, One> { using Quotient = BigUnsigned<n, tail...>; using Residue = Zero; }; template<class A, class B> struct DummyDivision { using Quotient = Zero; using Residue = A; }; template<uint32_t a_n, uint32_t b_n, uint32_t... a_tail, uint32_t... b_tail> struct Division<BigUnsigned<a_n, a_tail...>, BigUnsigned<b_n, b_tail...>> { private: using A = BigUnsigned<a_n, a_tail...>; using B = BigUnsigned<b_n, b_tail...>; using D = typename std::conditional< GreaterThanOrEqualTo<A, B>::value, Division<A, typename SmallShiftLeft<B, 1>::Result>, DummyDivision<A, B> >::type; using Q = typename D::Quotient; using R = typename D::Residue; public: using Quotient = typename Sum< typename SmallShiftLeft<Q, 1>::Result, typename std::conditional<GreaterThanOrEqualTo<R, B>::value, One, Zero>::type >::Result; using Residue = typename std::conditional< GreaterThanOrEqualTo<R, B>::value, typename Difference<R, B>::Result, R >::type; }; template<class A> struct DecimalRepresentation; template<> struct DecimalRepresentation<Zero> { static inline std::string str(void) { return "0"; } }; template<class A> struct Digit; template<> struct Digit<Zero> { static const uint32_t value = 0; }; template<uint32_t digit, uint32_t... tail> struct Digit<BigUnsigned<digit, tail...>> { static const uint32_t value = digit; }; template<uint32_t n, uint32_t... tail> struct DecimalRepresentation<BigUnsigned<n, tail...>> { private: static const uint32_t modulo = 1000000000UL; static const uint32_t modulo_log = 9; using D = Division<BigUnsigned<n, tail...>, BigUnsigned<modulo>>; using Q = typename D::Quotient; using R = typename D::Residue; static_assert(Digit<R>::value < modulo, "invalid division by power of 10"); public: static std::string str(void ){ std::string stail = DecimalRepresentation<Q>::str(); if(stail == "0") stail = ""; std::string curr = std::to_string(Digit<R>::value); if(stail != "") while(curr.size() < modulo_log) curr = "0" + curr; return stail + curr; } }; using A = BigUnsigned<0xFFFFFFFFULL>; using B = One; using C = Sum<A, B>::Result; int main(int argc, char** argv) { std::cout << DecimalRepresentation<C>::str() << std::endl; }
BigSigned<int, uint32_t...>
Source: https://habr.com/ru/post/248897/