Skip to content

Instantly share code, notes, and snippets.

@dakka
Last active May 25, 2023 07:43
Show Gist options
  • Save dakka/a092eeb6650e8555b0976341fcd3d51d to your computer and use it in GitHub Desktop.
Save dakka/a092eeb6650e8555b0976341fcd3d51d to your computer and use it in GitHub Desktop.
make_pow_array - create compiler generated constexpr C++20 arbitrary base and size power tables as std::array. Examples include uint128_t, bases 2, 3, 5, 8, 10, with constexpr printer and demangler. Uses folding, variable templates, constraints.
//-----------------------------------------------------------------------------------------
// Copyright (C) 2022-23 Fix8 Market Technologies Pty Ltd
// by David L. Dight
//
// make_pow_array - create compiler generated constexpr C++20 arbitrary base and size power
// tables as std::array. Examples include uint128_t, bases 2, 3, 5, 8, 10, with constexpr
// printer and demangler. Uses folding, variable templates, constraints.
//
// Distributed under the Boost Software License, Version 1.0 August 17th, 2003
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//----------------------------------------------------------------------------------------
#include <iostream>
#include <string_view>
#include <string>
#include <array>
#include <limits>
#include <concepts>
#include <memory>
#include <typeinfo>
#include <utility>
#if defined(__clang__) || defined(__GNUC__)
#include <cxxabi.h>
#endif
//-----------------------------------------------------------------------------------------
/*! pow variable template
\tparam T - type to define
\tparam base - base of power
\tparam val - exponent to raise */
template<typename T, int base, int val>
requires (base >= 0 && val >= 0)
constexpr T pow { T(base) * pow<T, base, val - 1> };
/*! pow variable template - 0 partial specialisation
\tparam T - type to define
\tparam base - base of power */
template<typename T, int base>
constexpr T pow<T, base, 0> { T(1) };
/*! Create a std::array of base with supplied sequence; called by make_pow_array
\tparam T - type to generate
\tparam base - base of power
\tparam Vals - sequence of T
\return std::array */
template<typename T, int base, T... Vals>
constexpr auto create_pow_array(std::integer_sequence<T, Vals...>) noexcept
{
return std::array<T, sizeof...(Vals)>{ (pow<T, base, Vals>)...};
}
/*! Calculate maximum digits that can be expressed for a given integral type and radix (at compile time)
\tparam T - type
\tparam base - radix, default base 10
\return int max digits */
template<std::integral T, int base=10, T mval=std::numeric_limits<T>::max()>
requires (base > 0)
constexpr int max_digits() noexcept
{
if constexpr (mval > 0)
return 1 + max_digits<T, base, mval / base>();
else
return 0;
}
/*! Make a std::array of base^cnt
\tparam T - type to generate
\tparam base - base of power
\tparam cnt - count of elements to generate
\return std::array */
template<std::integral T, int base, int cnt>
requires (cnt <= max_digits<T, base>())
constexpr auto make_pow_array() noexcept
{
return create_pow_array<T, base>(std::make_integer_sequence<T, cnt>{});
}
/*! Make a std::array of base^cnt with max digits that can be expressed for a given integral type and radix (at compile time)
\tparam T - type to generate
\tparam base - base of power, default 10
\return std::array */
template<std::integral T, int base=10>
constexpr auto make_pow_array() noexcept
{
return create_pow_array<T, base>(std::make_integer_sequence<T, max_digits<T, base>()>{});
}
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
// example
//-----------------------------------------------------------------------------------------
#if !defined uint128_t
using uint128_t = __uint128_t;
#endif
/*! unsigned 128 bit integer ostream inserter
\param os - std::ostream to insert into
\param val - const uint128_t& value to insert
\return os */
std::ostream& operator<<(std::ostream& os, const uint128_t& val) noexcept
{
if (std::ostream::sentry sent(os); sent)
{
std::array<char, 128> buffer;
auto back { buffer.end() };
for (auto tmp { val }; tmp; tmp /= 10)
*--back = (tmp % 10) + '0';
if (auto len { buffer.end() - back }; os.rdbuf()->sputn(back, len) != len)
os.setstate(std::ios_base::badbit);
}
return os;
}
//-----------------------------------------------------------------------------------------
/*! IA64 ABI demangler
\tparam T - type to demangle
\return std::string */
template<typename T>
constexpr std::string demangle() noexcept
{
#if defined(__clang__) || defined(__GNUC__)
int status;
std::unique_ptr<char, decltype(&free)> eptr(abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), &free);
using namespace std::string_literals;
switch (status)
{
case 0: return eptr.get();
case -1: return "memory allocation failiure"s;
case -2: return "invalid mangled name"s;
default:
case -3: return "invalid argument"s;
}
#endif
return typeid(T).name();
}
//-----------------------------------------------------------------------------------------
int main()
{
static constexpr auto ptab0 { make_pow_array<unsigned long long, 2>() }; // power of 2
static constexpr auto ptab1 { make_pow_array<unsigned short, 2>() };
static constexpr auto ptab2 { make_pow_array<long, 2>() };
static constexpr auto ptab3 { make_pow_array<uint128_t, 2>() };
static constexpr auto ptab4 { make_pow_array<unsigned long long>() }; // power of 10
static constexpr auto ptab5 { make_pow_array<uint128_t>() };
// alternative radix
static constexpr auto ptab6 { make_pow_array<unsigned, 3, 21>() }; // power of 3
static constexpr auto ptab7 { make_pow_array<unsigned, 3>() };
static constexpr auto ptab8 { make_pow_array<unsigned, 5>() }; // power of 5
static constexpr auto ptab9 { make_pow_array<uint128_t, 8, 43>() }; // power of 8
static constexpr auto ptab10 { make_pow_array<uint128_t, 8>() };
static_assert(pow<unsigned long long, 10, 4> == ptab4[4] && ptab4[4] == 10000ULL);
constexpr auto print([]<typename... Arrs>(const Arrs& ...arrs) constexpr noexcept
{
([]<typename T>(const T& arr) constexpr noexcept
{
std::cout << "// " << demangle<T>() << ", element size = " << sizeof(typename T::value_type) << ", radix = " << arr[1] << '\n';
for (const auto& pp : arr)
std::cout << pp << '\n';
std::cout << '\n';
}(arrs), ...);
});
print(ptab0, ptab1, ptab2, ptab3, ptab4, ptab5, ptab6, ptab7, ptab8, ptab9, ptab10);
std::cout << "10^4 = " << ptab5[4] << '\n'
<< "5^7 = " << ptab8[7] << '\n'
<< "2^126 = " << ptab5[126] << '\n'
<< "8^41 = " << ptab10[41] << '\n'
<< "6^4 = " << pow<int, 6, 4> << '\n';
constexpr auto print_limits([]<typename... Lims>(Lims ...lims) constexpr noexcept
{
([]<typename T>(T max) constexpr noexcept
{
std::cout << "type:" << demangle<T>() << " max:" << max
<< " base10 digits:" << max_digits<T>() << " base2 digits:" << max_digits<T, 2>() << std::endl;
}(lims), ...);
});
print_limits(std::numeric_limits<int>::max(), std::numeric_limits<unsigned>::max(), std::numeric_limits<short>::max(),
std::numeric_limits<unsigned short>::max(), std::numeric_limits<uint128_t>::max());
return 0;
}
@dakka
Copy link
Author

dakka commented Mar 1, 2023

To build

$ g++ -ggdb -O3 -std=gnu++20 pow_cpp20_template.cpp -o pow_cpp20_template

Example output

$ ./pow_cpp20_template
// std::array<unsigned long long, 64ul>, element size = 8, radix = 2
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
4294967296
8589934592
17179869184
34359738368
68719476736
137438953472
274877906944
549755813888
1099511627776
2199023255552
4398046511104
8796093022208
17592186044416
35184372088832
70368744177664
140737488355328
281474976710656
562949953421312
1125899906842624
2251799813685248
4503599627370496
9007199254740992
18014398509481984
36028797018963968
72057594037927936
144115188075855872
288230376151711744
576460752303423488
1152921504606846976
2305843009213693952
4611686018427387904
9223372036854775808

// std::array<unsigned short, 16ul>, element size = 2, radix = 2
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768

// std::array<long, 63ul>, element size = 8, radix = 2
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
4294967296
8589934592
17179869184
34359738368
68719476736
137438953472
274877906944
549755813888
1099511627776
2199023255552
4398046511104
8796093022208
17592186044416
35184372088832
70368744177664
140737488355328
281474976710656
562949953421312
1125899906842624
2251799813685248
4503599627370496
9007199254740992
18014398509481984
36028797018963968
72057594037927936
144115188075855872
288230376151711744
576460752303423488
1152921504606846976
2305843009213693952
4611686018427387904

// std::array<unsigned __int128, 128ul>, element size = 16, radix = 2
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
4294967296
8589934592
17179869184
34359738368
68719476736
137438953472
274877906944
549755813888
1099511627776
2199023255552
4398046511104
8796093022208
17592186044416
35184372088832
70368744177664
140737488355328
281474976710656
562949953421312
1125899906842624
2251799813685248
4503599627370496
9007199254740992
18014398509481984
36028797018963968
72057594037927936
144115188075855872
288230376151711744
576460752303423488
1152921504606846976
2305843009213693952
4611686018427387904
9223372036854775808
18446744073709551616
36893488147419103232
73786976294838206464
147573952589676412928
295147905179352825856
590295810358705651712
1180591620717411303424
2361183241434822606848
4722366482869645213696
9444732965739290427392
18889465931478580854784
37778931862957161709568
75557863725914323419136
151115727451828646838272
302231454903657293676544
604462909807314587353088
1208925819614629174706176
2417851639229258349412352
4835703278458516698824704
9671406556917033397649408
19342813113834066795298816
38685626227668133590597632
77371252455336267181195264
154742504910672534362390528
309485009821345068724781056
618970019642690137449562112
1237940039285380274899124224
2475880078570760549798248448
4951760157141521099596496896
9903520314283042199192993792
19807040628566084398385987584
39614081257132168796771975168
79228162514264337593543950336
158456325028528675187087900672
316912650057057350374175801344
633825300114114700748351602688
1267650600228229401496703205376
2535301200456458802993406410752
5070602400912917605986812821504
10141204801825835211973625643008
20282409603651670423947251286016
40564819207303340847894502572032
81129638414606681695789005144064
162259276829213363391578010288128
324518553658426726783156020576256
649037107316853453566312041152512
1298074214633706907132624082305024
2596148429267413814265248164610048
5192296858534827628530496329220096
10384593717069655257060992658440192
20769187434139310514121985316880384
41538374868278621028243970633760768
83076749736557242056487941267521536
166153499473114484112975882535043072
332306998946228968225951765070086144
664613997892457936451903530140172288
1329227995784915872903807060280344576
2658455991569831745807614120560689152
5316911983139663491615228241121378304
10633823966279326983230456482242756608
21267647932558653966460912964485513216
42535295865117307932921825928971026432
85070591730234615865843651857942052864
170141183460469231731687303715884105728

// std::array<unsigned long long, 20ul>, element size = 8, radix = 10
1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
10000000000
100000000000
1000000000000
10000000000000
100000000000000
1000000000000000
10000000000000000
100000000000000000
1000000000000000000
10000000000000000000

// std::array<unsigned __int128, 39ul>, element size = 16, radix = 10
1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
10000000000
100000000000
1000000000000
10000000000000
100000000000000
1000000000000000
10000000000000000
100000000000000000
1000000000000000000
10000000000000000000
100000000000000000000
1000000000000000000000
10000000000000000000000
100000000000000000000000
1000000000000000000000000
10000000000000000000000000
100000000000000000000000000
1000000000000000000000000000
10000000000000000000000000000
100000000000000000000000000000
1000000000000000000000000000000
10000000000000000000000000000000
100000000000000000000000000000000
1000000000000000000000000000000000
10000000000000000000000000000000000
100000000000000000000000000000000000
1000000000000000000000000000000000000
10000000000000000000000000000000000000
100000000000000000000000000000000000000

// std::array<unsigned int, 21ul>, element size = 4, radix = 3
1
3
9
27
81
243
729
2187
6561
19683
59049
177147
531441
1594323
4782969
14348907
43046721
129140163
387420489
1162261467
3486784401

// std::array<unsigned int, 21ul>, element size = 4, radix = 3
1
3
9
27
81
243
729
2187
6561
19683
59049
177147
531441
1594323
4782969
14348907
43046721
129140163
387420489
1162261467
3486784401

// std::array<unsigned int, 14ul>, element size = 4, radix = 5
1
5
25
125
625
3125
15625
78125
390625
1953125
9765625
48828125
244140625
1220703125

// std::array<unsigned __int128, 43ul>, element size = 16, radix = 8
1
8
64
512
4096
32768
262144
2097152
16777216
134217728
1073741824
8589934592
68719476736
549755813888
4398046511104
35184372088832
281474976710656
2251799813685248
18014398509481984
144115188075855872
1152921504606846976
9223372036854775808
73786976294838206464
590295810358705651712
4722366482869645213696
37778931862957161709568
302231454903657293676544
2417851639229258349412352
19342813113834066795298816
154742504910672534362390528
1237940039285380274899124224
9903520314283042199192993792
79228162514264337593543950336
633825300114114700748351602688
5070602400912917605986812821504
40564819207303340847894502572032
324518553658426726783156020576256
2596148429267413814265248164610048
20769187434139310514121985316880384
166153499473114484112975882535043072
1329227995784915872903807060280344576
10633823966279326983230456482242756608
85070591730234615865843651857942052864

// std::array<unsigned __int128, 43ul>, element size = 16, radix = 8
1
8
64
512
4096
32768
262144
2097152
16777216
134217728
1073741824
8589934592
68719476736
549755813888
4398046511104
35184372088832
281474976710656
2251799813685248
18014398509481984
144115188075855872
1152921504606846976
9223372036854775808
73786976294838206464
590295810358705651712
4722366482869645213696
37778931862957161709568
302231454903657293676544
2417851639229258349412352
19342813113834066795298816
154742504910672534362390528
1237940039285380274899124224
9903520314283042199192993792
79228162514264337593543950336
633825300114114700748351602688
5070602400912917605986812821504
40564819207303340847894502572032
324518553658426726783156020576256
2596148429267413814265248164610048
20769187434139310514121985316880384
166153499473114484112975882535043072
1329227995784915872903807060280344576
10633823966279326983230456482242756608
85070591730234615865843651857942052864

10^4 = 10000
5^7 = 78125
2^126 = 75557863725914323419136
8^41 = 10633823966279326983230456482242756608
6^4 = 1296
type:int max:2147483647 base10 digits:10 base2 digits:31
type:unsigned int max:4294967295 base10 digits:10 base2 digits:32
type:short max:32767 base10 digits:5 base2 digits:15
type:unsigned short max:65535 base10 digits:5 base2 digits:16
type:unsigned __int128 max:340282366920938463463374607431768211455 base10 digits:39 base2 digits:128
$

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment