#include "aoc.h" #include 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); }