knothash stuff
This commit is contained in:
parent
cb70a0b1d2
commit
a07ca5e34b
25
2017/10.cpp
Normal file
25
2017/10.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Startup.hpp>
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <knothash.hpp>
|
||||
|
||||
auto main(int argc, char** argv) -> int {
|
||||
std::string line;
|
||||
std::getline(*aocpp::Startup(argc, argv), line);
|
||||
|
||||
std::vector<std::uint8_t> lengths1;
|
||||
for (auto && x : aocpp::SplitOn(line, ",")) {
|
||||
lengths1.push_back(std::stoul(x));
|
||||
}
|
||||
|
||||
auto result = knothash::hash_ex<256>(1, lengths1.begin(), lengths1.end());
|
||||
std::cout << "Part 1: " << long{result[0]} * long{result[1]} << std::endl;
|
||||
|
||||
std::cout << "Part 2: " << knothash::render(knothash::hash(line)) << std::endl;
|
||||
}
|
103
2017/14.cpp
Normal file
103
2017/14.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Startup.hpp>
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <knothash.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
using Grid = std::vector<std::array<std::uint8_t, 16>>;
|
||||
|
||||
auto MakeRows(std::string input) -> Grid {
|
||||
Grid rows;
|
||||
rows.reserve(128);
|
||||
|
||||
input += "-";
|
||||
for (int i = 0; i < 128; i++) {
|
||||
rows.emplace_back(knothash::hash(input + std::to_string(i)));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
auto CountBits(Grid const& grid) -> std::size_t {
|
||||
std::size_t bits = 0;
|
||||
for (auto const& row : grid) {
|
||||
for (auto const x : row) {
|
||||
bits += std::popcount(x);
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
auto Connect(std::vector<std::uint16_t> & leaders, std::uint16_t who, std::uint16_t leader) {
|
||||
auto who_ = leaders[who];
|
||||
if (who_ == std::uint16_t(-1)) { return; } // there wasn't a node here, abort
|
||||
for (;;) {
|
||||
leaders[who] = leader;
|
||||
if (who_ == who) { return; }
|
||||
who = who_;
|
||||
who_ = leaders[who];
|
||||
}
|
||||
}
|
||||
|
||||
auto CountComponents(Grid const& grid) -> std::uint16_t {
|
||||
|
||||
std::vector<std::uint16_t> leaders(128 * 128);
|
||||
|
||||
// Visit all pairs of neighbors linking them if both are set
|
||||
for (std::uint16_t r = 0; r < 128; r++) {
|
||||
bool prev = false;
|
||||
for (std::uint16_t c = 0; c < 128; c++) {
|
||||
std::uint16_t me = r*128+c;
|
||||
|
||||
// first time we've visited this node, if it's in the graph it's a leader!
|
||||
if (grid[r][c/8] & (1<<(7-(c%8)))) {
|
||||
leaders[me] = me;
|
||||
|
||||
// If previous cell in row is a node, link it to me immediately
|
||||
// It will have just been set as its own group leader.
|
||||
if (prev) { leaders[me-1] = me; }
|
||||
|
||||
// Connect up's leader _after_ updating left's leader in case they were connected
|
||||
if (0 < r) { Connect(leaders, me - 128, me); }
|
||||
prev = true;
|
||||
} else {
|
||||
prev = false;
|
||||
leaders[me] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// count component leaders
|
||||
std::uint16_t n = 0;
|
||||
for (std::size_t i = 0; i < leaders.size(); i++) {
|
||||
if (leaders[i] == i) { n++; }
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("documented example") {
|
||||
auto rows = MakeRows("flqrgnkx");
|
||||
CHECK(CountBits(rows) == 8108);
|
||||
CHECK(CountComponents(rows) == 1242);
|
||||
}
|
||||
|
||||
auto main(int argc, char** argv) -> int {
|
||||
std::string input;
|
||||
std::getline(*aocpp::Startup(argc, argv), input);
|
||||
auto rows = MakeRows(std::move(input));
|
||||
std::cout << "Part 1: " << CountBits(rows) << std::endl;
|
||||
std::cout << "Part 2: " << CountComponents(rows) << std::endl;
|
||||
}
|
@ -1,2 +1,8 @@
|
||||
add_executable(2017_10 10.cpp)
|
||||
target_link_libraries(2017_10 aocpp knothash)
|
||||
|
||||
add_executable(2017_14 14.cpp)
|
||||
target_link_libraries(2017_14 aocpp knothash)
|
||||
|
||||
add_executable(2017_18 18.cpp)
|
||||
target_link_libraries(2017_18 aocpp)
|
||||
|
@ -21,6 +21,7 @@ pkg_check_modules(GMP REQUIRED IMPORTED_TARGET gmpxx)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(dlx)
|
||||
add_subdirectory(knothash)
|
||||
add_subdirectory(zmod)
|
||||
add_subdirectory(intcode)
|
||||
add_subdirectory(2017)
|
||||
|
3
knothash/CMakeLists.txt
Normal file
3
knothash/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
add_library(knothash src/knothash.cpp)
|
||||
target_include_directories(knothash PUBLIC include)
|
||||
target_link_libraries(knothash PRIVATE aocpp)
|
55
knothash/include/knothash.hpp
Normal file
55
knothash/include/knothash.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef KNOTHASH_HPP_
|
||||
#define KNOTHASH_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace knothash {
|
||||
|
||||
template <std::size_t N>
|
||||
auto reverser(std::array<std::uint8_t, N> & result, std::size_t cursor, std::size_t len) -> void {
|
||||
auto leftside = N - cursor;
|
||||
auto start = result.begin() + cursor;
|
||||
if (len <= leftside) {
|
||||
std::reverse(start, start + len);
|
||||
} else {
|
||||
auto rightside = len - leftside;
|
||||
if (leftside < rightside) {
|
||||
std::swap_ranges(start, result.end(), result.rend() - rightside);
|
||||
std::reverse(result.begin(), result.begin() + (rightside-leftside));
|
||||
} else {
|
||||
std::swap_ranges(result.rend() - rightside, result.rend(), start);
|
||||
std::reverse(result.end() - (leftside-rightside), result.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto hash(std::string key) -> std::array<std::uint8_t, 16>;
|
||||
auto render(std::array<std::uint8_t, 16> const& hash) -> std::string;
|
||||
|
||||
template <std::size_t N, typename It>
|
||||
auto hash_ex(std::size_t cycles, It begin, It end)
|
||||
-> std::array<std::uint8_t, N>
|
||||
{
|
||||
std::size_t cursor = 0;
|
||||
std::size_t skip = 0;
|
||||
|
||||
std::array<std::uint8_t, N> result;
|
||||
for (std::size_t i = 0; i < N; i++) { result[i] = i; }
|
||||
|
||||
for (std::size_t i = 0; i < cycles; i++) {
|
||||
std::for_each(begin, end, [&](std::size_t len) {
|
||||
reverser(result, cursor, len);
|
||||
cursor += skip + len;
|
||||
cursor %= N;
|
||||
skip++;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
79
knothash/src/knothash.cpp
Normal file
79
knothash/src/knothash.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include <knothash.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
namespace knothash {
|
||||
|
||||
auto hash(std::string key) -> std::array<std::uint8_t, 16>
|
||||
{
|
||||
key += 17;
|
||||
key += 31;
|
||||
key += 73;
|
||||
key += 47;
|
||||
key += 23;
|
||||
auto sparse = hash_ex<256>(64, key.begin(), key.end());
|
||||
|
||||
std::size_t offset = 0;
|
||||
std::array<std::uint8_t, 16> dense;
|
||||
for (auto & x : dense) {
|
||||
x = std::reduce(sparse.begin()+offset, sparse.begin()+offset+16, 0, std::bit_xor());
|
||||
offset += 16;
|
||||
}
|
||||
|
||||
return dense;
|
||||
}
|
||||
|
||||
auto render(std::array<std::uint8_t, 16> const& hash) -> std::string {
|
||||
std::ostringstream out;
|
||||
out << std::hex << std::setfill('0');
|
||||
for (int const x : hash) {
|
||||
out << std::setw(2) << int(x);
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
TEST_SUITE("knot hash") {
|
||||
|
||||
TEST_CASE("reverser") {
|
||||
std::array<std::uint8_t, 9> v {0,1,2,3,4,5,6,7,8};
|
||||
|
||||
SUBCASE("more left") {
|
||||
std::array<std::uint8_t, 9> expect {1,0,8,7,4,5,6,3,2};
|
||||
reverser(v, 7, 6);
|
||||
REQUIRE(v == expect);
|
||||
}
|
||||
|
||||
SUBCASE("more right") {
|
||||
std::array<std::uint8_t, 9> expect {6,5,2,3,4,1,0,8,7};
|
||||
reverser(v, 5, 6);
|
||||
REQUIRE(v == expect);
|
||||
}
|
||||
|
||||
SUBCASE("middle") {
|
||||
std::array<std::uint8_t, 9> expect {0,1,5,4,3,2,6,7,8};
|
||||
reverser(v, 2, 4);
|
||||
CHECK(v == expect);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("knot example") {
|
||||
std::array<std::size_t, 4> lengths {3, 4, 1, 5};
|
||||
auto result = hash_ex<5>(1, lengths.begin(), lengths.end());
|
||||
std::array<std::uint8_t, 5> expected {3, 4, 2, 1, 0};
|
||||
CHECK(result == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("hash examples") {
|
||||
CHECK(render(hash("")) == "a2582a3a0e66e6e86e3812dcb672a272");
|
||||
CHECK(render(hash("AoC 2017")) == "33efeb34ea91902bb2f59c9920caa6cd");
|
||||
CHECK(render(hash("1,2,3")) == "3efbe78a8d82f29979031a4aa0b16a9d");
|
||||
CHECK(render(hash("1,2,4")) == "63960835bcdc130f0b66d7ff4f6a5a8e");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
@ -13,6 +13,10 @@
|
||||
namespace aocpp {
|
||||
|
||||
auto Startup(int argc, char ** argv) -> std::unique_ptr<std::istream, ConditionalDeleter> {
|
||||
if (std::getenv("DOCTEST")) {
|
||||
exit(doctest::Context{argc, argv}.run());
|
||||
}
|
||||
|
||||
bool should_delete;
|
||||
std::istream* result_ptr;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user