#include "aoc.h" typedef struct CacheEntry { struct CacheEntry *next; u64 hash; str design; i64 count; } CacheEntry; #define CACHE_SIZE (1 << 16) _Static_assert((CACHE_SIZE & (CACHE_SIZE - 1)) == 0, "Cache size must be a power of two"); typedef struct { Arena *arena; CacheEntry *entries[CACHE_SIZE]; } Cache; static void cache_init(Cache *cache, Arena *arena) { cache->arena = arena; } static u64 hash_str(str s) { // fnv-1a hash u64 hash = 14695981039346656037ULL; for (i32 i = 0; i < s.len; i++) { hash ^= s.data[i]; hash *= 1099511628211ULL; } return hash; } static f32 cache_get_load_factor(Cache *cache) { i32 count = 0; for (i32 i = 0; i < CACHE_SIZE; i++) { count += (cache->entries[i] != NULL); } return (f32) count / CACHE_SIZE; } static CacheEntry * cache_get(Cache *cache, str design) { u64 hash = hash_str(design); u64 index = hash & (CACHE_SIZE - 1); for (CacheEntry *e = cache->entries[index]; e; e = e->next) { if (e->hash == hash && str_eq(e->design, design)) { return e; } } return NULL; } static void cache_put(Cache *cache, str design, i64 count) { u64 hash = hash_str(design); u64 index = hash & (CACHE_SIZE - 1); for (CacheEntry *e = cache->entries[index]; e; e = e->next) { if (e->hash == hash && str_eq(e->design, design)) { e->count = count; } } CacheEntry *entry = ARENA_ALLOC(cache->arena, CacheEntry); entry->hash = hash; entry->design = design; entry->count = count; entry->next = cache->entries[index]; cache->entries[index] = entry; } static i64 cache_hit_count = 0; static i64 cache_miss_count = 0; static i64 count_realizations(Cache *cache, str design, str *patterns, i32 len) { if (design.len == 0) { return 1; } else { CacheEntry *entry; i64 total = 0; if ((entry = cache_get(cache, design))) { cache_hit_count++; total = entry->count; } else { cache_miss_count++; for (i32 i = 0; i < len; i++) { if (str_starts_with(design, patterns[i])) { str remainder = str_sub(design, patterns[i].len, design.len); total += count_realizations(cache, remainder, patterns, len); } } cache_put(cache, design, total); } return total; } } int main(int argc, char **argv) { Arena *arena = make_arena(Megabytes(2)); Tokens lines = read_lines(arena, argv[1]); str avail_line = lines.tokens[0]; DYNAMIC_ARRAY(str) patterns = {0}; str pattern = {0}; while ((pattern = str_next_token(&avail_line, STR(","))).len > 0) { *push(&patterns, arena) = str_trim(pattern); } Tokens desired = { .tokens = lines.tokens + 2, .len = lines.len - 2, }; Cache *cache = ARENA_ALLOC(arena, Cache); cache_init(cache, arena); i64 part_1 = 0; i64 part_2 = 0; for (i32 i = 0; i < desired.len; i++) { str towel = str_trim(desired.tokens[i]); i64 count = count_realizations(cache, towel, patterns.data, patterns.len); part_1 += (i64) (count > 0); part_2 += count; /* printf(STR_FMT " -> %s (%ld)\n", STR_ARG(towel), (count > 0) ? "possible" : "impossible", count); */ } printf("%ld\n", part_1); printf("%ld\n", part_2); if (false) { printf("Hit count: %ld\n", cache_hit_count); printf("Miss count: %ld\n", cache_miss_count); printf("Hit Rate: %.2f%%\n", (f64) cache_hit_count / (cache_hit_count + cache_miss_count) * 100); printf("Load factor: %.2f%%\n", cache_get_load_factor(cache) * 100); } }