#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "Array2D.h" #include "Graph.h" #define BIT(x) (1 << (x)) template static std::ostream& operator<<(std::ostream& stream, const std::vector& container); template static std::ostream& operator<<(std::ostream& stream, const std::deque& container); template static std::ostream& operator<<(std::ostream& stream, const std::set& container); template static std::ostream& operator<<(std::ostream& stream, const std::map& container); template static std::ostream& operator<<(std::ostream& stream, const std::pair& pair); template static std::ostream& operator<<(std::ostream& stream, const std::variant& variant); struct Helper { struct ScopedTraceErrorDisabler { ScopedTraceErrorDisabler(); ~ScopedTraceErrorDisabler(); }; template static void RemoveIf(T& t, S func) { t.erase(std::remove_if(t.begin(), t.end(), func), t.end()); } static std::vector Split(const std::string& str, const std::string& delim); static bool StartsWith(const std::string_view& str, const std::string& prefix, size_t offset = 0); static bool EndsWith(const std::string_view& str, const std::string& prefix); static void Replace(std::string& str, size_t size, const std::string& other, size_t pos); static bool IsDigit(char c); static int GetNumberOfDigits(int n); static int GetNumberOfDigits(int64_t n); static int64_t Pow10(int pow); // Converts a binary string to an int32_t ie "10100111001" to 1337 static int BinStrToInt(const std::string& str); // Converts a binary string to an int64_t ie "10100111001" to 1337 static int64_t BinStrToInt64(const std::string& str); template static T Sum(const S& container) { return std::accumulate(container.begin(), container.end(), (T)0, std::plus()); } template static T Sum(const Container& container, S func) { return std::accumulate(container.begin(), container.end(), (T)0, func); } template static T Product(const S& container) { return std::accumulate(container.begin(), container.end(), (T)1, std::multiplies()); } template static T Product(const Container& container, S func) { return std::accumulate(container.begin(), container.end(), (T)1, func); } template static typename Container::value_type Min(const Container& container) { if (container.size() == 0) { std::cout << "Min: Container empty" << std::endl; return typename Container::value_type{}; } typename Container::value_type min = *container.begin(); for (auto&& element : container) { min = std::min(min, element); } return min; } template static typename Container::value_type Max(const Container& container) { if (container.size() == 0) { std::cout << "Min: Container empty" << std::endl; return typename Container::value_type{}; } typename Container::value_type max = *container.begin(); for (auto&& element : container) { max = std::max(max, element); } return max; } template static T Sign(const T& t) { return t > 0 ? 1 : t < 0 ? -1 : 0; } template static std::set GetUnion(const std::vector>& list) { std::set uniq; for (auto l : list) { for (auto t : l) { uniq.emplace(t); } } return uniq; } template static std::set GetIntersection(const std::vector>& list) { std::set intersection = list[0]; for (size_t i = 1; i < list.size(); i++) { for (auto it = intersection.begin(); it != intersection.end();) { if (list[i].find(*it) == list[i].end()) it = intersection.erase(it); else it++; } } return intersection; } template static std::set GetIntersection(const std::set& s1, const std::set& s2) { std::set intersection = s1; for (auto it = intersection.begin(); it != intersection.end();) { if (s2.find(*it) == s2.end()) it = intersection.erase(it); else it++; } return intersection; } template static bool IsSubset(const std::set& smallSet, const std::set& bigSet) { if (bigSet.size() < smallSet.size()) return false; for (auto&& t : smallSet) { if (bigSet.find(t) == bigSet.end()) return false; } return true; } // Input - Puzzle input // State - initial state of the path finder // Heuristic - int(Input, State) // Given a state, what is the minimum cost to get to the goal // state // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost // to get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static int AStar(const Input& input, const std::vector& initial, Heuristic heuristic, Branch branch, Goal goal) { std::map visited; std::multimap> open; for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair{0, initial[i]}); while (!open.empty()) { auto it = open.begin(); if (goal(input, it->second.second)) { return it->second.first; } int stateCost = it->second.first; for (auto& newState : branch(input, it->second.second)) { auto it = visited.find(newState.second); int cost = stateCost + newState.first; if (it == visited.end() || it->second > cost) { open.emplace(heuristic(input, newState.second) + cost, std::pair{cost, newState.second}); visited.emplace(newState.second, cost); } } open.erase(open.begin()); } if (traceErrors) std::cout << "No solution found" << std::endl; return 0; } // Input - Puzzle input // State - initial state of the path finder // Heuristic - int(Input, State) // Given a state, what is the minimum cost to get to the goal // state // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost // to get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static std::pair> AStarPath( const Input& input, const std::vector& initial, Heuristic heuristic, Branch branch, Goal goal) { std::map visited; std::multimap>> open; for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair>{0, {initial[i]}}); while (!open.empty()) { const auto& [heuristicVal, state] = *open.begin(); const auto& [cost, path] = state; if (goal(input, path.back())) return {cost, path}; for (const auto& [stateCost, newState] : branch(input, path.back())) { auto it = visited.find(newState); int newCost = cost + stateCost; if (it == visited.end() || it->second > cost) { auto newPath = path; newPath.emplace_back(newState); open.emplace(heuristic(input, newState) + newCost, std::pair>{newCost, newPath}); visited.emplace(newState, newCost); } } open.erase(open.begin()); } if (traceErrors) std::cout << "No solution found" << std::endl; return {0, {}}; } // Input - Puzzle input // State - initial state of the path finder // Heuristic - int(Input, State) // Given a state, what is the minimum cost to get to the goal // state // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost // to get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static std::pair> AStarPath( const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal) { return AStarPath(input, std::vector{initial}, heuristic, branch, goal); } // Input - Puzzle input // State - State of the path finder // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost to // get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static std::pair> DijkstrasPath(const Input& input, const std::vector& initial, Branch branch, Goal goal) { return AStarPath(input, initial, [](const Input&, const State&) { return 0; }, branch, goal); } // Input - Puzzle input // State - State of the path finder // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost to // get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static std::pair> DijkstrasPath(const Input& input, const State& initial, Branch branch, Goal goal) { return AStarPath(input, std::vector{initial}, [](const Input&, const State&) { return 0; }, branch, goal); } // Input - Puzzle input // State - State of the path finder // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost to // get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static std::set DijkstrasAllVisited(const Input& input, const State& initial, Branch branch, Goal goal) { std::set totalVisited; int bestCost = std::numeric_limits::max(); std::map visited; std::multimap> open; open.emplace(0, std::vector{initial}); while (!open.empty()) { const auto& [cost, path] = *open.begin(); if (cost > bestCost) { open.erase(open.begin()); continue; } if (goal(input, path.back())) { bestCost = cost; for (const auto& state : path) { totalVisited.emplace(state); } open.erase(open.begin()); continue; } for (const auto& [newStateCost, newState] : branch(input, path.back())) { int newCost = cost + newStateCost; auto it = visited.find(newState); if (it == visited.end() || it->second >= newCost) { auto cpy = path; cpy.emplace_back(newState); open.emplace(newCost, cpy); visited.emplace(newState, newCost); } } open.erase(open.begin()); } return totalVisited; } // Input - Puzzle input // State - initial state of the path finder // Heuristic - int(Input, State) // Given a state, what is the minimum cost to get to the goal // state // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost // to get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static int AStar(const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal) { return AStar(input, std::vector{initial}, heuristic, branch, goal); } // Input - Puzzle input // State - State of the path finder // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost to // get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static int Dijkstras(const Input& input, const std::vector& initial, Branch branch, Goal goal) { return AStar(input, initial, [](const Input&, const State&) { return 0; }, branch, goal); } // Input - Puzzle input // State - State of the path finder // Branch - std::vector>(Input, State); // Given a state, what are the possible next states and the cost to // get to it // Goal - bool(Input, State) // Given a state, are we in the goal state template static int Dijkstras(const Input& input, const State& initial, Branch branch, Goal goal) { return AStar(input, std::vector{initial}, [](const Input&, const State&) { return 0; }, branch, goal); } template static std::ostream& Print(std::ostream& stream, const Container& container) { stream << '['; int i = 0; for (auto it = container.begin(); it != container.end(); ++it) { if (i != 0) stream << ", "; stream << *it; i++; } stream << ']'; return stream; } static std::string Repeat(const std::string& str, int count); template static std::vector Repeat(const std::vector& vec, int count) { std::vector ret; for (int i = 0; i < count; i++) { for (const auto& t : vec) { ret.emplace_back(t); } } return ret; } static std::vector GetAllRegexMatches(const std::string& str, const std::string& regex); static std::vector GetNeighborDirections(); static Index2D GetDirection(char c); // Solve for N: // remainder1 = N % mod1 // remainder2 = N % mod2 static int64_t ChineseRemainderTheoremTwo(int64_t mod1, int64_t mod2, int64_t remainder1, int64_t remainder2); // Solve for N: // remainders = N % mods static int64_t ChineseRemainderTheorem(const std::vector& mods, const std::vector& remainders); // Solve for N: // remainders = (start + N) % mods static int64_t ChineseRemainderTheorem(const std::vector& mods, std::vector remainders, const std::vector& starts); static uint64_t FastExponentiation(uint64_t start, uint64_t multiplication, uint64_t power, uint64_t mod); // Input - Puzzle input // Function - bool(int64_t); // Given index, return whether the index should increase next // iteration // // Returns the last index where Function returns true, or -1 if it never // returns true template static int64_t BinarySearch(const Input& input, int64_t min, int64_t max, Function func) { if (min >= max) { if (func(input, min)) return min; else return min - 1; } int i = min + (max - min) / 2; if (func(input, i)) return BinarySearch(input, i + 1, max, func); else return BinarySearch(input, min, i - 1, func); } static int64_t ManhattanDistance(const Index2D& from, const Index2D& to); static void DisableTraceErrors(); static void ResetTraceErrors(); private: template static Value TSPCalculatePath(const Graph& graph, const std::vector& path, bool returnToStart) { int cost = 0; for (int i = 0; i < path.size() - 1; i++) { cost += graph.GetEdge(path[i], path[i + 1]); } if (returnToStart) cost += graph.GetEdge(path.back(), path.front()); return cost; } template static int TSP(const Graph& graph, const std::set& nodesLeft, const std::vector& order, bool returnToStart) { if (nodesLeft.empty()) return TSPCalculatePath(graph, order, returnToStart); bool hasSet = false; Value tsp; for (auto& node : nodesLeft) { std::set newNodesLeft = nodesLeft; newNodesLeft.erase(node); std::vector newOrder = order; newOrder.emplace_back(node); if (hasSet) tsp = std::min(tsp, TSP(graph, newNodesLeft, newOrder, returnToStart)); else tsp = TSP(graph, newNodesLeft, newOrder, returnToStart); hasSet = true; } return tsp; } public: // Key - Node type of the graph // Value - Edge type of the graph template static int TSP(const Graph& graph, const Key& start, bool returnToStart) { std::set nodes = graph.GetNodes(); nodes.erase(nodes.find(start)); return TSP(graph, nodes, std::vector{start}, returnToStart); } // Key - Node type of the graph // Value - Edge type of the graph template static int TSP(const Graph& graph, bool returnToStart) { return TSP(graph, graph.GetNodes(), std::vector{}, returnToStart); } static bool traceErrors; }; template static std::ostream& operator<<(std::ostream& stream, const std::vector& container) { return Helper::Print(stream, container); } template static std::ostream& operator<<(std::ostream& stream, const std::deque& container) { return Helper::Print(stream, container); } template static std::ostream& operator<<(std::ostream& stream, const std::set& container) { return Helper::Print(stream, container); } template static std::ostream& operator<<(std::ostream& stream, const std::array& container) { return Helper::Print(stream, container); } template static std::ostream& operator<<(std::ostream& stream, const std::map& container) { stream << "{"; int i = 0; for (const auto& [t, s] : container) { if (i != 0) stream << ", "; stream << t << " => " << s; i++; } stream << "}"; return stream; } template static std::ostream& operator<<(std::ostream& stream, const std::multimap& container) { stream << "{"; int i = 0; for (const auto& [t, s] : container) { if (i != 0) stream << ", "; stream << t << " => " << s; i++; } stream << "}"; return stream; } template static std::ostream& operator<<(std::ostream& stream, const std::unordered_map& container) { stream << "{"; int i = 0; for (const auto& [t, s] : container) { if (i != 0) stream << ", "; stream << t << " => " << s; i++; } stream << "}"; return stream; } template static std::ostream& operator<<(std::ostream& stream, const std::pair& pair) { return stream << "(" << pair.first << ", " << pair.second << ")"; } template static std::ostream& operator<<(std::ostream& stream, const std::variant& variant) { std::visit([&stream](const auto& value) { stream << value; }, variant); return stream; }