AoC code coverage
Current view: top level - puzzles/2025 - Day08.cpp (source / functions) Coverage Total Hit
Test: master Lines: 99.0 % 100 99
Test Date: 2025-12-11 19:43:23 Functions: 100.0 % 14 14

            Line data    Source code
       1              : #include "PuzzleImpl.h"
       2              : 
       3              : #include "IntegerCast.h"
       4              : #include "Vector.h"
       5              : #include "Views.h"
       6              : 
       7              : #include <re2/re2.h>
       8              : 
       9              : #include <algorithm>
      10              : #include <numeric>
      11              : #include <queue>
      12              : #include <string_view>
      13              : #include <vector>
      14              : 
      15              : namespace {
      16              : 
      17              : using NodePos = Vector<3, int64_t>;
      18              : 
      19      2995618 : int64_t sqDistance(NodePos const &a, NodePos const &b) {
      20      2995618 :   int64_t dist = 0;
      21     11982472 :   for (size_t i = 0; i < 3; ++i) {
      22      8986854 :     int64_t const diff = integerCast<int64_t>(a[i]) - integerCast<int64_t>(b[i]);
      23      8986854 :     dist += diff * diff;
      24      8986854 :   }
      25      2995618 :   return dist;
      26      2995618 : }
      27              : 
      28            2 : std::vector<NodePos> parseInput(std::string_view const input) {
      29         2000 :   return input | views::splitLines | std::views::transform([](std::string_view line) {
      30         2000 :            static RE2 const regex = R"((\d+),(\d+),(\d+))";
      31         2000 :            NodePos point;
      32         2000 :            ASSERT(RE2::FullMatch(line, regex, &point[0], &point[1], &point[2]));
      33         2000 :            return point;
      34         2000 :          }) |
      35            2 :          std::ranges::to<std::vector<NodePos>>();
      36            2 : }
      37              : 
      38              : using Edge = std::pair<unsigned, unsigned>;
      39              : 
      40            2 : std::vector<Edge> buildEdges(size_t const numNodes) {
      41            2 :   std::vector<Edge> edges;
      42            2 :   edges.reserve(numNodes * (numNodes - 1) / 2);
      43         2002 :   for (unsigned i = 0; i < numNodes; ++i) {
      44      1001000 :     for (unsigned j = i + 1; j < numNodes; ++j) {
      45       999000 :       edges.emplace_back(i, j);
      46       999000 :     }
      47         2000 :   }
      48            2 :   return edges;
      49            2 : }
      50              : 
      51              : unsigned constexpr root = std::numeric_limits<unsigned>::max();
      52              : 
      53              : struct Node {
      54         1000 :   [[nodiscard]] bool isRoot() const { return parent == root; }
      55              : 
      56              :   unsigned parent = root;
      57              :   unsigned size = 1;
      58              : };
      59              : 
      60              : class Forest {
      61              : public:
      62            2 :   Forest(size_t numNodes) : _nodes(numNodes) {}
      63              : 
      64         5933 :   Node const &operator[](unsigned const i) const { return _nodes[i]; }
      65              : 
      66         5660 :   [[nodiscard]] unsigned size() const { return _nodes.size(); }
      67              : 
      68        21515 :   [[nodiscard]] unsigned find(unsigned const nodeIdx) {
      69        21515 :     if (_nodes[nodeIdx].parent == root) {
      70        11314 :       return nodeIdx;
      71        11314 :     }
      72        10201 :     unsigned const rootIdx = find(_nodes[nodeIdx].parent);
      73        10201 :     _nodes[nodeIdx].parent = rootIdx;
      74        10201 :     return rootIdx;
      75        21515 :   }
      76              : 
      77         5657 :   unsigned merge(unsigned const x, unsigned const y) {
      78         5657 :     unsigned const rootX = find(x);
      79         5657 :     unsigned const rootY = find(y);
      80         5657 :     if (rootX == rootY) {
      81         3934 :       return rootX;
      82         3934 :     }
      83         1723 :     if (_nodes[rootX].size >= _nodes[rootY].size) {
      84         1132 :       _nodes[rootY].parent = rootX;
      85         1132 :       _nodes[rootX].size += _nodes[rootY].size;
      86         1132 :       return rootX;
      87         1132 :     } else {
      88          591 :       _nodes[rootX].parent = rootY;
      89          591 :       _nodes[rootY].size += _nodes[rootX].size;
      90          591 :       return rootY;
      91          591 :     }
      92         1723 :   }
      93              : 
      94              : private:
      95              :   std::vector<Node> _nodes;
      96              : };
      97              : 
      98              : } // namespace
      99              : 
     100            1 : template <> std::string solvePart1<2025, 8>(std::string_view const input) {
     101            1 :   std::vector<NodePos> nodesPos = parseInput(input);
     102            1 :   Forest forest(nodesPos.size());
     103              : 
     104            1 :   std::vector<Edge> edges = buildEdges(forest.size());
     105              : 
     106       582204 :   auto edgeCompare = [&](Edge const &a, Edge const &b) {
     107       582204 :     return sqDistance(nodesPos[a.first], nodesPos[a.second]) <
     108       582204 :            sqDistance(nodesPos[b.first], nodesPos[b.second]);
     109       582204 :   };
     110              : 
     111            1 :   auto const first1000It =
     112            1 :       std::next(edges.begin(), integerCast<std::ptrdiff_t>(std::min(size_t{1000}, edges.size())));
     113            1 :   std::ranges::partial_sort(edges, first1000It, edgeCompare);
     114              : 
     115         1001 :   for (auto it = edges.begin(); it != first1000It; ++it) {
     116         1000 :     auto const &[u, v] = *it;
     117         1000 :     forest.merge(u, v);
     118         1000 :   }
     119              : 
     120            1 :   std::vector<unsigned> rootSizes;
     121         1001 :   for (unsigned i = 0; i < forest.size(); ++i) {
     122         1000 :     if (forest[i].isRoot()) {
     123          276 :       rootSizes.push_back(forest[i].size);
     124          276 :     }
     125         1000 :   }
     126              : 
     127            1 :   auto const top3It = std::next(rootSizes.begin(),
     128            1 :                                 integerCast<std::ptrdiff_t>(std::min(size_t{3}, rootSizes.size())));
     129            1 :   std::ranges::partial_sort(rootSizes, top3It, std::greater{});
     130              : 
     131            1 :   return std::to_string(std::accumulate(rootSizes.begin(), top3It, uint64_t{1}, std::multiplies{}));
     132            1 : }
     133              : 
     134            1 : template <> std::string solvePart2<2025, 8>(std::string_view const input) {
     135            1 :   std::vector<NodePos> nodesPos = parseInput(input);
     136            1 :   Forest forest(nodesPos.size());
     137              : 
     138       915605 :   auto edgeCompare = [&](Edge const &a, Edge const &b) {
     139       915605 :     return sqDistance(nodesPos[a.first], nodesPos[a.second]) >
     140       915605 :            sqDistance(nodesPos[b.first], nodesPos[b.second]);
     141       915605 :   };
     142              : 
     143            1 :   std::priority_queue<Edge, std::vector<Edge>, decltype(edgeCompare)> edges(
     144            1 :       edgeCompare, buildEdges(forest.size()));
     145              : 
     146         4657 :   while (!edges.empty()) {
     147         4657 :     auto const &[u, v] = edges.top();
     148         4657 :     edges.pop();
     149         4657 :     unsigned const newRoot = forest.merge(u, v);
     150         4657 :     if (forest[newRoot].size == forest.size()) {
     151            1 :       return std::to_string(nodesPos[u].x() * nodesPos[v].x());
     152            1 :     }
     153         4657 :   }
     154              : 
     155            0 :   return "";
     156            1 : }
        

Generated by: LCOV version 2.0-1