From d0bde4fa0850c0f497569dc476e1486b41ef16ed Mon Sep 17 00:00:00 2001 From: Frederik Tobner Date: Sat, 7 Dec 2024 21:53:57 +0100 Subject: [PATCH 1/3] cleanup --- day_5/lib/parser.hpp | 2 +- day_7/lib/equation_operator.hpp | 26 +++----- day_7/lib/equation_puzzle.hpp | 6 +- day_7/lib/equation_puzzle_parser.hpp | 80 ++++++++++++++++------- day_7/lib/equation_puzzle_solver.hpp | 96 +++++++++++++++++----------- day_7/lib/equation_result.hpp | 28 +++++++- day_7/src/main.cpp | 26 +++++--- shared/src/parsing_rules.hpp | 15 ++--- 8 files changed, 179 insertions(+), 100 deletions(-) diff --git a/day_5/lib/parser.hpp b/day_5/lib/parser.hpp index 7214221..b246bd5 100644 --- a/day_5/lib/parser.hpp +++ b/day_5/lib/parser.hpp @@ -13,7 +13,7 @@ namespace aoc::day_5 { /*! * @brief Splits input into two parts separated by double newline - * @param input The raw input string to parse + * @param input The getRawValue input string to parse * @return A pair of string_views representing the two parts, or an error if parsing fails */ auto parse_input(std::string_view input) diff --git a/day_7/lib/equation_operator.hpp b/day_7/lib/equation_operator.hpp index 6583ee8..5d1d5b3 100644 --- a/day_7/lib/equation_operator.hpp +++ b/day_7/lib/equation_operator.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "equation_result.hpp" @@ -16,37 +17,30 @@ enum class operation_type { /// Represents an operator that can be applied to equation results /// @tparam T The numeric type used for calculations (defaults to size_t) -template struct equation_operator_t { +template + requires std::integral +struct equation_operator_t { operation_type type; ///< Type of the operation std::string_view name; ///< String representation of the operator equation_result (*apply)(equation_result, equation_result); ///< Function implementing the operation -}; +}; // namespace aoc::day_7 /// Available operators for type T /// @tparam T The numeric type used for calculations template + requires std::integral inline constexpr equation_operator_t equationOperators_t[] = { {operation_type::add, " + ", [](equation_result a, equation_result b) { return a + b; }}, {operation_type::multiply, " * ", [](equation_result a, equation_result b) { return a * b; }}, {operation_type::concat, " || ", [](equation_result a, equation_result b) { return a | b; }}}; -/// Size constant for basic operators (add, multiply) -/// @tparam T The numeric type used for calculations -template inline constexpr size_t BASIC_OPERATORS_SIZE = 2; - -/// Size constant for all operators (add, multiply, concat) -/// @tparam T The numeric type used for calculations -template inline constexpr size_t ALL_OPERATORS_SIZE = 3; - -/// Array type for storing operator pointers -/// @tparam T The numeric type used for calculations -template using operator_array_t = std::array const *, 3>; - template -inline constexpr operator_array_t ALL_OPERATORS_T{&equationOperators_t[0], &equationOperators_t[1], - &equationOperators_t[2]}; + requires std::integral +inline constexpr std::array const *, 3> ALL_OPERATORS_T{ + &equationOperators_t[0], &equationOperators_t[1], &equationOperators_t[2]}; template + requires std::integral inline constexpr auto BASIC_OPERATORS_T = std::array{ALL_OPERATORS_T[0], ALL_OPERATORS_T[1]}; } // namespace aoc::day_7 \ No newline at end of file diff --git a/day_7/lib/equation_puzzle.hpp b/day_7/lib/equation_puzzle.hpp index 2fdd6e5..b682a18 100644 --- a/day_7/lib/equation_puzzle.hpp +++ b/day_7/lib/equation_puzzle.hpp @@ -1,15 +1,19 @@ #pragma once #include "equation_result.hpp" +#include #include #include + namespace aoc::day_7 { /// Represents a puzzle consisting of a target result and a sequence of values /// that need to be combined using operators to reach that result /// @tparam T The numeric type used for the result value (defaults to size_t) -template struct equation_puzzle { +template + requires std::integral +struct equation_puzzle { equation_result result; ///< The target result that needs to be achieved std::vector values; ///< The sequence of values that need to be combined }; diff --git a/day_7/lib/equation_puzzle_parser.hpp b/day_7/lib/equation_puzzle_parser.hpp index a138125..e5c8118 100644 --- a/day_7/lib/equation_puzzle_parser.hpp +++ b/day_7/lib/equation_puzzle_parser.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -14,6 +15,14 @@ namespace aoc::day_7 { +/// @brief Parses a single puzzle from a string input +/// @tparam T The numeric type to use for puzzle results (defaults to size_t) +/// @param line String containing the puzzle in the format "result: value1 value2 ..." +/// @return Parsed equation puzzle +template + requires std::integral +[[nodiscard]] auto parsePuzzle(std::string_view line) -> equation_puzzle; + /// Parses puzzles from a string input into equation puzzles /// @tparam T The numeric type to use for puzzle results (defaults to size_t) /// @param input String containing puzzles in the format "result: value1 value2 ..." @@ -21,30 +30,53 @@ namespace aoc::day_7 { /// Each puzzle line should be in the format: "target_number: value1 value2 value3 ..." /// Lines are separated by newlines, values are separated by spaces template -auto parsePuzzles(std::string_view input) -> std::vector> { - return input | std::views::split('\n') | std::views::transform([](auto && line) { - if (line.back() == '\r') { - return std::string_view{line.begin(), line.end() - 1}; - } - return std::string_view{line.begin(), line.end()}; - }) | - std::views::filter([](auto && line) { return !line.empty(); }) | std::views::transform([](auto && line) { - auto colon_pos = line.find(':'); - auto numberParser = aoc::parser::rules::parse_number(std::string(line.substr(0, colon_pos))); - auto result = aoc::day_7::equation_result{*numberParser}; - auto values_range = line.substr(colon_pos + 1); - - auto values = values_range | std::views::split(' ') | std::views::transform([](auto && value) { - return std::string_view{value.begin(), value.end()}; - }) | - std::views::filter([](auto && value) { return !value.empty(); }) | - std::views::transform( - [](auto && value) { return static_cast(std::stoul(std::string(value))); }) | - aoc::ranges::to>; - - return equation_puzzle{result, std::move(values)}; - }) | - aoc::ranges::to>>; + requires std::integral +[[nodiscard]] auto parsePuzzles(std::string_view input) + -> std::expected>, std::error_code>; + +template + requires std::integral +[[nodiscard]] auto parsePuzzles(std::string_view input) + -> std::expected>, std::error_code> { + try { + return input | std::views::split('\n') | std::views::transform([](auto && line) { + if (line.back() == '\r') { + return std::string_view{line.begin(), line.end() - 1}; + } + return std::string_view{line.begin(), line.end()}; + }) | + std::views::filter([](auto && line) { return !line.empty(); }) | + std::views::transform([](auto && line) { return parsePuzzle(line); }) | + aoc::ranges::to>>; + } catch (...) { + return std::unexpected(std::make_error_code(std::errc::invalid_argument)); + } +} + +template + requires std::integral +[[nodiscard]] auto parsePuzzle(std::string_view line) -> equation_puzzle { + auto colon_pos = line.find(':'); + std::expected parsedExpectedResult = + aoc::parser::rules::parse_number(std::string(line.substr(0, colon_pos))); + if (!parsedExpectedResult) { + throw std::invalid_argument("Failed to parse number"); + } + return equation_puzzle{equation_result{*parsedExpectedResult}, + std::move(line.substr(colon_pos + 1) | std::views::split(' ') | + std::views::transform([](auto && value) { + return std::string_view{value.begin(), value.end()}; + }) | + std::views::filter([](auto && value) { return !value.empty(); }) | + std::views::transform([](auto && value) { + std::expected parsedValue = + aoc::parser::rules::parse_number(value); + if (!parsedValue) { + throw std::invalid_argument("Failed to parse number"); + } + return *parsedValue; + }) | + aoc::ranges::to>)}; } } // namespace aoc::day_7 \ No newline at end of file diff --git a/day_7/lib/equation_puzzle_solver.hpp b/day_7/lib/equation_puzzle_solver.hpp index c0c2548..a8ce741 100644 --- a/day_7/lib/equation_puzzle_solver.hpp +++ b/day_7/lib/equation_puzzle_solver.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -10,6 +11,30 @@ namespace aoc::day_7 { +/// Checks if a puzzle is solvable using the given operators +/// @tparam T The numeric type used in the equation (defaults to size_t) +/// @param eq The puzzle to check +/// @param allowedOperators Set of operators that can be used (defaults to all operators) +/// @return true if the puzzle can be solved with the given operators +/// @throws std::system_error if the puzzle is invalid +template + requires std::integral +[[nodiscard]] auto isSolvable(equation_puzzle const & eq, + std::span const * const> allowedOperators = ALL_OPERATORS_T) + -> bool; + +/// Attempts to solve an equation puzzle using the given operators +/// @tparam T The numeric type used in the equation (defaults to size_t) +/// @param equation The puzzle to solve +/// @param allowedOperators Set of operators that can be used (defaults to all operators) +/// @return A sequence of operators that solves the puzzle, or an error if no solution exists +template + requires std::integral +[[nodiscard]] auto +solveEquationPuzzle(equation_puzzle const & equation, + std::span const * const> allowedOperators = ALL_OPERATORS_T) + -> std::expected const *>, std::error_code>; + /// Recursively tries different operator combinations to solve the equation /// @tparam T The numeric type used in the equation (defaults to size_t) /// @param equation The puzzle to solve @@ -20,35 +45,30 @@ namespace aoc::day_7 { /// @param allowedOperators Set of operators that can be used /// @return true if a valid solution was found template + requires std::integral [[nodiscard]] auto tryOperatorCombinations(equation_puzzle const & equation, size_t depth, size_t maxDepth, std::vector const *> & current, equation_result currentResult, - std::span const * const> allowedOperators) -> bool { - if (depth == maxDepth) { - return currentResult == equation.result; - } - - for (auto const * op : allowedOperators) { - current.push_back(op); - equation_result newResult = op->apply(currentResult, equation_result{equation.values[depth + 1]}); + std::span const * const> allowedOperators) -> bool; - if (tryOperatorCombinations(equation, depth + 1, maxDepth, current, newResult, allowedOperators)) { - return true; - } - current.pop_back(); +template + requires std::integral +[[nodiscard]] auto isSolvable(equation_puzzle const & eq, + std::span const * const> allowedOperators) -> bool { + auto result = solveEquationPuzzle(eq, allowedOperators); + if (result) { + return true; } - return false; + if (result.error() == std::errc::result_out_of_range) { + return false; + } + throw std::system_error(result.error()); } -/// Attempts to solve an equation puzzle using the given operators -/// @tparam T The numeric type used in the equation (defaults to size_t) -/// @param equation The puzzle to solve -/// @param allowedOperators Set of operators that can be used -/// @return A sequence of operators that solves the puzzle, or an error if no solution exists template -[[nodiscard]] auto -solveEquationPuzzle(equation_puzzle const & equation, - std::span const * const> allowedOperators = ALL_OPERATORS_T) + requires std::integral +[[nodiscard]] auto solveEquationPuzzle(equation_puzzle const & equation, + std::span const * const> allowedOperators) -> std::expected const *>, std::error_code> { if (equation.values.empty()) { return std::unexpected(std::make_error_code(std::errc::invalid_argument)); @@ -65,24 +85,28 @@ solveEquationPuzzle(equation_puzzle const & equation, return std::unexpected(std::make_error_code(std::errc::result_out_of_range)); } -/// Checks if a puzzle is solvable using the given operators -/// @tparam T The numeric type used in the equation (defaults to size_t) -/// @param eq The puzzle to check -/// @param allowedOperators Set of operators that can be used -/// @return true if the puzzle can be solved with the given operators -/// @throws std::system_error if the puzzle is invalid template -[[nodiscard]] auto isSolvable(equation_puzzle const & eq, - std::span const * const> allowedOperators = ALL_OPERATORS_T) - -> bool { - auto result = solveEquationPuzzle(eq, allowedOperators); - if (result) { - return true; - } - if (result.error() == std::errc::result_out_of_range) { + requires std::integral +[[nodiscard]] auto tryOperatorCombinations(equation_puzzle const & equation, size_t depth, size_t maxDepth, + std::vector const *> & current, + equation_result currentResult, + std::span const * const> allowedOperators) -> bool { + if (currentResult > equation.result) { return false; } - throw std::system_error(result.error()); + if (depth == maxDepth) { + return currentResult == equation.result; + } + + for (auto const * op : allowedOperators) { + current.push_back(op); + equation_result newResult = op->apply(currentResult, equation_result{equation.values[depth + 1]}); + if (tryOperatorCombinations(equation, depth + 1, maxDepth, current, newResult, allowedOperators)) { + return true; + } + current.pop_back(); + } + return false; } } // namespace aoc::day_7 \ No newline at end of file diff --git a/day_7/lib/equation_result.hpp b/day_7/lib/equation_result.hpp index 1102923..b8e521f 100644 --- a/day_7/lib/equation_result.hpp +++ b/day_7/lib/equation_result.hpp @@ -1,12 +1,15 @@ #pragma once +#include #include namespace aoc::day_7 { /// Represents a wrapped numeric value with custom arithmetic operations /// @tparam T The underlying numeric type used for calculations (defaults to size_t) -template class equation_result { +template + requires std::integral +class equation_result { T val_; public: @@ -17,7 +20,7 @@ template class equation_result { /// Gets the raw underlying value /// @return The wrapped value - [[nodiscard]] constexpr T raw() const { + [[nodiscard]] constexpr T getRawValue() const { return val_; } @@ -51,9 +54,28 @@ template class equation_result { return equation_result{lhs.val_ * multiplier + rhs.val_}; } - /// Compares two equation results for equality + /// @brief Compares two equation results for equality + /// @param lhs The left-hand operand + /// @param rhs The right-hand operand + /// @return true if the two equation results are equal friend bool operator==(equation_result lhs, equation_result rhs) { return lhs.val_ == rhs.val_; } + + /// @brief Compares two equation results and checks if the left-hand operand is less than the right-hand operand + /// @param lhs The left-hand operand + /// @param rhs The right-hand operand + /// @return true if the left-hand operand is less than the right-hand operand + friend bool operator<(equation_result lhs, equation_result rhs) { + return lhs.val_ < rhs.val_; + } + + /// @brief Compares two equation results and checks if the left-hand operand is greater than the right-hand operand + /// @param lhs The left-hand operand + /// @param rhs The right-hand operand + /// @return true if the left-hand operand is greater than the right-hand operand + friend bool operator>(equation_result lhs, equation_result rhs) { + return lhs.val_ > rhs.val_; + } }; } // namespace aoc::day_7 \ No newline at end of file diff --git a/day_7/src/main.cpp b/day_7/src/main.cpp index 61bf8b5..902c865 100644 --- a/day_7/src/main.cpp +++ b/day_7/src/main.cpp @@ -25,25 +25,33 @@ auto main(int argc, char const ** argv) -> int { return aoc::EXIT_CODE_IO_ERROR; } - auto basic_ops = std::span const * const, aoc::day_7::BASIC_OPERATORS_SIZE<>>{ - aoc::day_7::BASIC_OPERATORS_T<>.data(), std::size(aoc::day_7::BASIC_OPERATORS_T<>)}; + std::expected>, std::error_code> puzzles = + aoc::day_7::parsePuzzles<>(*input); - auto all_ops = std::span const * const, aoc::day_7::ALL_OPERATORS_SIZE<>>{ - aoc::day_7::ALL_OPERATORS_T<>.data(), std::size(aoc::day_7::ALL_OPERATORS_T<>)}; + if (!puzzles) [[unlikely]] { + std::println(stderr, "Failed to parse puzzles: {}", puzzles.error().message()); + return aoc::EXIT_CODE_DATA_ERROR; + } auto calculate_sum = [](auto ops) { return [ops](auto const & puzzle) { - return aoc::day_7::isSolvable(puzzle, ops) ? puzzle.result.raw() : 0; + return aoc::day_7::isSolvable(puzzle, ops) ? puzzle.result.getRawValue() : 0; }; }; - std::vector> puzzles = aoc::day_7::parsePuzzles<>(*input); + auto basic_operators = + std::span const * const, std::size(aoc::day_7::BASIC_OPERATORS_T<>)>{ + aoc::day_7::BASIC_OPERATORS_T<>.data(), std::size(aoc::day_7::BASIC_OPERATORS_T<>)}; + + size_t part_1_sum = aoc::ranges::fold_left(*puzzles | std::views::transform(calculate_sum(basic_operators)), + size_t{0}, std::plus{}); - size_t part_1_sum = - aoc::ranges::fold_left(puzzles | std::views::transform(calculate_sum(basic_ops)), size_t{0}, std::plus{}); + auto all_operators = + std::span const * const, std::size(aoc::day_7::ALL_OPERATORS_T<>)>{ + aoc::day_7::ALL_OPERATORS_T<>.data(), std::size(aoc::day_7::ALL_OPERATORS_T<>)}; size_t part_2_sum = - aoc::ranges::fold_left(puzzles | std::views::transform(calculate_sum(all_ops)), size_t{0}, std::plus{}); + aoc::ranges::fold_left(*puzzles | std::views::transform(calculate_sum(all_operators)), size_t{0}, std::plus{}); std::println("Sum of all results: {} using basic operators", part_1_sum); std::println("Sum of all results: {} using all operators", part_2_sum); diff --git a/shared/src/parsing_rules.hpp b/shared/src/parsing_rules.hpp index f4fe7f7..5bdd2b5 100644 --- a/shared/src/parsing_rules.hpp +++ b/shared/src/parsing_rules.hpp @@ -9,26 +9,21 @@ #include #include - namespace aoc::parser::rules { -// Concept for types that can be parsed from string -template -concept IsNumber = std::is_arithmetic_v; - namespace detail { template auto parse_unsigned(std::string_view token) -> std::expected { T value; - auto [ptr, ec] = std::from_chars(token.data(), token.data() + token.size(), value); - if (ec != std::errc()) { - return std::unexpected(std::make_error_code(ec)); + auto [ptr, errorCode] = std::from_chars(token.data(), token.data() + token.size(), value); + if (errorCode != std::errc()) { + return std::unexpected(std::make_error_code(errorCode)); } return value; } template auto parse_signed(std::string_view token) -> std::expected { try { - if constexpr (sizeof(T) <= sizeof(long long)) { + if constexpr (std::is_integral_v) { return static_cast(std::stoll(std::string(token))); } else { return static_cast(std::stold(std::string(token))); @@ -40,7 +35,7 @@ template auto parse_signed(std::string_view token) -> std::expected } // namespace detail template - requires IsNumber + requires std::is_arithmetic_v auto parse_number(std::string_view token) -> std::expected { if constexpr (std::is_unsigned_v) { return detail::parse_unsigned(token); From 19e7be565ec70e608ce41aa2c550b86e31ff4e6c Mon Sep 17 00:00:00 2001 From: Frederik Tobner Date: Sat, 7 Dec 2024 23:22:41 +0100 Subject: [PATCH 2/3] cleanup --- day_1/benchmark/main.cpp | 59 ++------------------------------- day_1/src/main.cpp | 2 +- day_2/src/main.cpp | 2 +- day_5/lib/parser.hpp | 2 +- day_5/src/main.cpp | 2 +- day_6/src/main.cpp | 1 - day_7/lib/equation_operator.hpp | 12 +++++++ day_7/src/main.cpp | 17 +++------- shared/CMakeLists.txt | 3 ++ shared/benchmark/CMakeLists.txt | 10 ++++++ shared/benchmark/main.cpp | 55 ++++++++++++++++++++++++++++++ 11 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 shared/benchmark/CMakeLists.txt create mode 100644 shared/benchmark/main.cpp diff --git a/day_1/benchmark/main.cpp b/day_1/benchmark/main.cpp index e664bb0..25acac8 100644 --- a/day_1/benchmark/main.cpp +++ b/day_1/benchmark/main.cpp @@ -10,14 +10,6 @@ #include "../../shared/src/column_splitter.hpp" #include "../../shared/src/parsing_rules.hpp" -static std::string createInput(size_t size) { - std::string input; - for (int i = 0; i < size; i++) { - input += std::to_string(rand() % size) + " " + std::to_string(rand() % size) + "\n"; - } - return input; -} - static std::multiset createNewSet(size_t size) { std::multiset newSet; for (int i = 0; i < size; i++) { @@ -39,7 +31,7 @@ BENCHMARK(BM_CalulculateDistance) ->Unit(benchmark::kMillisecond) ->UseRealTime() ->Threads(std::thread::hardware_concurrency()) - ->Iterations(10); + ->Iterations(100); static void BM_CalulculateSimilarity(benchmark::State & state) { std::multiset leftList = createNewSet(10000); @@ -54,53 +46,6 @@ BENCHMARK(BM_CalulculateSimilarity) ->Unit(benchmark::kMillisecond) ->UseRealTime() ->Threads(std::thread::hardware_concurrency()) - ->Iterations(20); - -static void BM_ParseLinesInParrallel(benchmark::State & state) { - auto input = createInput(30000); - - for (auto _ : state) { - state.PauseTiming(); - auto result = aoc::splitter::columnbased::split(input, aoc::parser::rules::parse_number, - std::execution::par_unseq); - state.ResumeTiming(); - - benchmark::DoNotOptimize(result); - benchmark::ClobberMemory(); - } - - state.SetItemsProcessed(state.iterations() * input.size()); -} - -BENCHMARK(BM_ParseLinesInParrallel) - ->Unit(benchmark::kMillisecond) - ->UseRealTime() - ->Threads(std::thread::hardware_concurrency()) - ->Iterations(1); - -static void BM_ParseLinesInSequence(benchmark::State & state) { - auto input = createInput(30000); - volatile size_t size = input.size(); - - for (auto _ : state) { - state.PauseTiming(); - auto result = aoc::splitter::columnbased::split( - input, aoc::parser::rules::parse_number, std::execution::seq); - state.ResumeTiming(); - if (!result) { - state.SkipWithError("Parser error occurred"); - break; - } - benchmark::DoNotOptimize(result); - benchmark::ClobberMemory(); - } - - state.SetItemsProcessed(state.iterations() * input.size()); -} - -BENCHMARK(BM_ParseLinesInSequence) - ->Unit(benchmark::kMillisecond) - ->Threads(std::thread::hardware_concurrency()) - ->Iterations(1); + ->Iterations(100); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/day_1/src/main.cpp b/day_1/src/main.cpp index 40df9c1..9d404c6 100644 --- a/day_1/src/main.cpp +++ b/day_1/src/main.cpp @@ -26,7 +26,7 @@ auto main(int argc, char const ** argv) -> int { } auto tokens = aoc::splitter::columnbased::split( - *input, aoc::parser::rules::parse_number, std::execution::par_unseq); + *input, aoc::parser::rules::parse_number, std::execution::par); if (!tokens) [[unlikely]] { std::println(stderr, "Failed to parse input: {}", tokens.error().message()); return aoc::EXIT_CODE_DATA_ERROR; diff --git a/day_2/src/main.cpp b/day_2/src/main.cpp index 419879f..e18b9c2 100644 --- a/day_2/src/main.cpp +++ b/day_2/src/main.cpp @@ -18,7 +18,7 @@ auto main(int argc, char const ** argv) -> int { } std::expected>, std::error_code> parsed = aoc::splitter::linebased::split( - *input, aoc::parser::rules::parse_number, std::execution::par_unseq); + *input, aoc::parser::rules::parse_number, std::execution::par); if (!parsed) [[unlikely]] { std::println(stderr, "Failed to parse input: {}", parsed.error().message()); return aoc::EXIT_CODE_DATA_ERROR; diff --git a/day_5/lib/parser.hpp b/day_5/lib/parser.hpp index b246bd5..7214221 100644 --- a/day_5/lib/parser.hpp +++ b/day_5/lib/parser.hpp @@ -13,7 +13,7 @@ namespace aoc::day_5 { /*! * @brief Splits input into two parts separated by double newline - * @param input The getRawValue input string to parse + * @param input The raw input string to parse * @return A pair of string_views representing the two parts, or an error if parsing fails */ auto parse_input(std::string_view input) diff --git a/day_5/src/main.cpp b/day_5/src/main.cpp index 7a0dca6..ac9e0bb 100644 --- a/day_5/src/main.cpp +++ b/day_5/src/main.cpp @@ -44,7 +44,7 @@ auto main(int argc, char const ** argv) -> int { std::expected>, std::error_code> parsed_update_input = aoc::splitter::linebased::split(updates_input, aoc::parser::rules::parse_number, - std::execution::par_unseq, ','); + std::execution::par, ','); if (!parsed_update_input) { std::println(stderr, "Failed to parse updates: {}", parsed_update_input.error().message()); diff --git a/day_6/src/main.cpp b/day_6/src/main.cpp index 0808724..6aec688 100644 --- a/day_6/src/main.cpp +++ b/day_6/src/main.cpp @@ -11,7 +11,6 @@ #include "../lib/grid_parser.hpp" #include "../lib/puzzle_map.hpp" - auto main(int argc, char const ** argv) -> int { std::expected input = aoc::file_operations::read("input.txt"); if (!input) [[unlikely]] { diff --git a/day_7/lib/equation_operator.hpp b/day_7/lib/equation_operator.hpp index 5d1d5b3..f309303 100644 --- a/day_7/lib/equation_operator.hpp +++ b/day_7/lib/equation_operator.hpp @@ -39,8 +39,20 @@ template inline constexpr std::array const *, 3> ALL_OPERATORS_T{ &equationOperators_t[0], &equationOperators_t[1], &equationOperators_t[2]}; +template + requires std::integral +inline constexpr auto ALL_OPERATORS = + std::span const * const, std::size(aoc::day_7::ALL_OPERATORS_T)>{ + aoc::day_7::ALL_OPERATORS_T.data(), std::size(aoc::day_7::ALL_OPERATORS_T)}; + template requires std::integral inline constexpr auto BASIC_OPERATORS_T = std::array{ALL_OPERATORS_T[0], ALL_OPERATORS_T[1]}; +template + requires std::integral +inline constexpr auto BASIC_OPERATORS = + std::span const * const, std::size(aoc::day_7::BASIC_OPERATORS_T)>{ + aoc::day_7::BASIC_OPERATORS_T.data(), std::size(aoc::day_7::BASIC_OPERATORS_T)}; + } // namespace aoc::day_7 \ No newline at end of file diff --git a/day_7/src/main.cpp b/day_7/src/main.cpp index 902c865..f7b4fab 100644 --- a/day_7/src/main.cpp +++ b/day_7/src/main.cpp @@ -39,19 +39,10 @@ auto main(int argc, char const ** argv) -> int { }; }; - auto basic_operators = - std::span const * const, std::size(aoc::day_7::BASIC_OPERATORS_T<>)>{ - aoc::day_7::BASIC_OPERATORS_T<>.data(), std::size(aoc::day_7::BASIC_OPERATORS_T<>)}; - - size_t part_1_sum = aoc::ranges::fold_left(*puzzles | std::views::transform(calculate_sum(basic_operators)), - size_t{0}, std::plus{}); - - auto all_operators = - std::span const * const, std::size(aoc::day_7::ALL_OPERATORS_T<>)>{ - aoc::day_7::ALL_OPERATORS_T<>.data(), std::size(aoc::day_7::ALL_OPERATORS_T<>)}; - - size_t part_2_sum = - aoc::ranges::fold_left(*puzzles | std::views::transform(calculate_sum(all_operators)), size_t{0}, std::plus{}); + size_t part_1_sum = aoc::ranges::fold_left( + *puzzles | std::views::transform(calculate_sum(aoc::day_7::BASIC_OPERATORS<>)), size_t{0}, std::plus{}); + size_t part_2_sum = aoc::ranges::fold_left( + *puzzles | std::views::transform(calculate_sum(aoc::day_7::ALL_OPERATORS<>)), size_t{0}, std::plus{}); std::println("Sum of all results: {} using basic operators", part_1_sum); std::println("Sum of all results: {} using all operators", part_2_sum); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 3ccd38f..3ca3ca9 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -2,3 +2,6 @@ add_subdirectory(src) if(BUILD_TESTS) add_subdirectory(test) endif() +if(BUILD_BENCHMARKS) + add_subdirectory(benchmark) +endif() \ No newline at end of file diff --git a/shared/benchmark/CMakeLists.txt b/shared/benchmark/CMakeLists.txt new file mode 100644 index 0000000..90a010d --- /dev/null +++ b/shared/benchmark/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB_RECURSE BENCHMARK_TESTS_SOURCES *.cpp) +if(NOT BENCHMARK_TESTS_SOURCES) + return() +endif() +set(PERFORMANCE_TESTS_SHARED_NAME ${PROJECT_NAME}_shared_performance_tests) +add_executable(${PERFORMANCE_TESTS_SHARED_NAME} ${BENCHMARK_TESTS_SOURCES}) +include(AddDependency) +add_github_dependency_by_tag(googletest google/googletest v1.15.2) +add_github_dependency_by_tag(benchmark google/benchmark v1.9.1) +target_link_libraries(${PERFORMANCE_TESTS_SHARED_NAME} ${PROJECT_NAME}_shared_lib benchmark::benchmark) \ No newline at end of file diff --git a/shared/benchmark/main.cpp b/shared/benchmark/main.cpp new file mode 100644 index 0000000..b644a01 --- /dev/null +++ b/shared/benchmark/main.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include + +#include "../src/column_splitter.hpp" +#include "../src/parsing_rules.hpp" + +static std::string createInput(size_t size) { + std::string input; + for (int i = 0; i < size; i++) { + input += std::to_string(rand() % size) + " " + std::to_string(rand() % size) + "\n"; + } + return input; +} + +static void BM_ParseLinesInSequence(benchmark::State & state) { + auto input = createInput(20000); + volatile size_t size = input.size(); + + for (auto _ : state) { + auto result = aoc::splitter::columnbased::split( + input, aoc::parser::rules::parse_number, std::execution::seq); + // benchmark::DoNotOptimize(result); + } + + state.SetItemsProcessed(state.iterations() * input.size()); +} + +BENCHMARK(BM_ParseLinesInSequence) + ->Unit(benchmark::kMillisecond) + ->Threads(std::thread::hardware_concurrency()) + ->Iterations(1); + +static void BM_ParseLinesInParrallel(benchmark::State & state) { + auto input = createInput(2000); + + for (auto _ : state) { + auto result = aoc::splitter::columnbased::split(input, aoc::parser::rules::parse_number, + std::execution::par); + // benchmark::DoNotOptimize(result); + } + + state.SetItemsProcessed(state.iterations() * input.size()); +} + +BENCHMARK(BM_ParseLinesInParrallel) + ->Unit(benchmark::kMillisecond) + ->UseRealTime() + ->Threads(std::thread::hardware_concurrency()) + ->Iterations(1); + +BENCHMARK_MAIN(); \ No newline at end of file From 50dbe768567b97e46ae789462413b4f095ee4426 Mon Sep 17 00:00:00 2001 From: Frederik Tobner Date: Sat, 7 Dec 2024 23:31:19 +0100 Subject: [PATCH 3/3] pls --- day_7/test/equation_operator_test.cpp | 6 +- day_7/test/equation_puzzle_parser_test.cpp | 70 +++++++++++++--------- day_7/test/equation_puzzle_solver_test.cpp | 34 ++++------- 3 files changed, 56 insertions(+), 54 deletions(-) diff --git a/day_7/test/equation_operator_test.cpp b/day_7/test/equation_operator_test.cpp index bac747a..498660a 100644 --- a/day_7/test/equation_operator_test.cpp +++ b/day_7/test/equation_operator_test.cpp @@ -11,7 +11,7 @@ TEST(EquationOperatorTest, Addition) { EXPECT_EQ(add_op.name, " + "); auto result = add_op.apply(equation_result<>{5}, equation_result<>{3}); - EXPECT_EQ(result.raw(), 8); + EXPECT_EQ(result.getRawValue(), 8); } TEST(EquationOperatorTest, Multiplication) { @@ -20,7 +20,7 @@ TEST(EquationOperatorTest, Multiplication) { EXPECT_EQ(mul_op.name, " * "); auto result = mul_op.apply(equation_result<>{5}, equation_result<>{3}); - EXPECT_EQ(result.raw(), 15); + EXPECT_EQ(result.getRawValue(), 15); } TEST(EquationOperatorTest, Concatenation) { @@ -29,7 +29,7 @@ TEST(EquationOperatorTest, Concatenation) { EXPECT_EQ(concat_op.name, " || "); auto result = concat_op.apply(equation_result<>{5}, equation_result<>{3}); - EXPECT_EQ(result.raw(), 53); + EXPECT_EQ(result.getRawValue(), 53); } TEST(EquationOperatorTest, BasicOperatorsContainAddAndMultiply) { diff --git a/day_7/test/equation_puzzle_parser_test.cpp b/day_7/test/equation_puzzle_parser_test.cpp index d8b362f..d443b09 100644 --- a/day_7/test/equation_puzzle_parser_test.cpp +++ b/day_7/test/equation_puzzle_parser_test.cpp @@ -7,55 +7,67 @@ using namespace aoc::day_7; TEST(EquationPuzzleParserTest, ParsesSinglePuzzle) { auto puzzles = parsePuzzles("42: 6 7"); - ASSERT_EQ(puzzles.size(), 1); - EXPECT_EQ(puzzles[0].result.raw(), 42); - ASSERT_EQ(puzzles[0].values.size(), 2); - EXPECT_EQ(puzzles[0].values[0], 6); - EXPECT_EQ(puzzles[0].values[1], 7); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + ASSERT_EQ(puzzleExtracted.size(), 1); + EXPECT_EQ(puzzleExtracted[0].result.getRawValue(), 42); + ASSERT_EQ(puzzleExtracted[0].values.size(), 2); + EXPECT_EQ(puzzleExtracted[0].values[0], 6); + EXPECT_EQ(puzzleExtracted[0].values[1], 7); } TEST(EquationPuzzleParserTest, ParsesMultiplePuzzles) { auto puzzles = parsePuzzles("15: 7 8\n42: 6 7"); - ASSERT_EQ(puzzles.size(), 2); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + ASSERT_EQ(puzzleExtracted.size(), 2); - EXPECT_EQ(puzzles[0].result.raw(), 15); - ASSERT_EQ(puzzles[0].values.size(), 2); - EXPECT_EQ(puzzles[0].values[0], 7); - EXPECT_EQ(puzzles[0].values[1], 8); + EXPECT_EQ(puzzleExtracted[0].result.getRawValue(), 15); + ASSERT_EQ(puzzleExtracted[0].values.size(), 2); + EXPECT_EQ(puzzleExtracted[0].values[0], 7); + EXPECT_EQ(puzzleExtracted[0].values[1], 8); - EXPECT_EQ(puzzles[1].result.raw(), 42); - ASSERT_EQ(puzzles[1].values.size(), 2); - EXPECT_EQ(puzzles[1].values[0], 6); - EXPECT_EQ(puzzles[1].values[1], 7); + EXPECT_EQ(puzzleExtracted[1].result.getRawValue(), 42); + ASSERT_EQ(puzzleExtracted[1].values.size(), 2); + EXPECT_EQ(puzzleExtracted[1].values[0], 6); + EXPECT_EQ(puzzleExtracted[1].values[1], 7); } TEST(EquationPuzzleParserTest, HandlesEmptyInput) { auto puzzles = parsePuzzles(""); - EXPECT_TRUE(puzzles.empty()); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + EXPECT_TRUE(puzzleExtracted.empty()); } TEST(EquationPuzzleParserTest, HandlesWindowsLineEndings) { auto puzzles = parsePuzzles("15: 7 8\r\n42: 6 7"); - ASSERT_EQ(puzzles.size(), 2); - EXPECT_EQ(puzzles[0].result.raw(), 15); - EXPECT_EQ(puzzles[1].result.raw(), 42); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + ASSERT_EQ(puzzleExtracted.size(), 2); + EXPECT_EQ(puzzleExtracted[0].result.getRawValue(), 15); + EXPECT_EQ(puzzleExtracted[1].result.getRawValue(), 42); } TEST(EquationPuzzleParserTest, HandlesPuzzleWithMultipleValues) { auto puzzles = parsePuzzles("142: 1 4 2"); - ASSERT_EQ(puzzles.size(), 1); - EXPECT_EQ(puzzles[0].result.raw(), 142); - ASSERT_EQ(puzzles[0].values.size(), 3); - EXPECT_EQ(puzzles[0].values[0], 1); - EXPECT_EQ(puzzles[0].values[1], 4); - EXPECT_EQ(puzzles[0].values[2], 2); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + ASSERT_EQ(puzzleExtracted.size(), 1); + EXPECT_EQ(puzzleExtracted[0].result.getRawValue(), 142); + ASSERT_EQ(puzzleExtracted[0].values.size(), 3); + EXPECT_EQ(puzzleExtracted[0].values[0], 1); + EXPECT_EQ(puzzleExtracted[0].values[1], 4); + EXPECT_EQ(puzzleExtracted[0].values[2], 2); } TEST(EquationPuzzleParserTest, HandlesExtraWhitespace) { auto puzzles = parsePuzzles("42: 6 7 "); - ASSERT_EQ(puzzles.size(), 1); - EXPECT_EQ(puzzles[0].result.raw(), 42); - ASSERT_EQ(puzzles[0].values.size(), 2); - EXPECT_EQ(puzzles[0].values[0], 6); - EXPECT_EQ(puzzles[0].values[1], 7); + ASSERT_TRUE(puzzles); + auto puzzleExtracted = *puzzles; + ASSERT_EQ(puzzleExtracted.size(), 1); + EXPECT_EQ(puzzleExtracted[0].result.getRawValue(), 42); + ASSERT_EQ(puzzleExtracted[0].values.size(), 2); + EXPECT_EQ(puzzleExtracted[0].values[0], 6); + EXPECT_EQ(puzzleExtracted[0].values[1], 7); } \ No newline at end of file diff --git a/day_7/test/equation_puzzle_solver_test.cpp b/day_7/test/equation_puzzle_solver_test.cpp index cdc3011..8ab2bed 100644 --- a/day_7/test/equation_puzzle_solver_test.cpp +++ b/day_7/test/equation_puzzle_solver_test.cpp @@ -7,23 +7,13 @@ using namespace aoc::day_7; -class EquationPuzzleSolverTest : public ::testing::Test { - protected: - std::span const * const, aoc::day_7::BASIC_OPERATORS_SIZE> - basic_ops = - std::span const * const, aoc::day_7::BASIC_OPERATORS_SIZE>{ - aoc::day_7::BASIC_OPERATORS_T.data(), aoc::day_7::BASIC_OPERATORS_SIZE}; - - std::span const * const, aoc::day_7::ALL_OPERATORS_SIZE> all_ops = - std::span const * const, aoc::day_7::ALL_OPERATORS_SIZE>{ - aoc::day_7::ALL_OPERATORS_T.data(), aoc::day_7::ALL_OPERATORS_SIZE}; -}; +class EquationPuzzleSolverTest : public ::testing::Test {}; TEST_F(EquationPuzzleSolverTest, BasicAddition) { equation_puzzle<> puzzle{equation_result<>{15}, {7, 8}}; - EXPECT_TRUE(aoc::day_7::isSolvable(puzzle, basic_ops)); + EXPECT_TRUE(aoc::day_7::isSolvable(puzzle, aoc::day_7::BASIC_OPERATORS<>)); - auto solution = solveEquationPuzzle(puzzle, basic_ops); + auto solution = solveEquationPuzzle(puzzle, aoc::day_7::BASIC_OPERATORS<>); ASSERT_TRUE(solution); ASSERT_EQ(solution->size(), 1); EXPECT_EQ((*solution)[0]->type, operation_type::add); @@ -31,9 +21,9 @@ TEST_F(EquationPuzzleSolverTest, BasicAddition) { TEST_F(EquationPuzzleSolverTest, BasicMultiplication) { equation_puzzle puzzle{equation_result<>{42}, {6, 7}}; - EXPECT_TRUE(isSolvable(puzzle, basic_ops)); + EXPECT_TRUE(isSolvable(puzzle, aoc::day_7::BASIC_OPERATORS<>)); - auto solution = solveEquationPuzzle(puzzle, basic_ops); + auto solution = solveEquationPuzzle(puzzle, aoc::day_7::BASIC_OPERATORS<>); ASSERT_TRUE(solution); ASSERT_EQ(solution->size(), 1); EXPECT_EQ((*solution)[0]->type, operation_type::multiply); @@ -41,10 +31,10 @@ TEST_F(EquationPuzzleSolverTest, BasicMultiplication) { TEST_F(EquationPuzzleSolverTest, Concatenation) { equation_puzzle puzzle{equation_result<>{42}, {4, 2}}; - EXPECT_FALSE(isSolvable(puzzle, basic_ops)); - EXPECT_TRUE(isSolvable(puzzle, all_ops)); + EXPECT_FALSE(isSolvable(puzzle, aoc::day_7::BASIC_OPERATORS<>)); + EXPECT_TRUE(isSolvable(puzzle, aoc::day_7::ALL_OPERATORS<>)); - auto solution = solveEquationPuzzle(puzzle, all_ops); + auto solution = solveEquationPuzzle(puzzle, aoc::day_7::ALL_OPERATORS<>); ASSERT_TRUE(solution); ASSERT_EQ(solution->size(), 1); EXPECT_EQ((*solution)[0]->type, operation_type::concat); @@ -52,23 +42,23 @@ TEST_F(EquationPuzzleSolverTest, Concatenation) { TEST_F(EquationPuzzleSolverTest, ComplexExpression) { equation_puzzle puzzle{equation_result<>{142}, {1, 4, 2}}; - EXPECT_TRUE(isSolvable(puzzle, all_ops)); + EXPECT_TRUE(isSolvable(puzzle, aoc::day_7::ALL_OPERATORS<>)); - auto solution = solveEquationPuzzle(puzzle, all_ops); + auto solution = solveEquationPuzzle(puzzle, aoc::day_7::ALL_OPERATORS<>); ASSERT_TRUE(solution); ASSERT_EQ(solution->size(), 2); } TEST_F(EquationPuzzleSolverTest, EmptyPuzzle) { equation_puzzle puzzle{equation_result<>{0}, {}}; - auto result = solveEquationPuzzle(puzzle, basic_ops); + auto result = solveEquationPuzzle(puzzle, aoc::day_7::BASIC_OPERATORS<>); EXPECT_FALSE(result); EXPECT_EQ(result.error(), std::errc::invalid_argument); } TEST_F(EquationPuzzleSolverTest, UnsolvablePuzzle) { equation_puzzle puzzle{equation_result<>{10}, {1, 2, 3}}; - auto result = solveEquationPuzzle(puzzle, basic_ops); + auto result = solveEquationPuzzle(puzzle, aoc::day_7::BASIC_OPERATORS<>); EXPECT_FALSE(result); EXPECT_EQ(result.error(), std::errc::result_out_of_range); }