116 lines
2.8 KiB
C++
116 lines
2.8 KiB
C++
#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 {
|
|
|
|
//
|
|
template <typename T>
|
|
auto DivMod(T dividend, T divisor) -> std::pair<T, T>
|
|
{
|
|
auto const quotient = dividend / divisor;
|
|
auto const remainder = dividend % divisor;
|
|
if (remainder == 0 || (remainder > 0) == (divisor > 0)) {
|
|
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
|
|
{
|
|
using It = std::istream_iterator<std::string>;
|
|
return FromInt(std::transform_reduce(It(in), It(), 0, std::plus(), ToInt<std::int64_t>));
|
|
}
|
|
|
|
} // 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));
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
auto main(int argc, char** argv) -> int
|
|
{
|
|
std::cout << "Part 1: " << Solve(*aocpp::Startup(argc, argv)) << std::endl;
|
|
}
|