From 68c473e78f9038d96d650dd1de61d2e68388f51c Mon Sep 17 00:00:00 2001 From: Lennart ten Wolde Date: Fri, 8 Dec 2023 19:46:04 +0100 Subject: [PATCH] refactor: make day 5 part 2 code fast --- day_5.c | 109 ++++++++++++++++++++++++++++++++++++++---------- input_handler.h | 3 ++ list.h | 4 ++ 3 files changed, 95 insertions(+), 21 deletions(-) diff --git a/day_5.c b/day_5.c index ebefbfe..536855b 100644 --- a/day_5.c +++ b/day_5.c @@ -38,9 +38,15 @@ typedef struct { } Almanac; //endregion types +// Functions Almanac* load_almanac(FILE *f); RangeList* load_map(FILE *f, const char *name); void almanac_free(Almanac *almanac); +uint32_t nearest_location_in_range(RangeList **maps, int map_count, uint32_t range_start, uint32_t range_end); +static int compare_range(const Range *a, const Range *b); + +// Global state (used to avoid having to allocate new memory on every iteration) +RangeList *range_list_buffer; int main() { FILE *f = fopen("day_5.txt", "r"); @@ -49,6 +55,7 @@ int main() { exit(1); } Almanac *almanac = load_almanac(f); + range_list_buffer = rlist_create(16); // Part 1 - Iterate over each seed and transform number using first matching range for each map uint32_t min_location = UINT32_MAX; @@ -73,37 +80,92 @@ int main() { printf("Result #1: %u\n", min_location); // Part 2 - printf("Solving part 2.\nHold your horses... this might take a while.\n"); min_location = UINT32_MAX; for(int i = 0; i < almanac->seeds->length; i += 2) { uint32_t min = almanac->seeds->data[i]; - uint32_t icount = almanac->seeds->data[i + 1]; - for(int i2 = 0; i2 < icount; i2++) { - uint32_t source = min + i2; - for(int j = 0; j < 7; j++) { - RangeList ranges = * ((RangeList**) almanac)[j + 1]; - for(int k = 0; k < ranges.length; k++) { - Range range = ranges.data[k]; - if(source >= range.source && source < range.source + range.length) { - source = source - range.source + range.destination; - break; - } - } - } + uint32_t max = min + almanac->seeds->data[i + 1]; - if(source < min_location) { - min_location = source; - } - } - printf("."); + RangeList **maps = &((RangeList**) almanac)[1]; + min_location = MIN(min_location, nearest_location_in_range(maps, 7, min, max)); } - printf("\nResult #2: %u\n", min_location); + printf("Result #2: %u\n", min_location); + rlist_free(range_list_buffer); almanac_free(almanac); fclose(f); return 0; } +uint32_t nearest_location_in_range(RangeList **maps, int map_count, uint32_t range_start, uint32_t range_end) { + RangeList *map = maps[0]; + if(__glibc_unlikely(map->length == 0)) return range_start; + + RangeList *temp_ranges = range_list_buffer; + rlist_clear(temp_ranges); + + uint32_t cursor = range_start; + for(int i = 0; i < map->length; i++) { + Range input_range = map->data[i]; + if(input_range.source + input_range.length <= range_start) { + continue; // input range before current range - skip + } + if(input_range.source >= range_end) { + break; // input range after current range - finish + } + + // Add unmapped range + if(cursor < input_range.source) { + Range new_range; + new_range.source = cursor; + new_range.destination = cursor; + new_range.length = new_range.source - cursor; + rlist_add(temp_ranges, new_range); + cursor = input_range.source; + } + + // Add mapped range + uint32_t mapped_end = MIN(input_range.source + input_range.length, range_end); + Range mapped_range; + mapped_range.length = mapped_end - cursor; + mapped_range.source = cursor; + mapped_range.destination = input_range.destination + cursor - input_range.source; + rlist_add(temp_ranges, mapped_range); + cursor = MAX(cursor, mapped_range.source + mapped_range.length); + } + // Add final unmapped range + if(cursor < range_end) { + Range new_range; + new_range.source = cursor; + new_range.destination = cursor; + new_range.length = range_end - cursor; + rlist_add(temp_ranges, new_range); + } + + // Copy new ranges + uint num_ranges = temp_ranges->length; + Range ranges[num_ranges]; + memcpy(&ranges[0], temp_ranges->data, num_ranges * sizeof(Range)); + + // Find nearest + uint32_t nearest = UINT32_MAX; + if(map_count > 1) { + for(int i = 0; i < num_ranges; i++) { + Range range = ranges[i]; + uint32_t next_nearest = nearest_location_in_range( + &maps[1], map_count - 1, + range.destination, range.destination + range.length); + nearest = MIN(nearest, next_nearest); + } + } else { + for(int i = 0; i < num_ranges; i++) { + Range range = ranges[i]; + nearest = MIN(nearest, range.destination); + } + } + + return nearest; +} + Almanac* load_almanac(FILE *f) { Almanac *almanac = malloc(sizeof (Almanac)); char *buffer = NULL; @@ -120,7 +182,7 @@ Almanac* load_almanac(FILE *f) { ilist_add(almanac->seeds, seed); } read_line(f, &buffer, &bufferLen); - assert(strcmp(, "") == 0); // expect empty line at end + assert(strcmp(buffer, "") == 0); // expect empty line at end almanac->seed_to_soil = load_map(f, "seed-to-soil"); almanac->soil_to_fertilizer = load_map(f, "soil-to-fertilizer"); @@ -134,6 +196,10 @@ Almanac* load_almanac(FILE *f) { return almanac; } +static int compare_range(const Range *a, const Range *b) { + return a->source > b->source; +} + RangeList* load_map(FILE *f, const char *name) { RangeList *list = rlist_create(16); char *buffer = NULL; @@ -147,6 +213,7 @@ RangeList* load_map(FILE *f, const char *name) { rlist_add(list, range); } + qsort(list->data, list->length, sizeof(Range), (__compar_fn_t) compare_range); free(buffer); return list; } diff --git a/input_handler.h b/input_handler.h index aeaf2e8..1627f5c 100644 --- a/input_handler.h +++ b/input_handler.h @@ -8,6 +8,9 @@ char* read_line(FILE *f, char **buffer, int *capacity); +#define MAX(a, b) a > b ? a : b +#define MIN(a, b) a < b ? a : b + static inline int min(const int a, const int b) { return a < b ? a : b; } diff --git a/list.h b/list.h index 5cc1421..2a858ac 100644 --- a/list.h +++ b/list.h @@ -52,6 +52,10 @@ static __inline void LIST_FN(remove)(LIST_NAME *alist, int index) { #endif //LIST_UNORDERED } +static __inline void LIST_FN(clear)(LIST_NAME *alist) { + alist->length = 0; +} + static __inline void LIST_FN(free)(LIST_NAME *alist) { free(alist->data); free(alist);