diff --git a/CMakeLists.txt b/CMakeLists.txt index ab16be3..2258b26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,19 +23,32 @@ file(COPY input DESTINATION ${CMAKE_BINARY_DIR}) add_executable(advent ${SRC} ${SRC2015} ${SRC2016} ${SRC2017} ${SRC2018} ${SRC2019} ${SRC2020} ${SRC2021} ${SRC2022} ${SRC2023} ${SRC2024}) +set(INCLUDES + include + lib/libflint/include + lib/uthash/src +) + if ((${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")) - target_link_libraries(advent PRIVATE flint) - target_include_directories(advent PRIVATE include lib/libflint/include lib/uthash/src) + find_package(PCRE2 REQUIRED) + target_include_directories(advent PRIVATE ${INCLUDES} ${PCRE2_INCLUDE_DIR}) + target_link_libraries(advent PRIVATE flint ${PCRE2_LIBRARIES}) elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) find_package(OpenSSL REQUIRED) - target_link_libraries(advent PRIVATE bsd flint ${OPENSSL_LIBRARIES}) - target_include_directories(advent PRIVATE include lib/libflint/include lib/uthash/src ${OpenSSL_INCLUDE_DIR}) + target_include_directories(advent PRIVATE ${INCLUDES} ${OpenSSL_INCLUDE_DIR} ${PCRE2_INCLUDE_DIR}) + target_link_libraries(advent PRIVATE bsd flint ${PCRE2_LIBRARIES} ${OPENSSL_LIBRARIES}) elseif ((${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")) + # OpenSSL set(OPENSSL_ROOT_DIR /opt/homebrew/opt/openssl@3) set(OpenSSL_INCLUDE_DIR /opt/homebrew/opt/openssl@3/include) find_package(OpenSSL REQUIRED) - target_link_libraries(advent PRIVATE flint ${OPENSSL_LIBRARIES}) - target_include_directories(advent PRIVATE include lib/libflint/include lib/uthash/src ${OpenSSL_INCLUDE_DIR}) + + # PCRE2 + set(PCRE2_INCLUDE_DIR /opt/homebrew/opt/pcre2/include) + set(PCRE2_LIBRARIES /opt/homebrew/opt/pcre2/lib/libpcre2-8.dylib) + + target_include_directories(advent PRIVATE ${INCLUDES} ${OpenSSL_INCLUDE_DIR} ${PCRE2_INCLUDE_DIR}) + target_link_libraries(advent PRIVATE flint ${OPENSSL_LIBRARIES} ${PCRE2_LIBRARIES}) else() message(FATAL_ERROR "OS ${CMAKE_SYSTEM_NAME} is not supported" ) endif() diff --git a/include/advent_utility.h b/include/advent_utility.h index ef3296f..7cc64d1 100644 --- a/include/advent_utility.h +++ b/include/advent_utility.h @@ -9,7 +9,13 @@ #define MIN(x, y) (x) < (y) ? (x) : (y) char *md5_str(const char *); + Vector *string_to_int_vector(const char *input_string, const char *delim); + int int_comp(const void *a, const void *b); +char **get_matches(char *in, char *pat, size_t *sz, size_t max_matches); + +void free_matches(char **matches, size_t sz); + #endif diff --git a/src/2024/01.c b/src/2024/01.c index 2c0b74c..3a1bc05 100644 --- a/src/2024/01.c +++ b/src/2024/01.c @@ -5,54 +5,54 @@ #include "advent_utility.h" void advent2024day01(void) { - size_t sz = 0; - char **input = get_lines("input/2024/01", &sz); - int a[sz]; - int b[sz]; - const char* errstr; + size_t sz = 0; + char **input = get_lines("input/2024/01", &sz); + int a[sz]; + int b[sz]; + const char *errstr; - for (size_t i = 0; i < sz; i++) { - size_t sp_sz = 0; - char **sp = split(input[i], &sp_sz, " "); + for (size_t i = 0; i < sz; i++) { + size_t sp_sz = 0; + char **sp = split(input[i], &sp_sz, " "); - a[i] = (int)strtonum(sp[0], INT_MIN, INT_MAX, &errstr); - if (errstr != NULL) { - printf("ERR: %s\n", errstr); - free(sp); - break; + a[i] = (int) strtonum(sp[0], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + printf("ERR: %s\n", errstr); + free(sp); + break; + } + + b[i] = (int) strtonum(sp[1], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + printf("ERR: %s\n", errstr); + free(sp); + break; + } + free(sp); } - b[i] = (int)strtonum(sp[1], INT_MIN, INT_MAX, &errstr); - if (errstr != NULL) { - printf("ERR: %s\n", errstr); - free(sp); - break; + qsort(a, sz, sizeof(int), int_comp); + qsort(b, sz, sizeof(int), int_comp); + + int p1 = 0; + for (size_t i = 0; i < sz; i++) { + p1 += abs(a[i] - b[i]); } - free(sp); - } - qsort(a, sz, sizeof(int), int_comp); - qsort(b, sz, sizeof(int), int_comp); + printf("%d\n", p1); - int p1 = 0; - for (size_t i = 0; i < sz; i++) { - p1 += abs(a[i] - b[i]); - } - - printf("%d\n", p1); - - int p2 = 0; - for (size_t i = 0; i < sz; i++) { - int c = 0; - for (size_t j = 0; j < sz; j++) { - if (a[i] == b[j]) { - ++c; - } + int p2 = 0; + for (size_t i = 0; i < sz; i++) { + int c = 0; + for (size_t j = 0; j < sz; j++) { + if (a[i] == b[j]) { + ++c; + } + } + p2 += a[i] * c; } - p2 += a[i] * c; - } - printf("%d\n", p2); + printf("%d\n", p2); - del_lines(input); + del_lines(input); } diff --git a/src/2024/02.c b/src/2024/02.c index 0014c6c..98d1e33 100644 --- a/src/2024/02.c +++ b/src/2024/02.c @@ -1,10 +1,126 @@ +#include +#include +#include #include -#include #include "lfinput.h" +#include "lfbool.h" + +static int part1(List *a) { + ListNode *n = a->head; + + int inc = LFFALSE; + if (*(int *) n->data < *(int *) n->next->data) { + inc = LFTRUE; + } + + int safe = LFTRUE; + while (n->next != NULL) { + int prev = *(int *) n->data, cur = *(int *) n->next->data; + if (inc == LFTRUE) { + if (prev > cur || prev == cur || cur - prev > 3) { + safe = LFFALSE; + break; + } + } else { + if (prev < cur || prev == cur || prev - cur > 3) { + safe = LFFALSE; + break; + } + } + n = n->next; + } + + return safe; +} + +static int part2(List *a) { + ListNode *n = a->head; + + int inc = LFFALSE; + if (*(int *) n->data < *(int *) n->next->data) { + inc = LFTRUE; + } + int bad = 0; + + int safe = LFTRUE; + while (n->next != NULL) { + const int prev = *(int *) n->data; + const int cur = *(int *) n->next->data; + if (inc == LFTRUE) { + if (prev > cur || prev == cur || cur - prev > 3) { + if (bad == 0) { + void *t; + ll_remove_next(a, n, &t); + bad = 1; + continue; + } + safe = LFFALSE; + break; + } + } else { + if (prev < cur || prev == cur || prev - cur > 3) { + if (bad == 0) { + void *t; + ll_remove_next(a, n, &t); + bad = 1; + continue; + } + safe = LFFALSE; + break; + } + } + n = n->next; + } + + return safe; +} void advent2024day02(void) { - char *input = get_input("input/2024/02"); - printf("Solution for Day 02 of 2024 is not completed yet\n"); - free(input); + size_t sz = 0; + char **input = get_lines("input/2024/02", &sz); + char *errstr = NULL; + int p1 = 0, p2 = 0; + + for (size_t i = 0; i < sz; i++) { + List *a = malloc(sizeof(List)); + ll_init(a, NULL); + ArenaAllocator *arena = malloc(sizeof(ArenaAllocator)); + arena_init(arena, sizeof(int) * 32); + + size_t sp_sz = 0; + char **sp = split(input[i], &sp_sz, " "); + + for (size_t j = 0; j < sp_sz; j++) { + int *t = arena_malloc(arena, sizeof(int)); + *t = (int) strtonum(sp[j], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + printf("ERR: %s\n", errstr); + exit(1); + } + ll_ins_next(a, a->tail, t); + } + + p1 += part1(a); + //p2 += part2(a); + + int s = part2(a); + if (s) { + LL_ITER(a) { + int *str = node->data; + printf("%d ", *str); + } + printf("\n"); + p2 += s; + } + + free(sp); + ll_destroy(a); + arena_free(arena); + } + + printf("%d\n", p1); + printf("%d\n", p2); + + del_lines(input); } diff --git a/src/2024/03.c b/src/2024/03.c index 354e9f1..9db63b3 100644 --- a/src/2024/03.c +++ b/src/2024/03.c @@ -2,9 +2,33 @@ #include #include "lfinput.h" +#include "advent_utility.h" void advent2024day03(void) { - char *input = get_input("input/2024/03"); - printf("Solution for Day 03 of 2024 is not completed yet\n"); - free(input); + char *input = get_input("input/2024/03"); + size_t m_sz = 0; + char **matches = get_matches(input, "mul\\(\\d+,\\d+\\)|do\\(\\)|don't\\(\\)", &m_sz, 32); + int p1 = 0, p2 = 0, x, y, enabled = 1; + + for (size_t i = 0; i < m_sz; i++) { + if (strcmp(matches[i], "do()") == 0) { + enabled = 1; + } else if (strcmp(matches[i], "don't()") == 0) { + enabled = 0; + } + + if (strstr(matches[i], "mul") != NULL) { + sscanf(matches[i], "mul(%d,%d)", &x, &y); + p1 += x * y; + if (enabled) { + p2 += x * y; + } + } + } + + printf("%d\n", p1); + printf("%d\n", p2); + + free_matches(matches, m_sz); + free(input); } diff --git a/src/advent_utility.c b/src/advent_utility.c index 052d72c..6414f50 100644 --- a/src/advent_utility.c +++ b/src/advent_utility.c @@ -2,10 +2,11 @@ #include #include +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + #if defined __linux__ || defined __APPLE__ - #include - #else #include #endif @@ -34,9 +35,9 @@ char *md5_str(const char *input) { return md5string; } -Vector *string_to_int_vector(const char *input_string, const char* delim) { +Vector *string_to_int_vector(const char *input_string, const char *delim) { char *copy = strdup(input_string); - char* token = strtok(copy, delim); + char *token = strtok(copy, delim); Vector *v = malloc(sizeof(Vector)); vec_init(v, free); @@ -53,4 +54,116 @@ Vector *string_to_int_vector(const char *input_string, const char* delim) { int int_comp(const void *a, const void *b) { return *(int *) a - *(int *) b; +} + +char **get_matches(char *in, char *pat, size_t *sz, size_t max_matches) { + pcre2_code *re; + PCRE2_SPTR pattern = (PCRE2_SPTR)pat; + int errnum; + size_t erroff; + PCRE2_SPTR8 substr; + size_t substr_sz; + PCRE2_SPTR subject = (PCRE2_SPTR)in; + size_t subject_sz = strlen(in); + + re = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0, &errnum, &erroff, NULL); + if (re == NULL) { + char buffer[256]; + pcre2_get_error_message(errnum, buffer, sizeof(buffer)); + printf("PCRE2 compilation failed at offset %d: %s\n", (int) erroff, buffer); + return NULL; + } + + char **matches = NULL; + matches = malloc(sizeof(char *) * max_matches); + if (matches == NULL) { + printf("failed to malloc matches\n"); + pcre2_code_free(re); + return NULL; + } + + pcre2_match_data *match_data = pcre2_match_data_create_from_pattern_8(re, NULL); + int rc = pcre2_match(re, subject, subject_sz, 0, 0, match_data, NULL); + if (rc < 0) { + switch (rc) { + case PCRE2_ERROR_NOMATCH: + printf("PCRE2 no matches\n"); + break; + default: + printf("PCRE2 matching error %d\n", rc); + break; + } + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return NULL; + } + + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); + *sz = 0; + for (int i = 0; i < rc; ++i) { + substr = subject + ovector[2*i]; + substr_sz = ovector[2*i+1] - ovector[2*i]; + matches[*sz] = (char *) malloc(substr_sz + 1); + strncpy(matches[*sz], substr, substr_sz); + *sz += 1; + } + + // Now loop through rest of the matches, if there are any + for (;;) { + uint32_t options = 0; + PCRE2_SIZE start_offset = ovector[1]; + + // Check for empty match + if (ovector[0] == ovector[1]) { + if (ovector[0] == subject_sz) { + break; + } + options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + } + + rc = pcre2_match(re, subject, subject_sz, start_offset, options, match_data, NULL); + if (rc == PCRE2_ERROR_NOMATCH) { + // All matches have been found + if (options == 0) { + break; + } + + // Handle newlines + ovector[1] = start_offset + 1; + if (start_offset < subject_sz - 1 && + //subject[start_offset] == '\r' && + subject[start_offset + 1] == '\n') { + ovector[1] += 1; + } + continue; + } + + // Other failures, non-recoverable + if (rc < 0) { + printf("PCRE2 match-matching error %d\n", rc); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return NULL; + } + + + for (int i = 0; i < rc; ++i) { + substr = subject + ovector[2*i]; + substr_sz = ovector[2*i+1] - ovector[2*i]; + matches[*sz] = (char *) malloc(substr_sz + 1); + strncpy(matches[*sz], substr, substr_sz); + *sz += 1; + } + } + + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return matches; +} + +void free_matches(char **matches, size_t sz) { + for (size_t i = 0; i < sz; ++i) { + free(matches[i]); + } + free(matches); } \ No newline at end of file