A => .builds/alpine.yml +76 -0
@@ 1,76 @@
+image: alpine/edge
+packages:
+ - tcc
+sources:
+ - https://git.sr.ht:~quf/computers-are-fast-2020
+tasks:
+ - run-01: |
+ cd computers-are-fast-2020
+ tcc -run src/01.c < testinputs/01 | tee out
+ cmp out testoutputs/01
+ rm out
+ - run-02: |
+ cd computers-are-fast-2020
+ tcc -run src/02.c < testinputs/02 | tee out
+ cmp out testoutputs/02
+ rm out
+ - run-03: |
+ cd computers-are-fast-2020
+ tcc -run src/03.c < testinputs/03 | tee out
+ cmp out testoutputs/03
+ rm out
+ - run-04: |
+ cd computers-are-fast-2020
+ tcc -run src/04.c < testinputs/04 | tee out
+ cmp out testoutputs/04
+ rm out
+ - run-05: |
+ cd computers-are-fast-2020
+ tcc -run src/05.c < testinputs/05 | tee out
+ cmp out testoutputs/05
+ rm out
+ - run-06: |
+ cd computers-are-fast-2020
+ tcc -run src/06.c < testinputs/06 | tee out
+ cmp out testoutputs/06
+ rm out
+ - run-07: |
+ cd computers-are-fast-2020
+ tcc -run src/07.c < testinputs/07 | tee out
+ cmp out testoutputs/07
+ rm out
+ - run-08: |
+ cd computers-are-fast-2020
+ tcc -run src/08.c < testinputs/08 | tee out
+ cmp out testoutputs/08
+ rm out
+ - run-09: |
+ cd computers-are-fast-2020
+ tcc -run src/09.c < testinputs/09 | tee out
+ cmp out testoutputs/09
+ rm out
+ - run-10: |
+ cd computers-are-fast-2020
+ tcc -run src/10.c < testinputs/10 | tee out
+ cmp out testoutputs/10
+ rm out
+ - run-11: |
+ cd computers-are-fast-2020
+ tcc -run src/11.c < testinputs/11 | tee out
+ cmp out testoutputs/11
+ rm out
+ - run-12: |
+ cd computers-are-fast-2020
+ tcc -run src/12.c < testinputs/12 | tee out
+ cmp out testoutputs/12
+ rm out
+ - run-13: |
+ cd computers-are-fast-2020
+ tcc -run src/13.c < testinputs/13 | tee out
+ cmp out testoutputs/13
+ rm out
+ - run-14: |
+ cd computers-are-fast-2020
+ tcc -run src/14.c < testinputs/14 | tee out
+ cmp out testoutputs/14
+ rm out
A => .builds/archlinux.yml +91 -0
@@ 1,91 @@
+image: archlinux
+packages:
+ - gcc
+ - make
+sources:
+ - https://git.sr.ht:~quf/computers-are-fast-2020
+tasks:
+ - run-01: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/01
+ bin/01 < testinputs/01 | tee out
+ cmp out testoutputs/01
+ rm out
+ - run-02: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/02
+ bin/02 < testinputs/02 | tee out
+ cmp out testoutputs/02
+ rm out
+ - run-03: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/03
+ bin/03 < testinputs/03 | tee out
+ cmp out testoutputs/03
+ rm out
+ - run-04: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/04
+ bin/04 < testinputs/04 | tee out
+ cmp out testoutputs/04
+ rm out
+ - run-05: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/05
+ bin/05 < testinputs/05 | tee out
+ cmp out testoutputs/05
+ rm out
+ - run-06: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/06
+ bin/06 < testinputs/06 | tee out
+ cmp out testoutputs/06
+ rm out
+ - run-07: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/07
+ bin/07 < testinputs/07 | tee out
+ cmp out testoutputs/07
+ rm out
+ - run-08: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/08
+ bin/08 < testinputs/08 | tee out
+ cmp out testoutputs/08
+ rm out
+ - run-09: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/09
+ bin/09 < testinputs/09 | tee out
+ cmp out testoutputs/09
+ rm out
+ - run-10: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/10
+ bin/10 < testinputs/10 | tee out
+ cmp out testoutputs/10
+ rm out
+ - run-11: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/11
+ bin/11 < testinputs/11 | tee out
+ cmp out testoutputs/11
+ rm out
+ - run-12: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/12
+ bin/12 < testinputs/12 | tee out
+ cmp out testoutputs/12
+ rm out
+ - run-13: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/13
+ bin/13 < testinputs/13 | tee out
+ cmp out testoutputs/13
+ rm out
+ - run-14: |
+ cd computers-are-fast-2020
+ CC=gcc CFLAGS=-fsanitize=address,undefined make bin/14
+ env ASAN_OPTIONS=detect_leaks=0 bin/14 < testinputs/14 | tee out
+ cmp out testoutputs/14
+ rm out
A => .builds/freebsd.yml +88 -0
@@ 1,88 @@
+image: freebsd/latest
+sources:
+ - https://git.sr.ht:~quf/computers-are-fast-2020
+tasks:
+ - run-01: |
+ cd computers-are-fast-2020
+ make bin/01
+ bin/01 < testinputs/01 | tee out
+ cmp out testoutputs/01
+ rm out
+ - run-02: |
+ cd computers-are-fast-2020
+ make bin/02
+ bin/02 < testinputs/02 | tee out
+ cmp out testoutputs/02
+ rm out
+ - run-03: |
+ cd computers-are-fast-2020
+ make bin/03
+ bin/03 < testinputs/03 | tee out
+ cmp out testoutputs/03
+ rm out
+ - run-04: |
+ cd computers-are-fast-2020
+ make bin/04
+ bin/04 < testinputs/04 | tee out
+ cmp out testoutputs/04
+ rm out
+ - run-05: |
+ cd computers-are-fast-2020
+ make bin/05
+ bin/05 < testinputs/05 | tee out
+ cmp out testoutputs/05
+ rm out
+ - run-06: |
+ cd computers-are-fast-2020
+ make bin/06
+ bin/06 < testinputs/06 | tee out
+ cmp out testoutputs/06
+ rm out
+ - run-07: |
+ cd computers-are-fast-2020
+ CFLAGS=-Wno-missing-braces make bin/07
+ bin/07 < testinputs/07 | tee out
+ cmp out testoutputs/07
+ rm out
+ - run-08: |
+ cd computers-are-fast-2020
+ CFLAGS=-Wno-missing-braces make bin/08
+ bin/08 < testinputs/08 | tee out
+ cmp out testoutputs/08
+ rm out
+ - run-09: |
+ cd computers-are-fast-2020
+ make bin/09
+ bin/09 < testinputs/09 | tee out
+ cmp out testoutputs/09
+ rm out
+ - run-10: |
+ cd computers-are-fast-2020
+ make bin/10
+ bin/10 < testinputs/10 | tee out
+ cmp out testoutputs/10
+ rm out
+ - run-11: |
+ cd computers-are-fast-2020
+ make bin/11
+ bin/11 < testinputs/11 | tee out
+ cmp out testoutputs/11
+ rm out
+ - run-12: |
+ cd computers-are-fast-2020
+ make bin/12
+ bin/12 < testinputs/12 | tee out
+ cmp out testoutputs/12
+ rm out
+ - run-13: |
+ cd computers-are-fast-2020
+ make bin/13
+ bin/13 < testinputs/13 | tee out
+ cmp out testoutputs/13
+ rm out
+ - run-14: |
+ cd computers-are-fast-2020
+ make bin/14
+ bin/14 < testinputs/14 | tee out
+ cmp out testoutputs/14
+ rm out
A => .builds/openbsd.yml +88 -0
@@ 1,88 @@
+image: openbsd/latest
+sources:
+ - https://git.sr.ht:~quf/computers-are-fast-2020
+tasks:
+ - run-01: |
+ cd computers-are-fast-2020
+ make bin/01
+ bin/01 < testinputs/01 | tee out
+ cmp out testoutputs/01
+ rm out
+ - run-02: |
+ cd computers-are-fast-2020
+ make bin/02
+ bin/02 < testinputs/02 | tee out
+ cmp out testoutputs/02
+ rm out
+ - run-03: |
+ cd computers-are-fast-2020
+ make bin/03
+ bin/03 < testinputs/03 | tee out
+ cmp out testoutputs/03
+ rm out
+ - run-04: |
+ cd computers-are-fast-2020
+ make bin/04
+ bin/04 < testinputs/04 | tee out
+ cmp out testoutputs/04
+ rm out
+ - run-05: |
+ cd computers-are-fast-2020
+ make bin/05
+ bin/05 < testinputs/05 | tee out
+ cmp out testoutputs/05
+ rm out
+ - run-06: |
+ cd computers-are-fast-2020
+ make bin/06
+ bin/06 < testinputs/06 | tee out
+ cmp out testoutputs/06
+ rm out
+ - run-07: |
+ cd computers-are-fast-2020
+ make bin/07
+ bin/07 < testinputs/07 | tee out
+ cmp out testoutputs/07
+ rm out
+ - run-08: |
+ cd computers-are-fast-2020
+ make bin/08
+ bin/08 < testinputs/08 | tee out
+ cmp out testoutputs/08
+ rm out
+ - run-09: |
+ cd computers-are-fast-2020
+ make bin/09
+ bin/09 < testinputs/09 | tee out
+ cmp out testoutputs/09
+ rm out
+ - run-10: |
+ cd computers-are-fast-2020
+ make bin/10
+ bin/10 < testinputs/10 | tee out
+ cmp out testoutputs/10
+ rm out
+ - run-11: |
+ cd computers-are-fast-2020
+ make bin/11
+ bin/11 < testinputs/11 | tee out
+ cmp out testoutputs/11
+ rm out
+ - run-12: |
+ cd computers-are-fast-2020
+ make bin/12
+ bin/12 < testinputs/12 | tee out
+ cmp out testoutputs/12
+ rm out
+ - run-13: |
+ cd computers-are-fast-2020
+ make bin/13
+ bin/13 < testinputs/13 | tee out
+ cmp out testoutputs/13
+ rm out
+ - run-14: |
+ cd computers-are-fast-2020
+ make bin/14
+ bin/14 < testinputs/14 | tee out
+ cmp out testoutputs/14
+ rm out
A => .gitattributes +1 -0
A => .gitignore +2 -0
A => README.md +212 -0
@@ 1,212 @@
+Computers are fast.
+===================
+
+Or: C is my favourite scripting language
+
+The goal of this project is to solve all parts of [Advent of Code 2020](https://adventofcode.com/2020/):
+
+- With no dependencies other than a C99 compiler and libc,^(1,2)
+- where every solution compiles and runs in 1 s or less,^3
+- the total compile- and runtime does not exceed 10 s,^3
+- while running single-threaded, and
+- never exceeding 256 MiB memory use.^(3,4)
+
+Stretch goal: Total time does not exceed 1 second.
+
+^1 and, optionally, make.
+
+^2 Some programs contain commonly held assumptions, like "`uint32_t` is available", or "exit code 0 signal success", or "the stack doesn't overflow".
+ The programs may cause undefined behaviour if the input is not an unmodified one from AoC 2020.
+ If undefined behaviour (such as signed integer overflow) does occur for an unmodified AoC 2020 input, that's a bug and I'd like to know about it.
+
+^3 on the author's ~8 year old laptop with a i5-3320M CPU.
+
+^4 virtual memory.
+
+How to run
+----------
+
+First, copy or link the inputs files in the subfolder `inputs/`, with names `01`, `02`, …
+
+Then, if you're on a POSIX-y system and have `tcc` installed, run the following to measure runtime:
+
+```
+$ tcc -run time.c
+```
+
+Alternatively, and this will be much slower, run:
+
+```
+$ time make
+```
+
+Some compilers (e.g. clang on FreeBSD) may refuse to compile some programs because the makefile invokes the compiler with very strict error conditions.
+These errors are probably harmless suggestions and invoking `make` with the environment variable `CFLAGS=-Wno-error` should produce working executables.
+
+To run a program individually (e.g. to see the solutions), run:
+
+```
+$ tcc -run src/01.c < inputs/01
+```
+
+Or:
+
+```
+$ make run-01
+```
+
+To run each program repeatedly and print the best time, run:
+
+```
+$ tcc -Drepeat=10 -run time.c
+```
+
+Track/limit allocations
+-----------------------
+
+Track allocations with `make bin/01 && valgrind bin/01 < inputs/01`.
+
+Enforce memory limit with `ulimit -v 262144` (bash/fish shell on linux).
+
+Track partial runtimes
+----------------------
+
+```
+$ tcc -run time.c
+```
+
+Or (this will be slower):
+
+```
+$ time make clean run-01
+```
+
+[Day 1](src/01.c)
+-----------------
+
+Read every input number; create a lookup table of numbers (entry 0 if not present, 1 if present).
+For every input number `x` (part 2: pair of numbers), check if the table at `2020-x` is set.
+
+[Day 2](src/02.c)
+-----------------
+
+Who needs regular expressions when you have `scanf`?
+
+[Day 3](src/03.c)
+-----------------
+
+Modular arithmetic.
+
+[Day 4](src/04.c)
+-----------------
+
+Just read and validate input.
+
+[Day 5](src/05.c)
+-----------------
+
+Seat specs are binary expansions of seat IDs.
+
+The largest seat ID is pow(2, strlen(input)), so we can use statically allocate a bit vector that stores the IDs to avoid sorting.
+
+[Day 6](src/06.c)
+-----------------
+
+Set union and intersection.
+Sets are represented by 26 bits of a uint32_t.
+
+[Day 7](src/07.c)
+-----------------
+
+The problem itself is quite simple.
+Reading the input is a massive pain:
+
+- Read the input line by line.
+
+- Tokenize the input (split into words) and parse using a big switch that implements the following state machine:
+
+![finite state machine flowchart, see parsing-07.dot](./parsing-07.png/)
+
+To simplify memory management and speed up comparisons, each two-word color combination is assigned a number.
+All colors have the form "modifier base", with 17 different modifiers and 33 different base colors.
+The modifier can be mapped to a 5 bit number, and the base can be mapped to a 6 bit number.
+The bitwise concatenation of these numbers is the color id.
+
+All rules can be stored in a statically allocated array at the index of the outer bag id.
+
+Part 1:
+
+- While reading, generate a graph with flipped connections (transpose graph): For each inner bag, connect to the outer bags which need to contain that inner bag.
+
+- Starting at the ID of "shiny gold", walk through the graph with inverted connections and count the nodes encountered.
+
+Part 2:
+
+- While reading, generate a graph with the straightforward connections: For each outer bag, connect to the required bags inside it, and tag with the number.
+
+- Starting at the ID of "shiny gold", recursively compute the number of contained bags by walking through graph.
+
+In neither case are partial results memoized.
+Doing this may or may not reduce the runtime.
+
+[Day 8](src/08.c)
+-----------------
+
+Part 1: Implement the VM, stop once the value of the instruction pointer repeats.
+
+Part 2 is done with brute force: Change instructions one by one, check if the program terminates.
+
+[Day 9](src/09.c)
+-----------------
+
+Part 1: Brute force.
+
+Part 2: Sliding window sum.
+
+[Day 10](src/10.c)
+------------------
+
+Read the adapter joltages into a bool table; this avoids having to sort them.
+
+Part 1: Count the jolt differences.
+
+Part 2: Dynamic programming, starting with the largest joltage, for which we know there is exactly one combination.
+
+[Day 11](src/11.c)
+------------------
+
+There doesn't seem to be a way to shortcut the calculation, so just do it.
+Optimizations:
+
+- Use linear indices instead of Cartesian indices.
+
+- Precompute the neighbours of each seat instead of computing them on every update.
+
+- Avoid branches by computing all occupied neighbours before checking the update condition, and unrolling loops.
+
+[Day 12](src/12.c)
+------------------
+
+Just a bit of basic analytic geometry.
+
+[Day 13](src/13.c)
+------------------
+
+In part 1, the time until the bus with period `t` arrives after timestamp `t_0` is `t-(t_0%t)`.
+
+In part 2, the solution time `t` satisfies a system of linear congruences like this:
+
+```
+(t + 0) = 0 % bus_1_period
+(t + 1) = 0 % bus_2_period
+```
+
+It is solved with the Chinese Remainder Theorem.
+When computed naively, interim values can become very large (~80 bits), so some care is taken to avoid overflow.
+
+[Day 14](src/14.c)
+------------------
+
+Memory is saved in a hashmap.
+
+A previous version used a more elegent sparse binary tree, but this was slower by a factor of > 2.
A => inputs/.gitignore +2 -0
@@ 1,2 @@
+*
+!.gitignore
A => makefile +122 -0
@@ 1,122 @@
+.POSIX:
+
+COMPILE = $(CC) -std=c99 -O0 -W -Wall -Wextra -pedantic -pedantic-errors -Werror -Wfatal-errors $(CFLAGS) $(LDFLAGS)
+
+.PHONY: run
+run: run-01 run-02 run-03 run-04 run-05 run-06 run-07 run-08 run-09 run-10 run-11 run-12 run-13 run-14
+
+.PHONY: run-01
+run-01: bin/01
+ bin/01 < inputs/01
+
+bin/01: src/01.c
+ mkdir -p bin
+ $(COMPILE) src/01.c -o bin/01
+
+.PHONY: run-02
+run-02: bin/02
+ bin/02 < inputs/02
+
+bin/02: src/02.c
+ mkdir -p bin
+ $(COMPILE) src/02.c -o bin/02
+
+.PHONY: run-03
+run-03: bin/03
+ bin/03 < inputs/03
+
+bin/03: src/03.c
+ mkdir -p bin
+ $(COMPILE) src/03.c -o bin/03
+
+.PHONY: run-04
+run-04: bin/04
+ bin/04 < inputs/04
+
+bin/04: src/04.c
+ mkdir -p bin
+ $(COMPILE) src/04.c -o bin/04
+
+.PHONY: run-05
+run-05: bin/05
+ bin/05 < inputs/05
+
+bin/05: src/05.c
+ mkdir -p bin
+ $(COMPILE) src/05.c -o bin/05
+
+.PHONY: run-06
+run-06: bin/06
+ bin/06 < inputs/06
+
+bin/06: src/06.c
+ mkdir -p bin
+ $(COMPILE) src/06.c -o bin/06
+
+.PHONY: run-07
+run-07: bin/07
+ bin/07 < inputs/07
+
+bin/07: src/07.c
+ mkdir -p bin
+ $(COMPILE) src/07.c -o bin/07
+
+.PHONY: run-08
+run-08: bin/08
+ bin/08 < inputs/08
+
+bin/08: src/08.c
+ mkdir -p bin
+ $(COMPILE) src/08.c -o bin/08
+
+.PHONY: run-09
+run-09: bin/09
+ bin/09 < inputs/09
+
+bin/09: src/09.c
+ mkdir -p bin
+ $(COMPILE) src/09.c -o bin/09
+
+.PHONY: run-10
+run-10: bin/10
+ bin/10 < inputs/10
+
+bin/10: src/10.c
+ mkdir -p bin
+ $(COMPILE) src/10.c -o bin/10
+
+.PHONY: run-11
+run-11: bin/11
+ bin/11 < inputs/11
+
+bin/11: src/11.c
+ mkdir -p bin
+ $(COMPILE) src/11.c -o bin/11
+
+.PHONY: run-12
+run-12: bin/12
+ bin/12 < inputs/12
+
+bin/12: src/12.c
+ mkdir -p bin
+ $(COMPILE) src/12.c -o bin/12
+
+.PHONY: run-13
+run-13: bin/13
+ bin/13 < inputs/13
+
+bin/13: src/13.c
+ mkdir -p bin
+ $(COMPILE) src/13.c -o bin/13
+
+.PHONY: run-14
+run-14: bin/14
+ bin/14 < inputs/14
+
+bin/14: src/14.c
+ mkdir -p bin
+ $(COMPILE) src/14.c -o bin/14
+
+.PHONY: clean
+clean:
+ -rm -rf bin
A => parsing-07.dot +19 -0
@@ 1,19 @@
+digraph FSM {
+ size="12,8";
+ rankdir="LR";
+ "0 (Start)" [ shape=rectangle ];
+ "0 (Start)" -> 1 [ label="modifier" ];
+ 1 -> 2 [ label="base color" ];
+ 2 -> 3 [ label="'bags'" ];
+ 3 -> 4 [ label="'contain'" ];
+ 4 -> 5 [ label="'no'" ];
+ 5 -> 6 [ label="'other'" ];
+ 6 -> 7 [ label="'bags'" ];
+ 7 [ peripheries=2 ];
+ 4 -> 8 [ label="unsigned int" ];
+ 8 -> 9 [ label="modifier" ];
+ 9 -> 10 [ label="base color" ];
+ 10 -> 11 [ label="'bags'" ];
+ 11 [ peripheries=2 ];
+ 11 -> 8 [ label = "unsigned int" ];
+}
A => parsing-07.png +0 -0
A => src/01.c +66 -0
@@ 1,66 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define N 2020
+
+void day1_part1(const unsigned int *numbers, const bool *lookup_table, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ unsigned int x = numbers[i];
+ if (lookup_table[N - x]) {
+ printf("%ld\n", (long)x*(N-(long)x));
+ break;
+ }
+ }
+}
+
+void day1_part2(const unsigned int *numbers, const bool *lookup_table, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ unsigned int x = numbers[i];
+ for (size_t j = i; j < len; ++j) {
+ unsigned int y = numbers[j];
+ unsigned int tmp = x + y;
+ if (tmp > N) {
+ continue;
+ }
+ if (lookup_table[N - tmp]) {
+ printf("%lld\n", (long long)x*(long long)y*(N-(long long)tmp));
+ return;
+ }
+ }
+ }
+}
+
+int main(void) {
+ /* read input */
+ size_t len = 0;
+ unsigned int numbers[300] = { 0, };
+ bool lookup_table[N+1] = { 0, };
+ {
+ int ret;
+ unsigned int n;
+ while ((ret = scanf("%u\n", &n)) > 0) {
+ /* input validation to avoid overflow */
+ if (n > N) {
+ continue;
+ }
+ numbers[len++] = n;
+ lookup_table[n] = true;
+ if (len > sizeof numbers / sizeof *numbers) {
+ fprintf(stderr, "Error: Input too large.\n");
+ return 1;
+ }
+ }
+ if (ret != EOF) {
+ fprintf(stderr, "Error parsing input: %s.\n", strerror(errno));
+ return 1;
+ }
+ }
+
+ /* run search */
+ day1_part1(numbers, lookup_table, len);
+ day1_part2(numbers, lookup_table, len);
+
+ return 0;
+}
A => src/02.c +51 -0
@@ 1,51 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define MAXSIZE 40
+#define MAXSIZE_S "40"
+
+bool check_part1(unsigned i1, unsigned i2, char c, const char *pw) {
+ size_t count = 0;
+ while (*pw) {
+ count += (*pw == c);
+ ++pw;
+ }
+ return (i1 <= count) && (count <= i2);
+}
+
+bool check_part2(unsigned i1, unsigned i2, char c, const char *pw) {
+ return (pw[i1-1] == c) ^ (pw[i2-1] == c);
+}
+
+int main(void) {
+ size_t valid_1 = 0;
+ size_t valid_2 = 0;
+ int ret;
+ while (1) {
+ unsigned i1;
+ unsigned i2;
+ char c;
+ char pw[MAXSIZE+1];
+ if ((ret = scanf("%u-%u %c: %"MAXSIZE_S"s\n", &i1, &i2, &c, pw)) <= 0) {
+ break;
+ }
+ /* input validation */
+ if (i1 < 1 || i2 > strlen(pw)) {
+ continue;
+ }
+ /* check */
+ valid_1 += (size_t) check_part1(i1, i2, c, pw);
+ valid_2 += (size_t) check_part2(i1, i2, c, pw);
+ }
+ if (ret != EOF) {
+ fprintf(stderr, "Error parsing input: %s.\n", strerror(errno));
+ return 1;
+ }
+
+ printf("%zu\n", valid_1);
+ printf("%zu\n", valid_2);
+
+ return 0;
+}
A => src/03.c +85 -0
@@ 1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#define BUFSIZE 20000
+
+struct grid {
+ bool *data;
+ size_t w;
+ size_t h;
+};
+
+bool grid_get(const struct grid g, size_t x, size_t y) {
+ assert(x < g.w && y < g.h);
+ return g.data[x + y * g.w];
+}
+
+void grid_set(struct grid g, size_t x, size_t y, bool value) {
+ assert(x < g.w && y < g.h);
+ g.data[x + y * g.w] = value;
+}
+
+struct grid read_input() {
+ struct grid g;
+ char buf[BUFSIZE];
+ size_t bufsize = fread(buf, 1, sizeof buf, stdin);
+ assert(bufsize < sizeof buf);
+ /* First pass, get size */
+ g.w = 0;
+ g.h = 0;
+ for (size_t i = 0; i < bufsize; ++i) {
+ if (buf[i] == '\n') {
+ if (g.w == 0) {
+ g.w = i;
+ }
+ ++g.h;
+ }
+ }
+ assert(g.w > 0);
+ /* Second pass, read into result */
+ g.data = calloc(g.w * g.h, sizeof *g.data);
+ assert(g.data != NULL);
+ size_t i_x = 0;
+ size_t i_y = 0;
+ for (size_t i = 0; i < bufsize; ++i) {
+ char c = buf[i];
+ if (c == '#') {
+ grid_set(g, i_x, i_y, true);
+ }
+ if (c == '\n') {
+ assert(i_x == g.w);
+ i_x = 0;
+ ++i_y;
+ }
+ else {
+ ++i_x;
+ }
+ }
+ return g;
+}
+
+size_t part1(const struct grid g, size_t dx, size_t dy) {
+ size_t x = 0;
+ size_t y = 0;
+ size_t count = 0;
+ do {
+ count += grid_get(g, x, y);
+ x = (x + dx) % g.w;
+ y += dy;
+ } while (y < g.h);
+ return count;
+}
+
+int main(void) {
+ struct grid g = read_input();
+ size_t count = part1(g, 3, 1);
+ printf("%zu\n", count);
+ count *= part1(g, 1, 1);
+ count *= part1(g, 5, 1);
+ count *= part1(g, 7, 1);
+ count *= part1(g, 1, 2);
+ printf("%zu\n", count);
+ free(g.data);
+}
A => src/04.c +213 -0
@@ 1,213 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#define BUFSIZE 100
+#define BUFSIZE_S "100"
+
+size_t find_whitespace(const char *buf) {
+ size_t i = 0;
+ for (; buf[i] != '\0' && buf[i] != '\n' && buf[i] != ' '; ++i) { }
+ return i;
+}
+
+size_t find_colon(const char *buf) {
+ size_t i = 0;
+ for (; buf[i] != '\0' && buf[i] != ':'; ++i) { }
+ return i;
+}
+
+void *xmalloc(size_t size) {
+ void *ret = calloc(size, 1);
+ assert(ret);
+ return ret;
+}
+
+struct passport {
+ /* Any of these may be NULL */
+ char *byr;
+ char *iyr;
+ char *eyr;
+ char *hgt;
+ char *hcl;
+ char *ecl;
+ char *pid;
+ //char *cid; // unnecessary
+};
+
+bool validate_passport1(const struct passport *p) {
+ return p->byr && p->iyr && p->eyr && p->hgt && p->hgt && p->hcl && p->ecl && p->pid;
+}
+
+bool validate_byr(const char *byr) {
+ return byr != NULL && strlen(byr) == 4 && 1920 <= atoi(byr) && atoi(byr) <= 2002;
+}
+
+bool validate_iyr(const char *iyr) {
+ return iyr != NULL && strlen(iyr) == 4 && 2010 <= atoi(iyr) && atoi(iyr) <= 2020;
+}
+
+bool validate_eyr(const char *eyr) {
+ return eyr != NULL && strlen(eyr) == 4 && 2020 <= atoi(eyr) && atoi(eyr) <= 2030;
+}
+
+bool validate_hgt(const char *hgt) {
+ if (hgt == NULL) {
+ return false;
+ }
+ size_t len = strlen(hgt);
+ if (len == 5) {
+ if (strcmp(hgt + 3, "cm") != 0) {
+ return false;
+ }
+ unsigned n = atoi(hgt);
+ return 150 <= n && n <= 193;
+ }
+ else if (len == 4) {
+ if (strcmp(hgt + 2, "in") != 0) {
+ return false;
+ }
+ unsigned n = atoi(hgt);
+ return 59 <= n && n <= 76;
+ }
+ return false;
+}
+
+bool validate_hcl(const char *hcl) {
+ if (hcl == NULL || strlen(hcl) != 7 || hcl[0] != '#') {
+ return false;
+ }
+ for (size_t i = 1; i < 7; ++i) {
+ if (!isxdigit(hcl[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool validate_ecl(const char *ecl) {
+ if (ecl == NULL) {
+ return false;
+ }
+ char *valid[] = { "amb", "blu", "brn", "gry", "grn", "hzl", "oth" };
+ for (size_t i = 0; i < sizeof valid / sizeof *valid; ++i) {
+ if (strcmp(ecl, valid[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool validate_pid(const char *pid) {
+ if (pid == NULL || strlen(pid) != 9) {
+ return false;
+ }
+ for (size_t i = 0; i < 9; ++i) {
+ if (!isdigit(pid[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool validate_passport2(const struct passport *p) {
+ return
+ validate_byr(p->byr) &&
+ validate_iyr(p->iyr) &&
+ validate_eyr(p->eyr) &&
+ validate_hgt(p->hgt) &&
+ validate_hcl(p->hcl) &&
+ validate_ecl(p->ecl) &&
+ validate_pid(p->pid);
+}
+
+int main(void) {
+ size_t valid1 = 0;
+ size_t valid2 = 0;
+ struct passport p = { 0, };
+ while (true) {
+ char buffer[BUFSIZE+1] = { 0, }; /* BUFSIZE+1 ensures that the line ends with two null bytes */
+ if (fgets(buffer, BUFSIZE, stdin) == NULL) {
+ if (ferror(stdin)) {
+ fprintf(stderr, "Read error: %s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ break;
+ }
+ size_t bufsize = strlen(buffer);
+ assert(bufsize > 0);
+ if (buffer[bufsize-1] != '\n') {
+ fprintf(stderr, "Line too long, longer than %d.\n", BUFSIZE);
+ return EXIT_FAILURE;
+ }
+
+ if (bufsize == 1) {
+ /* empty line */
+ valid1 += validate_passport1(&p);
+ valid2 += validate_passport2(&p);
+ free(p.byr);
+ free(p.iyr);
+ free(p.eyr);
+ free(p.hgt);
+ free(p.hcl);
+ free(p.ecl);
+ free(p.pid);
+ memset(&p, 0, sizeof p);
+ continue;
+ }
+
+ char *buf = buffer;
+ while (*buf) { /* this loop relies on buf ending in two null bytes (or a space and a null byte) */
+ /* find colon, which gives the end of the key */
+ size_t k_len = find_colon(buf);
+ char **entry = NULL;
+ if (strncmp(buf, "byr", k_len) == 0) {
+ entry = &p.byr;
+ }
+ else if (strncmp(buf, "iyr", k_len) == 0) {
+ entry = &p.iyr;
+ }
+ else if (strncmp(buf, "eyr", k_len) == 0) {
+ entry = &p.eyr;
+ }
+ else if (strncmp(buf, "hgt", k_len) == 0) {
+ entry = &p.hgt;
+ }
+ else if (strncmp(buf, "hcl", k_len) == 0) {
+ entry = &p.hcl;
+ }
+ else if (strncmp(buf, "ecl", k_len) == 0) {
+ entry = &p.ecl;
+ }
+ else if (strncmp(buf, "pid", k_len) == 0) {
+ entry = &p.pid;
+ }
+ /* advance buffer and find space or newline, which ends the value */
+ buf += k_len + 1;
+ size_t v_len = find_whitespace(buf);
+ /* if the key is "interesting", save the value */
+ if (entry != NULL) {
+ *entry = xmalloc(v_len+1);
+ memcpy(*entry, buf, v_len);
+ }
+ buf += v_len + 1;
+ }
+ }
+ free(p.byr);
+ free(p.iyr);
+ free(p.eyr);
+ free(p.hgt);
+ free(p.hcl);
+ free(p.ecl);
+ free(p.pid);
+
+
+ printf("%zu\n", valid1);
+ printf("%zu\n", valid2);
+
+ return EXIT_SUCCESS;
+}
A => src/05.c +59 -0
@@ 1,59 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#define BUFSIZE 10
+#define BUFSIZE_S "10"
+
+int main(void) {
+ bool assigned[1024] = { 0, };
+ unsigned min = 65535;
+ unsigned max = 0;
+
+ /* read input and get min and max id */
+ int ret = 0;
+ char buf[BUFSIZE+1];
+ while ((ret = scanf("%"BUFSIZE_S"s\n", buf)) > 0) {
+ unsigned id = 0;
+ for (size_t i = 0; buf[i]; ++i) {
+ id = id << 1 | (buf[i] == 'B' || buf[i] == 'R');
+ }
+ if (id > sizeof assigned / sizeof *assigned) {
+ fprintf(stderr, "Error: Seat ID too large!\n");
+ return 0;
+ }
+ else {
+ assigned[id] = true;
+ }
+ if (id < min) {
+ min = id;
+ }
+ if (id > max) {
+ max = id;
+ }
+ }
+ if (ret != EOF) {
+ fprintf(stderr, "Error: %s.\n", strerror(errno));
+ return 1;
+ }
+
+ /* part 1: print max */
+ printf("%u\n", max);
+
+ /* part 2: find empty spot in the middle */
+ if (min == 0) {
+ min = 1;
+ }
+ if (max >= (sizeof assigned / sizeof *assigned) - 1) {
+ max = (sizeof assigned / sizeof *assigned) - 2;
+ }
+ for (size_t i = min; i < max; ++i) {
+ if (!assigned[i] && assigned[i-1] && assigned[i+1]) {
+ printf("%zu\n", i);
+ return 0;
+ }
+ }
+ fprintf(stderr, "Error: No seat found.\n");
+ return 1;
+}
A => src/06.c +67 -0
@@ 1,67 @@
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+#define ANSWERS_ALL_INIT 0x03ffffff /* bits 0 to 25 set */
+
+uint16_t count_ones(uint32_t x) {
+ uint16_t sum = 0;
+ for (size_t i = 0; i < CHAR_BIT * sizeof x; ++i) {
+ sum += (x >> i) & 1;
+ }
+ return sum;
+}
+
+int main(void) {
+ uint16_t sum1 = 0;
+ uint16_t sum2 = 0;
+
+ uint32_t answers_any = 0;
+ uint32_t answers_all = ANSWERS_ALL_INIT;
+ bool group_empty = true;
+
+ while (1) {
+ char buf[28] = { 0, };
+ char *ret = fgets(buf, sizeof buf, stdin);
+ if (ret == NULL || strlen(buf) == 0 || (strlen(buf) == 1 && buf[0] == '\n')) {
+ /* end of group, end of file, or error */
+ if (!group_empty) {
+ /* update sum */
+ group_empty = true;
+ sum1 += count_ones(answers_any);
+ sum2 += count_ones(answers_all);
+ answers_any = 0;
+ answers_all = ANSWERS_ALL_INIT;
+ }
+ if (ret == NULL) {
+ /* EOF */
+ if (ferror(stdin)) {
+ fprintf(stderr, "Input error: %s.\n", strerror(errno));
+ return 1;
+ }
+ break;
+ }
+ }
+ else {
+ /* got answer line */
+ group_empty = false;
+ uint32_t answers = 0;
+ for (size_t i = 0; buf[i]; ++i) {
+ if ('a' <= buf[i] && buf[i] <= 'z') {
+ answers |= ((uint32_t) 1) << (buf[i] - 'a');
+ }
+ }
+ answers_any |= answers;
+ answers_all &= answers;
+ }
+ }
+
+ printf("%"PRIu16"\n", sum1);
+ printf("%"PRIu16"\n", sum2);
+
+ return 0;
+}
A => src/07.c +199 -0
@@ 1,199 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+
+#define die(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+#define die_unless(cond, ...) do { \
+ if (!(cond)) { \
+ die(__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define die_if(cond, ...) die_unless(!(cond), __VA_ARGS__)
+
+typedef struct {
+ uint16_t out_nodes[20];
+ uint8_t out_numbers[20];
+} rule_t;
+
+const char * const modifiers[] = { "bright", "clear", "dark", "dim", "dotted", "drab", "dull", "faded", "light", "mirrored", "muted", "pale", "plaid", "posh", "shiny", "striped", "vibrant", "wavy", };
+const int modifier_bits = 5;
+const char * const base_colors[] = { "aqua", "beige", "black", "blue", "bronze", "brown", "chartreuse", "coral", "crimson", "cyan", "fuchsia", "gold", "gray", "green", "indigo", "lavender", "lime", "magenta", "maroon", "olive", "orange", "plum", "purple", "red", "salmon", "silver", "tan", "teal", "tomato", "turquoise", "violet", "white", "yellow", };
+const int base_color_bits = 6;
+
+size_t bisect(const char * const arr[], size_t len, const char *s) {
+ size_t lo = 0;
+ size_t hi = len - 1;
+ if (strcmp(s, arr[lo]) == 0) {
+ return lo;
+ }
+ if (strcmp(s, arr[hi]) == 0) {
+ return hi;
+ }
+ while (1) {
+ /* we loop forever if "s" is not found. */
+ size_t mid = lo + (hi - lo) / 2;
+ int x = strcmp(s, arr[mid]);
+ if (x == 0) {
+ return mid;
+ }
+ else if (x < 0) {
+ hi = mid;
+ }
+ else {
+ lo = mid;
+ }
+ }
+}
+
+uint16_t get_id(uint16_t mod_index, uint16_t base_index) {
+ uint16_t x = (mod_index << base_color_bits) | base_index;
+ die_unless(x < (1<<11), "Internal error, this should never happen.\n");
+ return x;
+}
+
+size_t next_space(const char *s) {
+ size_t off = 0;
+ for (; s[off] && s[off] != ' '; ++off);
+ return off;
+}
+
+size_t next_line(const char *s) {
+ size_t off = 0;
+ for (; s[off] && s[off] != '\n'; ++off);
+ return off;
+}
+
+void read_input(rule_t *rules_contains, rule_t *rules_contained) {
+ char line[1<<8];
+ while (fgets(line, sizeof line, stdin) != NULL) {
+ die_if(strlen(line) == (sizeof line), "Error: line too long.\n"); /* TODO could maybe save some time here. */
+ char *word;
+ /* parse rule using a handrolled state machine */
+ rule_t r = { { 0, }, { 0, } };
+ uint16_t mod = 0;
+ uint16_t base = 1;
+ uint16_t rule_id = 0;
+ uint8_t num = 0;
+ int state = 0;
+ while ((word = strtok(state ? NULL : line, " \n")) != 0) {
+ switch (state) {
+ case 0: /* fall through */
+ case 8:
+ mod = bisect(modifiers, sizeof modifiers / sizeof *modifiers, word);
+ ++state;
+ break;
+ case 1:
+ base = bisect(base_colors, sizeof base_colors / sizeof *base_colors, word);
+ rule_id = get_id(mod, base);
+ ++state;
+ break;
+ case 4:
+ {
+ int n = atoi(word);
+ if (n > 0) {
+ /* TODO: maybe check that n <= UINT8_MAX; */
+ num = (uint8_t) n;
+ state = 8;
+ }
+ else {
+ ++state;
+ }
+ }
+ break;
+ case 7:
+ die("Error: unexpected word in input: %s\n", word);
+ break;
+ case 9:
+ base = bisect(base_colors, sizeof base_colors / sizeof *base_colors, word);
+ /* got number and color ID of the inner bag, now store it */
+ {
+ size_t i = 0;
+ for (; i < sizeof r.out_numbers / sizeof *r.out_numbers && r.out_numbers[i] != 0; ++i);
+ die_unless(i < sizeof r.out_numbers / sizeof *r.out_numbers, "Error: too many for one bag");
+ r.out_numbers[i] = num;
+ r.out_nodes[i] = get_id(mod, base);
+ }
+ ++state;
+ break;
+ case 11:
+ num = atoi(word);
+ state = 8;
+ break;
+ case 2: /* fall through*/
+ case 3: /* fall through*/
+ case 5: /* fall through*/
+ case 6: /* fall through*/
+ case 10:
+ ++state;
+ break;
+ default:
+ die("Internal error: this should never happen.\n");
+ }
+ }
+ die_unless(state == 7 || state == 11, "Unexpected end of input (in state %d)\n", state);
+ /* add rules to the first graph ("contains") */
+ memcpy(rules_contains + rule_id, &r, sizeof r);
+ /* and the second ("contained") */
+ for (size_t i = 0; i < sizeof r.out_nodes / sizeof *r.out_nodes; ++i) {
+ if (r.out_numbers[i] == 0) {
+ break;
+ }
+ uint16_t id = r.out_nodes[i];
+ size_t j = 0;
+ /* find a place to store the reverse connection */
+ for (j = 0; j < (sizeof r.out_nodes / sizeof *r.out_nodes) && rules_contained[id].out_numbers[j] != 0; ++j);
+ die_unless(j < sizeof r.out_nodes / sizeof *r.out_nodes, "Error: too many reverse connections.\n");
+ rules_contained[id].out_numbers[j] = 1; /* or any nonzero number */
+ rules_contained[id].out_nodes[j] = rule_id;
+ }
+ }
+ die_if(ferror(stdin), "Read error: %s", strerror(errno));
+}
+
+size_t part1(rule_t *rules, uint16_t id) {
+ bool visited[1<<11] = { 0, };
+ uint16_t to_visit[1<<7] = { 0, };
+ to_visit[0] = id;
+ size_t to_visit_len = 1;
+ size_t sum = 0;
+ while (to_visit_len > 0) {
+ uint16_t id = to_visit[--to_visit_len];
+ for (size_t i = 0; i < (sizeof rules[id].out_nodes / sizeof *rules[id].out_nodes) && rules[id].out_numbers[i] != 0; ++i) {
+ die_unless(to_visit_len < sizeof to_visit / sizeof *to_visit - 1, "Error: ran out of space.\n");
+ size_t new_id = rules[id].out_nodes[i];
+ if (!visited[new_id]) {
+ visited[new_id] = true;
+ sum += 1;
+ to_visit[to_visit_len++] = new_id;
+ continue;
+ }
+ }
+ }
+ return sum;
+}
+
+size_t part2(const rule_t *rules, uint16_t id) {
+ size_t sum = 1;
+ for (size_t i = 0; i < (sizeof rules[id].out_nodes / sizeof *rules[id].out_nodes) && rules[id].out_nodes[i] != 0; ++i) {
+ sum += rules[id].out_numbers[i] * part2(rules, rules[id].out_nodes[i]);
+ }
+ return sum;
+}
+
+int main(void) {
+ rule_t rules_contains[1<<11] = { 0, };
+ rule_t rules_contained[1<<11] = { 0, };
+ read_input(rules_contains, rules_contained);
+ const uint16_t shiny_gold_id = get_id(bisect(modifiers, sizeof modifiers / sizeof *modifiers, "shiny"), bisect(base_colors, sizeof base_colors / sizeof *base_colors, "gold"));
+ printf("%zu\n", part1(rules_contained, shiny_gold_id));
+ printf("%zu\n", part2(rules_contains, shiny_gold_id) - 1);
+ return 0;
+}
A => src/08.c +100 -0
@@ 1,100 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+
+#define BUFSIZE 10
+#define BUFSIZE_S "10"
+#define MAX_INS (1<<10)
+
+#define die(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+#define die_unless(cond, ...) do { \
+ if (!(cond)) { \
+ die(__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define die_if(cond, ...) die_unless(!(cond), __VA_ARGS__)
+
+typedef enum {
+ NOP, ACC, JMP,
+} op_t;
+
+typedef struct {
+ op_t op;
+ int arg;
+} ins_t;
+
+bool run_until_loop_or_end(const ins_t instructions[], size_t n_instructions, int *acc_ret) {
+ size_t ip = 0;
+ int acc = 0;
+ bool visited[MAX_INS] = { 0, };
+ while (ip < n_instructions && !visited[ip]) {
+ visited[ip] = true;
+ ins_t ins = instructions[ip];
+ switch (ins.op) {
+ case NOP: ++ip; break;
+ case ACC: ++ip; acc += ins.arg; break;
+ case JMP: ip += ins.arg; break;
+ }
+ }
+ *acc_ret = acc;
+ return (ip >= n_instructions);
+}
+
+int main(void) {
+ ins_t instructions[1<<10] = { 0, };
+ size_t n_ins = 0;
+
+ /* Read input */
+ char buf[BUFSIZE+1] = { 0, };
+ int ret = 0;
+ int n = 0;
+ while ((ret = scanf("%"BUFSIZE_S"s %d\n", buf, &n)) > 0) {
+ die_if(n_ins + 1 >= sizeof instructions / sizeof *instructions, "Too many instructions.\n");
+ if (strcmp(buf, "nop") == 0) {
+ instructions[n_ins].op = NOP;
+ }
+ else if (strcmp(buf, "acc") == 0) {
+ instructions[n_ins].op = ACC;
+ }
+ else if (strcmp(buf, "jmp") == 0) {
+ instructions[n_ins].op = JMP;
+ }
+ else {
+ die("Invalid operation: %s.\n", buf);
+ }
+ instructions[n_ins++].arg = n;
+ }
+ die_unless(feof(stdin), "Expected_eof.\n");
+ die_if(ferror(stdin), "Read error: %s\n", strerror(errno));
+
+ /* Part 1 */
+ (void) run_until_loop_or_end(instructions, n_ins, &ret);
+ printf("%d\n", ret);
+
+ /* Part 2 */
+ for (size_t i = 0; i < n_ins; ++i) {
+ ins_t ins = instructions[i];
+ if (ins.op == NOP) {
+ instructions[i].op = JMP;
+ }
+ else if (ins.op == JMP) {
+ instructions[i].op = NOP;
+ }
+ else {
+ continue;
+ }
+ if (run_until_loop_or_end(instructions, n_ins, &ret)) {
+ printf("%d\n", ret);
+ return EXIT_SUCCESS;
+ }
+ instructions[i].op = ins.op;
+ }
+ die("No termination.\n");
+}
A => src/09.c +82 -0
@@ 1,82 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <limits.h>
+
+bool is_sum_of_two(long long n, const long long *numbers, size_t len) {
+ for (size_t i = 1; i < len; ++i) {
+ for (size_t j = 0; j < i; ++j) {
+ if (numbers[i] + numbers[j] == n) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void extrema(const long long *numbers, size_t len, long long *min, long long *max) {
+ *min = LLONG_MAX;
+ *max = 0;
+ for (size_t i = 0; i < len; ++i) {
+ if (numbers[i] < *min) {
+ *min = numbers[i];
+ }
+ else if (numbers[i] > *max) {
+ *max = numbers[i];
+ }
+ }
+}
+
+int main(void) {
+ long long numbers[1<<10] = { 0, };
+ size_t N = 0;
+
+ /* read input */
+ int ret = 0;
+ while (N < sizeof numbers / sizeof *numbers && (ret = scanf("%lld\n", &numbers[N])) > 0) {
+ ++N;
+ }
+ if (ret < 0 && !feof(stdin)) {
+ fprintf(stderr, "Error: %s.\n", strerror(errno));
+ return 1;
+ }
+ if (N < 26) {
+ fprintf(stderr, "Error: input too short.\n");
+ return 1;
+ }
+
+ /* part 1 */
+ long long weak = 0;
+ for (size_t i = 25; i < N; ++i) {
+ if (!is_sum_of_two(numbers[i], numbers + (i - 25), 25)) {
+ weak = numbers[i];
+ break;
+ }
+ }
+ printf("%lld\n", weak);
+
+ /* part 2*/
+ size_t i = 0;
+ size_t j = 1;
+ long long sum = numbers[0] + numbers[1];
+ while (j < N) {
+ if (sum == weak) {
+ long long min, max;
+ extrema(numbers + i, j - i + 1, &min, &max);
+ printf("%lld\n", min + max);
+ return 0;
+ }
+ if (j == i + 1) {
+ sum += numbers[++j];
+ }
+ else if (sum < weak) {
+ sum += numbers[++j];
+ }
+ else {
+ sum -= numbers[i++];
+ }
+ }
+ fprintf(stderr, "No solution.\n");
+ return 1;
+}
A => src/10.c +52 -0
@@ 1,52 @@
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(void) {
+ bool adapters[200] = { 0, };
+ adapters[0] = true;
+ int ret = 0;
+ size_t max_rating = 0;
+ size_t rating;
+ while ((ret = scanf("%zu\n", &rating)) > 0) {
+ if (rating > sizeof adapters / sizeof *adapters) {
+ fprintf(stderr, "Error: Too many adapters.\n");
+ return 1;
+ }
+ adapters[rating] = true;
+ if (rating > max_rating) {
+ max_rating = rating;
+ }
+ }
+ if (ferror(stdin)) {
+ fprintf(stderr, "Input error: %s.\n", strerror(errno));
+ return 1;
+ }
+
+ long long combinations[2 + sizeof adapters / sizeof *adapters] = { 0, };
+ combinations[max_rating] = 1;
+ unsigned deltas[4] = { 0, };
+ size_t delta = 0;
+ for (size_t i = max_rating - 1; i != SIZE_MAX; --i) {
+ ++delta;
+ if (adapters[i]) {
+ combinations[i] += combinations[i+1];
+ combinations[i] += combinations[i+2];
+ combinations[i] += combinations[i+3];
+ if (delta > sizeof deltas / sizeof *deltas) {
+ fprintf(stderr, "Error: gap in ratings larger than 3.\n");
+ return 1;
+ }
+ else {
+ ++deltas[delta];
+ delta = 0;
+ }
+ }
+ }
+
+ printf("%u\n", deltas[1] * (deltas[3]+1));
+ printf("%lld\n", combinations[0]);
+ return 0;
+}
A => src/11.c +130 -0
@@ 1,130 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#define MAX_DATASIZE 10000
+
+/* tile_t was originally an enum, but this is a few ms faster: */
+#define tile_t char
+#define FLOOR 0
+#define EMPTY 1
+#define OCC 2
+
+typedef struct {
+ tile_t data[MAX_DATASIZE+1]; /* data[MAX_DATASIZE] == FLOOR for neighbour map */
+ size_t sx;
+ size_t sy;
+} grid_t;
+
+#define flatten(g, i, j) (i * g.sy + j)
+
+size_t count_occupied(const grid_t *g) {
+ size_t count = 0;
+ for (size_t i = 0; i < g->sx * g->sy; ++i) {
+ count += g->data[i] == OCC;
+ }
+ return count;
+}
+
+size_t run(const grid_t *g, const size_t *neighbour_map, size_t max_neigh) {
+ grid_t copy;
+ memcpy(©, g, sizeof copy);
+ size_t size = g->sx * g->sy;
+ bool updated = false;
+ unsigned occupied_neighbours[MAX_DATASIZE];
+ do {
+ for (size_t i = 0; i < size; ++i) {
+ occupied_neighbours[i] =
+ (copy.data[neighbour_map[i*8+0]] >> 1) /* FLOOR>>1 == 0 */
+ + (copy.data[neighbour_map[i*8+1]] >> 1) /* EMPTY>>1 == 1 */
+ + (copy.data[neighbour_map[i*8+2]] >> 1) /* OCC>>1 == 1 */
+ + (copy.data[neighbour_map[i*8+3]] >> 1)
+ + (copy.data[neighbour_map[i*8+4]] >> 1)
+ + (copy.data[neighbour_map[i*8+5]] >> 1)
+ + (copy.data[neighbour_map[i*8+6]] >> 1)
+ + (copy.data[neighbour_map[i*8+7]] >> 1);
+ }
+ updated = false;
+ for (size_t i = 0; i < size; ++i) {
+ if (copy.data[i] == EMPTY && occupied_neighbours[i] == 0) {
+ copy.data[i] = OCC;
+ updated = true;
+ }
+ else if (copy.data[i] == OCC && occupied_neighbours[i] > max_neigh) {
+ copy.data[i] = EMPTY;
+ updated = true;
+ }
+ }
+ } while (updated);
+ return count_occupied(©);
+}
+
+int main(void) {
+ grid_t grid = { .data = { 0, }, .sx = 0, .sy = 0};
+ {
+ size_t i = 0;
+ size_t ix = 0;
+ size_t iy = 0;
+ int c;
+ while ((c = getc(stdin)) != EOF && i < sizeof grid.data / sizeof *grid.data) {
+ if (c == '\n') {
+ ++iy;
+ if (ix > grid.sx) {
+ grid.sx = ix;
+ }
+ ix = 0;
+ }
+ else {
+ grid.data[i++] = (c == 'L');
+ ++ix;
+ }
+ }
+ grid.sy = iy;
+ if (ferror(stdin)) {
+ fprintf(stderr, "Read error: %s\n", strerror(errno));
+ return 1;
+ }
+ else if (grid.sx * grid.sy > (sizeof grid.data / sizeof *grid.data) - 1) {
+ fprintf(stderr, "Input too large.\n");
+ return 1;
+ }
+ }
+
+ /* get nearest neighbours for each seat */
+ const size_t NEIGH_DX[] = { 1, 1, 0, -1, -1, -1, 0, 1 };
+ const size_t NEIGH_DY[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
+ size_t neighbour_map_1[MAX_DATASIZE*8] = { 0, };
+ size_t neighbour_map_2[MAX_DATASIZE*8] = { 0, };
+ for (size_t i = 0; i < grid.sx; ++i) {
+ for (size_t j = 0; j < grid.sy; ++j) {
+ size_t index = flatten(grid, i, j);
+ for (size_t k = 0; k < 8; ++k) {
+ size_t i_ = i + NEIGH_DX[k];
+ size_t j_ = j + NEIGH_DY[k];
+ if (i_ < grid.sx && j_ < grid.sy && grid.data[flatten(grid, i_, j_)] != FLOOR) {
+ neighbour_map_1[index*8+k] = flatten(grid, i_, j_);
+ neighbour_map_2[index*8+k] = flatten(grid, i_, j_);
+ }
+ else {
+ neighbour_map_1[index*8+k] = MAX_DATASIZE;
+ while (i_ < grid.sx && j_ < grid.sy && grid.data[flatten(grid, i_, j_)] == FLOOR) {
+ i_ += NEIGH_DX[k];
+ j_ += NEIGH_DY[k];
+ }
+ if (i_ < grid.sx && j_ < grid.sy) {
+ neighbour_map_2[index*8+k] = flatten(grid, i_, j_);
+ }
+ else {
+ neighbour_map_2[index*8+k] = MAX_DATASIZE;
+ }
+ }
+ }
+ }
+ }
+
+ printf("%zu\n", run(&grid, (const size_t*) neighbour_map_1, 3));
+ printf("%zu\n", run(&grid, (const size_t*) neighbour_map_2, 4));
+
+ return 0;
+}
A => src/12.c +106 -0
@@ 1,106 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#define abs(x) (x > 0 ? x : -x)
+
+typedef struct {
+ long x;
+ long y;
+} point_t;
+
+#define dir_t unsigned
+#define EAST 0
+#define NORTH 1
+#define WEST 2
+#define SOUTH 3
+
+point_t move_forward(point_t position, dir_t direction, long steps) {
+ point_t result = position;
+ switch (direction) {
+ case EAST: /* east */
+ result.x += steps;
+ break;
+ case NORTH: /* north */
+ result.y += steps;
+ break;
+ case WEST: /* west */
+ result.x -= steps;
+ break;
+ case SOUTH: /* south */
+ result.y -= steps;
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+point_t rot_r(point_t p) {
+ point_t result = {
+ .x = p.y,
+ .y = -p.x,
+ };
+ return result;
+}
+
+point_t rot_l(point_t p) {
+ point_t result = {
+ .x = -p.y,
+ .y = p.x,
+ };
+ return result;
+}
+
+int main(void) {
+ point_t pos1 = { .x = 0, .y = 0 };
+ dir_t dir1 = EAST;
+
+ point_t pos2 = { .x = 0, .y = 0 };
+ point_t wp2 = { .x = 10, .y = 1 };
+
+ dir_t directions[1<<CHAR_BIT] = { 0, };
+ directions['E'] = EAST;
+ directions['N'] = NORTH;
+ directions['W'] = WEST;
+ directions['S'] = SOUTH;
+
+ int ret = 0;
+ char c = 0;
+ long n = 0;
+ while ((ret = scanf("%c%ld\n", &c, &n)) > 0) {
+ switch(c) {
+ case 'L':
+ for (long i = 0; i < n; i += 90) {
+ dir1 = (dir1 + 1) % 4;
+ wp2 = rot_l(wp2);
+ }
+ break;
+ case 'R':
+ for (long i = 0; i < n; i += 90) {
+ dir1 = (dir1 + 3) % 4;
+ wp2 = rot_r(wp2);
+ }
+ break;
+ case 'F':
+ pos1 = move_forward(pos1, dir1, n);
+ pos2.x += n * wp2.x;
+ pos2.y += n * wp2.y;
+ break;
+ default:
+ pos1 = move_forward(pos1, directions[(size_t) c], n);
+ wp2 = move_forward(wp2, directions[(size_t) c], n);
+ break;
+ }
+ }
+ if (ret != EOF) {
+ fprintf(stderr, "Input error: %s.\n", strerror(errno));
+ return 1;
+ }
+
+ printf("%ld\n", abs(pos1.x) + abs(pos1.y));
+ printf("%ld\n", abs(pos2.x) + abs(pos2.y));
+
+ return 0;
+}
A => src/13.c +101 -0
@@ 1,101 @@
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#define abs(a) ((a<0)? -a : a)
+
+long long mulmod(unsigned long long a, unsigned long long b, unsigned long long m) {
+ /* Returns mod(a*b, c) while avoiding overflow for a*b */
+ if (a < (1ULL<<32) && b < (1ULL<<32)) { /* long long is at least 64 bits wide */
+ return (a * b) % m;
+ }
+ unsigned long long ret = 0;
+ a = a % m;
+ b = b % m;
+ while (b) {
+ if (b & 1) {
+ ret = (ret + a) % m;
+ }
+ a = (a << 1) % m;
+ b >>= 1;
+ }
+ return ret;
+}
+
+unsigned long long mod(long long a, long long b) {
+ /* assumes b > 0*/
+ if (a >= 0) {
+ return (unsigned long long) a % b;
+ }
+ else {
+ return (unsigned long long) ((a % b) + b);
+ }
+}
+
+long long egcd(long long a, long long b, long long *n, long long *m) {
+ if (b == 0) {
+ *n = 1;
+ *m = 0;
+ return a;
+ }
+ else if (abs(a) < abs(b)) {
+ return egcd(b, a, m, n);
+ }
+ long long k = a / b;
+ long long r = a - k * b;
+ long long tmp;
+ long long g = egcd(b, r, &tmp, n);
+ *m = tmp - k * (*n);
+ return g;
+}
+
+int main(void) {
+ long earliest;
+ scanf("%ld\n", &earliest);
+ long part1 = 0;
+ long min_wait = LONG_MAX;
+ long long part2 = 0;
+ long long period = 1;
+ long t = 0;
+ int c = 0;
+ for (int i = 0; (c = getchar()) != EOF;) {
+ if (isdigit(c)) {
+ t = 10 * t + (c - '0');
+ }
+ else if ((c == ',' || c == '\n') && t != 0) { /* got our number */
+ /* part 1 */
+ long rem = t - (earliest % t);
+ if (rem < min_wait) {
+ min_wait = rem;
+ part1 = rem * t;
+ }
+
+ /* part 2 */
+ long long g, n, m;
+ g = egcd(period, t, &n, &m);
+ long long d = -(part2 + (long long) i);
+ long long k = d / g;
+ if (d != k * g) {
+ fprintf(stderr, "part2 is not solvable.\n");
+ return 1;
+ }
+ period = t * (period / g);
+ part2 = mod(mulmod((unsigned long long) t, mulmod(mod(-k, period), mod(m, period), period), period) - i, period);
+
+ t = 0;
+ ++i;
+ }
+ else if (c == 'x') {
+ ++i;
+ }
+ }
+ if (ferror(stdin)) {
+ fprintf(stderr, "Error: %s\n", strerror(errno));
+ return 1;
+ }
+ printf("%ld\n", part1);
+ printf("%lld\n", part2);
+ return 0;
+}
A => src/14.c +144 -0
@@ 1,144 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#ifndef BUCKETSIZE
+ #define BUCKETSIZE 50
+#endif
+
+typedef struct {
+ size_t occupied;
+ unsigned long long keys[BUCKETSIZE];
+ unsigned long long values[BUCKETSIZE];
+} bucket_t;
+
+typedef struct {
+ size_t n_buckets;
+ bucket_t *buckets;
+} hash_table_t;
+
+hash_table_t *ht_init(size_t n_buckets) {
+ /* Note: buckets NEEDS to be a power of 2! */
+ hash_table_t *table = malloc(sizeof *table);
+ table->n_buckets = n_buckets;
+ if (table == NULL) {
+ fprintf(stderr, "Allocation erorr: %s.\n", strerror(errno));
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+ }
+ table->buckets = calloc(n_buckets, sizeof *table->buckets);
+ if (table->buckets == NULL) {
+ fprintf(stderr, "Allocation erorr: %s.\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ for (size_t i = 0; i < n_buckets; ++i) {
+ table->buckets[i].occupied = 0;
+ }
+ return table;
+}
+
+uint32_t ht_hash(uint64_t x) {
+ /* adapted djb2 from http://www.cse.yorku.ca/~oz/hash.html */
+ uint32_t h = 5381;
+ h = ((h << 5) + h) + (uint32_t) ( x & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 8) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 16) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 24) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 32) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 40) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 48) & 255);
+ h = ((h << 5) + h) + (uint32_t) ((x >> 54) & 255);
+ return h;
+}
+
+void ht_insert(hash_table_t *table, uint64_t key, uint64_t value) {
+ size_t bucket_id = ht_hash(key) & (table->n_buckets-1);
+ bucket_t *bucket = table->buckets + bucket_id;
+ for (size_t i = 0; i < bucket->occupied; ++i) {
+ if (bucket->keys[i] == key) {
+ bucket->values[i] = value;
+ return;
+ }
+ }
+ if (bucket->occupied + 1 >= table->n_buckets) {
+ fprintf(stderr, "Error inserting (%"PRIu64", %"PRIu64"): Bucket %zu is already full!\n", key, value, bucket_id);
+ exit(EXIT_FAILURE);
+ }
+ bucket->keys[bucket->occupied] = key;
+ bucket->values[bucket->occupied] = value;
+ ++bucket->occupied;
+}
+
+unsigned long long sum(hash_table_t *table) {
+ unsigned long long s = 0;
+ for (size_t i = 0; i < table->n_buckets; ++i) {
+ for (size_t j = 0; j < table->buckets[i].occupied; ++j) {
+ s += table->buckets[i].values[j];
+ }
+ }
+ return s;
+}
+
+void part2_ht_insert(hash_table_t *mem, unsigned long long loc, unsigned long long val, unsigned long long floating) {
+ if (!floating) {
+ ht_insert(mem, (uint64_t) loc, val);
+ return;
+ }
+ int i = 0;
+ for (; !(floating & 1); ++i, floating >>= 1);
+ floating &= ~1LLU;
+ floating <<= i;
+ part2_ht_insert(mem, loc, val, floating);
+ part2_ht_insert(mem, loc ^ (1ULL<<i), val, floating);
+}
+
+
+int main(void) {
+ hash_table_t *mem1 = ht_init(1<<8);
+ hash_table_t *mem2 = ht_init(1<<12);
+ char line[50];
+ unsigned long long mask = 0, maskmask = 0;
+ while (fgets(line, sizeof line, stdin) != NULL) {
+ if (strncmp(line, "mask", 4) == 0) {
+ /* set mask */
+ mask = 0;
+ maskmask = 0;
+ char *b = line;
+ for (; *b && *b != '0' && *b != '1' && *b != 'X'; ++b);
+ for (; *b && *b != '\n'; ++b) {
+ mask <<= 1;
+ maskmask <<= 1;
+ if (*b == '0') {
+ maskmask |= 1;
+ }
+ else if (*b == '1') {
+ mask |= 1;
+ maskmask |= 1;
+ }
+ }
+ }
+ else {
+ /* memory */
+ unsigned long long loc = 0;
+ unsigned long long val = 0;
+ if (sscanf(line, "mem[%llu] = %llu", &loc, &val) != 2) {
+ fprintf(stderr, "input error for line: %s\n", line);
+ return EXIT_FAILURE;
+ }
+
+ ht_insert(mem1, (uint64_t) loc, (val | (mask & maskmask)) & (mask | ~maskmask));
+
+ unsigned long long floating = (~maskmask) & ((1ULL<<36)-1);
+ part2_ht_insert(mem2, loc | mask, val, floating);
+ }
+ }
+ if (ferror(stdin)) {
+ fprintf(stderr, "Read error: %s.\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ printf("%llu\n", sum(mem1));
+ printf("%llu\n", sum(mem2));
+ return 0;
+}
A => testinputs/01 +6 -0
@@ 1,6 @@
+1721
+979
+366
+299
+675
+1456
A => testinputs/02 +3 -0
@@ 1,3 @@
+1-3 a: abcde
+1-3 b: cdefg
+2-9 c: ccccccccc
A => testinputs/03 +11 -0
@@ 1,11 @@
+..##.......
+#...#...#..
+.#....#..#.
+..#.#...#.#
+.#...##..#.
+..#.##.....
+.#.#.#....#
+.#........#
+#.##...#...
+#...##....#
+.#..#...#.#
A => testinputs/04 +13 -0
@@ 1,13 @@
+ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
+byr:1937 iyr:2017 cid:147 hgt:183cm
+
+iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
+hcl:#cfa07d byr:1929
+
+hcl:#ae17e1 iyr:2013
+eyr:2024
+ecl:brn pid:760753108 byr:1931
+hgt:179cm
+
+hcl:#cfa07d eyr:2025 pid:166559648
+iyr:2011 ecl:brn hgt:59in
A => testinputs/05 +4 -0
@@ 1,4 @@
+BFFFBBFRRR
+FFFBBBFRRR
+FFFBBBRFFR
+BBFFBBFRLL
A => testinputs/06 +15 -0
A => testinputs/07 +9 -0
@@ 1,9 @@
+light red bags contain 1 bright white bag, 2 muted yellow bags.
+dark orange bags contain 3 bright white bags, 4 muted yellow bags.
+bright white bags contain 1 shiny gold bag.
+muted yellow bags contain 2 shiny gold bags, 9 faded blue bags.
+shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags.
+dark olive bags contain 3 faded blue bags, 4 dotted black bags.
+vibrant plum bags contain 5 faded blue bags, 6 dotted black bags.
+faded blue bags contain no other bags.
+dotted black bags contain no other bags.
A => testinputs/08 +9 -0
@@ 1,9 @@
+nop +0
+acc +1
+jmp +4
+acc +3
+jmp -3
+acc -99
+acc +1
+jmp -4
+acc +6
A => testinputs/09 +100 -0
@@ 1,100 @@
+9
+12
+28
+42
+46
+30
+23
+10
+29
+40
+27
+32
+8
+3
+13
+1
+5
+48
+33
+17
+41
+47
+44
+22
+2
+42
+57
+73
+88
+98
+15
+53
+89
+93
+71
+115
+57
+120
+54
+101
+147
+209
+161
+90
+130
+124
+214
+235
+308
+218
+309
+382
+448
+625
+709
+969
+158
+1407
+1274
+713
+779
+1555
+1725
+661
+2507
+3426
+2120
+4746
+4471
+7803
+5237
+4603
+4112
+3125
+7371
+901
+10545
+2175
+6899
+7025
+17163
+17729
+23500
+20506
+14145
+33537
+34488
+33485
+42978
+58063
+43160
+22429
+34340
+79306
+81123
+136180
+184439
+341147
+356694
+548428
A => testinputs/10 +11 -0
A => testinputs/11 +10 -0
@@ 1,10 @@
+L.LL.LL.LL
+LLLLLLL.LL
+L.L.L..L..
+LLLL.LL.LL
+L.LL.LL.LL
+L.LLLLL.LL
+..L.L.....
+LLLLLLLLLL
+L.LLLLLL.L
+L.LLLLL.LL
A => testinputs/12 +5 -0
@@ 1,5 @@
+F10
+N3
+F7
+R90
+F11
A => testinputs/13 +2 -0
@@ 1,2 @@
+939
+7,13,x,x,59,x,31,19
A => testinputs/14 +4 -0
@@ 1,4 @@
+mask = 000000000000000000000000000000X1001X
+mem[42] = 100
+mask = 00000000000000000000000000000000X0XX
+mem[26] = 1
A => testoutputs/01 +2 -0
@@ 1,2 @@
+514579
+241861950
A => testoutputs/02 +2 -0
A => testoutputs/03 +2 -0
A => testoutputs/04 +2 -0
A => testoutputs/05 +2 -0
A => testoutputs/06 +2 -0
A => testoutputs/07 +2 -0
A => testoutputs/08 +2 -0
A => testoutputs/09 +2 -0
A => testoutputs/10 +2 -0
A => testoutputs/11 +2 -0
A => testoutputs/12 +2 -0
A => testoutputs/13 +2 -0
A => testoutputs/14 +2 -0
A => time.c +202 -0
@@ 1,202 @@
+#define _XOPEN_SOURCE 600
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/wait.h>
+
+typedef struct {
+ struct timespec start;
+ struct timespec stop;
+} timer;
+
+void timer_start(timer *t) {
+ clock_gettime(CLOCK_REALTIME, &t->start);
+}
+
+void timer_stop(timer *t) {
+ clock_gettime(CLOCK_REALTIME, &t->stop);
+}
+
+double timer_delta_ns(timer *t) {
+ double delta_ns = (double) t->stop.tv_nsec - (double) t->start.tv_nsec;
+ double delta_s = (double) t->stop.tv_sec - (double) t->start.tv_sec;
+ return delta_ns + 1e9 * delta_s;
+}
+
+void print_delta_ns(FILE *f, double ns) {
+ if (ns >= 1e9) {
+ /* seconds */
+ fprintf(f, "%0.3lg s", ns * 1e-9);
+ }
+ else if (ns >= 1e6) {
+ fprintf(f, "%0.3lg ms", ns * 1e-6);
+ }
+ else if (ns >= 1e3) {
+ fprintf(f, "%0.3lg us", ns * 1e-3);
+ }
+ else {
+ fprintf(f, "%0.3lg ns", ns);
+ }
+}
+
+double smallest(const double *numbers, size_t n) {
+ double min = numbers[0];
+ for (size_t i = 1; i < n; ++i) {
+ if (numbers[i] < min) {
+ min = numbers[i];
+ }
+ }
+ return min;
+}
+
+char *my_asprintf(const char *fmt, ...) {
+ va_list args;
+ /* calculate size */
+ va_start(args, fmt);
+ int n = vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+ /* allocate */
+ size_t size = (size_t) n + 1; // + 1 for final \0.
+ char *buf = malloc(size);
+ assert(buf != NULL);
+ /* print */
+ va_start(args, fmt);
+ n = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+ /* check and return */
+ assert(n >= 0);
+ return buf;
+}
+
+bool run_day(unsigned day, double *time_ns) {
+ /* tries to run; return true if successful, false if the source file does not exist; aborts in case of any other problem */
+ char *exe_fn = my_asprintf("src/%02u.c", day);
+ char *inp_fn = my_asprintf("inputs/%02u", day);
+ /* check if source code exists */
+ struct stat buf;
+ if (stat(exe_fn, &buf) != 0) {
+ if (errno == ENOENT) {
+ free(exe_fn);
+ free(inp_fn);
+ return false;
+ }
+ else {
+ fprintf(stderr, "Day %02u ERROR: %s\n", day, strerror(errno));
+ abort();
+ }
+ }
+ /* Open input */
+ int inp_fd = open(inp_fn, O_RDONLY);
+ if (inp_fd < 0) {
+ fprintf(stderr, "Day %02u ERROR: Could not open input file: %s\n", day, strerror(errno));
+ abort();
+ }
+ /* Open /dev/null */
+ int devnull_fd = open("/dev/null", O_WRONLY);
+ if (devnull_fd < 0) {
+ fprintf(stderr, "Day %02u ERROR: Could not open input file: %s\n", day, strerror(errno));
+ abort();
+ }
+ /* Start, fork & exec, stop timer */
+ timer t;
+ timer_start(&t);
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ /* child: replace stdin with the input, stdin & stdout with /dev/null; then exec */
+ if ((dup2(inp_fd, 0) < 0) || (dup2(devnull_fd, 1) < 0) || (dup2(devnull_fd, 2) < 0)) {
+ fprintf(stderr, "IO redirect error: %s\n", strerror(errno));
+ abort();
+ }
+ char * const args[] = { "tcc", "-run", exe_fn, NULL };
+ assert(execvp("tcc", args) == 0);
+ }
+ else if (pid > 0) {
+ /* parent, success */
+ int status;
+ assert(wait(&status) == pid);
+ if (status != 0) {
+ fprintf(stderr, "Day %02u ERROR: Program exit code %d.\n", day, status);
+ abort();
+ }
+ }
+ else {
+ /* parent, failure */
+ fprintf(stderr, "Day %02u ERROR: fork() failed: %s\n", day, strerror(errno));
+ abort();
+ }
+ /* stop timer, clean up */
+ timer_stop(&t);
+ if (time_ns != NULL) {
+ *time_ns = timer_delta_ns(&t);
+ }
+ assert(close(inp_fd) == 0);
+ assert(close(devnull_fd) == 0);
+ free(inp_fn);
+ free(exe_fn);
+ return true;
+}
+
+#ifndef repeat
+#define repeat 1
+#endif
+
+int main(void) {
+ /* prepare */
+ double total_times_ns[repeat] = { 0, };
+ double *times_ns[25] = { 0, };
+ for (size_t i = 0; i < sizeof times_ns / sizeof *times_ns; ++i) {
+ times_ns[i] = calloc(repeat, sizeof **times_ns);
+ assert(times_ns[i] != NULL);
+ }
+
+ /* run with total timer and local timers */
+ for (unsigned rep = 0; rep < repeat; ++rep) {
+ timer t;
+ timer_start(&t);
+ for (unsigned day = 1; day <= 25; ++day) {
+ run_day(day, times_ns[day-1]+rep);
+ }
+ timer_stop(&t);
+ total_times_ns[rep] = timer_delta_ns(&t);
+ }
+
+ /* print result */
+ size_t successes = 0;
+ if (repeat > 1) {
+ printf("Times (best out of %d):\n", repeat);
+ }
+ else {
+ printf("Times:\n");
+ }
+ for (unsigned day = 1; day <= 25; ++day) {
+ printf("Day %02u: ", day);
+ if (times_ns[day-1][0] != 0.0) {
+ ++successes;
+ print_delta_ns(stdout, smallest(times_ns[day-1], repeat));
+ printf("\n");
+ }
+ else {
+ printf("MISSING\n");
+ }
+ free(times_ns[day-1]);
+ }
+ printf("Total: ");
+ print_delta_ns(stdout, smallest(total_times_ns, repeat));
+ printf("\n");
+ if (0 < successes && successes < 25) {
+ printf("Extrapolated to 25 days: ");
+ print_delta_ns(stdout, smallest(total_times_ns, repeat) * 25 / successes);
+ printf("\n");
+ }
+
+ return 0;
+}