1+ #include " common_headers.hpp"
2+ #include < cmath>
3+ #include < regex>
4+
5+ void printHelp ()
6+ {
7+ std::cerr << " \n Usage:\n "
8+ << " The program requires 2 args: (part1, part2) and the path to the file."
9+ << " \n For example, ./day7 part1 data/day7.txt" ;
10+ }
11+
12+ using Matrix = std::vector<std::vector<long double >>;
13+ using Vector = std::vector<long double >;
14+
15+ struct EquationParams final
16+ {
17+ Matrix coords;
18+ Vector result;
19+
20+ void appendXY (std::pair<int64_t , int64_t > xy) {
21+ coords.resize (2 );
22+ coords[0 ].push_back (xy.first );
23+ coords[1 ].push_back (xy.second );
24+ }
25+
26+ void setResult (std::pair<int64_t , int64_t > resultXY) {
27+ result.push_back (resultXY.first );
28+ result.push_back (resultXY.second );
29+ }
30+ };
31+
32+
33+ void gaussianElimination (Matrix& A, Vector& b) {
34+ int n = A.size ();
35+
36+ for (int i = 0 ; i < n; ++i) {
37+ // Partial pivoting
38+ int maxRow = i;
39+ for (int k = i + 1 ; k < n; ++k) {
40+ if (std::abs (A[k][i]) > std::abs (A[maxRow][i])) {
41+ maxRow = k;
42+ }
43+ }
44+
45+ // Swap rows in A and b
46+ std::swap (A[i], A[maxRow]);
47+ std::swap (b[i], b[maxRow]);
48+
49+ // Check for singularity
50+ if (std::abs (A[i][i]) < 1e-10 ) {
51+ throw std::runtime_error (" Matrix is singular or nearly singular" );
52+ }
53+
54+ // Eliminate column i below the pivot
55+ for (int k = i + 1 ; k < n; ++k) {
56+ double factor = A[k][i] / A[i][i];
57+ for (int j = i; j < n; ++j) {
58+ A[k][j] -= factor * A[i][j];
59+ }
60+ b[k] -= factor * b[i];
61+ }
62+ }
63+ }
64+
65+ Vector backSubstitution (const Matrix& A, const Vector& b) {
66+ int n = A.size ();
67+ Vector x (n);
68+
69+ for (int i = n - 1 ; i >= 0 ; --i) {
70+ x[i] = b[i];
71+ for (int j = i + 1 ; j < n; ++j) {
72+ x[i] -= A[i][j] * x[j];
73+ }
74+ x[i] /= A[i][i];
75+ }
76+
77+ return x;
78+ }
79+
80+ Vector solveLinearSystem (Matrix A, Vector b) {
81+ gaussianElimination (A, b);
82+ return backSubstitution (A, b);
83+ }
84+
85+ bool checkResultCorrectness (const Vector& result, double upperBoundValue) {
86+ for (auto r : result) {
87+ if (!std::isfinite (r) || std::abs (std::trunc (std::round (r)) - r) > 0.0003 || r > upperBoundValue) {
88+ if (r > 100 ) {
89+ }
90+ std::cerr << " \n solution incorrect: " << r ;
91+ return false ;
92+ }
93+ }
94+ return true ;
95+ }
96+
97+ size_t solveFirstPart (const std::vector<EquationParams>& equationParams) {
98+ constexpr size_t costButtonA{3 };
99+ constexpr size_t costButtonB{1 };
100+ size_t tokens{};
101+
102+ for (const auto & equation : equationParams) {
103+ Vector result = solveLinearSystem (equation.coords , equation.result );
104+ if (checkResultCorrectness (result, 100 )) {
105+ tokens += static_cast <size_t >(std::round (result.at (0 ))) * costButtonA + static_cast <size_t >(std::round (result.at (1 )) * costButtonB);
106+ }
107+ }
108+ return tokens;
109+ }
110+
111+ size_t solveSecondPart (const std::vector<EquationParams>& equationParams) {
112+ constexpr size_t costButtonA{3 };
113+ constexpr size_t costButtonB{1 };
114+ size_t tokens{};
115+
116+ for (const auto & equation : equationParams) {
117+ Vector result = solveLinearSystem (equation.coords , equation.result );
118+ if (checkResultCorrectness (result, 10000000000000 )) {
119+ tokens += static_cast <size_t >(std::round (result.at (0 ))) * costButtonA + static_cast <size_t >(std::round (result.at (1 )) * costButtonB);
120+ }
121+ }
122+ return tokens;
123+ }
124+
125+
126+ [[nodiscard]] std::pair<int64_t , int64_t > parseXY (const std::string& input) {
127+ std::pair<int64_t , int64_t > values{};
128+
129+ // Regular expression to match the input pattern
130+ std::regex pattern (R"( (\w+ \w+|\w+): X[+=](\-?\d+), Y[+=](\-?\d+))" );
131+ std::smatch matches;
132+
133+ std::string::const_iterator searchStart (input.cbegin ());
134+ std::regex_search (searchStart, input.cend (), matches, pattern);
135+ // std::cerr << "\nx " << matches[2] << " y " << matches[3];
136+ return std::pair{std::stoll (matches[2 ]), std::stoll (matches[3 ])};
137+ }
138+
139+ [[nodiscard]] EquationParams parse (const std::string& buttonA, const std::string& buttonB, const std::string& result) {
140+ EquationParams params;
141+ params.appendXY (parseXY (buttonA));
142+ params.appendXY (parseXY (buttonB));
143+ params.setResult (parseXY (result));
144+ return params;
145+ }
146+
147+ [[nodiscard]] std::vector<EquationParams> parseEquationParams (const std::vector<std::string>& inputLines) {
148+ std::vector<EquationParams> equationParams;
149+ for (size_t i = 0 ; i + 3 <= inputLines.size ();) {
150+ if (inputLines[i].empty ()) {
151+ ++i;
152+ }
153+ else {
154+ equationParams.emplace_back (parse (inputLines[i], inputLines[i + 1 ], inputLines[i + 2 ]));
155+ i += 3 ;
156+ }
157+ }
158+ return equationParams;
159+ }
160+
161+ int main (int argc, char * argv[])
162+ {
163+ if (argc != 3 ) {
164+ printHelp ();
165+ return 1 ;
166+ }
167+
168+ std::string_view task{argv[1 ]};
169+ if (task != " part1" && task != " part2" ) {
170+ std::cerr << " \n first arg can be either `part1` or `part2`\n " ;
171+ printHelp ();
172+ return 1 ;
173+ }
174+
175+ std::vector<std::string> inputVec;
176+ readInput (argv[2 ], std::back_inserter (inputVec));
177+ std::vector<EquationParams> equationParams = parseEquationParams (inputVec);
178+
179+
180+ if (task == " part1" ) {
181+ std::cout << solveFirstPart (equationParams);
182+ }
183+ else {
184+ for (auto & param : equationParams) {
185+ param.result [0 ] += 10000000000000 ;
186+ param.result [1 ] += 10000000000000 ;
187+ }
188+ std::cout << solveSecondPart (equationParams);
189+ }
190+
191+ return 0 ;
192+ }
0 commit comments