aocpp/2022/25.cpp

117 lines
2.8 KiB
C++
Raw Normal View History

2023-01-13 16:21:25 -08:00
#include <cstdint> // int64_t
#include <functional> // plus
#include <iostream> // istream
#include <iterator> // istream_iterator
#include <numeric> // transform_reduce
#include <sstream> // istringstream
#include <string> // string
#include <tuple> // pair, make_pair
#include <doctest.h>
#include <aocpp/Startup.hpp>
namespace {
2023-01-14 20:00:55 -08:00
using It = std::istream_iterator<std::string>;
2023-01-13 16:21:25 -08:00
//
template <typename T>
auto DivMod(T dividend, T divisor) -> std::pair<T, T>
{
auto const quotient = dividend / divisor;
auto const remainder = dividend % divisor;
2023-01-13 17:03:44 -08:00
if (remainder == 0 || (remainder > 0) == (divisor > 0)) {
2023-01-13 16:21:25 -08:00
return {quotient, remainder};
} else {
return {quotient - 1, remainder + divisor};
}
}
template <typename T>
auto FromInt(T x) -> std::string
{
std::string output;
char const digits[] {"=-012"};
while (x != 0) {
auto [x_, i] = DivMod<T>(x + 2, 5); // plain % and / are insufficient for negatives
x = x_;
output.push_back(digits[i]);
}
std::reverse(output.begin(), output.end());
return output;
}
template <typename T>
auto ToInt(std::string const& str) -> T
{
T acc {0};
for (char c : str) {
acc *= 5;
switch (c) {
case '2': acc += 2; break;
case '1': acc += 1; break;
case '0': break;
case '-': acc -= 1; break;
case '=': acc -= 2; break;
}
}
return acc;
}
auto Solve(std::istream & in) -> std::string
{
2023-01-14 20:00:55 -08:00
return FromInt(std::transform_reduce(It{in}, It{}, std::int64_t{}, std::plus(), ToInt<std::int64_t>));
2023-01-13 16:21:25 -08:00
}
} // namespace
TEST_SUITE("2022-25 examples") {
TEST_CASE("example") {
std::istringstream in {
"1=-0-2\n"
"12111\n"
"2=0=\n"
"21\n"
"2=01\n"
"111\n"
"20012\n"
"112\n"
"1=-1=\n"
"1-12\n"
"12\n"
"1=\n"
"122\n"
};
CHECK(Solve(in) == "2=-1=0");
}
// The example input doesn't test out any negative numbers
TEST_CASE("Negative numbers") {
CHECK(ToInt<int>("-") == -1);
CHECK(ToInt<int>("=") == -2);
CHECK(ToInt<int>("-=") == -7);
CHECK(ToInt<int>("=-") == -11);
CHECK(FromInt(-1) == "-");
CHECK(FromInt(-2) == "=");
CHECK(FromInt(-7) == "-=");
CHECK(FromInt(-11) == "=-");
}
TEST_CASE("DivMod behavior") {
CHECK(DivMod<int>(2,5) == std::make_pair(0,2));
CHECK(DivMod<int>(-2,5) == std::make_pair(-1,3));
CHECK(DivMod<int>(2,-5) == std::make_pair(-1,-3));
CHECK(DivMod<int>(-2,-5) == std::make_pair(0,-2));
2023-01-13 17:03:44 -08:00
CHECK(DivMod<int>(-5,-5) == std::make_pair(1,0));
CHECK(DivMod<int>(-5,5) == std::make_pair(-1,0));
CHECK(DivMod<int>(5,-5) == std::make_pair(-1,0));
CHECK(DivMod<int>(-5,-5) == std::make_pair(1,0));
2023-01-13 16:21:25 -08:00
}
}
auto main(int argc, char** argv) -> int
{
std::cout << "Part 1: " << Solve(*aocpp::Startup(argc, argv)) << std::endl;
}