Skip to content

Commit 3f624f8

Browse files
committed
Add aoc::math::prev_power_of_10 and tests for {next,prev}_power_of_10
1 parent c6a6523 commit 3f624f8

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

aoc_lib/src/test_math.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ std::size_t test_powers_of_10() {
5656

5757
constexpr auto POWERS_OF_10 = aoc::math::gen_powers_of_10<IntegerT>();
5858
std::size_t size = POWERS_OF_10.size();
59+
test(size, std::numeric_limits<IntegerT>::digits10 + 1);
5960
IntegerT max = std::numeric_limits<IntegerT>::max();
6061
int i = 0;
61-
IntegerT x = 10;
62+
IntegerT x = 1;
6263
while (x <= max / 10) {
6364
test(POWERS_OF_10.at(i), x,
6465
"incorrect element at index " + std::to_string(i));
@@ -82,6 +83,71 @@ std::size_t test_powers_of_10() {
8283
return test.num_failed();
8384
}
8485

86+
template <class IntegerT>
87+
std::size_t test_next_power_of_10() {
88+
const std::string type_name = util::demangle(typeid(IntegerT).name());
89+
unit_test::PureTest test("aoc::math::next_power_of_10<" + type_name + ">",
90+
&aoc::math::next_power_of_10<IntegerT>);
91+
92+
test(0, 1);
93+
test(1, 10);
94+
test(2, 10);
95+
test(9, 10);
96+
test(10, 100);
97+
test(11, 100);
98+
test(99, 100);
99+
for (unsigned int i = 0; i < std::numeric_limits<IntegerT>::digits10; ++i) {
100+
IntegerT p10 = powi(static_cast<IntegerT>(10), i);
101+
if (p10 >= 1) {
102+
test(p10 - 1, p10);
103+
}
104+
if (IntegerT(p10 * 10) % IntegerT(10) == 0) {
105+
test(p10, 10 * p10);
106+
test(p10 + 1, 10 * p10);
107+
}
108+
}
109+
110+
test.done();
111+
if (test.num_failed() > 0) {
112+
std::cout << "POWERS_OF_10: "
113+
<< pretty_print::repr(aoc::math::gen_powers_of_10<IntegerT>())
114+
<< "\n";
115+
}
116+
return test.num_failed();
117+
}
118+
119+
template <class IntegerT>
120+
std::size_t test_prev_power_of_10() {
121+
const std::string type_name = util::demangle(typeid(IntegerT).name());
122+
unit_test::PureTest test("aoc::math::prev_power_of_10<" + type_name + ">",
123+
&aoc::math::prev_power_of_10<IntegerT>);
124+
125+
test(0, 0);
126+
test(1, 0);
127+
test(2, 1);
128+
test(9, 1);
129+
test(10, 1);
130+
test(11, 10);
131+
test(99, 10);
132+
for (unsigned int i = 1; i <= std::numeric_limits<IntegerT>::digits10;
133+
++i) {
134+
IntegerT p10 = powi(static_cast<IntegerT>(10), i);
135+
if (p10 >= 1) {
136+
test(p10 - 1, p10 / 10);
137+
}
138+
test(p10, p10 / 10);
139+
test(p10 + 1, p10);
140+
}
141+
142+
test.done();
143+
if (test.num_failed() > 0) {
144+
std::cout << "POWERS_OF_10: "
145+
<< pretty_print::repr(aoc::math::gen_powers_of_10<IntegerT>())
146+
<< "\n";
147+
}
148+
return test.num_failed();
149+
}
150+
85151
template <class IntegerT>
86152
std::size_t test_num_digits() {
87153
const std::string type_name = util::demangle(typeid(IntegerT).name());
@@ -184,6 +250,16 @@ int main() {
184250
failed_count += test_powers_of_10<std::int64_t>();
185251
failed_count += test_powers_of_10<std::uint64_t>();
186252

253+
failed_count += test_next_power_of_10<int>();
254+
failed_count += test_next_power_of_10<unsigned int>();
255+
failed_count += test_next_power_of_10<std::int64_t>();
256+
failed_count += test_next_power_of_10<std::uint64_t>();
257+
258+
failed_count += test_prev_power_of_10<int>();
259+
failed_count += test_prev_power_of_10<unsigned int>();
260+
failed_count += test_prev_power_of_10<std::int64_t>();
261+
failed_count += test_prev_power_of_10<std::uint64_t>();
262+
187263
failed_count += test_num_digits<int>();
188264
failed_count += test_num_digits<unsigned int>();
189265
failed_count += test_num_digits<std::int64_t>();

aoc_lib/src/util/math.hpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ namespace aoc::math {
2424

2525
template <typename IntegerT,
2626
int max_digits = std::numeric_limits<IntegerT>::digits10>
27-
constexpr std::array<IntegerT, max_digits> gen_powers_of_10() {
28-
std::array<IntegerT, max_digits> arr;
27+
constexpr std::array<IntegerT, max_digits + 1> gen_powers_of_10() {
28+
std::array<IntegerT, max_digits + 1> arr;
2929
IntegerT x = 1;
30-
for (std::size_t i = 0; i < max_digits; ++i) {
30+
arr[0] = 1;
31+
for (std::size_t i = 1; i <= max_digits; ++i) {
3132
x *= 10;
3233
arr[i] = x;
3334
}
@@ -43,9 +44,13 @@ constexpr int num_digits(IntegerT value) {
4344
break;
4445
}
4546
}
46-
return i + 1;
47+
return i;
4748
}
4849

50+
/**
51+
* Throws std::overflow_error if the next power of 10 cannot be represented in
52+
* an IntegerT.
53+
*/
4954
template <std::integral IntegerT>
5055
IntegerT next_power_of_10(IntegerT value) {
5156
constexpr auto POWERS = gen_powers_of_10<IntegerT>();
@@ -56,7 +61,24 @@ IntegerT next_power_of_10(IntegerT value) {
5661
return pow;
5762
}
5863
}
59-
assert(false);
64+
throw std::overflow_error("overflow in aoc::math::next_power_of_10");
65+
}
66+
67+
/**
68+
* Returns 0 for values 0 and 1.
69+
*/
70+
template <std::integral IntegerT>
71+
IntegerT prev_power_of_10(IntegerT value) {
72+
constexpr auto POWERS = gen_powers_of_10<IntegerT>();
73+
// this array is small, so a linear search performs better
74+
auto end = POWERS.rend();
75+
for (auto it = POWERS.rbegin(); it != end; ++it) {
76+
// cppcheck-suppress useStlAlgorithm
77+
if (*it < value) {
78+
return *it;
79+
}
80+
}
81+
return 0;
6082
}
6183

6284
template <typename IntegerT>

0 commit comments

Comments
 (0)