commit 30e53593bf1d336520bc046f62965cd73579a711
parent 10848ee1a0e718d6a48e6bde1cf07d4138eb4b1c
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Sun, 24 Dec 2023 20:12:34 +0100
Added solution for 24
Diffstat:
| A | 2023/24/24a.c |  |  | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
| A | 2023/24/24b.c |  |  | 205 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
2 files changed, 281 insertions(+), 0 deletions(-)
diff --git a/2023/24/24a.c b/2023/24/24a.c
@@ -0,0 +1,76 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define N    300
+#define XMIN 2e14
+#define XMAX 4e14
+#define eps  1e-6
+
+#define ABS(x) ((x)>0?(x):-(x))
+
+typedef struct { double x, y, z; } point_t;
+typedef struct { point_t p, v; } line_t;
+
+char *buf, line[N];
+int n, s;
+line_t l[N];
+
+bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; }
+
+line_t readl(char *buf) {
+	double a[6];
+	for (int i = 0; i < 6; i++) {
+		a[i] = (double)atoll(buf);
+		while (isnum(*buf)) buf++;
+		while (*buf != '\n' && !isnum(*buf)) buf++;
+	}
+	return (line_t) {
+	    .p = (point_t){.x = a[0], .y = a[1], .z = a[2]},
+	    .v = (point_t){.x = a[3], .y = a[4], .z = a[5]}
+	};
+}
+
+double det(double a, double b, double c, double d) { return a*d-b*c; }
+
+bool parallel2d(line_t l1, line_t l2) {
+	double d = det(l1.v.x, l2.v.x, l1.v.y, l2.v.y);
+	return ABS(d) < eps;
+}
+
+point_t collide2d(line_t l1, line_t l2, double *t1, double *t2) {
+	double a1 = l1.v.x;
+	double b1 = -l2.v.x;
+	double c1 = l2.p.x - l1.p.x;
+	double a2 = l1.v.y;
+	double b2 = -l2.v.y;
+	double c2 = l2.p.y - l1.p.y;
+	
+	double d = det(a1, b1, a2, b2);
+	*t1 = det(c1, b1, c2, b2) / d;
+	*t2 = det(a1, c1, a2, c2) / d;
+
+	double x = l1.p.x + l1.v.x * (*t1);
+	double y = l1.p.y + l1.v.y * (*t1);
+
+	return (point_t) {.x = x, .y = y};
+}
+
+int main() {
+	for (n = 0; (buf = fgets(line, N, stdin)) != NULL; n++)
+		l[n] = readl(buf);
+
+	for (int i = 0; i < n; i++) {
+		for (int j = i+1; j < n; j++) {
+			if (parallel2d(l[i], l[j])) continue;
+			double t1, t2;
+			point_t p = collide2d(l[i], l[j], &t1, &t2);
+			if (t1 < -eps || t2 < -eps) continue;
+			s += p.x > XMIN-eps && p.x < XMAX+eps &&
+			     p.y > XMIN-eps && p.y < XMAX+eps;
+		}
+	}
+
+	printf("%d\n", s);
+	return 0;
+}
diff --git a/2023/24/24b.c b/2023/24/24b.c
@@ -0,0 +1,205 @@
+/*
+Due to numerical errors, the result of this program was not correct on my
+machine (ahah - doesn't work on my machine (: )
+However, with slightly different values for eps I got two answers whose
+difference was 4 and one was too high, the other too low. A quick binary
+search gave the correct result.
+I'll make a more stable version if I feel like it.
+*/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define D    10
+#define N    300
+
+#define ABS(x) ((x)>0?(x):-(x))
+#define eps 1e1
+#define EQ(x,y) (ABS((x)-(y))<eps)
+
+typedef struct { double x, y, z; } point_t;
+typedef struct { point_t p, v; } line_t;
+
+char *buf, line[N];
+int nlines, s;
+line_t l[N];
+
+bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; }
+
+line_t readl(char *buf) {
+	double a[6];
+	for (int i = 0; i < 6; i++) {
+		a[i] = (double)atoll(buf);
+		while (isnum(*buf)) buf++;
+		while (*buf != '\n' && !isnum(*buf)) buf++;
+	}
+	return (line_t) {
+	    .p = (point_t){.x = a[0], .y = a[1], .z = a[2]},
+	    .v = (point_t){.x = a[3], .y = a[4], .z = a[5]}
+	};
+}
+
+void swap(double *x, double *y) { double aux = *x; *x = *y; *y = aux; }
+
+void printa(double A[D][D], double C[D], int d) {
+	for (int i = 0; i < d; i++) {
+		printf("[ ");
+		for (int j = 0; j < d; j++)
+			printf("%.2lf ", A[i][j]);
+		printf("]\t[%.2lf]\n", C[i]);
+	}
+}
+
+/* Solve the linear system AX = C with row reduction */
+/* Return true if it has a unique solution, false in any other case */
+bool solvesystem(double A[D][D], double C[D], int d, double *X) {
+	/* Row reduction */
+	for (int i = 0; i < d; i++) {
+		/* Make first nonzero */
+		int nz;
+		for (nz = i; nz < d && EQ(A[i][nz], 0.0); nz++) ;
+		if (nz == d) return false;
+		swap(&C[i], &C[nz]);
+		for (int j = 0; j < d; j++)
+			swap(&A[i][j], &A[nz][j]);
+
+		/* Reduce rows */
+		for (int ii = i+1; ii < d; ii++) {
+			double r = A[ii][i] / A[i][i];
+			for (int k = i; k < d; k++)
+				A[ii][k] -= r*A[i][k];
+			C[ii] -= r*C[i];
+		}
+	}
+
+	/* Back substitution */
+	for (int i = d-1; i >= 0; i--) {
+		X[i] = C[i];
+		for (int j = i+1; j < d; j++)
+			X[i] -= A[i][j]*X[j];
+		X[i] /= A[i][i];
+	}
+	
+	return true;
+}
+
+bool equal(point_t p, point_t q) {
+	return EQ(p.x, q.x) && EQ(p.y, q.y) && EQ(p.z, q.z);
+}
+
+point_t pos(line_t l, double t) {
+	return (point_t) {
+	    .x = l.p.x + t*l.v.x,
+	    .y = l.p.y + t*l.v.y,
+	    .z = l.p.z + t*l.v.z
+	};
+}
+
+void testsolution(line_t sol) {
+	for (int i = 0; i < nlines; i++) {
+		double t = -1.0;
+		if (!EQ(sol.v.x, l[i].v.x))
+			t = (sol.p.x-l[i].p.x)/(l[i].v.x-sol.v.x);
+		if (!EQ(sol.v.y, l[i].v.y))
+			t = (sol.p.y-l[i].p.y)/(l[i].v.y-sol.v.y);
+		if (!EQ(sol.v.z, l[i].v.z))
+			t = (sol.p.z-l[i].p.z)/(l[i].v.z-sol.v.z);
+		point_t p1 = pos(sol, t), p2 = pos(l[i], t);
+		printf("Line %d intersected at t = %.2lf "
+		       "in (%.2lf, %.2lf, %.2lf) and (%.2lf, %.2lf, %.2lf)\n",
+		       i, t, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
+		if (t < eps ||
+		    !EQ(p1.x, p2.x) || !EQ(p1.y, p2.y) || !EQ(p1.z, p2.z)) {
+			printf("Error!\n");
+			return;
+		}
+	}
+	printf("All lines intersected correctly, solution is valid\n");
+}
+
+int main() {
+	for (nlines = 0; (buf = fgets(line, N, stdin)) != NULL; nlines++)
+		l[nlines] = readl(buf);
+
+	/* To figure the correct starting point + velocity, we solve some
+	 * systems of linear equations. Write your starting point and velocity
+	 * as unknowns x, y, z, Vx, Vy, Vz. Equating the position of the
+	 * rock at time t1 (another unknown parameter) with the position
+	 * of one of the hailstones at the same time t1, we get a system
+	 * of 3 equations and 7 unknowns. Unfortunately, these equations
+	 * have degree 2 - this is not a linear system!
+	 * However, manipulating these equations a bit we can get a linear
+	 * equation of the type:
+	 *     (Vy1-Vy2)x - (Vx1-Vx2)y + - (y1-y2)Vx + (x1-x2)Vy =
+	 *         y2Vx2 + x2Vy2 - y1Vx1 - x1Vy1
+	 * Where x1, y1, z2, Vx1, Vy1, Vz1 and x2, y2, z2, Vx2, Vy2, Vz2
+	 * are the starting points and velocities of two of the hailstones.
+	 * So with 2 lines we can get a linear equation. Similarly, we can
+	 * get equations involving the unknowns z and Vz.
+	 * We can use the myriad of hailstones we have to generate as many
+	 * equations as we like. The system is going to be overdetermined,
+	 * but the problem statement seems to ensure that there is going to
+	 * be a solution. On the other hand it can happen that we make a
+	 * bad choice of lines and the equation we use are underdetermined.
+	 * This last problem is not accounted for in the code - if it happens,
+	 * one can shuffle the input file until it works.
+	 */
+	int d = 6;
+	double A[D][D] = {
+		/* First equation: lines 0 and 1, x and y only */
+		{l[0].v.y-l[1].v.y, l[1].v.x-l[0].v.x, 0.0,
+		    l[1].p.y-l[0].p.y, l[0].p.x-l[1].p.x, 0.0},
+		/* Second equation: lines 0 and 2, x and y only */
+		{l[0].v.y-l[2].v.y, l[2].v.x-l[0].v.x, 0.0,
+		    l[2].p.y-l[0].p.y, l[0].p.x-l[2].p.x, 0.0},
+		/* Third equation: lines 0 and 1, x and z only */
+		{l[0].v.z-l[1].v.z, 0.0, l[1].v.x-l[0].v.x,
+		    l[1].p.z-l[0].p.z, 0.0, l[0].p.x-l[1].p.x},
+		/* Fourth equation: lines 0 and 2, x and z only */
+		{l[0].v.z-l[2].v.z, 0.0, l[2].v.x-l[0].v.x,
+		    l[2].p.z-l[0].p.z, 0.0, l[0].p.x-l[2].p.x},
+		/* Fifth equation: lines 0 and 1, y and z only */
+		{0.0, l[0].v.z-l[1].v.z, l[1].v.y-l[0].v.y,
+		    0.0, l[1].p.z-l[0].p.z, l[0].p.y-l[1].p.y},
+		/* Sixth equation: lines 0 and 2, y and z only */
+		{0.0, l[0].v.z-l[2].v.z, l[2].v.y-l[0].v.y,
+		    0.0, l[2].p.z-l[0].p.z, l[0].p.y-l[2].p.y},
+	};
+	double C[D] = {
+		/* First equation: lines 0 and 1, x and y only */
+		l[1].p.y*l[1].v.x - l[1].p.x*l[1].v.y 
+		    - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y,
+		/* Second equation: lines 0 and 2, x and y only */
+		l[2].p.y*l[2].v.x - l[2].p.x*l[2].v.y 
+		    - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y,
+		/* Third equation: lines 0 and 1, x and z only */
+		l[1].p.z*l[1].v.x - l[1].p.x*l[1].v.z 
+		    - l[0].p.z*l[0].v.x + l[0].p.x*l[0].v.z,
+		/* Fourth equation: lines 0 and 2, x and z only */
+		l[2].p.z*l[2].v.x - l[2].p.x*l[2].v.z 
+		    - l[0].p.z*l[0].v.x + l[0].p.x*l[0].v.z,
+		/* Fifth equation: lines 0 and 1, y and z only */
+		l[1].p.z*l[1].v.y - l[1].p.y*l[1].v.z 
+		    - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z,
+		/* Sixth equation: lines 0 and 2, y and z only */
+		l[2].p.z*l[2].v.y - l[2].p.y*l[2].v.z 
+		    - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z,
+	};
+	double X[d];
+	if (!solvesystem(A, C, d, X)) {
+		printf("No unique solution, shuffle input and try again.\n");
+		exit(1);
+	}
+
+	line_t sol = {
+		.p = {.x = X[0], .y = X[1], .z = X[2]},
+		.v = {.x = X[3], .y = X[4], .z = X[5]}
+	};
+	testsolution(sol);
+
+	printf("p = (%.2lf, %.2lf, %.2lf), v = (%.2lf, %.2lf, %.2lf)\n",
+	    sol.p.x, sol.p.y, sol.p.z, sol.v.x, sol.v.y, sol.v.z);
+	printf("%lf\n", sol.p.x + sol.p.y + sol.p.z);
+	return 0;
+}