139 lines
3.7 KiB
C
139 lines
3.7 KiB
C
#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);
|
|
}
|
|
}
|