aboutsummaryrefslogtreecommitdiff
path: root/advent-of-code/2023/lib
diff options
context:
space:
mode:
authorMistivia <i@mistivia.com>2024-01-27 14:02:35 +0800
committerMistivia <i@mistivia.com>2024-01-27 14:02:35 +0800
commit6580dcd9127f69aaa794472ec92bc46015dc4019 (patch)
treedc2c7e102c75180f7bd98c2f3a14f8b55f83c0f2 /advent-of-code/2023/lib
init
Diffstat (limited to 'advent-of-code/2023/lib')
-rw-r--r--advent-of-code/2023/lib/map.c90
-rw-r--r--advent-of-code/2023/lib/map.h33
-rw-r--r--advent-of-code/2023/lib/rb_tree.c378
-rw-r--r--advent-of-code/2023/lib/rb_tree.h64
-rw-r--r--advent-of-code/2023/lib/str.c143
-rw-r--r--advent-of-code/2023/lib/str.h24
-rw-r--r--advent-of-code/2023/lib/vec.c72
-rw-r--r--advent-of-code/2023/lib/vec.h15
8 files changed, 819 insertions, 0 deletions
diff --git a/advent-of-code/2023/lib/map.c b/advent-of-code/2023/lib/map.c
new file mode 100644
index 0000000..846033e
--- /dev/null
+++ b/advent-of-code/2023/lib/map.c
@@ -0,0 +1,90 @@
+#include "map.h"
+#include "rb_tree.h"
+
+#include <string.h>
+
+typedef int (*rb_cmp_t)(void*, void*);
+
+void* new_map(cmp_t compare) {
+ rb_tree_t *tree = malloc(sizeof(rb_tree_t));
+ rb_cmp_t cmp = (rb_cmp_t)compare;
+ *tree = (rb_tree_t){NULL, cmp, NULL};
+ return tree;
+}
+
+void map_set(void* self, void* key, void* value) {
+ node_entry_t *iter = rb_tree_find(self, &key);
+ if (iter == NULL) {
+ iter = malloc(sizeof(*iter));
+ iter->key = key;
+ iter->value = value;
+ rb_tree_insert(self, iter);
+ } else {
+ iter->value = value;
+ }
+}
+
+void* map_get(void* self, void* key) {
+ node_entry_t *iter = rb_tree_find(self, &key);
+ if (iter == NULL) return NULL;
+ return iter->value;
+}
+
+void map_erase(void* self, void* key) {
+ rb_tree_remove(self, key);
+}
+
+void* map_begin(void *self) {
+ return rb_tree_min(self);
+}
+
+void* map_next(void *self, void *iter) {
+ return rb_tree_next(self, iter);
+}
+
+void* map_iter_key(void* iter_) {
+ node_entry_t *iter = iter_;
+ return iter->key;
+}
+
+void* map_iter_value(void* iter_) {
+ node_entry_t *iter = iter_;
+ return iter->value;
+}
+
+static int dict_cmp(void **a, void** b) {
+ return strcmp(*a, *b);
+}
+void* new_dict() {
+ return new_map(dict_cmp);
+}
+
+void dict_set(void* self, const char *key, void* value) {
+ map_set(self, (void*)key, value);
+}
+
+void* dict_get(void* self, const char* key) {
+ return map_get(self, (void*)key);
+}
+
+void dict_erase(void* self, const char* key) {
+ map_erase(self, (void*)key);
+}
+
+void* dict_begin(void *self) {
+ return map_begin(self);
+}
+
+void* dict_next(void *self, void *iter) {
+ return map_next(self, iter);
+}
+
+const char* dict_iter_key(void* iter) {
+ return map_iter_key(iter);
+}
+
+void* dict_iter_value(void* iter) {
+ return map_iter_value(iter);
+}
+
+
diff --git a/advent-of-code/2023/lib/map.h b/advent-of-code/2023/lib/map.h
new file mode 100644
index 0000000..c54048c
--- /dev/null
+++ b/advent-of-code/2023/lib/map.h
@@ -0,0 +1,33 @@
+#ifndef MAP_H_
+#define MAP_H_
+
+#include "rb_tree.h"
+
+typedef struct {
+ rb_node_t node;
+ void *key;
+ void *value;
+} node_entry_t;
+typedef int (*cmp_t)(void** a, void** b);
+
+void* new_map(cmp_t compare);
+void map_set(void* self, void* key, void* value);
+void* map_get(void* self, void* key);
+void map_erase(void* self, void* key);
+
+void* map_begin(void *self);
+void* map_next(void *self, void *iter);
+void* map_iter_key(void* iter);
+void* map_iter_value(void* iter);
+
+void* new_dict();
+void dict_set(void* self, const char *key, void* value);
+void* dict_get(void* self, const char* key);
+void dict_erase(void* self, const char* key);
+
+void* dict_begin(void *self);
+void* dict_next(void *self, void *iter);
+const char* dict_iter_key(void* iter);
+void* dict_iter_value(void* iter);
+
+#endif
diff --git a/advent-of-code/2023/lib/rb_tree.c b/advent-of-code/2023/lib/rb_tree.c
new file mode 100644
index 0000000..5aa7905
--- /dev/null
+++ b/advent-of-code/2023/lib/rb_tree.c
@@ -0,0 +1,378 @@
+// Copyright (C) 2023 Mistivia <i@mistivia.com>
+// Licensed under GPLv3. See LICENSE for details.
+
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rb_tree.h"
+
+#define RED 1
+#define BLACK 0
+
+static rb_node_t *rb_tree_minmax(rb_tree_t *, int);
+void *rb_tree_min(rb_tree_t *head) { return rb_tree_minmax(head, -1); }
+void *rb_tree_max(rb_tree_t *head) { return rb_tree_minmax(head, 1); }
+
+void *rb_tree_left(void *node) {
+ rb_node_t *elm = node;
+ if (node == NULL) return NULL;
+ return elm->entry.rbe_left;
+}
+void *rb_tree_right(void *node) {
+ rb_node_t *elm = node;
+ if (node == NULL) return NULL;
+ return elm->entry.rbe_right;
+}
+void *rb_tree_parent(void *node) {
+ rb_node_t *elm = node;
+ if (node == NULL) return NULL;
+ return elm->entry.rbe_parent;
+}
+
+static void augment(rb_tree_t *head, rb_node_t *elm) {
+ if (head->augment != NULL) head->augment(elm);
+}
+
+static void rb_tree_insert_color(rb_tree_t *head, rb_node_t *elm);
+static void rb_tree_remove_color(rb_tree_t *head, rb_node_t *parent,
+ rb_node_t *elm);
+
+static void rotate_left(rb_tree_t *head, rb_node_t *elm) {
+ rb_node_t *tmp = elm->entry.rbe_right;
+ if ((elm->entry.rbe_right = tmp->entry.rbe_left)) {
+ tmp->entry.rbe_left->entry.rbe_parent = elm;
+ }
+ augment(head, elm);
+ if ((tmp->entry.rbe_parent = elm->entry.rbe_parent)) {
+ if (elm == elm->entry.rbe_parent->entry.rbe_left)
+ elm->entry.rbe_parent->entry.rbe_left = tmp;
+ else
+ elm->entry.rbe_parent->entry.rbe_right = tmp;
+ } else {
+ head->rbh_root = tmp;
+ }
+ tmp->entry.rbe_left = elm;
+ elm->entry.rbe_parent = tmp;
+ augment(head, tmp);
+ if (tmp->entry.rbe_parent) {
+ augment(head, tmp->entry.rbe_parent);
+ }
+}
+
+static void rotate_right(rb_tree_t *head, rb_node_t *elm) {
+ rb_node_t *tmp = elm->entry.rbe_left;
+ if ((elm->entry.rbe_left = tmp->entry.rbe_right)) {
+ tmp->entry.rbe_right->entry.rbe_parent = elm;
+ }
+ augment(head, elm);
+ if ((tmp->entry.rbe_parent = elm->entry.rbe_parent)) {
+ if (elm == elm->entry.rbe_parent->entry.rbe_left)
+ elm->entry.rbe_parent->entry.rbe_left = tmp;
+ else
+ elm->entry.rbe_parent->entry.rbe_right = tmp;
+ } else {
+ head->rbh_root = tmp;
+ }
+ tmp->entry.rbe_right = elm;
+ elm->entry.rbe_parent = tmp;
+ augment(head, tmp);
+ if (tmp->entry.rbe_parent) {
+ augment(head, tmp->entry.rbe_parent);
+ }
+}
+
+static void rb_tree_insert_color(rb_tree_t *head, rb_node_t *elm) {
+ rb_node_t *parent, *gparent, *tmp;
+ while ((parent = elm->entry.rbe_parent) && parent->entry.rbe_color == 1) {
+ gparent = parent->entry.rbe_parent;
+ if (parent == gparent->entry.rbe_left) {
+ tmp = gparent->entry.rbe_right;
+ if (tmp && tmp->entry.rbe_color == 1) {
+ tmp->entry.rbe_color = BLACK;
+ parent->entry.rbe_color = BLACK;
+ gparent->entry.rbe_color = RED;
+ elm = gparent;
+ continue;
+ }
+ if (parent->entry.rbe_right == elm) {
+ rotate_left(head, parent);
+ tmp = parent;
+ parent = elm;
+ elm = tmp;
+ }
+ parent->entry.rbe_color = BLACK;
+ gparent->entry.rbe_color = RED;
+ rotate_right(head, gparent);
+ } else {
+ tmp = gparent->entry.rbe_left;
+ if (tmp && tmp->entry.rbe_color == 1) {
+ tmp->entry.rbe_color = BLACK;
+ parent->entry.rbe_color = BLACK;
+ gparent->entry.rbe_color = RED;
+ ;
+ elm = gparent;
+ continue;
+ }
+ if (parent->entry.rbe_left == elm) {
+ rotate_right(head, parent);
+ tmp = parent;
+ parent = elm;
+ elm = tmp;
+ }
+ parent->entry.rbe_color = BLACK;
+ gparent->entry.rbe_color = RED;
+ rotate_left(head, gparent);
+ }
+ }
+ head->rbh_root->entry.rbe_color = BLACK;
+}
+
+static void rb_tree_remove_color(rb_tree_t *head, rb_node_t *parent,
+ rb_node_t *elm) {
+ rb_node_t *tmp;
+ while ((elm == NULL || elm->entry.rbe_color == 0) &&
+ elm != head->rbh_root) {
+ if (parent->entry.rbe_left == elm) {
+ tmp = parent->entry.rbe_right;
+ if (tmp->entry.rbe_color == 1) {
+ tmp->entry.rbe_color = BLACK;
+ parent->entry.rbe_color = RED;
+ rotate_left(head, parent);
+ tmp = parent->entry.rbe_right;
+ }
+ if ((tmp->entry.rbe_left == NULL ||
+ tmp->entry.rbe_left->entry.rbe_color == 0) &&
+ (tmp->entry.rbe_right == NULL ||
+ tmp->entry.rbe_right->entry.rbe_color == 0)) {
+ tmp->entry.rbe_color = RED;
+ elm = parent;
+ parent = elm->entry.rbe_parent;
+ } else {
+ if (tmp->entry.rbe_right == NULL ||
+ tmp->entry.rbe_right->entry.rbe_color == 0) {
+ rb_node_t *oleft;
+ if ((oleft = tmp->entry.rbe_left))
+ oleft->entry.rbe_color = BLACK;
+ tmp->entry.rbe_color = RED;
+ rotate_right(head, tmp);
+ tmp = parent->entry.rbe_right;
+ }
+ tmp->entry.rbe_color = parent->entry.rbe_color;
+ parent->entry.rbe_color = BLACK;
+ if (tmp->entry.rbe_right)
+ tmp->entry.rbe_right->entry.rbe_color = BLACK;
+ rotate_left(head, parent);
+ elm = head->rbh_root;
+ break;
+ }
+ } else {
+ tmp = parent->entry.rbe_left;
+ if (tmp->entry.rbe_color == 1) {
+ tmp->entry.rbe_color = BLACK;
+ parent->entry.rbe_color = RED;
+ rotate_right(head, parent);
+ tmp = parent->entry.rbe_left;
+ }
+ if ((tmp->entry.rbe_left == NULL ||
+ tmp->entry.rbe_left->entry.rbe_color == 0) &&
+ (tmp->entry.rbe_right == NULL ||
+ tmp->entry.rbe_right->entry.rbe_color == 0)) {
+ tmp->entry.rbe_color = RED;
+ elm = parent;
+ parent = elm->entry.rbe_parent;
+ } else {
+ if (tmp->entry.rbe_left == NULL ||
+ tmp->entry.rbe_left->entry.rbe_color == 0) {
+ rb_node_t *oright;
+ if ((oright = tmp->entry.rbe_right))
+ oright->entry.rbe_color = BLACK;
+ tmp->entry.rbe_color = RED;
+ rotate_left(head, tmp);
+ tmp = parent->entry.rbe_left;
+ }
+ tmp->entry.rbe_color = parent->entry.rbe_color;
+ parent->entry.rbe_color = BLACK;
+ if (tmp->entry.rbe_left)
+ tmp->entry.rbe_left->entry.rbe_color = BLACK;
+ rotate_right(head, parent);
+ elm = head->rbh_root;
+ break;
+ }
+ }
+ }
+ if (elm) elm->entry.rbe_color = BLACK;
+}
+
+void rb_tree_remove(rb_tree_t *head, void *elmv) {
+ rb_node_t *elm = elmv;
+ rb_node_t *child, *parent;
+ int color;
+ if (elm->entry.rbe_left == NULL)
+ child = elm->entry.rbe_right;
+ else if (elm->entry.rbe_right == NULL)
+ child = elm->entry.rbe_left;
+ else {
+ rb_node_t *old = elm, *left;
+ elm = elm->entry.rbe_right;
+ while ((left = elm->entry.rbe_left))
+ elm = left;
+ child = elm->entry.rbe_right;
+ parent = elm->entry.rbe_parent;
+ color = elm->entry.rbe_color;
+ if (child) child->entry.rbe_parent = parent;
+ if (parent) {
+ if (parent->entry.rbe_left == elm)
+ parent->entry.rbe_left = child;
+ else
+ parent->entry.rbe_right = child;
+ augment(head, parent);
+ } else
+ head->rbh_root = child;
+ if (elm->entry.rbe_parent == old) parent = elm;
+ elm->entry = old->entry;
+ if (old->entry.rbe_parent) {
+ if ((old->entry.rbe_parent)->entry.rbe_left == old)
+ (old->entry.rbe_parent)->entry.rbe_left = elm;
+ else
+ (old->entry.rbe_parent)->entry.rbe_right = elm;
+ augment(head, old->entry.rbe_parent);
+ } else
+ head->rbh_root = elm;
+ old->entry.rbe_left->entry.rbe_parent = elm;
+ if (old->entry.rbe_right) old->entry.rbe_right->entry.rbe_parent = elm;
+ if (parent) {
+ left = parent;
+ if (head->augment != NULL) {
+ do {
+ augment(head, left);
+ } while ((left = left->entry.rbe_parent));
+ }
+ }
+ goto color;
+ }
+ parent = elm->entry.rbe_parent;
+ color = elm->entry.rbe_color;
+ if (child) child->entry.rbe_parent = parent;
+ if (parent) {
+ if (parent->entry.rbe_left == elm)
+ parent->entry.rbe_left = child;
+ else
+ parent->entry.rbe_right = child;
+ rb_node_t *goback = parent;
+ if (head->augment != NULL) {
+ do {
+ augment(head, goback);
+ } while ((goback = goback->entry.rbe_parent));
+ }
+ } else
+ head->rbh_root = child;
+color:
+ if (color == 0) rb_tree_remove_color(head, parent, child);
+}
+
+void *rb_tree_insert(rb_tree_t *head, void *elmv) {
+ rb_node_t *elm = elmv;
+ rb_node_t *tmp;
+ rb_node_t *parent = NULL;
+ int comp = 0;
+ tmp = head->rbh_root;
+ while (tmp) {
+ parent = tmp;
+ comp = head->cmp((void *)elm->content, (void *)parent->content);
+ if (comp < 0)
+ tmp = tmp->entry.rbe_left;
+ else if (comp > 0)
+ tmp = tmp->entry.rbe_right;
+ else
+ return tmp;
+ }
+ elm->entry.rbe_parent = parent;
+ elm->entry.rbe_left = elm->entry.rbe_right = NULL;
+ elm->entry.rbe_color = RED;
+ if (parent != NULL) {
+ if (comp < 0)
+ parent->entry.rbe_left = elm;
+ else
+ parent->entry.rbe_right = elm;
+ rb_node_t *goback = parent;
+ if (head->augment != NULL) {
+ do {
+ augment(head, goback);
+ } while ((goback = goback->entry.rbe_parent));
+ }
+ } else
+ head->rbh_root = elm;
+ rb_tree_insert_color(head, elm);
+ return (NULL);
+}
+
+void *rb_tree_find(rb_tree_t *head, void *key) {
+ rb_node_t *tmp = head->rbh_root;
+ int comp;
+ while (tmp) {
+ comp = head->cmp(key, (void *)tmp->content);
+ if (comp < 0)
+ tmp = tmp->entry.rbe_left;
+ else if (comp > 0)
+ tmp = tmp->entry.rbe_right;
+ else
+ return tmp;
+ }
+ return (NULL);
+}
+
+void *rb_tree_next(rb_tree_t *head, void *elmv) {
+ rb_node_t *elm = elmv;
+ if (elm->entry.rbe_right) {
+ elm = elm->entry.rbe_right;
+ while (elm->entry.rbe_left)
+ elm = elm->entry.rbe_left;
+ } else {
+ if (elm->entry.rbe_parent &&
+ (elm == (elm->entry.rbe_parent)->entry.rbe_left))
+ elm = elm->entry.rbe_parent;
+ else {
+ while (elm->entry.rbe_parent &&
+ (elm == (elm->entry.rbe_parent)->entry.rbe_right))
+ elm = elm->entry.rbe_parent;
+ elm = elm->entry.rbe_parent;
+ }
+ }
+ return elm;
+}
+
+static rb_node_t *rb_tree_minmax(rb_tree_t *head, int val) {
+ rb_node_t *tmp = head->rbh_root;
+ rb_node_t *parent = NULL;
+ while (tmp) {
+ parent = tmp;
+ if (val < 0)
+ tmp = tmp->entry.rbe_left;
+ else
+ tmp = tmp->entry.rbe_right;
+ }
+ return parent;
+};
+
diff --git a/advent-of-code/2023/lib/rb_tree.h b/advent-of-code/2023/lib/rb_tree.h
new file mode 100644
index 0000000..2096ff6
--- /dev/null
+++ b/advent-of-code/2023/lib/rb_tree.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 Mistivia <i@mistivia.com>
+// Licensed under GPLv3. See LICENSE for details.
+
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RB_TREE_H_
+#define RB_TREE_H_
+
+#include <stdlib.h>
+
+struct rb_node {
+ struct {
+ struct rb_node *rbe_left;
+ struct rb_node *rbe_right;
+ struct rb_node *rbe_parent;
+ int rbe_color;
+ } entry;
+ char content[0];
+};
+typedef struct rb_node rb_node_t;
+
+struct rb_tree {
+ rb_node_t *rbh_root;
+ int (*cmp)(void *k1, void *k2);
+ void (*augment)(void *elm);
+};
+typedef struct rb_tree rb_tree_t;
+
+void rb_tree_remove(rb_tree_t *, void *iter);
+
+// return a iterator
+void *rb_tree_insert(rb_tree_t *, void *treenode);
+void *rb_tree_find(rb_tree_t *, void *val);
+void *rb_tree_next(rb_tree_t *, void *iter);
+void *rb_tree_min(rb_tree_t *);
+void *rb_tree_max(rb_tree_t *);
+void *rb_tree_left(void *node);
+void *rb_tree_right(void *node);
+void *rb_tree_parent(void *node);
+
+#endif
diff --git a/advent-of-code/2023/lib/str.c b/advent-of-code/2023/lib/str.c
new file mode 100644
index 0000000..511e45b
--- /dev/null
+++ b/advent-of-code/2023/lib/str.c
@@ -0,0 +1,143 @@
+// Copyright (C) 2023 Mistivia <i@mistivia.com>
+// Licensed under GPLv3. See LICENSE for details.
+
+#include "str.h"
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vec.h"
+
+void *str_split(const char *str, char delim) {
+ void* ret = new_vec();
+
+ if (str == NULL) return NULL;
+ if (*str == '\n') {
+ return ret;
+ }
+ int count = 0;
+ const char *begin = str;
+ for (const char *p = str; *p != '\0'; p++) {
+ if (*p != delim && !(delim == '\0' && isspace(*p))) {
+ continue;
+ }
+ int size = p - begin;
+ if (size > 0) count++;
+ }
+ count++;
+
+ begin = str;
+ int i = 0;
+ bool finished = false;
+ for (const char *p = str; !finished; p++) {
+ if (*p == '\0') finished = true;
+ if (*p != delim && *p != '\0' && !(delim == '\0' && isspace(*p))) {
+ continue;
+ }
+ int size = p - begin;
+ if (size == 0) {
+ begin = p + 1;
+ continue;
+ }
+ char *buf = malloc(sizeof(char) * (size + 1));
+ buf[size] = '\0';
+ memcpy(buf, begin, size * sizeof(char));
+ begin = p + 1;
+ vec_push_back(ret, buf);
+ }
+ return ret;
+}
+
+char *str_strip(const char *str) {
+ if (str == NULL) return NULL;
+ int len = strlen(str);
+ const char *begin = str;
+ const char *end = str + len - 1;
+ while (isspace(*begin) && begin < end) {
+ begin++;
+ }
+ while (isspace(*end) && end >= begin) {
+ end--;
+ }
+ len = end - begin + 1;
+ char *buf = malloc(sizeof(char) * (len) + 1);
+ buf[len] = '\0';
+ memcpy(buf, begin, len);
+ return buf;
+}
+
+typedef struct {
+ size_t size;
+ size_t cap;
+ char *buf;
+} str_builder_t;
+
+// string stream
+void* new_ss() {
+ str_builder_t *self = malloc(sizeof(str_builder_t));
+ *self = (str_builder_t){.size = 0, .cap = 16};
+ self->buf = malloc(sizeof(char) * 17);
+ return self;
+}
+
+static void ss_reserve(str_builder_t *self, int extra) {
+ if (self->size + extra <= self->cap) {
+ return;
+ }
+ int new_cap = (self->size + extra) * 2;
+ self->buf = realloc(self->buf, new_cap + 1);
+ memset(self->buf + self->cap, 0, new_cap - self->cap + 1);
+ self->cap = new_cap;
+}
+
+void ss_add(void *self_, char *format, ...) {
+ str_builder_t *self = self_;
+ va_list va1;
+ va_list va2;
+ va_start(va1, format);
+ va_copy(va2, va1);
+ int size = vsnprintf(NULL, 0, format, va1);
+ ss_reserve(self, size);
+ vsnprintf(self->buf + self->size, self->cap - self->size + 1, format, va2);
+ self->size += size;
+}
+
+void ss_addc(void *self_, char c) {
+ str_builder_t *self = self_;
+ ss_reserve(self, 1);
+ self->buf[self->size] = c;
+ self->size++;
+}
+
+char *ss_cstr(void *self_) {
+ str_builder_t *self = self_;
+ return self->buf;
+}
+
+size_t ss_size(void *self_) {
+ str_builder_t *self = self_;
+ return self->size;
+}
+
+char *fgetline(FILE *fp) {
+ void *ss = new_ss();
+ while (true) {
+ int c = fgetc(fp);
+ if (c == EOF && ss_size(ss) == 0) return NULL;
+ if (c != EOF) ss_addc(ss, c);
+ if (c == EOF || c == '\n') return ss_cstr(ss);
+ }
+ return NULL;
+}
+
+int fpeek(FILE *fp) {
+ int c = fgetc(fp);
+ if (c == EOF) return c;
+ ungetc(c, fp);
+ return c;
+}
diff --git a/advent-of-code/2023/lib/str.h b/advent-of-code/2023/lib/str.h
new file mode 100644
index 0000000..214e297
--- /dev/null
+++ b/advent-of-code/2023/lib/str.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 Mistivia <i@mistivia.com>
+// Licensed under GPLv3. See LICENSE for details.
+
+#ifndef DYMC_STR_H_
+#define DYMC_STR_H_
+
+#include <stdio.h>
+#include <stddef.h>
+
+char *str_strip(const char *str);
+void* str_split(const char *str, char delim);
+
+
+// string stream
+void* new_ss();
+void ss_add(void *self, char *format, ...);
+void ss_addc(void *self, char c);
+char *ss_cstr(void *self);
+size_t ss_size(void* self);
+
+char *fgetline(FILE *fp);
+int fpeek(FILE *fp);
+
+#endif
diff --git a/advent-of-code/2023/lib/vec.c b/advent-of-code/2023/lib/vec.c
new file mode 100644
index 0000000..c3d8a0a
--- /dev/null
+++ b/advent-of-code/2023/lib/vec.c
@@ -0,0 +1,72 @@
+#include "vec.h"
+
+#include <string.h>
+#include "stdlib.h"
+
+struct vec {
+ size_t length;
+ size_t capacity;
+ void** buf;
+};
+
+static void vec_enlarge(struct vec* self) {
+ self->buf = realloc(self->buf, self->capacity * sizeof(void*) * 2);
+ self->capacity = self->capacity * 2;
+}
+
+void *new_vec() {
+ struct vec* vec = malloc(sizeof(struct vec));
+ vec->length = 0;
+ vec->capacity = 16;
+ vec->buf = malloc(16 * sizeof(void*));
+ return vec;
+}
+
+void vec_push_back(void *self_, void* obj) {
+ struct vec *self = self_;
+ if (self->length == self->capacity) vec_enlarge(self);
+ self->buf[self->length] = obj;
+ self->length++;
+}
+
+void* vec_get(void *self_, size_t n) {
+ struct vec *self = self_;
+ if (n < 0 || n >= self->length) return NULL;
+ return self->buf[n];
+}
+
+
+void vec_erase(void *self_, size_t n) {
+ struct vec *self = self_;
+ if (self->length <= n) {
+ return;
+ }
+ memmove(self->buf + n, self->buf + n + 1, (self->length - n - 1) * sizeof(void*));
+ self->length--;
+}
+
+size_t vec_size(void *self_) {
+ struct vec *self = self_;
+ return self->length;
+}
+
+void vec_reserve(void *self_, size_t n) {
+ struct vec *self = self_;
+ if (n <= self->capacity) {
+ return;
+ }
+ self->buf = malloc(sizeof(void*) * n);
+ self->capacity = n;
+}
+
+void vec_insert(void *self_, size_t pos, void *obj) {
+ struct vec *self = self_;
+ if (self->length == self->capacity) {
+ vec_enlarge(self);
+ }
+ if (pos > self->length || pos < 0) return;
+ memmove(self->buf + pos + 1, self->buf + pos, sizeof(void*) * (self->length - pos));
+ self->buf[pos] = obj;
+ self->length++;
+}
+
diff --git a/advent-of-code/2023/lib/vec.h b/advent-of-code/2023/lib/vec.h
new file mode 100644
index 0000000..5778689
--- /dev/null
+++ b/advent-of-code/2023/lib/vec.h
@@ -0,0 +1,15 @@
+#ifndef VEC_H_
+#define VEC_H_
+
+#include <stddef.h>
+
+void *new_vec();
+void vec_push_back(void *self, void* obj);
+void* vec_get(void *self, size_t n);
+size_t vec_length(void *self);
+void vec_erase(void *self, size_t n);
+size_t vec_size(void* self);
+void vec_reserve(void* self, size_t n);
+void vec_insert(void* self, size_t pos, void* obj);
+
+#endif