This commit is contained in:
Georgios Samaras 2024-12-24 00:24:08 +01:00
parent 6a9cce73bf
commit ce7958dfaf
3 changed files with 3768 additions and 0 deletions

3380
day-23/input.txt Normal file

File diff suppressed because it is too large Load Diff

356
day-23/main.c Normal file
View File

@ -0,0 +1,356 @@
#include "aoc.h"
#include <stdlib.h>
typedef struct Node {
str label;
i32 index;
} Node;
// Represent edges of graph by an adjacency matrix.
typedef struct BitMatrix {
i32 size;
u64 *data;
} BitMatrix;
typedef DYNAMIC_ARRAY(Node) Nodes;
typedef struct Graph {
Nodes nodes;
BitMatrix edges;
} Graph;
typedef struct BitSet {
i32 size;
i32 array_size;
u64 *data;
} BitSet;
typedef struct BitSetIter {
BitSet *bs;
i32 u64_index;
u64 current_u64;
} BitSetIter;
static void
bm_init(Arena *arena, BitMatrix *bm, i32 size) {
bm->size = size;
isize alloc_size = (bm->size * bm->size + 63) / 64;
bm->data = ARENA_ALLOC_ARRAY(arena, u64, alloc_size);
}
static u8
bm_at(BitMatrix *bm, i32 row, i32 col) {
i32 u64_index = (row * bm->size + col) / 64;
i32 bit_index = (row * bm->size + col) % 64;
u64 mask = 1ULL << bit_index;
return (bm->data[u64_index] & mask) != 0;
}
static void
bm_set(BitMatrix *bm, i32 row, i32 col, u8 bit) {
i32 u64_index = (row * bm->size + col) / 64;
i32 bit_index = (row * bm->size + col) % 64;
u64 mask = 1ULL << bit_index;
if (bit) {
bm->data[u64_index] |= mask;
}
else {
bm->data[u64_index] &= ~mask;
}
}
BitSet make_bitset(Arena *arena, i32 size) {
isize array_size = (size + 63) / 64;
BitSet set = {
.size = size,
.array_size = array_size,
.data = ARENA_ALLOC_ARRAY(arena, u64, array_size)
};
return set;
}
static bool
bs_empty(BitSet *bs) {
for (i32 i = 0; i < (bs->size + 63) / 64; i++) {
if (bs->data[i] != 0) {
return false;
}
}
return true;
}
static void
bs_set(BitSet *bs, i32 index) {
i32 u64_index = index / 64;
i32 bit_index = index % 64;
bs->data[u64_index] |= 1ULL << bit_index;
}
static void
bs_clear(BitSet *bs, i32 index) {
i32 u64_index = index / 64;
i32 bit_index = index % 64;
bs->data[u64_index] &= ~(1ULL << bit_index);
}
static u32
bs_get(BitSet *bs, i32 index) {
i32 u64_index = index / 64;
i32 bit_index = index % 64;
return (bs->data[u64_index] & (1ULL << bit_index)) != 0;
}
BitSetIter
bs_iter(BitSet *bs) {
BitSetIter iter = {
.bs = bs,
.u64_index = -1,
.current_u64 = 0,
};
return iter;
}
static bool
bsi_next(BitSetIter *iter, i32 *index) {
while (iter->current_u64 == 0) {
iter->u64_index++;
if (iter->u64_index >= iter->bs->array_size) {
return false;
}
iter->current_u64 = iter->bs->data[iter->u64_index];
}
i32 bit_index = __builtin_ctzll(iter->current_u64);
*index = iter->u64_index * 64 + bit_index;
iter->current_u64 &= ~(1ULL << bit_index);
return true;
}
static BitSet
bs_copy(Arena *arena, BitSet *bs) {
BitSet copy = make_bitset(arena, bs->size);
memcpy(copy.data, bs->data, bs->array_size * sizeof(bs->data[0]));
return copy;
}
static BitSet
bs_intersect(Arena *arena, BitSet *a, BitSet *b) {
BitSet result = make_bitset(arena, a->size);
for (i32 i = 0; i < a->array_size; i++) {
result.data[i] = a->data[i] & b->data[i];
}
return result;
}
static i32
bs_count(BitSet *bs) {
i32 count = 0;
for (i32 i = 0; i < bs->array_size; i++) {
count += __builtin_popcountll(bs->data[i]);
}
return count;
}
static i32
cmp_i32(const void *p1, const void *p2) {
i32 a = *(i32 *)p1;
i32 b = *(i32 *)p2;
return a - b;
}
static inline bool
graph_connected(Graph *graph, i32 a, i32 b) {
bool result = bm_at(&graph->edges, a, b);
return result;
}
static Node *
graph_get_or_insert(Arena *arena, Graph *graph, str label) {
Node *result = NULL;
for (isize i = 0; i < graph->nodes.len; i++) {
if (str_eq(graph->nodes.data[i].label, label)) {
result = &graph->nodes.data[i];
break;
}
}
if (!result) {
Node node = { .label = label, .index = graph->nodes.len };
*push(&graph->nodes, arena) = node;
result = &graph->nodes.data[graph->nodes.len - 1];
}
return result;
}
static i32
str_cmp(str a, str b) {
i32 res = memcmp(a.data, b.data, MIN(a.len, b.len));
if (res == 0) {
return a.len - b.len;
}
else {
return res;
}
}
static i32
cmp_str(const void *p1, const void *p2) {
str *s1 = (str *)p1;
str *s2 = (str *)p2;
return str_cmp(*s1, *s2);
}
static BitSet
get_neighbors(Arena *arena, Graph *graph, i32 node) {
BitSet neighbors = make_bitset(arena, graph->nodes.len);
for (i32 i = 0; i < graph->nodes.len; i++) {
if (graph_connected(graph, node, i)) {
bs_set(&neighbors, i);
}
}
return neighbors;
}
typedef void(CliqueCallback)(BitSet *set, bool maximal, void *context);
static void
bron_kerbosch(Arena *arena, Graph *graph, BitSet R, BitSet P, BitSet X, bool only_maximal, CliqueCallback *callback, void *context) {
if (bs_empty(&P) && bs_empty(&X)) {
if (callback) {
callback(&R, false, context);
}
}
else {
if (!only_maximal) {
if (callback) {
callback(&R, false, context);
}
}
BitSet P_copy = bs_copy(arena, &P);
BitSetIter iter = bs_iter(&P_copy);
i32 node;
void *watermark = arena->top;
while (bsi_next(&iter, &node)) {
bs_set(&R, node);
BitSet neighbors = get_neighbors(arena, graph, node);
BitSet P_intersect_v = bs_intersect(arena, &P, &neighbors);
BitSet X_intersect_v = bs_intersect(arena, &X, &neighbors);
bs_intersect(arena, &P, &P_intersect_v);
bron_kerbosch(arena, graph, R, P_intersect_v, X_intersect_v, only_maximal, callback, context);
// undo
bs_clear(&R, node);
bs_clear(&P, node);
bs_set(&X, node);
}
arena->top = watermark;
}
}
typedef struct {
i32 count;
Graph *graph;
} Part1_Context;
static void
clique3_callback(BitSet *set, bool maximal, void *context) {
Part1_Context *p1 = context;
if (bs_count(set) == 3) {
i32 node = 0;
BitSetIter iter = bs_iter(set);
bool starts_with_t = false;
while (bsi_next(&iter, &node)) {
Node *n = &p1->graph->nodes.data[node];
if (n->label.data[0] == 't') {
starts_with_t = true;
break;
}
}
p1->count += starts_with_t;
}
}
static i32
count_cliques_of_size_3_with_t(Arena *arena, Graph *graph) {
BitSet P = make_bitset(arena, graph->nodes.len);
BitSet R = make_bitset(arena, graph->nodes.len);
BitSet X = make_bitset(arena, graph->nodes.len);
// create set of all nodes which start with a 't'
for (i32 i = 0; i < graph->nodes.len; i++) {
bs_set(&P, i);
}
Part1_Context context = {
.count = 0,
.graph = graph
};
bron_kerbosch(arena, graph, R, P, X, false, clique3_callback, &context);
return context.count;
}
static void
largest_clique_callback(BitSet *set, bool maximal, void *context) {
BitSet *largest = context;
if (bs_count(set) > bs_count(largest)) {
memcpy(largest->data, set->data, set->array_size * sizeof(set->data[0]));
}
}
static void
find_largest_clique(Arena *arena, Graph *graph) {
BitSet P = make_bitset(arena, graph->nodes.len);
BitSet R = make_bitset(arena, graph->nodes.len);
BitSet X = make_bitset(arena, graph->nodes.len);
for (i32 i = 0; i < graph->nodes.len; i++) {
bs_set(&P, i);
}
BitSet result = make_bitset(arena, graph->nodes.len);
bron_kerbosch(arena, graph, R, P, X, true, largest_clique_callback, &result);
i32 count = bs_count(&result);
str *labels = ARENA_ALLOC_ARRAY(arena, str, count);
i32 node;
BitSetIter iter = bs_iter(&result);
i32 counter = 0;
while (bsi_next(&iter, &node)) {
labels[counter++] = graph->nodes.data[node].label;
}
qsort(labels, count, sizeof(labels[0]), cmp_str);
for (i32 i = 0; i < count; i++) {
printf(STR_FMT ",", STR_ARG(labels[i]));
}
}
int main(int argc, char **argv) {
Arena *arena = make_arena(Megabytes(2));
Tokens input = read_lines(arena, argv[1]);
/* printf("# of edges: %td\n", input.len); */
Graph graph = {0};
bm_init(arena, &graph.edges, input.len);
for (isize i = 0; i < input.len; i++) {
str line = str_trim(input.tokens[i]);
if (line.len == 0) {
continue;
}
str a, b;
str_split(line, STR("-"), &a, &b);
Node *node_a = graph_get_or_insert(arena, &graph, a);
Node *node_b = graph_get_or_insert(arena, &graph, b);
bm_set(&graph.edges, node_a->index, node_b->index, 1);
bm_set(&graph.edges, node_b->index, node_a->index, 1);
}
// Part 1
i32 result = count_cliques_of_size_3_with_t(arena, &graph);
printf("%d\n", result);
// Part 2
find_largest_clique(arena, &graph);
}

32
day-23/test.txt Normal file
View File

@ -0,0 +1,32 @@
kh-tc
qp-kh
de-cg
ka-co
yn-aq
qp-ub
cg-tb
vc-aq
tb-ka
wh-tc
yn-cg
kh-ub
ta-co
de-co
tc-td
tb-wq
wh-td
ta-ka
td-qp
aq-cg
wq-ub
ub-vc
de-ta
wq-aq
wq-vc
wh-yn
ka-de
kh-ta
co-tc
wh-qp
tb-vc
td-yn