11#include " common_headers.hpp"
22#include " utils/numeric_algorithm.hpp"
33
4- #include < sstream>
4+ #include < chrono>
5+ #include < concepts>
56#include < limits>
6- #include < cmath>
7+ #include < functional>
8+ #include < sstream>
79
10+ constexpr std::string_view gTestDataShort {
11+ #include " ../data/2024/day7_short.txt"
12+ };
13+
14+ constexpr std::string_view gInput {
15+ #include " ../data/2024/day7_constexpr.txt"
16+ };
817
9- struct Equation final
18+ constexpr size_t gArgumentsSizeMax {100 };
19+ using ArgumentsArray = std::array<int64_t , gArgumentsSizeMax >;
20+ struct Equation
1021{
1122 int64_t result{};
12- std::vector<int64_t > values;
23+ size_t argumentsCount{};
24+ ArgumentsArray arguments;
1325};
1426
15- [[nodiscard]] constexpr int64_t cat (int64_t a, int64_t b) noexcept {
27+ template <typename T>
28+ concept IsEquation = std::is_same_v<T, Equation>;
29+
30+ template <typename T>
31+ concept EquationArrayContainer = requires (T a)
32+ {
33+ requires IsEquation<std::iter_value_t <decltype (std::begin (a))>>;
34+ requires std::forward_iterator<decltype (std::begin (a))>;
35+ requires std::forward_iterator<decltype (std::end (a))>;
36+ };
37+
38+ template <typename T>
39+ concept EquationPointerArray = std::is_pointer_v<T> && IsEquation<std::remove_pointer_t <T>>;
40+
41+ template <typename T>
42+ concept EquationBoundedArray = std::is_bounded_array_v<T> && requires (T a)
43+ {
44+ requires IsEquation<std::remove_cvref_t <decltype (a[0 ])>>;
45+ };
46+
47+ template <typename T>
48+ concept EquationArray = EquationArrayContainer<T> || EquationPointerArray<T> || EquationBoundedArray<T>;
49+
50+
51+ // TODO: replace the function with std::from_chairs, that becomes constexpr in C++23.
52+ [[nodiscard]] constexpr std::pair<int64_t , size_t > parseNumber (std::string_view str) noexcept {
53+ constexpr auto char_to_int = [](char c) {
54+ return c - ' 0' ;
55+ };
56+
57+ size_t index{};
58+ int64_t result{};
59+ while (index < str.size () && std::isdigit (str[index])) {
60+ result = result * 10 + char_to_int (str[index]);
61+ ++index;
62+ };
63+ return std::pair{result, index};
64+ }
65+
66+ constexpr void skipNonDigits (std::string_view& str) noexcept {
67+ const auto it = std::find_if (str.begin (), str.end (), [](const char ch) {
68+ return std::isdigit (ch);
69+ });
70+
71+ if (it != str.end ()) {
72+ str.remove_prefix (std::distance (str.begin (), it));
73+ }
74+ else {
75+ str = {};
76+ }
77+ }
78+
79+ [[nodiscard]] constexpr std::pair<ArgumentsArray, size_t > parseArgumentsEquation (std::string_view& data)
80+ {
81+ size_t id{};
82+ ArgumentsArray arguments{};
83+ while (id < gArgumentsSizeMax && !data.empty () && data.front () != ' \n ' ) {
84+ skipNonDigits (data);
85+ if (data.empty ()) break ;
86+
87+ const auto [argument, parsedNums] = parseNumber (data);
88+
89+ arguments[id++] = argument;
90+ data.remove_prefix (parsedNums);
91+ }
92+ if (id == gArgumentsSizeMax ) {
93+ throw std::logic_error{" Number of equation arguments exceeds the threshold" };
94+ }
95+
96+ return std::pair{arguments, id};
97+ }
98+
99+ [[nodiscard]] constexpr size_t numOfEquastions (std::string_view testData) noexcept
100+ {
101+ return std::accumulate (testData.begin (), testData.end (), size_t {}, [](size_t value, char ch) {
102+ return value + (ch == ' :' );
103+ });
104+ }
105+
106+ [[nodiscard]] constexpr Equation parseEquation (std::string_view& testData) {
107+ // Equestion pattern looks like: "1234: 32 12 12".
108+ // The first number is result followed by comma.
109+ // The rest numbers are arguments separated by whitespace.
110+ const auto [result, parsedNums] = parseNumber (testData);
111+ if (parsedNums + 2 >= testData.size () || testData[parsedNums] != ' :' ) {
112+ throw std::runtime_error{" invalid input data" };
113+ }
114+ testData.remove_prefix (parsedNums + 2 );
115+ auto [arguments, count] = parseArgumentsEquation (testData);
116+
117+ Equation equation{
118+ .result = result,
119+ .argumentsCount = count,
120+ .arguments = arguments
121+ };
122+ return equation;
123+ }
124+
125+ [[nodiscard]] constexpr auto parseEquations (std::string_view testData, EquationArray auto & equations) {
126+ size_t index{};
127+ const auto N{equations.size ()};
128+ while (index < N && !testData.empty ()) {
129+ skipNonDigits (testData);
130+ if (testData.empty ()) break ;
131+
132+ equations[index] = parseEquation (testData);
133+ ++index;
134+ }
135+ }
136+
137+ [[nodiscard]] constexpr int64_t cat (int64_t a, int64_t b) noexcept
138+ {
16139 const int multiplier = std::pow (10 , numOfDigits (b));
17140 return a * multiplier + b;
18141};
19142
20- template <typename ...Operation>
143+ template <typename T>
144+ concept OperatorInvocable = std::regular_invocable<T, int64_t , int64_t >;
145+
146+ template <typename ...Operations>
147+ requires (OperatorInvocable<Operations> && ...)
21148class Combiner final
22149{
23150public:
24- constexpr explicit Combiner (Operation && ...operations) : m_functions {std::forward<Operation >(operations)...} {}
151+ constexpr explicit Combiner (Operations && ...operations ) : m_operations {std::forward<Operations >(operations)...} {}
25152
26- [[nodiscard]] constexpr auto compute (const Equation& equation, size_t id, int64_t curValue) const {
153+ [[nodiscard]] constexpr bool compute (const Equation& equation, size_t id, int64_t curValue) const {
27154 if (curValue >= equation.result ) {
28155 return curValue == equation.result ;
29156 }
30- if (id >= equation.values . size () ) {
157+ if (id >= equation.argumentsCount ) {
31158 return false ;
32159 }
33-
160+
34161 return std::apply (
35162 [&, this ](const auto & ...op ) {
36- return (op.operate (equation, id, curValue, *this ) || ...);
163+ const auto arg{equation.arguments [id++]};
164+ return ((compute (equation, id, op (curValue, arg))) || ...);
37165 },
38- m_functions);
166+ m_operations);
167+ }
168+
169+ [[nodiscard]] constexpr bool compute (const Equation& equation) const {
170+ constexpr size_t id{};
171+ constexpr size_t initValue{};
172+ return compute (equation, id, initValue);
39173 }
40174
41175private:
42- std::tuple<Operation ...> m_functions ;
176+ std::tuple<Operations ...> m_operations ;
43177};
44178
45- template <typename ...Operation>
46- [[nodiscard]] constexpr auto makeCombiner (Operation&& ...ops) {
179+ template <typename ...Operation> requires (OperatorInvocable<Operation> && ...)
180+ [[nodiscard]] constexpr auto makeCombiner(Operation&& ...ops) noexcept {
47181 return Combiner<Operation...>{std::forward<Operation>(ops)...};
48182}
49183
50184class CatOperation final
51185{
52186public:
53- template <typename C>
54- constexpr auto operate (const Equation& equation, size_t id, int64_t curValue, C context) const noexcept {
55- return context.compute (equation, id + 1 , cat (curValue, equation.values [id]));
56- }
57- };
58-
59- class AddOperation final
60- {
61- public:
62- template <typename C>
63- constexpr auto operate (const Equation& equation, size_t id, int64_t curValue, C context) const noexcept {
64- return context.compute (equation, id + 1 , curValue + equation.values [id]);
65- }
66- };
67-
68- class MultOperation final
69- {
70- public:
71- template <typename C>
72- constexpr auto operate (const Equation& equation, size_t id, int64_t curValue, C context) const noexcept {
73- return context.compute (equation, id + 1 , curValue * equation.values [id]);
187+ constexpr auto operator ()(int64_t initValue, int64_t curValue) const noexcept {
188+ return cat (initValue, curValue);
74189 }
75190};
76191
77- [[nodiscard]] constexpr bool canBeCombinedBy2Operations (const Equation& equation, size_t id, int64_t curValue ) {
78- auto combiner{makeCombiner (AddOperation {}, MultOperation {})};
79- return combiner.compute (equation, id, curValue );
192+ [[nodiscard]] constexpr bool canBeCombinedBy2Operations (const Equation& equation) {
193+ constexpr auto combiner{makeCombiner (std::multiplies<> {}, std::plus<> {})};
194+ return combiner.compute (equation);
80195}
81196
82- [[nodiscard]] constexpr bool canBeCombinedBy3Operations (const Equation& equation, size_t id, int64_t curValue ) {
83- auto combiner{makeCombiner (CatOperation {}, AddOperation {}, MultOperation {})};
84- return combiner.compute (equation, id, curValue );
197+ [[nodiscard]] constexpr bool canBeCombinedBy3Operations (const Equation& equation) {
198+ auto combiner{makeCombiner (std::plus<> {}, std::multiplies<> {}, CatOperation {})};
199+ return combiner.compute (equation);
85200}
86201
87202template <typename CombineFun>
88- [[nodiscard]] size_t solveImpl (const std::vector<Equation> & equations, CombineFun canBeCombined) noexcept
203+ [[nodiscard]] constexpr size_t solveImpl (const EquationArray auto & equations, const CombineFun& canBeCombined) noexcept
89204{
90- size_t totalSum{};
91- for (const auto & equation : equations) {
92- if (canBeCombined (equation, 0 , 0 )) {
93- totalSum += equation.result ;
94- }
95- }
96- return totalSum;
205+ return std::accumulate (std::begin (equations), std::end (equations), size_t {}, [&canBeCombined](size_t value, const Equation& equation) {
206+ return canBeCombined (equation) ? value + equation.result : value;
207+ });
97208}
98209
99- [[nodiscard]] size_t solveFirstPart (const std::vector<Equation> & equations) noexcept
210+ [[nodiscard]] constexpr size_t solveFirstPart (const EquationArray auto & equations) noexcept
100211{
101212 return solveImpl (equations, canBeCombinedBy2Operations);
102213}
103214
104- [[nodiscard]] size_t solveSecondPart (const std::vector<Equation> & equations) noexcept
215+ [[nodiscard]] constexpr size_t solveSecondPart (const EquationArray auto & equations) noexcept
105216{
106217 return solveImpl (equations, canBeCombinedBy3Operations);
107218}
108219
220+ [[nodiscard]] std::string readFile (std::string_view filename) {
221+ std::ifstream ifile (filename.data ());
222+ if (!ifile) {
223+ throw std::runtime_error{" Failed to open file" };
224+ }
225+ std::string str (std::istreambuf_iterator<char >{ifile}, {});
226+ return str;
227+ }
228+
109229void printHelp ()
110230{
111231 std::cerr << " \n Usage:\n "
@@ -121,38 +241,48 @@ int main(int argc, char* argv[])
121241 }
122242
123243 std::string_view task{argv[1 ]};
124- if (task != " part1" && task != " part2" ) {
125- std::cerr << " \n first arg can be either `part1` or `part2`\n " ;
126- printHelp ();
127- return 1 ;
128- }
129244
130- std::ifstream ifile (argv[2 ]);
131- if (!ifile) {
132- std::cerr << " \n File cannot be open" ;
133- return 1 ;
134- }
245+ if (task == " part1" || task == " part2" ) {
246+ using SolutionImplFunction = size_t (*)(const std::vector<Equation>&);
247+ std::unordered_map<std::string_view, SolutionImplFunction> handlers{
248+ {" part1" , solveFirstPart<std::vector<Equation>>},
249+ {" part2" , solveSecondPart<std::vector<Equation>>},
250+ };
135251
136- std::vector<Equation> equations;
137- std::string line;
138- while (std::getline (ifile, line)) {
139- equations.emplace_back ();
140- Equation& eq = equations.back ();
141-
142- std::istringstream ss (line);
143- char colon{};
144- ss >> eq.result >> colon;
145- int num2{};
146- while ((ss >> num2)) {
147- eq.values .push_back (num2);
148- }
252+ const auto start = std::chrono::high_resolution_clock::now ();
253+ const auto fileContent{readFile (argv[2 ])};
254+ std::string_view fileContentView{fileContent};
255+ const size_t N{numOfEquastions (fileContentView)};
256+ std::vector<Equation> equations (N);
257+ parseEquations (fileContentView, equations);
258+
259+ const auto res = handlers.at (task)(equations);
260+ const auto end = std::chrono::high_resolution_clock::now ();
261+
262+ std::cout << res
263+ << " elapsed " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count ();
149264 }
265+ else if (task == " part1_constexpr" ) {
266+ const auto constexpr_context = [] {
267+ constexpr size_t N{numOfEquastions (gInput )};
268+ std::array<Equation, N> equations{};
269+ parseEquations (gInput , equations);
150270
151- if (task == " part1" ) {
152- std::cout << solveFirstPart (equations);
271+ return equations;
272+ };
273+
274+ constexpr auto equations{constexpr_context ()};
275+ constexpr auto firstPartAns{solveFirstPart (equations)};
276+ std::cout << firstPartAns;
277+
278+ // TODO: solving second part takes a lot of time and consumes lots of memory.
279+ // constexpr auto secondPartAns{solveSecondPart(equations)};
280+ // std::cout << "\n" << secondPartAns;
153281 }
154282 else {
155- std::cout << solveSecondPart (equations);
283+ std::cerr << " \n first arg can be either `part1`, `part2`, or `part1_constexpr`\n " ;
284+ printHelp ();
285+ return 1 ;
156286 }
157287
158288 return 0 ;
0 commit comments