diff --git a/2021/03.cpp b/2021/03.cpp new file mode 100644 index 0000000..d8679d2 --- /dev/null +++ b/2021/03.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace +{ + namespace phx = boost::phoenix; + using phx::placeholders::arg1; + + auto Parse(std::istream &in) -> std::vector + { + using It = std::istream_iterator; + std::vector result; + std::move(It{in}, It{}, std::back_inserter(result)); + + if (result.empty()) + { + throw std::runtime_error{"Empty input"}; + } + + if (std::any_of(std::next(result.begin()), result.end(), + [W = result[0].size()](auto const &s) + { return s.size() != W; })) + { + throw std::runtime_error{"input line length mismatch"}; + } + + return result; + } + + auto Part1(std::vector const &entries) -> std::uint64_t + { + auto const N{entries.size()}; + auto const W{entries[0].size()}; + + std::uint64_t lo{}; + std::uint64_t hi{}; + + for (auto const i : boost::irange(W)) + { + auto const bit_test = '1' == arg1[i]; + + auto const n = boost::count_if(entries, bit_test); + lo <<= 1; + hi <<= 1; + (2 * n >= N ? hi : lo)++; + } + + return lo * hi; + } + + auto Part2_(std::vector entries, bool const big) -> std::uint64_t + { + auto const N{entries.size()}; + auto const W{entries[0].size()}; + + std::uint64_t result{}; + auto begin{entries.begin()}; + auto end{entries.end()}; + + for (auto const i : boost::irange(W)) + { + auto const bit_test = '1' == arg1[i]; + + result <<= 1; + + // If there's only one number left we should select it + if (std::distance(begin, end) == 1) + { + result |= bit_test(*begin); + } + else + { + auto const mid = std::partition(begin, end, bit_test); + auto const ones = std::distance(begin, mid); + auto const zeros = std::distance(mid, end); + auto const bit = big == (ones >= zeros); + result |= bit; + (bit ? end : begin) = mid; + } + } + + return result; + } + + auto Part2(std::vector entries) -> std::uint64_t + { + return Part2_(entries, false) * Part2_(entries, true); + } +} + +TEST_SUITE("2021-03") +{ + TEST_CASE("example") + { + std::istringstream in{ + "00100\n" + "11110\n" + "10110\n" + "10111\n" + "10101\n" + "01111\n" + "00111\n" + "11100\n" + "10000\n" + "11001\n" + "00010\n" + "01010\n"}; + auto entries = Parse(in); + CHECK(Part1(entries) == 198); + CHECK(Part2_(entries, false) == 10); + CHECK(Part2_(entries, true) == 23); + CHECK(Part2(std::move(entries)) == 230); + } +} + +auto Main(std::istream &in, std::ostream &out) -> void +{ + auto entries{Parse(in)}; + out << "Part 1: " << Part1(entries) << std::endl; + out << "Part 2: " << Part2(std::move(entries)) << std::endl; +} diff --git a/2021/CMakeLists.txt b/2021/CMakeLists.txt new file mode 100644 index 0000000..fd0c11c --- /dev/null +++ b/2021/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(2021_03 03.cpp) +target_link_libraries(2021_03 aocpp Boost::headers) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32a8bd0..8c276c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,4 +29,5 @@ add_subdirectory(2017) add_subdirectory(2018) add_subdirectory(2019) add_subdirectory(2020) +add_subdirectory(2021) add_subdirectory(2022)