aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/build/packages/gleam_stdlib/src
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023/build/packages/gleam_stdlib/src')
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/dict.mjs957
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/base.gleam21
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/bit_array.gleam157
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/bit_builder.gleam80
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/bit_string.gleam43
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/bool.gleam428
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/bytes_builder.gleam197
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/dict.gleam544
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/dynamic.gleam1508
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam546
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/function.gleam162
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/int.gleam874
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/io.gleam117
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/iterator.gleam1530
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/list.gleam2154
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/map.gleam127
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/option.gleam346
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/order.gleam133
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/pair.gleam85
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/queue.gleam292
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/regex.gleam214
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/result.gleam482
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/set.gleam264
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/string.gleam906
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/string_builder.gleam298
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam/uri.gleam462
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@base.erl20
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@bit_array.erl102
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@bit_builder.erl66
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@bit_string.erl33
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@bool.erl162
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@bytes_builder.erl87
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@dict.erl97
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@dynamic.erl808
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@float.erl181
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@function.erl67
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@int.erl332
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@io.erl27
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@iterator.erl744
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@list.erl1129
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@map.erl76
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@option.erl147
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@order.erl79
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@pair.erl33
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@queue.erl121
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@regex.erl33
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@result.erl201
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@set.erl85
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@string.erl352
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@string_builder.erl91
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam@uri.erl252
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.app.src31
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.erl529
-rw-r--r--aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.mjs875
54 files changed, 19687 insertions, 0 deletions
diff --git a/aoc2023/build/packages/gleam_stdlib/src/dict.mjs b/aoc2023/build/packages/gleam_stdlib/src/dict.mjs
new file mode 100644
index 0000000..a8309e0
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/dict.mjs
@@ -0,0 +1,957 @@
+/**
+ * This file uses jsdoc to annotate types.
+ * These types can be checked using the typescript compiler with "checkjs" option.
+ */
+
+import { isEqual } from "./gleam.mjs";
+
+const referenceMap = new WeakMap();
+const tempDataView = new DataView(new ArrayBuffer(8));
+let referenceUID = 0;
+/**
+ * hash the object by reference using a weak map and incrementing uid
+ * @param {any} o
+ * @returns {number}
+ */
+function hashByReference(o) {
+ const known = referenceMap.get(o);
+ if (known !== undefined) {
+ return known;
+ }
+ const hash = referenceUID++;
+ if (referenceUID === 0x7fffffff) {
+ referenceUID = 0;
+ }
+ referenceMap.set(o, hash);
+ return hash;
+}
+/**
+ * merge two hashes in an order sensitive way
+ * @param {number} a
+ * @param {number} b
+ * @returns {number}
+ */
+function hashMerge(a, b) {
+ return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0;
+}
+/**
+ * standard string hash popularised by java
+ * @param {string} s
+ * @returns {number}
+ */
+function hashString(s) {
+ let hash = 0;
+ const len = s.length;
+ for (let i = 0; i < len; i++) {
+ hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0;
+ }
+ return hash;
+}
+/**
+ * hash a number by converting to two integers and do some jumbling
+ * @param {number} n
+ * @returns {number}
+ */
+function hashNumber(n) {
+ tempDataView.setFloat64(0, n);
+ const i = tempDataView.getInt32(0);
+ const j = tempDataView.getInt32(4);
+ return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j;
+}
+/**
+ * hash a BigInt by converting it to a string and hashing that
+ * @param {BigInt} n
+ * @returns {number}
+ */
+function hashBigInt(n) {
+ return hashString(n.toString());
+}
+/**
+ * hash any js object
+ * @param {any} o
+ * @returns {number}
+ */
+function hashObject(o) {
+ const proto = Object.getPrototypeOf(o);
+ if (proto !== null && typeof proto.hashCode === "function") {
+ try {
+ const code = o.hashCode(o);
+ if (typeof code === "number") {
+ return code;
+ }
+ } catch {}
+ }
+ if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) {
+ return hashByReference(o);
+ }
+ if (o instanceof Date) {
+ return hashNumber(o.getTime());
+ }
+ let h = 0;
+ if (o instanceof ArrayBuffer) {
+ o = new Uint8Array(o);
+ }
+ if (Array.isArray(o) || o instanceof Uint8Array) {
+ for (let i = 0; i < o.length; i++) {
+ h = (Math.imul(31, h) + getHash(o[i])) | 0;
+ }
+ } else if (o instanceof Set) {
+ o.forEach((v) => {
+ h = (h + getHash(v)) | 0;
+ });
+ } else if (o instanceof Map) {
+ o.forEach((v, k) => {
+ h = (h + hashMerge(getHash(v), getHash(k))) | 0;
+ });
+ } else {
+ const keys = Object.keys(o);
+ for (let i = 0; i < keys.length; i++) {
+ const k = keys[i];
+ const v = o[k];
+ h = (h + hashMerge(getHash(v), hashString(k))) | 0;
+ }
+ }
+ return h;
+}
+/**
+ * hash any js value
+ * @param {any} u
+ * @returns {number}
+ */
+export function getHash(u) {
+ if (u === null) return 0x42108422;
+ if (u === undefined) return 0x42108423;
+ if (u === true) return 0x42108421;
+ if (u === false) return 0x42108420;
+ switch (typeof u) {
+ case "number":
+ return hashNumber(u);
+ case "string":
+ return hashString(u);
+ case "bigint":
+ return hashBigInt(u);
+ case "object":
+ return hashObject(u);
+ case "symbol":
+ return hashByReference(u);
+ case "function":
+ return hashByReference(u);
+ default:
+ return 0; // should be unreachable
+ }
+}
+/**
+ * @template K,V
+ * @typedef {ArrayNode<K,V> | IndexNode<K,V> | CollisionNode<K,V>} Node
+ */
+/**
+ * @template K,V
+ * @typedef {{ type: typeof ENTRY, k: K, v: V }} Entry
+ */
+/**
+ * @template K,V
+ * @typedef {{ type: typeof ARRAY_NODE, size: number, array: (undefined | Entry<K,V> | Node<K,V>)[] }} ArrayNode
+ */
+/**
+ * @template K,V
+ * @typedef {{ type: typeof INDEX_NODE, bitmap: number, array: (Entry<K,V> | Node<K,V>)[] }} IndexNode
+ */
+/**
+ * @template K,V
+ * @typedef {{ type: typeof COLLISION_NODE, hash: number, array: Entry<K, V>[] }} CollisionNode
+ */
+/**
+ * @typedef {{ val: boolean }} Flag
+ */
+const SHIFT = 5; // number of bits you need to shift by to get the next bucket
+const BUCKET_SIZE = Math.pow(2, SHIFT);
+const MASK = BUCKET_SIZE - 1; // used to zero out all bits not in the bucket
+const MAX_INDEX_NODE = BUCKET_SIZE / 2; // when does index node grow into array node
+const MIN_ARRAY_NODE = BUCKET_SIZE / 4; // when does array node shrink to index node
+const ENTRY = 0;
+const ARRAY_NODE = 1;
+const INDEX_NODE = 2;
+const COLLISION_NODE = 3;
+/** @type {IndexNode<any,any>} */
+const EMPTY = {
+ type: INDEX_NODE,
+ bitmap: 0,
+ array: [],
+};
+/**
+ * Mask the hash to get only the bucket corresponding to shift
+ * @param {number} hash
+ * @param {number} shift
+ * @returns {number}
+ */
+function mask(hash, shift) {
+ return (hash >>> shift) & MASK;
+}
+/**
+ * Set only the Nth bit where N is the masked hash
+ * @param {number} hash
+ * @param {number} shift
+ * @returns {number}
+ */
+function bitpos(hash, shift) {
+ return 1 << mask(hash, shift);
+}
+/**
+ * Count the number of 1 bits in a number
+ * @param {number} x
+ * @returns {number}
+ */
+function bitcount(x) {
+ x -= (x >> 1) & 0x55555555;
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x + (x >> 4)) & 0x0f0f0f0f;
+ x += x >> 8;
+ x += x >> 16;
+ return x & 0x7f;
+}
+/**
+ * Calculate the array index of an item in a bitmap index node
+ * @param {number} bitmap
+ * @param {number} bit
+ * @returns {number}
+ */
+function index(bitmap, bit) {
+ return bitcount(bitmap & (bit - 1));
+}
+/**
+ * Efficiently copy an array and set one value at an index
+ * @template T
+ * @param {T[]} arr
+ * @param {number} at
+ * @param {T} val
+ * @returns {T[]}
+ */
+function cloneAndSet(arr, at, val) {
+ const len = arr.length;
+ const out = new Array(len);
+ for (let i = 0; i < len; ++i) {
+ out[i] = arr[i];
+ }
+ out[at] = val;
+ return out;
+}
+/**
+ * Efficiently copy an array and insert one value at an index
+ * @template T
+ * @param {T[]} arr
+ * @param {number} at
+ * @param {T} val
+ * @returns {T[]}
+ */
+function spliceIn(arr, at, val) {
+ const len = arr.length;
+ const out = new Array(len + 1);
+ let i = 0;
+ let g = 0;
+ while (i < at) {
+ out[g++] = arr[i++];
+ }
+ out[g++] = val;
+ while (i < len) {
+ out[g++] = arr[i++];
+ }
+ return out;
+}
+/**
+ * Efficiently copy an array and remove one value at an index
+ * @template T
+ * @param {T[]} arr
+ * @param {number} at
+ * @returns {T[]}
+ */
+function spliceOut(arr, at) {
+ const len = arr.length;
+ const out = new Array(len - 1);
+ let i = 0;
+ let g = 0;
+ while (i < at) {
+ out[g++] = arr[i++];
+ }
+ ++i;
+ while (i < len) {
+ out[g++] = arr[i++];
+ }
+ return out;
+}
+/**
+ * Create a new node containing two entries
+ * @template K,V
+ * @param {number} shift
+ * @param {K} key1
+ * @param {V} val1
+ * @param {number} key2hash
+ * @param {K} key2
+ * @param {V} val2
+ * @returns {Node<K,V>}
+ */
+function createNode(shift, key1, val1, key2hash, key2, val2) {
+ const key1hash = getHash(key1);
+ if (key1hash === key2hash) {
+ return {
+ type: COLLISION_NODE,
+ hash: key1hash,
+ array: [
+ { type: ENTRY, k: key1, v: val1 },
+ { type: ENTRY, k: key2, v: val2 },
+ ],
+ };
+ }
+ const addedLeaf = { val: false };
+ return assoc(
+ assocIndex(EMPTY, shift, key1hash, key1, val1, addedLeaf),
+ shift,
+ key2hash,
+ key2,
+ val2,
+ addedLeaf
+ );
+}
+/**
+ * @template T,K,V
+ * @callback AssocFunction
+ * @param {T} root
+ * @param {number} shift
+ * @param {number} hash
+ * @param {K} key
+ * @param {V} val
+ * @param {Flag} addedLeaf
+ * @returns {Node<K,V>}
+ */
+/**
+ * Associate a node with a new entry, creating a new node
+ * @template T,K,V
+ * @type {AssocFunction<Node<K,V>,K,V>}
+ */
+function assoc(root, shift, hash, key, val, addedLeaf) {
+ switch (root.type) {
+ case ARRAY_NODE:
+ return assocArray(root, shift, hash, key, val, addedLeaf);
+ case INDEX_NODE:
+ return assocIndex(root, shift, hash, key, val, addedLeaf);
+ case COLLISION_NODE:
+ return assocCollision(root, shift, hash, key, val, addedLeaf);
+ }
+}
+/**
+ * @template T,K,V
+ * @type {AssocFunction<ArrayNode<K,V>,K,V>}
+ */
+function assocArray(root, shift, hash, key, val, addedLeaf) {
+ const idx = mask(hash, shift);
+ const node = root.array[idx];
+ // if the corresponding index is empty set the index to a newly created node
+ if (node === undefined) {
+ addedLeaf.val = true;
+ return {
+ type: ARRAY_NODE,
+ size: root.size + 1,
+ array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }),
+ };
+ }
+ if (node.type === ENTRY) {
+ // if keys are equal replace the entry
+ if (isEqual(key, node.k)) {
+ if (val === node.v) {
+ return root;
+ }
+ return {
+ type: ARRAY_NODE,
+ size: root.size,
+ array: cloneAndSet(root.array, idx, {
+ type: ENTRY,
+ k: key,
+ v: val,
+ }),
+ };
+ }
+ // otherwise upgrade the entry to a node and insert
+ addedLeaf.val = true;
+ return {
+ type: ARRAY_NODE,
+ size: root.size,
+ array: cloneAndSet(
+ root.array,
+ idx,
+ createNode(shift + SHIFT, node.k, node.v, hash, key, val)
+ ),
+ };
+ }
+ // otherwise call assoc on the child node
+ const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf);
+ // if the child node hasn't changed just return the old root
+ if (n === node) {
+ return root;
+ }
+ // otherwise set the index to the new node
+ return {
+ type: ARRAY_NODE,
+ size: root.size,
+ array: cloneAndSet(root.array, idx, n),
+ };
+}
+/**
+ * @template T,K,V
+ * @type {AssocFunction<IndexNode<K,V>,K,V>}
+ */
+function assocIndex(root, shift, hash, key, val, addedLeaf) {
+ const bit = bitpos(hash, shift);
+ const idx = index(root.bitmap, bit);
+ // if there is already a item at this hash index..
+ if ((root.bitmap & bit) !== 0) {
+ // if there is a node at the index (not an entry), call assoc on the child node
+ const node = root.array[idx];
+ if (node.type !== ENTRY) {
+ const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf);
+ if (n === node) {
+ return root;
+ }
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap,
+ array: cloneAndSet(root.array, idx, n),
+ };
+ }
+ // otherwise there is an entry at the index
+ // if the keys are equal replace the entry with the updated value
+ const nodeKey = node.k;
+ if (isEqual(key, nodeKey)) {
+ if (val === node.v) {
+ return root;
+ }
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap,
+ array: cloneAndSet(root.array, idx, {
+ type: ENTRY,
+ k: key,
+ v: val,
+ }),
+ };
+ }
+ // if the keys are not equal, replace the entry with a new child node
+ addedLeaf.val = true;
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap,
+ array: cloneAndSet(
+ root.array,
+ idx,
+ createNode(shift + SHIFT, nodeKey, node.v, hash, key, val)
+ ),
+ };
+ } else {
+ // else there is currently no item at the hash index
+ const n = root.array.length;
+ // if the number of nodes is at the maximum, expand this node into an array node
+ if (n >= MAX_INDEX_NODE) {
+ // create a 32 length array for the new array node (one for each bit in the hash)
+ const nodes = new Array(32);
+ // create and insert a node for the new entry
+ const jdx = mask(hash, shift);
+ nodes[jdx] = assocIndex(EMPTY, shift + SHIFT, hash, key, val, addedLeaf);
+ let j = 0;
+ let bitmap = root.bitmap;
+ // place each item in the index node into the correct spot in the array node
+ // loop through all 32 bits / array positions
+ for (let i = 0; i < 32; i++) {
+ if ((bitmap & 1) !== 0) {
+ const node = root.array[j++];
+ nodes[i] = node;
+ }
+ // shift the bitmap to process the next bit
+ bitmap = bitmap >>> 1;
+ }
+ return {
+ type: ARRAY_NODE,
+ size: n + 1,
+ array: nodes,
+ };
+ } else {
+ // else there is still space in this index node
+ // simply insert a new entry at the hash index
+ const newArray = spliceIn(root.array, idx, {
+ type: ENTRY,
+ k: key,
+ v: val,
+ });
+ addedLeaf.val = true;
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap | bit,
+ array: newArray,
+ };
+ }
+ }
+}
+/**
+ * @template T,K,V
+ * @type {AssocFunction<CollisionNode<K,V>,K,V>}
+ */
+function assocCollision(root, shift, hash, key, val, addedLeaf) {
+ // if there is a hash collision
+ if (hash === root.hash) {
+ const idx = collisionIndexOf(root, key);
+ // if this key already exists replace the entry with the new value
+ if (idx !== -1) {
+ const entry = root.array[idx];
+ if (entry.v === val) {
+ return root;
+ }
+ return {
+ type: COLLISION_NODE,
+ hash: hash,
+ array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }),
+ };
+ }
+ // otherwise insert the entry at the end of the array
+ const size = root.array.length;
+ addedLeaf.val = true;
+ return {
+ type: COLLISION_NODE,
+ hash: hash,
+ array: cloneAndSet(root.array, size, { type: ENTRY, k: key, v: val }),
+ };
+ }
+ // if there is no hash collision, upgrade to an index node
+ return assoc(
+ {
+ type: INDEX_NODE,
+ bitmap: bitpos(root.hash, shift),
+ array: [root],
+ },
+ shift,
+ hash,
+ key,
+ val,
+ addedLeaf
+ );
+}
+/**
+ * Find the index of a key in the collision node's array
+ * @template K,V
+ * @param {CollisionNode<K,V>} root
+ * @param {K} key
+ * @returns {number}
+ */
+function collisionIndexOf(root, key) {
+ const size = root.array.length;
+ for (let i = 0; i < size; i++) {
+ if (isEqual(key, root.array[i].k)) {
+ return i;
+ }
+ }
+ return -1;
+}
+/**
+ * @template T,K,V
+ * @callback FindFunction
+ * @param {T} root
+ * @param {number} shift
+ * @param {number} hash
+ * @param {K} key
+ * @returns {undefined | Entry<K,V>}
+ */
+/**
+ * Return the found entry or undefined if not present in the root
+ * @template K,V
+ * @type {FindFunction<Node<K,V>,K,V>}
+ */
+function find(root, shift, hash, key) {
+ switch (root.type) {
+ case ARRAY_NODE:
+ return findArray(root, shift, hash, key);
+ case INDEX_NODE:
+ return findIndex(root, shift, hash, key);
+ case COLLISION_NODE:
+ return findCollision(root, key);
+ }
+}
+/**
+ * @template K,V
+ * @type {FindFunction<ArrayNode<K,V>,K,V>}
+ */
+function findArray(root, shift, hash, key) {
+ const idx = mask(hash, shift);
+ const node = root.array[idx];
+ if (node === undefined) {
+ return undefined;
+ }
+ if (node.type !== ENTRY) {
+ return find(node, shift + SHIFT, hash, key);
+ }
+ if (isEqual(key, node.k)) {
+ return node;
+ }
+ return undefined;
+}
+/**
+ * @template K,V
+ * @type {FindFunction<IndexNode<K,V>,K,V>}
+ */
+function findIndex(root, shift, hash, key) {
+ const bit = bitpos(hash, shift);
+ if ((root.bitmap & bit) === 0) {
+ return undefined;
+ }
+ const idx = index(root.bitmap, bit);
+ const node = root.array[idx];
+ if (node.type !== ENTRY) {
+ return find(node, shift + SHIFT, hash, key);
+ }
+ if (isEqual(key, node.k)) {
+ return node;
+ }
+ return undefined;
+}
+/**
+ * @template K,V
+ * @param {CollisionNode<K,V>} root
+ * @param {K} key
+ * @returns {undefined | Entry<K,V>}
+ */
+function findCollision(root, key) {
+ const idx = collisionIndexOf(root, key);
+ if (idx < 0) {
+ return undefined;
+ }
+ return root.array[idx];
+}
+/**
+ * @template T,K,V
+ * @callback WithoutFunction
+ * @param {T} root
+ * @param {number} shift
+ * @param {number} hash
+ * @param {K} key
+ * @returns {undefined | Node<K,V>}
+ */
+/**
+ * Remove an entry from the root, returning the updated root.
+ * Returns undefined if the node should be removed from the parent.
+ * @template K,V
+ * @type {WithoutFunction<Node<K,V>,K,V>}
+ * */
+function without(root, shift, hash, key) {
+ switch (root.type) {
+ case ARRAY_NODE:
+ return withoutArray(root, shift, hash, key);
+ case INDEX_NODE:
+ return withoutIndex(root, shift, hash, key);
+ case COLLISION_NODE:
+ return withoutCollision(root, key);
+ }
+}
+/**
+ * @template K,V
+ * @type {WithoutFunction<ArrayNode<K,V>,K,V>}
+ */
+function withoutArray(root, shift, hash, key) {
+ const idx = mask(hash, shift);
+ const node = root.array[idx];
+ if (node === undefined) {
+ return root; // already empty
+ }
+ let n = undefined;
+ // if node is an entry and the keys are not equal there is nothing to remove
+ // if node is not an entry do a recursive call
+ if (node.type === ENTRY) {
+ if (!isEqual(node.k, key)) {
+ return root; // no changes
+ }
+ } else {
+ n = without(node, shift + SHIFT, hash, key);
+ if (n === node) {
+ return root; // no changes
+ }
+ }
+ // if the recursive call returned undefined the node should be removed
+ if (n === undefined) {
+ // if the number of child nodes is at the minimum, pack into an index node
+ if (root.size <= MIN_ARRAY_NODE) {
+ const arr = root.array;
+ const out = new Array(root.size - 1);
+ let i = 0;
+ let j = 0;
+ let bitmap = 0;
+ while (i < idx) {
+ const nv = arr[i];
+ if (nv !== undefined) {
+ out[j] = nv;
+ bitmap |= 1 << i;
+ ++j;
+ }
+ ++i;
+ }
+ ++i; // skip copying the removed node
+ while (i < arr.length) {
+ const nv = arr[i];
+ if (nv !== undefined) {
+ out[j] = nv;
+ bitmap |= 1 << i;
+ ++j;
+ }
+ ++i;
+ }
+ return {
+ type: INDEX_NODE,
+ bitmap: bitmap,
+ array: out,
+ };
+ }
+ return {
+ type: ARRAY_NODE,
+ size: root.size - 1,
+ array: cloneAndSet(root.array, idx, n),
+ };
+ }
+ return {
+ type: ARRAY_NODE,
+ size: root.size,
+ array: cloneAndSet(root.array, idx, n),
+ };
+}
+/**
+ * @template K,V
+ * @type {WithoutFunction<IndexNode<K,V>,K,V>}
+ */
+function withoutIndex(root, shift, hash, key) {
+ const bit = bitpos(hash, shift);
+ if ((root.bitmap & bit) === 0) {
+ return root; // already empty
+ }
+ const idx = index(root.bitmap, bit);
+ const node = root.array[idx];
+ // if the item is not an entry
+ if (node.type !== ENTRY) {
+ const n = without(node, shift + SHIFT, hash, key);
+ if (n === node) {
+ return root; // no changes
+ }
+ // if not undefined, the child node still has items, so update it
+ if (n !== undefined) {
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap,
+ array: cloneAndSet(root.array, idx, n),
+ };
+ }
+ // otherwise the child node should be removed
+ // if it was the only child node, remove this node from the parent
+ if (root.bitmap === bit) {
+ return undefined;
+ }
+ // otherwise just remove the child node
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap ^ bit,
+ array: spliceOut(root.array, idx),
+ };
+ }
+ // otherwise the item is an entry, remove it if the key matches
+ if (isEqual(key, node.k)) {
+ if (root.bitmap === bit) {
+ return undefined;
+ }
+ return {
+ type: INDEX_NODE,
+ bitmap: root.bitmap ^ bit,
+ array: spliceOut(root.array, idx),
+ };
+ }
+ return root;
+}
+/**
+ * @template K,V
+ * @param {CollisionNode<K,V>} root
+ * @param {K} key
+ * @returns {undefined | Node<K,V>}
+ */
+function withoutCollision(root, key) {
+ const idx = collisionIndexOf(root, key);
+ // if the key not found, no changes
+ if (idx < 0) {
+ return root;
+ }
+ // otherwise the entry was found, remove it
+ // if it was the only entry in this node, remove the whole node
+ if (root.array.length === 1) {
+ return undefined;
+ }
+ // otherwise just remove the entry
+ return {
+ type: COLLISION_NODE,
+ hash: root.hash,
+ array: spliceOut(root.array, idx),
+ };
+}
+/**
+ * @template K,V
+ * @param {undefined | Node<K,V>} root
+ * @param {(value:V,key:K)=>void} fn
+ * @returns {void}
+ */
+function forEach(root, fn) {
+ if (root === undefined) {
+ return;
+ }
+ const items = root.array;
+ const size = items.length;
+ for (let i = 0; i < size; i++) {
+ const item = items[i];
+ if (item === undefined) {
+ continue;
+ }
+ if (item.type === ENTRY) {
+ fn(item.v, item.k);
+ continue;
+ }
+ forEach(item, fn);
+ }
+}
+/**
+ * Extra wrapper to keep track of Dict size and clean up the API
+ * @template K,V
+ */
+export default class Dict {
+ /**
+ * @template V
+ * @param {Record<string,V>} o
+ * @returns {Dict<string,V>}
+ */
+ static fromObject(o) {
+ const keys = Object.keys(o);
+ /** @type Dict<string,V> */
+ let m = Dict.new();
+ for (let i = 0; i < keys.length; i++) {
+ const k = keys[i];
+ m = m.set(k, o[k]);
+ }
+ return m;
+ }
+ /**
+ * @template K,V
+ * @param {Map<K,V>} o
+ * @returns {Dict<K,V>}
+ */
+ static fromMap(o) {
+ /** @type Dict<K,V> */
+ let m = Dict.new();
+ o.forEach((v, k) => {
+ m = m.set(k, v);
+ });
+ return m;
+ }
+ static new() {
+ return new Dict(undefined, 0);
+ }
+ /**
+ * @param {undefined | Node<K,V>} root
+ * @param {number} size
+ */
+ constructor(root, size) {
+ this.root = root;
+ this.size = size;
+ }
+ /**
+ * @template NotFound
+ * @param {K} key
+ * @param {NotFound} notFound
+ * @returns {NotFound | V}
+ */
+ get(key, notFound) {
+ if (this.root === undefined) {
+ return notFound;
+ }
+ const found = find(this.root, 0, getHash(key), key);
+ if (found === undefined) {
+ return notFound;
+ }
+ return found.v;
+ }
+ /**
+ * @param {K} key
+ * @param {V} val
+ * @returns {Dict<K,V>}
+ */
+ set(key, val) {
+ const addedLeaf = { val: false };
+ const root = this.root === undefined ? EMPTY : this.root;
+ const newRoot = assoc(root, 0, getHash(key), key, val, addedLeaf);
+ if (newRoot === this.root) {
+ return this;
+ }
+ return new Dict(newRoot, addedLeaf.val ? this.size + 1 : this.size);
+ }
+ /**
+ * @param {K} key
+ * @returns {Dict<K,V>}
+ */
+ delete(key) {
+ if (this.root === undefined) {
+ return this;
+ }
+ const newRoot = without(this.root, 0, getHash(key), key);
+ if (newRoot === this.root) {
+ return this;
+ }
+ if (newRoot === undefined) {
+ return Dict.new();
+ }
+ return new Dict(newRoot, this.size - 1);
+ }
+ /**
+ * @param {K} key
+ * @returns {boolean}
+ */
+ has(key) {
+ if (this.root === undefined) {
+ return false;
+ }
+ return find(this.root, 0, getHash(key), key) !== undefined;
+ }
+ /**
+ * @returns {[K,V][]}
+ */
+ entries() {
+ if (this.root === undefined) {
+ return [];
+ }
+ /** @type [K,V][] */
+ const result = [];
+ this.forEach((v, k) => result.push([k, v]));
+ return result;
+ }
+ /**
+ *
+ * @param {(val:V,key:K)=>void} fn
+ */
+ forEach(fn) {
+ forEach(this.root, fn);
+ }
+ hashCode() {
+ let h = 0;
+ this.forEach((v, k) => {
+ h = (h + hashMerge(getHash(v), getHash(k))) | 0;
+ });
+ return h;
+ }
+ /**
+ * @param {unknown} o
+ * @returns {boolean}
+ */
+ equals(o) {
+ if (!(o instanceof Dict) || this.size !== o.size) {
+ return false;
+ }
+ let equal = true;
+ this.forEach((v, k) => {
+ equal = equal && isEqual(o.get(k, !v), v);
+ });
+ return equal;
+ }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/base.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/base.gleam
new file mode 100644
index 0000000..eab2f0b
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/base.gleam
@@ -0,0 +1,21 @@
+import gleam/bit_array
+
+@deprecated("Please use `base64_encode` in the `gleam/bit_array` module instead.")
+pub fn encode64(input: BitArray, padding: Bool) -> String {
+ bit_array.base64_encode(input, padding)
+}
+
+@deprecated("Please use `base64_decode` in the `gleam/bit_array` module instead.")
+pub fn decode64(encoded: String) -> Result(BitArray, Nil) {
+ bit_array.base64_decode(encoded)
+}
+
+@deprecated("Please use `base64_url_encode` in the `gleam/bit_array` module instead.")
+pub fn url_encode64(input: BitArray, padding: Bool) -> String {
+ bit_array.base64_url_encode(input, padding)
+}
+
+@deprecated("Please use `base64_url_decode` in the `gleam/bit_array` module instead.")
+pub fn url_decode64(encoded: String) -> Result(BitArray, Nil) {
+ bit_array.base64_url_decode(encoded)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_array.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_array.gleam
new file mode 100644
index 0000000..79860e9
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_array.gleam
@@ -0,0 +1,157 @@
+//// BitArrays are a sequence of binary data of any length.
+
+import gleam/string
+
+/// Converts a UTF-8 `String` type into a `BitArray`.
+///
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "bit_array_from_string")
+pub fn from_string(x: String) -> BitArray
+
+/// Returns an integer which is the number of bytes in the bit array.
+///
+@external(erlang, "erlang", "byte_size")
+@external(javascript, "../gleam_stdlib.mjs", "length")
+pub fn byte_size(x: BitArray) -> Int
+
+/// Creates a new bit array by joining two bit arrays.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > append(to: from_string("butter"), suffix: from_string("fly"))
+/// from_string("butterfly")
+/// ```
+///
+pub fn append(to first: BitArray, suffix second: BitArray) -> BitArray {
+ concat([first, second])
+}
+
+/// Extracts a sub-section of a bit array.
+///
+/// The slice will start at given position and continue up to specified
+/// length.
+/// A negative length can be used to extract bytes at the end of a bit array.
+///
+/// This function runs in constant time.
+///
+@external(erlang, "gleam_stdlib", "bit_array_slice")
+@external(javascript, "../gleam_stdlib.mjs", "bit_array_slice")
+pub fn slice(
+ from string: BitArray,
+ at position: Int,
+ take length: Int,
+) -> Result(BitArray, Nil)
+
+/// Tests to see whether a bit array is valid UTF-8.
+///
+pub fn is_utf8(bits: BitArray) -> Bool {
+ do_is_utf8(bits)
+}
+
+@target(erlang)
+fn do_is_utf8(bits: BitArray) -> Bool {
+ case bits {
+ <<>> -> True
+ <<_:utf8, rest:bytes>> -> do_is_utf8(rest)
+ _ -> False
+ }
+}
+
+@target(javascript)
+fn do_is_utf8(bits: BitArray) -> Bool {
+ case to_string(bits) {
+ Ok(_) -> True
+ _ -> False
+ }
+}
+
+/// Converts a bit array to a string.
+///
+/// Returns an error if the bit array is invalid UTF-8 data.
+///
+pub fn to_string(bits: BitArray) -> Result(String, Nil) {
+ do_to_string(bits)
+}
+
+@target(erlang)
+@external(erlang, "gleam_stdlib", "identity")
+fn unsafe_to_string(a: BitArray) -> String
+
+@target(erlang)
+fn do_to_string(bits: BitArray) -> Result(String, Nil) {
+ case is_utf8(bits) {
+ True -> Ok(unsafe_to_string(bits))
+ False -> Error(Nil)
+ }
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "bit_array_to_string")
+fn do_to_string(a: BitArray) -> Result(String, Nil)
+
+/// Creates a new bit array by joining multiple binaries.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > concat([from_string("butter"), from_string("fly")])
+/// from_string("butterfly")
+/// ```
+///
+@external(erlang, "gleam_stdlib", "bit_array_concat")
+@external(javascript, "../gleam_stdlib.mjs", "bit_array_concat")
+pub fn concat(bit_arrays: List(BitArray)) -> BitArray
+
+/// Encodes a BitArray into a base 64 encoded string.
+///
+pub fn base64_encode(input: BitArray, padding: Bool) -> String {
+ let encoded = encode64(input)
+ case padding {
+ True -> encoded
+ False -> string.replace(encoded, "=", "")
+ }
+}
+
+@external(erlang, "base64", "encode")
+@external(javascript, "../gleam_stdlib.mjs", "encode64")
+fn encode64(a: BitArray) -> String
+
+/// Decodes a base 64 encoded string into a `BitArray`.
+///
+pub fn base64_decode(encoded: String) -> Result(BitArray, Nil) {
+ let padded = case byte_size(from_string(encoded)) % 4 {
+ 0 -> encoded
+ n -> string.append(encoded, string.repeat("=", 4 - n))
+ }
+ decode64(padded)
+}
+
+@external(erlang, "gleam_stdlib", "base_decode64")
+@external(javascript, "../gleam_stdlib.mjs", "decode64")
+fn decode64(a: String) -> Result(BitArray, Nil)
+
+/// Encodes a `BitArray` into a base 64 encoded string with URL and filename safe alphabet.
+///
+pub fn base64_url_encode(input: BitArray, padding: Bool) -> String {
+ base64_encode(input, padding)
+ |> string.replace("+", "-")
+ |> string.replace("/", "_")
+}
+
+/// Decodes a base 64 encoded string with URL and filename safe alphabet into a `BitArray`.
+///
+pub fn base64_url_decode(encoded: String) -> Result(BitArray, Nil) {
+ encoded
+ |> string.replace("-", "+")
+ |> string.replace("_", "/")
+ |> base64_decode()
+}
+
+@external(erlang, "binary", "encode_hex")
+@external(javascript, "../gleam_stdlib.mjs", "base16_encode")
+pub fn base16_encode(input: BitArray) -> String
+
+@external(erlang, "gleam_stdlib", "base16_decode")
+@external(javascript, "../gleam_stdlib.mjs", "base16_decode")
+pub fn base16_decode(input: String) -> Result(BitArray, Nil)
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_builder.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_builder.gleam
new file mode 100644
index 0000000..ce6fe52
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_builder.gleam
@@ -0,0 +1,80 @@
+//// This module has been deprecated in favour of `gleam/bytes_builder`.
+
+import gleam/bytes_builder
+import gleam/string_builder.{type StringBuilder}
+
+pub type BitBuilder =
+ bytes_builder.BytesBuilder
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn new() -> BitBuilder {
+ bytes_builder.new()
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn prepend(to: BitBuilder, prefix: BitArray) -> BitBuilder {
+ bytes_builder.prepend(to, prefix)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn append(to: BitBuilder, suffix: BitArray) -> BitBuilder {
+ bytes_builder.append(to, suffix)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn prepend_builder(to: BitBuilder, prefix: BitBuilder) -> BitBuilder {
+ bytes_builder.prepend_builder(to, prefix)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn append_builder(
+ to first: BitBuilder,
+ suffix second: BitBuilder,
+) -> BitBuilder {
+ bytes_builder.append_builder(first, second)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder {
+ bytes_builder.prepend_string(to, prefix)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn append_string(to: BitBuilder, suffix: String) -> BitBuilder {
+ bytes_builder.append_string(to, suffix)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn concat(builders: List(BitBuilder)) -> BitBuilder {
+ bytes_builder.concat(builders)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn concat_bit_strings(bits: List(BitArray)) -> BitBuilder {
+ bytes_builder.concat_bit_arrays(bits)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn from_string(string: String) -> BitBuilder {
+ bytes_builder.from_string(string)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn from_string_builder(builder: StringBuilder) -> BitBuilder {
+ bytes_builder.from_string_builder(builder)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn from_bit_string(bits: BitArray) -> BitBuilder {
+ bytes_builder.from_bit_array(bits)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn to_bit_string(builder: BitBuilder) -> BitArray {
+ bytes_builder.to_bit_array(builder)
+}
+
+@deprecated("Please use the `gleam/bytes_builder` module instead.")
+pub fn byte_size(builder: BitBuilder) -> Int {
+ bytes_builder.byte_size(builder)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_string.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_string.gleam
new file mode 100644
index 0000000..b703da0
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/bit_string.gleam
@@ -0,0 +1,43 @@
+//// This module has been deprecated. Please use the `gleam/bit_array` module
+//// instead.
+
+import gleam/bit_array
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn from_string(x: String) -> BitArray {
+ bit_array.from_string(x)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn byte_size(x: BitArray) -> Int {
+ bit_array.byte_size(x)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn append(to first: BitArray, suffix second: BitArray) -> BitArray {
+ bit_array.append(first, second)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn slice(
+ from string: BitArray,
+ at position: Int,
+ take length: Int,
+) -> Result(BitArray, Nil) {
+ bit_array.slice(string, position, length)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn is_utf8(bits: BitArray) -> Bool {
+ bit_array.is_utf8(bits)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn to_string(bits: BitArray) -> Result(String, Nil) {
+ bit_array.to_string(bits)
+}
+
+@deprecated("Please use the `gleam/bit_array` module instead.")
+pub fn concat(bit_strings: List(BitArray)) -> BitArray {
+ bit_array.concat(bit_strings)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/bool.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/bool.gleam
new file mode 100644
index 0000000..91bd6b7
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/bool.gleam
@@ -0,0 +1,428 @@
+//// A type with two possible values, `True` and `False`. Used to indicate whether
+//// things are... true or false!
+////
+//// Often is it clearer and offers more type safety to define a custom type
+//// than to use `Bool`. For example, rather than having a `is_teacher: Bool`
+//// field consider having a `role: SchoolRole` field where `SchoolRole` is a custom
+//// type that can be either `Student` or `Teacher`.
+
+import gleam/order.{type Order}
+
+/// Returns the and of two bools, but it evaluates both arguments.
+///
+/// It's the function equivalent of the `&&` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > and(True, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > and(False, True)
+/// False
+/// ```
+///
+/// ```gleam
+/// > False |> and(True)
+/// False
+/// ```
+///
+pub fn and(a: Bool, b: Bool) -> Bool {
+ a && b
+}
+
+/// Returns the or of two bools, but it evaluates both arguments.
+///
+/// It's the function equivalent of the `||` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > or(True, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > or(False, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > False |> or(True)
+/// True
+/// ```
+///
+pub fn or(a: Bool, b: Bool) -> Bool {
+ a || b
+}
+
+/// Returns the opposite bool value.
+///
+/// This is the same as the `!` or `not` operators in some other languages.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > negate(True)
+/// False
+/// ```
+///
+/// ```gleam
+/// > negate(False)
+/// True
+/// ```
+///
+pub fn negate(bool: Bool) -> Bool {
+ case bool {
+ True -> False
+ False -> True
+ }
+}
+
+/// Returns the nor of two bools.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > nor(False, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > nor(False, True)
+/// False
+/// ```
+///
+/// ```gleam
+/// > nor(True, False)
+/// False
+/// ```
+///
+/// ```gleam
+/// > nor(True, True)
+/// False
+/// ```
+///
+pub fn nor(a: Bool, b: Bool) -> Bool {
+ case a, b {
+ False, False -> True
+ False, True -> False
+ True, False -> False
+ True, True -> False
+ }
+}
+
+/// Returns the nand of two bools.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > nand(False, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > nand(False, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > nand(True, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > nand(True, True)
+/// False
+/// ```
+///
+pub fn nand(a: Bool, b: Bool) -> Bool {
+ case a, b {
+ False, False -> True
+ False, True -> True
+ True, False -> True
+ True, True -> False
+ }
+}
+
+/// Returns the exclusive or of two bools.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > exclusive_or(False, False)
+/// False
+/// ```
+///
+/// ```gleam
+/// > exclusive_or(False, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > exclusive_or(True, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > exclusive_or(True, True)
+/// False
+/// ```
+///
+pub fn exclusive_or(a: Bool, b: Bool) -> Bool {
+ case a, b {
+ False, False -> False
+ False, True -> True
+ True, False -> True
+ True, True -> False
+ }
+}
+
+/// Returns the exclusive nor of two bools.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > exclusive_nor(False, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > exclusive_nor(False, True)
+/// False
+/// ```
+///
+/// ```gleam
+/// > exclusive_nor(True, False)
+/// False
+/// ```
+///
+/// ```gleam
+/// > exclusive_nor(True, True)
+/// True
+/// ```
+///
+pub fn exclusive_nor(a: Bool, b: Bool) -> Bool {
+ case a, b {
+ False, False -> True
+ False, True -> False
+ True, False -> False
+ True, True -> True
+ }
+}
+
+/// Compares two bools and returns the first value's `Order` to the second.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/order
+/// > compare(True, False)
+/// order.Gt
+/// ```
+///
+pub fn compare(a: Bool, with b: Bool) -> Order {
+ case a, b {
+ True, True -> order.Eq
+ True, False -> order.Gt
+ False, False -> order.Eq
+ False, True -> order.Lt
+ }
+}
+
+/// Returns `True` if either argument's value is `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > max(True, False)
+/// True
+/// ```
+///
+/// ```gleam
+/// > max(False, True)
+/// True
+/// ```
+///
+/// ```gleam
+/// > max(False, False)
+/// False
+/// ```
+///
+pub fn max(a: Bool, b: Bool) -> Bool {
+ case a {
+ True -> True
+ False -> b
+ }
+}
+
+/// Returns `False` if either bool value is `False`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > min(True, False)
+/// False
+/// ```
+///
+/// ```gleam
+/// > min(False, True)
+/// False
+///
+/// > min(False, False)
+/// False
+/// ```
+///
+pub fn min(a: Bool, b: Bool) -> Bool {
+ case a {
+ False -> False
+ True -> b
+ }
+}
+
+/// Returns a numeric representation of the given bool.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_int(True)
+/// 1
+///
+/// > to_int(False)
+/// 0
+/// ```
+///
+pub fn to_int(bool: Bool) -> Int {
+ case bool {
+ False -> 0
+ True -> 1
+ }
+}
+
+/// Returns a string representation of the given bool.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_string(True)
+/// "True"
+/// ```
+///
+/// ```gleam
+/// > to_string(False)
+/// "False"
+/// ```
+///
+pub fn to_string(bool: Bool) -> String {
+ case bool {
+ False -> "False"
+ True -> "True"
+ }
+}
+
+/// Run a callback function if the given bool is `False`, otherwise return a
+/// default value.
+///
+/// With a `use` expression this function can simulate the early-return pattern
+/// found in some other programming languages.
+///
+/// In a procedural language:
+///
+/// ```js
+/// if (predicate) return value;
+/// // ...
+/// ```
+///
+/// In Gleam with a `use` expression:
+///
+/// ```gleam
+/// use <- guard(when: predicate, return: value)
+/// // ...
+/// ```
+///
+/// Like everything in Gleam `use` is an expression, so it short circuits the
+/// current block, not the entire function. As a result you can assign the value
+/// to a variable:
+///
+/// ```gleam
+/// let x = {
+/// use <- guard(when: predicate, return: value)
+/// // ...
+/// }
+/// ```
+///
+/// Note that unlike in procedural languages the `return` value is evaluated
+/// even when the predicate is `False`, so it is advisable not to perform
+/// expensive computation there.
+///
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let name = ""
+/// > use <- guard(when: name == "", return: "Welcome!")
+/// > "Hello, " <> name
+/// "Welcome!"
+/// ```
+///
+/// ```gleam
+/// > let name = "Kamaka"
+/// > use <- guard(when: name == "", return: "Welcome!")
+/// > "Hello, " <> name
+/// "Hello, Kamaka"
+/// ```
+///
+pub fn guard(
+ when requirement: Bool,
+ return consequence: t,
+ otherwise alternative: fn() -> t,
+) -> t {
+ case requirement {
+ True -> consequence
+ False -> alternative()
+ }
+}
+
+/// Runs a callback function if the given bool is `True`, otherwise runs an
+/// alternative callback function.
+///
+/// Useful when further computation should be delayed regardless of the given
+/// bool's value.
+///
+/// See [`guard`](#guard) for more info.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let name = "Kamaka"
+/// > let inquiry = fn() { "How may we address you?" }
+/// > use <- lazy_guard(when: name == "", return: inquiry)
+/// > "Hello, " <> name
+/// "Hello, Kamaka"
+/// ```
+///
+/// ```gleam
+/// > import gleam/int
+/// > let name = ""
+/// > let greeting = fn() { "Hello, " <> name }
+/// > use <- lazy_guard(when: name == "", otherwise: greeting)
+/// > let number = int.random(1, 99)
+/// > let name = "User " <> int.to_string(number)
+/// > "Welcome, " <> name
+/// "Welcome, User 54"
+/// ```
+///
+pub fn lazy_guard(
+ when requirement: Bool,
+ return consequence: fn() -> a,
+ otherwise alternative: fn() -> a,
+) -> a {
+ case requirement {
+ True -> consequence()
+ False -> alternative()
+ }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/bytes_builder.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/bytes_builder.gleam
new file mode 100644
index 0000000..20c145d
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/bytes_builder.gleam
@@ -0,0 +1,197 @@
+//// BytesBuilder is a type used for efficiently concatenating bytes together
+//// without copying.
+////
+//// If we append one bit array to another the bit arrays must be copied to a
+//// new location in memory so that they can sit together. This behaviour
+//// enables efficient reading of the string but copying can be expensive,
+//// especially if we want to join many bit arrays together.
+////
+//// BytesBuilder is different in that it can be joined together in constant
+//// time using minimal memory, and then can be efficiently converted to a
+//// bit array using the `to_bit_array` function.
+////
+//// Byte builders are always byte aligned, so that a number of bits that is not
+//// divisible by 8 will be padded with 0s.
+////
+//// On Erlang this type is compatible with Erlang's iolists.
+
+// TODO: pad bit arrays to byte boundaries when adding to a builder.
+import gleam/string_builder.{type StringBuilder}
+import gleam/list
+import gleam/bit_array
+
+pub opaque type BytesBuilder {
+ Bytes(BitArray)
+ Text(StringBuilder)
+ Many(List(BytesBuilder))
+}
+
+/// Create an empty `BytesBuilder`. Useful as the start of a pipe chaining many
+/// builders together.
+///
+pub fn new() -> BytesBuilder {
+ concat([])
+}
+
+/// Prepends a bit array to the start of a builder.
+///
+/// Runs in constant time.
+///
+pub fn prepend(to second: BytesBuilder, prefix first: BitArray) -> BytesBuilder {
+ append_builder(from_bit_array(first), second)
+}
+
+/// Appends a bit array to the end of a builder.
+///
+/// Runs in constant time.
+///
+pub fn append(to first: BytesBuilder, suffix second: BitArray) -> BytesBuilder {
+ append_builder(first, from_bit_array(second))
+}
+
+/// Prepends a builder onto the start of another.
+///
+/// Runs in constant time.
+///
+pub fn prepend_builder(
+ to second: BytesBuilder,
+ prefix first: BytesBuilder,
+) -> BytesBuilder {
+ append_builder(first, second)
+}
+
+/// Appends a builder onto the end of another.
+///
+/// Runs in constant time.
+///
+@external(erlang, "gleam_stdlib", "iodata_append")
+pub fn append_builder(
+ to first: BytesBuilder,
+ suffix second: BytesBuilder,
+) -> BytesBuilder {
+ case second {
+ Many(builders) -> Many([first, ..builders])
+ _ -> Many([first, second])
+ }
+}
+
+/// Prepends a string onto the start of a builder.
+///
+/// Runs in constant time when running on Erlang.
+/// Runs in linear time with the length of the string otherwise.
+///
+pub fn prepend_string(
+ to second: BytesBuilder,
+ prefix first: String,
+) -> BytesBuilder {
+ append_builder(from_string(first), second)
+}
+
+/// Appends a string onto the end of a builder.
+///
+/// Runs in constant time when running on Erlang.
+/// Runs in linear time with the length of the string otherwise.
+///
+pub fn append_string(
+ to first: BytesBuilder,
+ suffix second: String,
+) -> BytesBuilder {
+ append_builder(first, from_string(second))
+}
+
+/// Joins a list of builders into a single builder.
+///
+/// Runs in constant time.
+///
+@external(erlang, "gleam_stdlib", "identity")
+pub fn concat(builders: List(BytesBuilder)) -> BytesBuilder {
+ Many(builders)
+}
+
+/// Joins a list of bit arrays into a single builder.
+///
+/// Runs in constant time.
+///
+@external(erlang, "gleam_stdlib", "identity")
+pub fn concat_bit_arrays(bits: List(BitArray)) -> BytesBuilder {
+ bits
+ |> list.map(fn(b) { from_bit_array(b) })
+ |> concat()
+}
+
+/// Creates a new builder from a string.
+///
+/// Runs in constant time when running on Erlang.
+/// Runs in linear time otherwise.
+///
+@external(erlang, "gleam_stdlib", "wrap_list")
+pub fn from_string(string: String) -> BytesBuilder {
+ Text(string_builder.from_string(string))
+}
+
+/// Creates a new builder from a string builder.
+///
+/// Runs in constant time when running on Erlang.
+/// Runs in linear time otherwise.
+///
+@external(erlang, "gleam_stdlib", "wrap_list")
+pub fn from_string_builder(builder: StringBuilder) -> BytesBuilder {
+ Text(builder)
+}
+
+/// Creates a new builder from a bit array.
+///
+/// Runs in constant time.
+///
+@external(erlang, "gleam_stdlib", "wrap_list")
+pub fn from_bit_array(bits: BitArray) -> BytesBuilder {
+ Bytes(bits)
+}
+
+/// Turns an builder into a bit array.
+///
+/// Runs in linear time.
+///
+/// When running on Erlang this function is implemented natively by the
+/// virtual machine and is highly optimised.
+///
+@external(erlang, "erlang", "list_to_bitstring")
+pub fn to_bit_array(builder: BytesBuilder) -> BitArray {
+ [[builder]]
+ |> to_list([])
+ |> list.reverse
+ |> bit_array.concat
+}
+
+fn to_list(
+ stack: List(List(BytesBuilder)),
+ acc: List(BitArray),
+) -> List(BitArray) {
+ case stack {
+ [] -> acc
+
+ [[], ..remaining_stack] -> to_list(remaining_stack, acc)
+
+ [[Bytes(bits), ..rest], ..remaining_stack] ->
+ to_list([rest, ..remaining_stack], [bits, ..acc])
+
+ [[Text(builder), ..rest], ..remaining_stack] -> {
+ let bits = bit_array.from_string(string_builder.to_string(builder))
+ to_list([rest, ..remaining_stack], [bits, ..acc])
+ }
+
+ [[Many(builders), ..rest], ..remaining_stack] ->
+ to_list([builders, rest, ..remaining_stack], acc)
+ }
+}
+
+/// Returns the size of the builder's content in bytes.
+///
+/// Runs in linear time.
+///
+@external(erlang, "erlang", "iolist_size")
+pub fn byte_size(builder: BytesBuilder) -> Int {
+ [[builder]]
+ |> to_list([])
+ |> list.fold(0, fn(acc, builder) { bit_array.byte_size(builder) + acc })
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/dict.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/dict.gleam
new file mode 100644
index 0000000..280bf9d
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/dict.gleam
@@ -0,0 +1,544 @@
+import gleam/option.{type Option}
+
+/// A dictionary of keys and values.
+///
+/// Any type can be used for the keys and values of a dict, but all the keys
+/// must be of the same type and all the values must be of the same type.
+///
+/// Each key can only be present in a dict once.
+///
+/// Dicts are not ordered in any way, and any unintentional ordering is not to
+/// be relied upon in your code as it may change in future versions of Erlang
+/// or Gleam.
+///
+/// See [the Erlang map module](https://erlang.org/doc/man/maps.html) for more
+/// information.
+///
+pub type Dict(key, value)
+
+/// Determines the number of key-value pairs in the dict.
+/// This function runs in constant time and does not need to iterate the dict.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> size()
+/// 0
+/// ```
+///
+/// ```gleam
+/// > new() |> insert("key", "value") |> size()
+/// 1
+/// ```
+///
+pub fn size(dict: Dict(k, v)) -> Int {
+ do_size(dict)
+}
+
+@external(erlang, "maps", "size")
+@external(javascript, "../gleam_stdlib.mjs", "map_size")
+fn do_size(a: Dict(k, v)) -> Int
+
+/// Converts the dict to a list of 2-element tuples `#(key, value)`, one for
+/// each key-value pair in the dict.
+///
+/// The tuples in the list have no specific order.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> to_list()
+/// []
+/// ```
+///
+/// ```gleam
+/// > new() |> insert("key", 0) |> to_list()
+/// [#("key", 0)]
+/// ```
+///
+pub fn to_list(dict: Dict(key, value)) -> List(#(key, value)) {
+ do_to_list(dict)
+}
+
+@external(erlang, "maps", "to_list")
+@external(javascript, "../gleam_stdlib.mjs", "map_to_list")
+fn do_to_list(a: Dict(key, value)) -> List(#(key, value))
+
+/// Converts a list of 2-element tuples `#(key, value)` to a dict.
+///
+/// If two tuples have the same key the last one in the list will be the one
+/// that is present in the dict.
+///
+pub fn from_list(list: List(#(k, v))) -> Dict(k, v) {
+ do_from_list(list)
+}
+
+@target(erlang)
+@external(erlang, "maps", "from_list")
+fn do_from_list(a: List(#(key, value))) -> Dict(key, value)
+
+@target(javascript)
+fn fold_list_of_pair(
+ over list: List(#(k, v)),
+ from initial: Dict(k, v),
+) -> Dict(k, v) {
+ case list {
+ [] -> initial
+ [x, ..rest] -> fold_list_of_pair(rest, insert(initial, x.0, x.1))
+ }
+}
+
+@target(javascript)
+fn do_from_list(list: List(#(k, v))) -> Dict(k, v) {
+ fold_list_of_pair(list, new())
+}
+
+/// Determines whether or not a value present in the dict for a given key.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> has_key("a")
+/// True
+/// ```
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> has_key("b")
+/// False
+/// ```
+///
+pub fn has_key(dict: Dict(k, v), key: k) -> Bool {
+ do_has_key(key, dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "is_key")
+fn do_has_key(a: key, b: Dict(key, v)) -> Bool
+
+@target(javascript)
+fn do_has_key(key: k, dict: Dict(k, v)) -> Bool {
+ get(dict, key) != Error(Nil)
+}
+
+/// Creates a fresh dict that contains no values.
+///
+pub fn new() -> Dict(key, value) {
+ do_new()
+}
+
+@external(erlang, "maps", "new")
+@external(javascript, "../gleam_stdlib.mjs", "new_map")
+fn do_new() -> Dict(key, value)
+
+/// Fetches a value from a dict for a given key.
+///
+/// The dict may not have a value for the key, so the value is wrapped in a
+/// `Result`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> get("a")
+/// Ok(0)
+/// ```
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> get("b")
+/// Error(Nil)
+/// ```
+///
+pub fn get(from: Dict(key, value), get: key) -> Result(value, Nil) {
+ do_get(from, get)
+}
+
+@external(erlang, "gleam_stdlib", "map_get")
+@external(javascript, "../gleam_stdlib.mjs", "map_get")
+fn do_get(a: Dict(key, value), b: key) -> Result(value, Nil)
+
+/// Inserts a value into the dict with the given key.
+///
+/// If the dict already has a value for the given key then the value is
+/// replaced with the new value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> to_list
+/// [#("a", 0)]
+/// ```
+///
+/// ```gleam
+/// > new() |> insert("a", 0) |> insert("a", 5) |> to_list
+/// [#("a", 5)]
+/// ```
+///
+pub fn insert(into dict: Dict(k, v), for key: k, insert value: v) -> Dict(k, v) {
+ do_insert(key, value, dict)
+}
+
+@external(erlang, "maps", "put")
+@external(javascript, "../gleam_stdlib.mjs", "map_insert")
+fn do_insert(a: key, b: value, c: Dict(key, value)) -> Dict(key, value)
+
+/// Updates all values in a given dict by calling a given function on each key
+/// and value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [#(3, 3), #(2, 4)]
+/// > |> from_list
+/// > |> map_values(fn(key, value) { key * value })
+/// [#(3, 9), #(2, 8)]
+/// ```
+///
+pub fn map_values(in dict: Dict(k, v), with fun: fn(k, v) -> w) -> Dict(k, w) {
+ do_map_values(fun, dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "map")
+fn do_map_values(a: fn(key, value) -> b, b: Dict(key, value)) -> Dict(key, b)
+
+@target(javascript)
+fn do_map_values(f: fn(key, value) -> b, dict: Dict(key, value)) -> Dict(key, b) {
+ let f = fn(dict, k, v) { insert(dict, k, f(k, v)) }
+ dict
+ |> fold(from: new(), with: f)
+}
+
+/// Gets a list of all keys in a given dict.
+///
+/// Dicts are not ordered so the keys are not returned in any specific order. Do
+/// not write code that relies on the order keys are returned by this function
+/// as it may change in later versions of Gleam or Erlang.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > keys([#("a", 0), #("b", 1)])
+/// ["a", "b"]
+/// ```
+///
+pub fn keys(dict: Dict(keys, v)) -> List(keys) {
+ do_keys(dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "keys")
+fn do_keys(a: Dict(keys, v)) -> List(keys)
+
+@target(javascript)
+fn reverse_and_concat(remaining, accumulator) {
+ case remaining {
+ [] -> accumulator
+ [item, ..rest] -> reverse_and_concat(rest, [item, ..accumulator])
+ }
+}
+
+@target(javascript)
+fn do_keys_acc(list: List(#(k, v)), acc: List(k)) -> List(k) {
+ case list {
+ [] -> reverse_and_concat(acc, [])
+ [x, ..xs] -> do_keys_acc(xs, [x.0, ..acc])
+ }
+}
+
+@target(javascript)
+fn do_keys(dict: Dict(k, v)) -> List(k) {
+ let list_of_pairs = to_list(dict)
+ do_keys_acc(list_of_pairs, [])
+}
+
+/// Gets a list of all values in a given dict.
+///
+/// Dicts are not ordered so the values are not returned in any specific order. Do
+/// not write code that relies on the order values are returned by this function
+/// as it may change in later versions of Gleam or Erlang.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > values(from_list([#("a", 0), #("b", 1)]))
+/// [0, 1]
+/// ```
+///
+pub fn values(dict: Dict(k, values)) -> List(values) {
+ do_values(dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "values")
+fn do_values(a: Dict(k, values)) -> List(values)
+
+@target(javascript)
+fn do_values_acc(list: List(#(k, v)), acc: List(v)) -> List(v) {
+ case list {
+ [] -> reverse_and_concat(acc, [])
+ [x, ..xs] -> do_values_acc(xs, [x.1, ..acc])
+ }
+}
+
+@target(javascript)
+fn do_values(dict: Dict(k, v)) -> List(v) {
+ let list_of_pairs = to_list(dict)
+ do_values_acc(list_of_pairs, [])
+}
+
+/// Creates a new dict from a given dict, minus any entries that a given function
+/// returns `False` for.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([#("a", 0), #("b", 1)])
+/// > |> filter(fn(key, value) { value != 0 })
+/// from_list([#("b", 1)])
+/// ```
+///
+/// ```gleam
+/// > from_list([#("a", 0), #("b", 1)])
+/// > |> filter(fn(key, value) { True })
+/// from_list([#("a", 0), #("b", 1)])
+/// ```
+///
+pub fn filter(
+ in dict: Dict(k, v),
+ keeping predicate: fn(k, v) -> Bool,
+) -> Dict(k, v) {
+ do_filter(predicate, dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "filter")
+fn do_filter(a: fn(key, value) -> Bool, b: Dict(key, value)) -> Dict(key, value)
+
+@target(javascript)
+fn do_filter(
+ f: fn(key, value) -> Bool,
+ dict: Dict(key, value),
+) -> Dict(key, value) {
+ let insert = fn(dict, k, v) {
+ case f(k, v) {
+ True -> insert(dict, k, v)
+ _ -> dict
+ }
+ }
+ dict
+ |> fold(from: new(), with: insert)
+}
+
+/// Creates a new dict from a given dict, only including any entries for which the
+/// keys are in a given list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([#("a", 0), #("b", 1)])
+/// > |> take(["b"])
+/// from_list([#("b", 1)])
+/// ```
+///
+/// ```gleam
+/// > from_list([#("a", 0), #("b", 1)])
+/// > |> take(["a", "b", "c"])
+/// from_list([#("a", 0), #("b", 1)])
+/// ```
+///
+pub fn take(from dict: Dict(k, v), keeping desired_keys: List(k)) -> Dict(k, v) {
+ do_take(desired_keys, dict)
+}
+
+@target(erlang)
+@external(erlang, "maps", "with")
+fn do_take(a: List(k), b: Dict(k, v)) -> Dict(k, v)
+
+@target(javascript)
+fn insert_taken(
+ dict: Dict(k, v),
+ desired_keys: List(k),
+ acc: Dict(k, v),
+) -> Dict(k, v) {
+ let insert = fn(taken, key) {
+ case get(dict, key) {
+ Ok(value) -> insert(taken, key, value)
+ _ -> taken
+ }
+ }
+ case desired_keys {
+ [] -> acc
+ [x, ..xs] -> insert_taken(dict, xs, insert(acc, x))
+ }
+}
+
+@target(javascript)
+fn do_take(desired_keys: List(k), dict: Dict(k, v)) -> Dict(k, v) {
+ insert_taken(dict, desired_keys, new())
+}
+
+/// Creates a new dict from a pair of given dicts by combining their entries.
+///
+/// If there are entries with the same keys in both dicts the entry from the
+/// second dict takes precedence.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let a = from_list([#("a", 0), #("b", 1)])
+/// > let b = from_list([#("b", 2), #("c", 3)])
+/// > merge(a, b)
+/// from_list([#("a", 0), #("b", 2), #("c", 3)])
+/// ```
+///
+pub fn merge(into dict: Dict(k, v), from new_entries: Dict(k, v)) -> Dict(k, v) {
+ do_merge(dict, new_entries)
+}
+
+@target(erlang)
+@external(erlang, "maps", "merge")
+fn do_merge(a: Dict(k, v), b: Dict(k, v)) -> Dict(k, v)
+
+@target(javascript)
+fn insert_pair(dict: Dict(k, v), pair: #(k, v)) -> Dict(k, v) {
+ insert(dict, pair.0, pair.1)
+}
+
+@target(javascript)
+fn fold_inserts(new_entries: List(#(k, v)), dict: Dict(k, v)) -> Dict(k, v) {
+ case new_entries {
+ [] -> dict
+ [x, ..xs] -> fold_inserts(xs, insert_pair(dict, x))
+ }
+}
+
+@target(javascript)
+fn do_merge(dict: Dict(k, v), new_entries: Dict(k, v)) -> Dict(k, v) {
+ new_entries
+ |> to_list
+ |> fold_inserts(dict)
+}
+
+/// Creates a new dict from a given dict with all the same entries except for the
+/// one with a given key, if it exists.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > delete([#("a", 0), #("b", 1)], "a")
+/// from_list([#("b", 1)])
+/// ```
+///
+/// ```gleam
+/// > delete([#("a", 0), #("b", 1)], "c")
+/// from_list([#("a", 0), #("b", 1)])
+/// ```
+///
+pub fn delete(from dict: Dict(k, v), delete key: k) -> Dict(k, v) {
+ do_delete(key, dict)
+}
+
+@external(erlang, "maps", "remove")
+@external(javascript, "../gleam_stdlib.mjs", "map_remove")
+fn do_delete(a: k, b: Dict(k, v)) -> Dict(k, v)
+
+/// Creates a new dict from a given dict with all the same entries except any with
+/// keys found in a given list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > drop([#("a", 0), #("b", 1)], ["a"])
+/// from_list([#("b", 2)])
+/// ```
+///
+/// ```gleam
+/// > delete([#("a", 0), #("b", 1)], ["c"])
+/// from_list([#("a", 0), #("b", 1)])
+/// ```
+///
+/// ```gleam
+/// > drop([#("a", 0), #("b", 1)], ["a", "b", "c"])
+/// from_list([])
+/// ```
+///
+pub fn drop(from dict: Dict(k, v), drop disallowed_keys: List(k)) -> Dict(k, v) {
+ case disallowed_keys {
+ [] -> dict
+ [x, ..xs] -> drop(delete(dict, x), xs)
+ }
+}
+
+/// Creates a new dict with one entry updated using a given function.
+///
+/// If there was not an entry in the dict for the given key then the function
+/// gets `None` as its argument, otherwise it gets `Some(value)`.
+///
+/// ## Example
+///
+/// ```gleam
+/// > let increment = fn(x) {
+/// > case x {
+/// > Some(i) -> i + 1
+/// > None -> 0
+/// > }
+/// > }
+/// > let dict = from_list([#("a", 0)])
+/// >
+/// > update(dict, "a", increment)
+/// from_list([#("a", 1)])
+/// ```
+///
+/// ```gleam
+/// > update(dict, "b", increment)
+/// from_list([#("a", 0), #("b", 0)])
+/// ```
+///
+pub fn update(
+ in dict: Dict(k, v),
+ update key: k,
+ with fun: fn(Option(v)) -> v,
+) -> Dict(k, v) {
+ dict
+ |> get(key)
+ |> option.from_result
+ |> fun
+ |> insert(dict, key, _)
+}
+
+fn do_fold(list: List(#(k, v)), initial: acc, fun: fn(acc, k, v) -> acc) -> acc {
+ case list {
+ [] -> initial
+ [#(k, v), ..rest] -> do_fold(rest, fun(initial, k, v), fun)
+ }
+}
+
+/// Combines all entries into a single value by calling a given function on each
+/// one.
+///
+/// Dicts are not ordered so the values are not returned in any specific order. Do
+/// not write code that relies on the order entries are used by this function
+/// as it may change in later versions of Gleam or Erlang.
+///
+/// # Examples
+///
+/// ```gleam
+/// > let dict = from_list([#("a", 1), #("b", 3), #("c", 9)])
+/// > fold(dict, 0, fn(accumulator, key, value) { accumulator + value })
+/// 13
+/// ```
+///
+/// ```gleam
+/// > import gleam/string.{append}
+/// > fold(dict, "", fn(accumulator, key, value) { append(accumulator, key) })
+/// "abc"
+/// ```
+///
+pub fn fold(
+ over dict: Dict(k, v),
+ from initial: acc,
+ with fun: fn(acc, k, v) -> acc,
+) -> acc {
+ dict
+ |> to_list
+ |> do_fold(initial, fun)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/dynamic.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/dynamic.gleam
new file mode 100644
index 0000000..c71c6f3
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/dynamic.gleam
@@ -0,0 +1,1508 @@
+import gleam/int
+import gleam/list
+import gleam/dict.{type Dict}
+import gleam/option.{type Option}
+import gleam/result
+import gleam/string_builder
+@target(erlang)
+import gleam/bit_array
+
+/// `Dynamic` data is data that we don't know the type of yet.
+/// We likely get data like this from interop with Erlang, or from
+/// IO with the outside world.
+///
+pub type Dynamic
+
+/// Error returned when unexpected data is encountered
+///
+pub type DecodeError {
+ DecodeError(expected: String, found: String, path: List(String))
+}
+
+pub type DecodeErrors =
+ List(DecodeError)
+
+pub type Decoder(t) =
+ fn(Dynamic) -> Result(t, DecodeErrors)
+
+/// Converts any Gleam data into `Dynamic` data.
+///
+pub fn from(a) -> Dynamic {
+ do_from(a)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "identity")
+fn do_from(a: anything) -> Dynamic
+
+/// Unsafely casts a Dynamic value into any other type.
+///
+/// This is an escape hatch for the type system that may be useful when wrapping
+/// native Erlang APIs. It is to be used as a last measure only!
+///
+/// If you can avoid using this function, do!
+///
+pub fn unsafe_coerce(a: Dynamic) -> anything {
+ do_unsafe_coerce(a)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "identity")
+fn do_unsafe_coerce(a: Dynamic) -> a
+
+/// Decodes a `Dynamic` value from a `Dynamic` value.
+///
+/// This function doesn't seem very useful at first, but it can be convenient
+/// when you need to give a decoder function but you don't actually care what
+/// the to-decode value is.
+///
+pub fn dynamic(value: Dynamic) -> Result(Dynamic, List(DecodeError)) {
+ Ok(value)
+}
+
+/// Checks to see whether a `Dynamic` value is a bit array, and returns that bit
+/// array if it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > bit_array(from("Hello")) == bit_array.from_string("Hello")
+/// True
+/// ```
+///
+/// ```gleam
+/// > bit_array(from(123))
+/// Error([DecodeError(expected: "BitArray", found: "Int", path: [])])
+/// ```
+///
+pub fn bit_array(from data: Dynamic) -> Result(BitArray, DecodeErrors) {
+ decode_bit_array(data)
+}
+
+@deprecated("Please use `bit_array` instead")
+pub fn bit_string(from data: Dynamic) -> Result(BitArray, DecodeErrors) {
+ bit_array(data)
+}
+
+@external(erlang, "gleam_stdlib", "decode_bit_array")
+@external(javascript, "../gleam_stdlib.mjs", "decode_bit_array")
+fn decode_bit_array(a: Dynamic) -> Result(BitArray, DecodeErrors)
+
+/// Checks to see whether a `Dynamic` value is a string, and returns that string if
+/// it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > string(from("Hello"))
+/// Ok("Hello")
+/// ```
+///
+/// ```gleam
+/// > string(from(123))
+/// Error([DecodeError(expected: "String", found: "Int", path: [])])
+/// ```
+///
+pub fn string(from data: Dynamic) -> Result(String, DecodeErrors) {
+ decode_string(data)
+}
+
+fn map_errors(
+ result: Result(t, DecodeErrors),
+ f: fn(DecodeError) -> DecodeError,
+) -> Result(t, DecodeErrors) {
+ result.map_error(result, list.map(_, f))
+}
+
+@target(erlang)
+fn decode_string(data: Dynamic) -> Result(String, DecodeErrors) {
+ bit_array(data)
+ |> map_errors(put_expected(_, "String"))
+ |> result.try(fn(raw) {
+ case bit_array.to_string(raw) {
+ Ok(string) -> Ok(string)
+ Error(Nil) ->
+ Error([DecodeError(expected: "String", found: "BitArray", path: [])])
+ }
+ })
+}
+
+@target(erlang)
+fn put_expected(error: DecodeError, expected: String) -> DecodeError {
+ DecodeError(..error, expected: expected)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "decode_string")
+fn decode_string(a: Dynamic) -> Result(String, DecodeErrors)
+
+/// Return a string indicating the type of the dynamic value.
+///
+/// ```gleam
+/// > classify(from("Hello"))
+/// "String"
+/// ```
+///
+pub fn classify(data: Dynamic) -> String {
+ do_classify(data)
+}
+
+@external(erlang, "gleam_stdlib", "classify_dynamic")
+@external(javascript, "../gleam_stdlib.mjs", "classify_dynamic")
+fn do_classify(a: Dynamic) -> String
+
+/// Checks to see whether a `Dynamic` value is an int, and returns that int if it
+/// is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > int(from(123))
+/// Ok(123)
+/// ```
+///
+/// ```gleam
+/// > int(from("Hello"))
+/// Error([DecodeError(expected: "Int", found: "String", path: [])])
+/// ```
+///
+pub fn int(from data: Dynamic) -> Result(Int, DecodeErrors) {
+ decode_int(data)
+}
+
+@external(erlang, "gleam_stdlib", "decode_int")
+@external(javascript, "../gleam_stdlib.mjs", "decode_int")
+fn decode_int(a: Dynamic) -> Result(Int, DecodeErrors)
+
+/// Checks to see whether a `Dynamic` value is a float, and returns that float if
+/// it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > float(from(2.0))
+/// Ok(2.0)
+/// ```
+///
+/// ```gleam
+/// > float(from(123))
+/// Error([DecodeError(expected: "Float", found: "Int", path: [])])
+/// ```
+///
+pub fn float(from data: Dynamic) -> Result(Float, DecodeErrors) {
+ decode_float(data)
+}
+
+@external(erlang, "gleam_stdlib", "decode_float")
+@external(javascript, "../gleam_stdlib.mjs", "decode_float")
+fn decode_float(a: Dynamic) -> Result(Float, DecodeErrors)
+
+/// Checks to see whether a `Dynamic` value is a bool, and returns that bool if
+/// it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > bool(from(True))
+/// Ok(True)
+/// ```
+///
+/// ```gleam
+/// > bool(from(123))
+/// Error([DecodeError(expected: "Bool", found: "Int", path: [])])
+/// ```
+///
+pub fn bool(from data: Dynamic) -> Result(Bool, DecodeErrors) {
+ decode_bool(data)
+}
+
+@external(erlang, "gleam_stdlib", "decode_bool")
+@external(javascript, "../gleam_stdlib.mjs", "decode_bool")
+fn decode_bool(a: Dynamic) -> Result(Bool, DecodeErrors)
+
+/// Checks to see whether a `Dynamic` value is a list, and returns that list if it
+/// is. The types of the elements are not checked.
+///
+/// If you wish to decode all the elements in the list use the `list` function
+/// instead.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > shallow_list(from(["a", "b", "c"]))
+/// Ok([from("a"), from("b"), from("c")])
+/// ```
+///
+/// ```gleam
+/// > shallow_list(1)
+/// Error([DecodeError(expected: "List", found: "Int", path: [])])
+/// ```
+///
+pub fn shallow_list(from value: Dynamic) -> Result(List(Dynamic), DecodeErrors) {
+ decode_list(value)
+}
+
+@external(erlang, "gleam_stdlib", "decode_list")
+@external(javascript, "../gleam_stdlib.mjs", "decode_list")
+fn decode_list(a: Dynamic) -> Result(List(Dynamic), DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_result")
+@external(javascript, "../gleam_stdlib.mjs", "decode_result")
+fn decode_result(a: Dynamic) -> Result(Result(a, e), DecodeErrors)
+
+/// Checks to see whether a `Dynamic` value is a result of a particular type, and
+/// returns that result if it is.
+///
+/// The `ok` and `error` arguments are decoders for decoding the `Ok` and
+/// `Error` values of the result.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(Ok(1))
+/// > |> result(ok: int, error: string)
+/// Ok(Ok(1))
+/// ```
+///
+/// ```gleam
+/// > from(Error("boom"))
+/// > |> result(ok: int, error: string)
+/// Ok(Error("boom"))
+/// ```
+///
+/// ```gleam
+/// > from(123)
+/// > |> result(ok: int, error: string)
+/// Error([DecodeError(expected: "Result", found: "Int", path: [])])
+/// ```
+///
+pub fn result(
+ ok decode_ok: Decoder(a),
+ error decode_error: Decoder(e),
+) -> Decoder(Result(a, e)) {
+ fn(value) {
+ use inner_result <- result.try(decode_result(value))
+
+ case inner_result {
+ Ok(raw) -> {
+ use value <- result.try(
+ decode_ok(raw)
+ |> map_errors(push_path(_, "ok")),
+ )
+ Ok(Ok(value))
+ }
+ Error(raw) -> {
+ use value <- result.try(
+ decode_error(raw)
+ |> map_errors(push_path(_, "error")),
+ )
+ Ok(Error(value))
+ }
+ }
+ }
+}
+
+/// Checks to see whether a `Dynamic` value is a list of a particular type, and
+/// returns that list if it is.
+///
+/// The second argument is a decoder function used to decode the elements of
+/// the list. The list is only decoded if all elements in the list can be
+/// successfully decoded using this function.
+///
+/// If you do not wish to decode all the elements in the list use the `shallow_list`
+/// function instead.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(["a", "b", "c"])
+/// > |> list(of: string)
+/// Ok(["a", "b", "c"])
+/// ```
+///
+/// ```gleam
+/// > from([1, 2, 3])
+/// > |> list(of: string)
+/// Error([DecodeError(expected: "String", found: "Int", path: ["*"])])
+/// ```
+///
+/// ```gleam
+/// > from("ok")
+/// > |> list(of: string)
+/// Error([DecodeError(expected: "List", found: "String", path: [])])
+/// ```
+///
+pub fn list(
+ of decoder_type: fn(Dynamic) -> Result(inner, DecodeErrors),
+) -> Decoder(List(inner)) {
+ fn(dynamic) {
+ use list <- result.try(shallow_list(dynamic))
+ list
+ |> list.try_map(decoder_type)
+ |> map_errors(push_path(_, "*"))
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a nullable version of a particular
+/// type, and returns a corresponding `Option` if it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from("Hello")
+/// > |> optional(string)
+/// Ok(Some("Hello"))
+/// ```
+///
+/// ```gleam
+/// > from("Hello")
+/// > |> optional(string)
+/// Ok(Some("Hello"))
+/// ```
+///
+/// ```gleam
+/// > from(atom.from_string("null"))
+/// > |> optional(string)
+/// Ok(None)
+/// ```
+///
+/// ```gleam
+/// > from(atom.from_string("nil"))
+/// > |> optional(string)
+/// Ok(None)
+/// ```
+///
+/// ```gleam
+/// > from(atom.from_string("undefined"))
+/// > |> optional(string)
+/// Ok(None)
+/// ```
+///
+/// ```gleam
+/// > from(123)
+/// > |> optional(string)
+/// Error([DecodeError(expected: "String", found: "Int", path: [])])
+/// ```
+///
+pub fn optional(of decode: Decoder(inner)) -> Decoder(Option(inner)) {
+ fn(value) { decode_optional(value, decode) }
+}
+
+@external(erlang, "gleam_stdlib", "decode_option")
+@external(javascript, "../gleam_stdlib.mjs", "decode_option")
+fn decode_optional(a: Dynamic, b: Decoder(a)) -> Result(Option(a), DecodeErrors)
+
+/// Checks to see if a `Dynamic` value is a map with a specific field, and returns
+/// the value of that field if it is.
+///
+/// This will not succeed on a record.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/dict
+/// > dict.new()
+/// > |> dict.insert("Hello", "World")
+/// > |> from
+/// > |> field(named: "Hello", of: string)
+/// Ok("World")
+/// ```
+///
+/// ```gleam
+/// > from(123)
+/// > |> field("Hello", string)
+/// Error([DecodeError(expected: "Map", found: "Int", path: [])])
+/// ```
+///
+pub fn field(named name: a, of inner_type: Decoder(t)) -> Decoder(t) {
+ fn(value) {
+ let missing_field_error =
+ DecodeError(expected: "field", found: "nothing", path: [])
+
+ use maybe_inner <- result.try(decode_field(value, name))
+ maybe_inner
+ |> option.to_result([missing_field_error])
+ |> result.try(inner_type)
+ |> map_errors(push_path(_, name))
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a map with a specific field.
+/// If the map does not have the specified field, returns an `Ok(None)` instead of failing; otherwise,
+/// returns the decoded field wrapped in `Some(_)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/dict
+/// > dict.new()
+/// > |> dict.insert("Hello", "World")
+/// > |> from
+/// > |> field(named: "Hello", of: string)
+/// Ok(Some("World"))
+/// ```
+///
+/// ```gleam
+/// > import gleam/dict
+/// > dict.new()
+/// > |> from
+/// > |> field(named: "Hello", of: string)
+/// Ok(None)
+/// ```
+///
+/// ```gleam
+/// > from(123)
+/// > |> field("Hello", string)
+/// Error([DecodeError(expected: "Map", found: "Int", path: [])])
+/// ```
+///
+pub fn optional_field(
+ named name: a,
+ of inner_type: Decoder(t),
+) -> Decoder(Option(t)) {
+ fn(value) {
+ use maybe_inner <- result.try(decode_field(value, name))
+ case maybe_inner {
+ option.None -> Ok(option.None)
+ option.Some(dynamic_inner) ->
+ dynamic_inner
+ |> decode_optional(inner_type)
+ |> map_errors(push_path(_, name))
+ }
+ }
+}
+
+@external(erlang, "gleam_stdlib", "decode_field")
+@external(javascript, "../gleam_stdlib.mjs", "decode_field")
+fn decode_field(a: Dynamic, b: name) -> Result(Option(Dynamic), DecodeErrors)
+
+/// Checks to see if a `Dynamic` value is a tuple large enough to have a certain
+/// index, and returns the value of that index if it is.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> element(0, int)
+/// Ok(from(1))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> element(2, int)
+/// Error([
+/// DecodeError(
+/// expected: "Tuple of at least 3 elements",
+/// found: "Tuple of 2 elements",
+/// path: [],
+/// ),
+/// ])
+/// ```
+///
+pub fn element(at index: Int, of inner_type: Decoder(t)) -> Decoder(t) {
+ fn(data: Dynamic) {
+ use tuple <- result.try(decode_tuple(data))
+ let size = tuple_size(tuple)
+ use data <- result.try(case index >= 0 {
+ True ->
+ case index < size {
+ True -> tuple_get(tuple, index)
+ False -> at_least_decode_tuple_error(index + 1, data)
+ }
+ False ->
+ case int.absolute_value(index) <= size {
+ True -> tuple_get(tuple, size + index)
+ False -> at_least_decode_tuple_error(int.absolute_value(index), data)
+ }
+ })
+ inner_type(data)
+ |> map_errors(push_path(_, index))
+ }
+}
+
+fn at_least_decode_tuple_error(
+ size: Int,
+ data: Dynamic,
+) -> Result(a, DecodeErrors) {
+ let s = case size {
+ 1 -> ""
+ _ -> "s"
+ }
+ let error =
+ ["Tuple of at least ", int.to_string(size), " element", s]
+ |> string_builder.from_strings
+ |> string_builder.to_string
+ |> DecodeError(found: classify(data), path: [])
+ Error([error])
+}
+
+// A tuple of unknown size
+type UnknownTuple
+
+@external(erlang, "gleam_stdlib", "decode_tuple")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple")
+fn decode_tuple(a: Dynamic) -> Result(UnknownTuple, DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_tuple2")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple2")
+fn decode_tuple2(a: Dynamic) -> Result(#(Dynamic, Dynamic), DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_tuple3")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple3")
+fn decode_tuple3(
+ a: Dynamic,
+) -> Result(#(Dynamic, Dynamic, Dynamic), DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_tuple4")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple4")
+fn decode_tuple4(
+ a: Dynamic,
+) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_tuple5")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple5")
+fn decode_tuple5(
+ a: Dynamic,
+) -> Result(#(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic), DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "decode_tuple6")
+@external(javascript, "../gleam_stdlib.mjs", "decode_tuple6")
+fn decode_tuple6(
+ a: Dynamic,
+) -> Result(
+ #(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic),
+ DecodeErrors,
+)
+
+@external(erlang, "gleam_stdlib", "tuple_get")
+@external(javascript, "../gleam_stdlib.mjs", "tuple_get")
+fn tuple_get(a: UnknownTuple, b: Int) -> Result(Dynamic, DecodeErrors)
+
+@external(erlang, "gleam_stdlib", "size_of_tuple")
+@external(javascript, "../gleam_stdlib.mjs", "length")
+fn tuple_size(a: UnknownTuple) -> Int
+
+fn tuple_errors(
+ result: Result(a, List(DecodeError)),
+ name: String,
+) -> List(DecodeError) {
+ case result {
+ Ok(_) -> []
+ Error(errors) -> list.map(errors, push_path(_, name))
+ }
+}
+
+fn push_path(error: DecodeError, name: t) -> DecodeError {
+ let name = from(name)
+ let decoder = any([string, fn(x) { result.map(int(x), int.to_string) }])
+ let name = case decoder(name) {
+ Ok(name) -> name
+ Error(_) ->
+ ["<", classify(name), ">"]
+ |> string_builder.from_strings
+ |> string_builder.to_string
+ }
+ DecodeError(..error, path: [name, ..error.path])
+}
+
+/// Checks to see if a `Dynamic` value is a 2-element tuple, list or array containing
+/// specifically typed elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> tuple2(int, int)
+/// Ok(#(1, 2))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2.0))
+/// > |> tuple2(int, float)
+/// Ok(#(1, 2.0))
+/// ```
+///
+/// ```gleam
+/// > from([1, 2])
+/// > |> tuple2(int, int)
+/// Ok(#(1, 2))
+/// ```
+///
+/// ```gleam
+/// > from([from(1), from(2.0)])
+/// > |> tuple2(int, float)
+/// Ok(#(1, 2.0))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2, 3))
+/// > |> tuple2(int, float)
+/// Error([
+/// DecodeError(expected: "Tuple of 2 elements", found: "Tuple of 3 elements", path: []),
+/// ])
+/// ```
+///
+/// ```gleam
+/// > from("")
+/// > |> tuple2(int, float)
+/// Error([DecodeError(expected: "Tuple of 2 elements", found: "String", path: [])])
+/// ```
+///
+pub fn tuple2(
+ first decode1: Decoder(a),
+ second decode2: Decoder(b),
+) -> Decoder(#(a, b)) {
+ fn(value) {
+ use #(a, b) <- result.try(decode_tuple2(value))
+ case decode1(a), decode2(b) {
+ Ok(a), Ok(b) -> Ok(#(a, b))
+ a, b ->
+ tuple_errors(a, "0")
+ |> list.append(tuple_errors(b, "1"))
+ |> Error
+ }
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a 3-element tuple, list or array containing
+/// specifically typed elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2, 3))
+/// > |> tuple3(int, int, int)
+/// Ok(#(1, 2, 3))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3"))
+/// > |> tuple3(int, float, string)
+/// Ok(#(1, 2.0, "3"))
+/// ```
+///
+/// ```gleam
+/// > from([1, 2, 3])
+/// > |> tuple3(int, int, int)
+/// Ok(#(1, 2, 3))
+/// ```
+///
+/// ```gleam
+/// > from([from(1), from(2.0), from("3")])
+/// > |> tuple3(int, float, string)
+/// Ok(#(1, 2.0, "3"))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> tuple3(int, float, string)
+/// Error([
+/// DecodeError(expected: "Tuple of 3 elements", found: "Tuple of 2 elements", path: [])),
+/// ])
+/// ```
+///
+/// ```gleam
+/// > from("")
+/// > |> tuple3(int, float, string)
+/// Error([
+/// DecodeError(expected: "Tuple of 3 elements", found: "String", path: []),
+/// ])
+/// ```
+///
+pub fn tuple3(
+ first decode1: Decoder(a),
+ second decode2: Decoder(b),
+ third decode3: Decoder(c),
+) -> Decoder(#(a, b, c)) {
+ fn(value) {
+ use #(a, b, c) <- result.try(decode_tuple3(value))
+ case decode1(a), decode2(b), decode3(c) {
+ Ok(a), Ok(b), Ok(c) -> Ok(#(a, b, c))
+ a, b, c ->
+ tuple_errors(a, "0")
+ |> list.append(tuple_errors(b, "1"))
+ |> list.append(tuple_errors(c, "2"))
+ |> Error
+ }
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a 4-element tuple, list or array containing
+/// specifically typed elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2, 3, 4))
+/// > |> tuple4(int, int, int, int)
+/// Ok(#(1, 2, 3, 4))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3", 4))
+/// > |> tuple4(int, float, string, int)
+/// Ok(#(1, 2.0, "3", 4))
+/// ```
+///
+/// ```gleam
+/// > from([1, 2, 3, 4])
+/// > |> tuple4(int, int, int, int)
+/// Ok(#(1, 2, 3, 4))
+/// ```
+///
+/// ```gleam
+/// > from([from(1), from(2.0), from("3"), from(4)])
+/// > |> tuple4(int, float, string, int)
+/// Ok(#(1, 2.0, "3", 4))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> tuple4(int, float, string, int)
+/// Error([
+/// DecodeError(expected: "Tuple of 4 elements", found: "Tuple of 2 elements", path: []),
+/// ])
+/// ```
+///
+/// ```gleam
+/// > from("")
+/// > |> tuple4(int, float, string, int)
+/// Error([
+/// DecodeError(expected: "Tuple of 4 elements", found: "String", path: []),
+/// ])
+/// ```
+///
+pub fn tuple4(
+ first decode1: Decoder(a),
+ second decode2: Decoder(b),
+ third decode3: Decoder(c),
+ fourth decode4: Decoder(d),
+) -> Decoder(#(a, b, c, d)) {
+ fn(value) {
+ use #(a, b, c, d) <- result.try(decode_tuple4(value))
+ case decode1(a), decode2(b), decode3(c), decode4(d) {
+ Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(#(a, b, c, d))
+ a, b, c, d ->
+ tuple_errors(a, "0")
+ |> list.append(tuple_errors(b, "1"))
+ |> list.append(tuple_errors(c, "2"))
+ |> list.append(tuple_errors(d, "3"))
+ |> Error
+ }
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a 5-element tuple, list or array containing
+/// specifically typed elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2, 3, 4, 5))
+/// > |> tuple5(int, int, int, int, int)
+/// Ok(#(1, 2, 3, 4, 5))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3", 4, 5))
+/// > |> tuple5(int, float, string, int, int)
+/// Ok(#(1, 2.0, "3", 4, 5))
+/// ```
+///
+/// ```gleam
+/// > from([1, 2, 3, 4, 5])
+/// > |> tuple5(int, int, int, int, int)
+/// Ok(#(1, 2, 3, 4, 5))
+/// ```
+///
+/// ```gleam
+/// > from([from(1), from(2.0), from("3"), from(4), from(True)])
+/// > |> tuple5(int, float, string, int, bool)
+/// Ok(#(1, 2.0, "3", 4, True))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> tuple5(int, float, string, int, int)
+/// Error([
+/// DecodeError(expected: "Tuple of 5 elements", found: "Tuple of 2 elements", path: [])),
+/// ])
+/// ```
+///
+/// ```gleam
+/// > from("")
+/// > |> tuple5(int, float, string, int, int)
+/// Error([DecodeError(expected: "Tuple of 5 elements", found: "String", path: [])])
+/// ```
+///
+pub fn tuple5(
+ first decode1: Decoder(a),
+ second decode2: Decoder(b),
+ third decode3: Decoder(c),
+ fourth decode4: Decoder(d),
+ fifth decode5: Decoder(e),
+) -> Decoder(#(a, b, c, d, e)) {
+ fn(value) {
+ use #(a, b, c, d, e) <- result.try(decode_tuple5(value))
+ case decode1(a), decode2(b), decode3(c), decode4(d), decode5(e) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(#(a, b, c, d, e))
+ a, b, c, d, e ->
+ tuple_errors(a, "0")
+ |> list.append(tuple_errors(b, "1"))
+ |> list.append(tuple_errors(c, "2"))
+ |> list.append(tuple_errors(d, "3"))
+ |> list.append(tuple_errors(e, "4"))
+ |> Error
+ }
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a 6-element tuple, list or array containing
+/// specifically typed elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2, 3, 4, 5, 6))
+/// > |> tuple6(int, int, int, int, int, int)
+/// Ok(#(1, 2, 3, 4, 5, 6))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3", 4, 5, 6))
+/// > |> tuple6(int, float, string, int, int, int)
+/// Ok(#(1, 2.0, "3", 4, 5, 6))
+/// ```
+///
+/// ```gleam
+/// > from([1, 2, 3, 4, 5, 6])
+/// > |> tuple6(int, int, int, int, int, int)
+/// Ok(#(1, 2, 3, 4, 5, 6))
+/// ```
+///
+/// ```gleam
+/// > from([from(1), from(2.0), from("3"), from(4), from(True), from(False)])
+/// > |> tuple6(int, float, string, int, bool, bool)
+/// Ok(#(1, 2.0, "3", 4, True, False))
+/// ```
+///
+/// ```gleam
+/// > from(#(1, 2))
+/// > |> tuple6(int, float, string, int, int, int)
+/// Error([
+/// DecodeError(expected: "Tuple of 6 elements", found: "Tuple of 2 elements", path: []),
+/// ])
+/// ```
+///
+/// ```gleam
+/// > from("")
+/// > |> tuple6(int, float, string, int, int, int)
+/// Error([DecodeError(expected: "Tuple of 6 elements", found: "String", path: [])])
+/// ```
+///
+pub fn tuple6(
+ first decode1: Decoder(a),
+ second decode2: Decoder(b),
+ third decode3: Decoder(c),
+ fourth decode4: Decoder(d),
+ fifth decode5: Decoder(e),
+ sixth decode6: Decoder(f),
+) -> Decoder(#(a, b, c, d, e, f)) {
+ fn(value) {
+ use #(a, b, c, d, e, f) <- result.try(decode_tuple6(value))
+ case
+ decode1(a),
+ decode2(b),
+ decode3(c),
+ decode4(d),
+ decode5(e),
+ decode6(f)
+ {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f) -> Ok(#(a, b, c, d, e, f))
+ a, b, c, d, e, f ->
+ tuple_errors(a, "0")
+ |> list.append(tuple_errors(b, "1"))
+ |> list.append(tuple_errors(c, "2"))
+ |> list.append(tuple_errors(d, "3"))
+ |> list.append(tuple_errors(e, "4"))
+ |> list.append(tuple_errors(f, "5"))
+ |> Error
+ }
+ }
+}
+
+/// Checks to see if a `Dynamic` value is a dict.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/dict
+/// > dict.new() |> from |> map(string, int)
+/// Ok(dict.new())
+/// ```
+///
+/// ```gleam
+/// > from(1) |> map(string, int)
+/// Error(DecodeError(expected: "Map", found: "Int", path: []))
+/// ```
+///
+/// ```gleam
+/// > from("") |> map(string, int)
+/// Error(DecodeError(expected: "Map", found: "String", path: []))
+/// ```
+///
+pub fn dict(
+ of key_type: Decoder(k),
+ to value_type: Decoder(v),
+) -> Decoder(Dict(k, v)) {
+ fn(value) {
+ use map <- result.try(decode_map(value))
+ use pairs <- result.try(
+ map
+ |> dict.to_list
+ |> list.try_map(fn(pair) {
+ let #(k, v) = pair
+ use k <- result.try(
+ key_type(k)
+ |> map_errors(push_path(_, "keys")),
+ )
+ use v <- result.try(
+ value_type(v)
+ |> map_errors(push_path(_, "values")),
+ )
+ Ok(#(k, v))
+ }),
+ )
+ Ok(dict.from_list(pairs))
+ }
+}
+
+@deprecated("Use `dict` instead")
+pub fn map(
+ of key_type: Decoder(k),
+ to value_type: Decoder(v),
+) -> Decoder(Dict(k, v)) {
+ dict(key_type, value_type)
+}
+
+@external(erlang, "gleam_stdlib", "decode_map")
+@external(javascript, "../gleam_stdlib.mjs", "decode_map")
+fn decode_map(a: Dynamic) -> Result(Dict(Dynamic, Dynamic), DecodeErrors)
+
+/// Joins multiple decoders into one. When run they will each be tried in turn
+/// until one succeeds, or they all fail.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/result
+/// > let bool_or_string = any(of: [
+/// > string,
+/// > fn(x) { result.map(bool(x), fn(_) { "a bool" }) }
+/// > ])
+/// > bool_or_string(from("ok"))
+/// Ok("ok")
+/// ```
+///
+/// ```gleam
+/// > bool_or_string(from(True))
+/// Ok("a bool")
+/// ```
+///
+/// ```gleam
+/// > bool_or_string(from(1))
+/// Error(DecodeError(expected: "another type", found: "Int", path: []))
+/// ```
+///
+pub fn any(of decoders: List(Decoder(t))) -> Decoder(t) {
+ fn(data) {
+ case decoders {
+ [] ->
+ Error([
+ DecodeError(found: classify(data), expected: "another type", path: []),
+ ])
+
+ [decoder, ..decoders] ->
+ case decoder(data) {
+ Ok(decoded) -> Ok(decoded)
+ Error(_) -> any(decoders)(data)
+ }
+ }
+ }
+}
+
+/// Decode 1 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3"))
+/// > |> decode1(MyRecord, element(0, int))
+/// Ok(MyRecord(1))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", ""))
+/// > |> decode1(MyRecord, element(0, int))
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// ])
+/// ```
+///
+pub fn decode1(constructor: fn(t1) -> t, t1: Decoder(t1)) -> Decoder(t) {
+ fn(value) {
+ case t1(value) {
+ Ok(a) -> Ok(constructor(a))
+ a -> Error(all_errors(a))
+ }
+ }
+}
+
+/// Decode 2 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3"))
+/// > |> decode2(MyRecord, element(0, int), element(1, float))
+/// Ok(MyRecord(1, 2.0))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", ""))
+/// > |> decode2(MyRecord, element(0, int), element(1, float))
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode2(
+ constructor: fn(t1, t2) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+) -> Decoder(t) {
+ fn(value) {
+ case t1(value), t2(value) {
+ Ok(a), Ok(b) -> Ok(constructor(a, b))
+ a, b -> Error(list.concat([all_errors(a), all_errors(b)]))
+ }
+ }
+}
+
+/// Decode 3 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.0, "3"))
+/// > |> decode3(MyRecord, element(0, int), element(1, float), element(2, string))
+/// Ok(MyRecord(1, 2.0, "3"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", ""))
+/// > |> decode3(MyRecord, element(0, int), element(1, float), element(2, string))
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode3(
+ constructor: fn(t1, t2, t3) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+) -> Decoder(t) {
+ fn(value) {
+ case t1(value), t2(value), t3(value) {
+ Ok(a), Ok(b), Ok(c) -> Ok(constructor(a, b, c))
+ a, b, c ->
+ Error(list.concat([all_errors(a), all_errors(b), all_errors(c)]))
+ }
+ }
+}
+
+/// Decode 4 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4"))
+/// > |> decode4(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", ""))
+/// > |> decode4(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode4(
+ constructor: fn(t1, t2, t3, t4) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d) -> Ok(constructor(a, b, c, d))
+ a, b, c, d ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ ]))
+ }
+ }
+}
+
+/// Decode 5 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4", "5"))
+/// > |> decode5(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4", "5"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", "", ""))
+/// > |> decode5(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode5(
+ constructor: fn(t1, t2, t3, t4, t5) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+ t5: Decoder(t5),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x), t5(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e) -> Ok(constructor(a, b, c, d, e))
+ a, b, c, d, e ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ all_errors(e),
+ ]))
+ }
+ }
+}
+
+/// Decode 6 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4", "5", "6"))
+/// > |> decode6(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4", "5", "6"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", "", "", ""))
+/// > |> decode6(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode6(
+ constructor: fn(t1, t2, t3, t4, t5, t6) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+ t5: Decoder(t5),
+ t6: Decoder(t6),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f) ->
+ Ok(constructor(a, b, c, d, e, f))
+ a, b, c, d, e, f ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ all_errors(e),
+ all_errors(f),
+ ]))
+ }
+ }
+}
+
+/// Decode 7 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4", "5", "6"))
+/// > |> decode7(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4", "5", "6", "7"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", "", "", "", ""))
+/// > |> decode7(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode7(
+ constructor: fn(t1, t2, t3, t4, t5, t6, t7) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+ t5: Decoder(t5),
+ t6: Decoder(t6),
+ t7: Decoder(t7),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g) ->
+ Ok(constructor(a, b, c, d, e, f, g))
+ a, b, c, d, e, f, g ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ all_errors(e),
+ all_errors(f),
+ all_errors(g),
+ ]))
+ }
+ }
+}
+
+/// Decode 8 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4", "5", "6", "7", "8"))
+/// > |> decode8(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > element(7, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4", "5", "6", "7", "8"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", "", "", "", "", ""))
+/// > |> decode8(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > element(7, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode8(
+ constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+ t5: Decoder(t5),
+ t6: Decoder(t6),
+ t7: Decoder(t7),
+ t8: Decoder(t8),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x), t8(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g), Ok(h) ->
+ Ok(constructor(a, b, c, d, e, f, g, h))
+ a, b, c, d, e, f, g, h ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ all_errors(e),
+ all_errors(f),
+ all_errors(g),
+ all_errors(h),
+ ]))
+ }
+ }
+}
+
+/// Decode 9 values from a `Dynamic` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from(#(1, 2.1, "3", "4", "5", "6", "7", "8", "9"))
+/// > |> decode9(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > element(7, string),
+/// > element(8, string),
+/// > )
+/// Ok(MyRecord(1, 2.1, "3", "4", "5", "6", "7", "8", "9"))
+/// ```
+///
+/// ```gleam
+/// > from(#("", "", "", "", "", "", "", "", ""))
+/// > |> decode9(
+/// > MyRecord,
+/// > element(0, int),
+/// > element(1, float),
+/// > element(2, string),
+/// > element(3, string),
+/// > element(4, string),
+/// > element(5, string),
+/// > element(6, string),
+/// > element(7, string),
+/// > element(8, string),
+/// > )
+/// Error([
+/// DecodeError(expected: "Int", found: "String", path: ["0"]),
+/// DecodeError(expected: "Float", found: "String", path: ["1"]),
+/// ])
+/// ```
+///
+pub fn decode9(
+ constructor: fn(t1, t2, t3, t4, t5, t6, t7, t8, t9) -> t,
+ t1: Decoder(t1),
+ t2: Decoder(t2),
+ t3: Decoder(t3),
+ t4: Decoder(t4),
+ t5: Decoder(t5),
+ t6: Decoder(t6),
+ t7: Decoder(t7),
+ t8: Decoder(t8),
+ t9: Decoder(t9),
+) -> Decoder(t) {
+ fn(x: Dynamic) {
+ case t1(x), t2(x), t3(x), t4(x), t5(x), t6(x), t7(x), t8(x), t9(x) {
+ Ok(a), Ok(b), Ok(c), Ok(d), Ok(e), Ok(f), Ok(g), Ok(h), Ok(i) ->
+ Ok(constructor(a, b, c, d, e, f, g, h, i))
+ a, b, c, d, e, f, g, h, i ->
+ Error(list.concat([
+ all_errors(a),
+ all_errors(b),
+ all_errors(c),
+ all_errors(d),
+ all_errors(e),
+ all_errors(f),
+ all_errors(g),
+ all_errors(h),
+ all_errors(i),
+ ]))
+ }
+ }
+}
+
+fn all_errors(result: Result(a, List(DecodeError))) -> List(DecodeError) {
+ case result {
+ Ok(_) -> []
+ Error(errors) -> errors
+ }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam
new file mode 100644
index 0000000..5d62419
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam
@@ -0,0 +1,546 @@
+//// Functions for working with floats.
+////
+//// ## Division by zero
+////
+//// Gleam runs on the Erlang virtual machine, which does not follow the IEEE
+//// 754 standard for floating point arithmetic and does not have an `Infinity`
+//// value. In Erlang division by zero results in a crash, however Gleam does
+//// not have partial functions and operators in core so instead division by zero
+//// returns zero, a behaviour taken from Pony, Coq, and Lean.
+////
+//// This may seem unexpected at first, but it is no less mathematically valid
+//// than crashing or returning a special value. Division by zero is undefined
+//// in mathematics.
+
+import gleam/order.{type Order}
+
+/// Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was
+/// not possible.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > parse("2.3")
+/// Ok(2.3)
+/// ```
+///
+/// ```gleam
+/// > parse("ABC")
+/// Error(Nil)
+/// ```
+///
+pub fn parse(string: String) -> Result(Float, Nil) {
+ do_parse(string)
+}
+
+@external(erlang, "gleam_stdlib", "parse_float")
+@external(javascript, "../gleam_stdlib.mjs", "parse_float")
+fn do_parse(a: String) -> Result(Float, Nil)
+
+/// Returns the string representation of the provided `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_string(2.3)
+/// "2.3"
+/// ```
+///
+pub fn to_string(x: Float) -> String {
+ do_to_string(x)
+}
+
+@external(erlang, "gleam_stdlib", "float_to_string")
+@external(javascript, "../gleam_stdlib.mjs", "float_to_string")
+fn do_to_string(a: Float) -> String
+
+/// Restricts a `Float` between a lower and upper bound.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > clamp(1.2, min: 1.4, max: 1.6)
+/// 1.4
+/// ```
+///
+pub fn clamp(x: Float, min min_bound: Float, max max_bound: Float) -> Float {
+ x
+ |> min(max_bound)
+ |> max(min_bound)
+}
+
+/// Compares two `Float`s, returning an `Order`:
+/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > compare(2.0, 2.3)
+/// Lt
+/// ```
+///
+/// To handle
+/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems)
+/// you may use [`loosely_compare`](#loosely_compare) instead.
+///
+pub fn compare(a: Float, with b: Float) -> Order {
+ case a == b {
+ True -> order.Eq
+ False ->
+ case a <. b {
+ True -> order.Lt
+ False -> order.Gt
+ }
+ }
+}
+
+/// Compares two `Float`s within a tolerance, returning an `Order`:
+/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.
+///
+/// This function allows Float comparison while handling
+/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).
+///
+/// Notice: For `Float`s the tolerance won't be exact:
+/// `5.3 - 5.0` is not exactly `0.3`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > loosely_compare(5.0, with: 5.3, tolerating: 0.5)
+/// Eq
+/// ```
+///
+/// If you want to check only for equality you may use
+/// [`loosely_equals`](#loosely_equals) instead.
+///
+pub fn loosely_compare(
+ a: Float,
+ with b: Float,
+ tolerating tolerance: Float,
+) -> Order {
+ let difference = absolute_value(a -. b)
+ case difference <=. tolerance {
+ True -> order.Eq
+ False -> compare(a, b)
+ }
+}
+
+/// Checks for equality of two `Float`s within a tolerance,
+/// returning an `Bool`.
+///
+/// This function allows Float comparison while handling
+/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).
+///
+/// Notice: For `Float`s the tolerance won't be exact:
+/// `5.3 - 5.0` is not exactly `0.3`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > loosely_equals(5.0, with: 5.3, tolerating: 0.5)
+/// True
+/// ```
+///
+/// ```gleam
+/// > loosely_equals(5.0, with: 5.1, tolerating: 0.1)
+/// False
+/// ```
+///
+pub fn loosely_equals(
+ a: Float,
+ with b: Float,
+ tolerating tolerance: Float,
+) -> Bool {
+ let difference = absolute_value(a -. b)
+ difference <=. tolerance
+}
+
+/// Compares two `Float`s, returning the smaller of the two.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > min(2.0, 2.3)
+/// 2.0
+/// ```
+///
+pub fn min(a: Float, b: Float) -> Float {
+ case a <. b {
+ True -> a
+ False -> b
+ }
+}
+
+/// Compares two `Float`s, returning the larger of the two.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > max(2.0, 2.3)
+/// 2.3
+/// ```
+///
+pub fn max(a: Float, b: Float) -> Float {
+ case a >. b {
+ True -> a
+ False -> b
+ }
+}
+
+/// Rounds the value to the next highest whole number as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > ceiling(2.3)
+/// 3.0
+/// ```
+///
+pub fn ceiling(x: Float) -> Float {
+ do_ceiling(x)
+}
+
+@external(erlang, "math", "ceil")
+@external(javascript, "../gleam_stdlib.mjs", "ceiling")
+fn do_ceiling(a: Float) -> Float
+
+/// Rounds the value to the next lowest whole number as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > floor(2.3)
+/// 2.0
+/// ```
+///
+pub fn floor(x: Float) -> Float {
+ do_floor(x)
+}
+
+@external(erlang, "math", "floor")
+@external(javascript, "../gleam_stdlib.mjs", "floor")
+fn do_floor(a: Float) -> Float
+
+/// Rounds the value to the nearest whole number as an `Int`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > round(2.3)
+/// 2
+/// ```
+///
+/// ```gleam
+/// > round(2.5)
+/// 3
+/// ```
+///
+pub fn round(x: Float) -> Int {
+ do_round(x)
+}
+
+@target(erlang)
+@external(erlang, "erlang", "round")
+fn do_round(a: Float) -> Int
+
+@target(javascript)
+fn do_round(x: Float) -> Int {
+ case x >=. 0.0 {
+ True -> js_round(x)
+ _ -> 0 - js_round(negate(x))
+ }
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "round")
+fn js_round(a: Float) -> Int
+
+/// Returns the value as an `Int`, truncating all decimal digits.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > truncate(2.4343434847383438)
+/// 2
+/// ```
+///
+pub fn truncate(x: Float) -> Int {
+ do_truncate(x)
+}
+
+@external(erlang, "erlang", "trunc")
+@external(javascript, "../gleam_stdlib.mjs", "truncate")
+fn do_truncate(a: Float) -> Int
+
+/// Returns the absolute value of the input as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > absolute_value(-12.5)
+/// 12.5
+/// ```
+///
+/// ```gleam
+/// > absolute_value(10.2)
+/// 10.2
+/// ```
+///
+pub fn absolute_value(x: Float) -> Float {
+ case x >=. 0.0 {
+ True -> x
+ _ -> 0.0 -. x
+ }
+}
+
+/// Returns the results of the base being raised to the power of the
+/// exponent, as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > power(2.0, -1.0)
+/// Ok(0.5)
+/// ```
+///
+/// ```gleam
+/// > power(2.0, 2.0)
+/// Ok(4.0)
+/// ```
+///
+/// ```gleam
+/// > power(8.0, 1.5)
+/// Ok(22.627416997969522)
+/// ```
+///
+/// ```gleam
+/// > 4.0 |> power(of: 2.0)
+/// Ok(16.0)
+/// ```
+///
+/// ```gleam
+/// > power(-1.0, 0.5)
+/// Error(Nil)
+/// ```
+///
+pub fn power(base: Float, of exponent: Float) -> Result(Float, Nil) {
+ let fractional: Bool = ceiling(exponent) -. exponent >. 0.0
+ // In the following check:
+ // 1. If the base is negative and the exponent is fractional then
+ // return an error as it will otherwise be an imaginary number
+ // 2. If the base is 0 and the exponent is negative then the expression
+ // is equivalent to the exponent divided by 0 and an error should be
+ // returned
+ case base <. 0.0 && fractional || base == 0.0 && exponent <. 0.0 {
+ True -> Error(Nil)
+ False -> Ok(do_power(base, exponent))
+ }
+}
+
+@external(erlang, "math", "pow")
+@external(javascript, "../gleam_stdlib.mjs", "power")
+fn do_power(a: Float, b: Float) -> Float
+
+/// Returns the square root of the input as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > square_root(4.0)
+/// Ok(2.0)
+/// ```
+///
+/// ```gleam
+/// > square_root(-16.0)
+/// Error(Nil)
+/// ```
+///
+pub fn square_root(x: Float) -> Result(Float, Nil) {
+ power(x, 0.5)
+}
+
+/// Returns the negative of the value provided.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > negate(1.0)
+/// -1.0
+/// ```
+///
+pub fn negate(x: Float) -> Float {
+ -1.0 *. x
+}
+
+/// Sums a list of `Float`s.
+///
+/// ## Example
+///
+/// ```gleam
+/// > sum([1.0, 2.2, 3.3])
+/// 6.5
+/// ```
+///
+pub fn sum(numbers: List(Float)) -> Float {
+ numbers
+ |> do_sum(0.0)
+}
+
+fn do_sum(numbers: List(Float), initial: Float) -> Float {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_sum(rest, x +. initial)
+ }
+}
+
+/// Multiplies a list of `Float`s and returns the product.
+///
+/// ## Example
+///
+/// ```gleam
+/// > product([2.5, 3.2, 4.2])
+/// 33.6
+/// ```
+///
+pub fn product(numbers: List(Float)) -> Float {
+ case numbers {
+ [] -> 1.0
+ _ -> do_product(numbers, 1.0)
+ }
+}
+
+fn do_product(numbers: List(Float), initial: Float) -> Float {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_product(rest, x *. initial)
+ }
+}
+
+/// Generates a random float between the given minimum and maximum values.
+///
+///
+/// ## Examples
+///
+/// ```gleam
+/// > random(1.0, 5.0)
+/// 2.646355926896028
+/// ```
+///
+pub fn random(min: Float, max: Float) -> Float {
+ do_random_uniform() *. { max -. min } +. min
+}
+
+/// Returns a random float uniformly distributed in the value range
+/// 0.0 =< X < 1.0 and updates the state in the process dictionary.
+/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0>
+///
+@external(erlang, "rand", "uniform")
+@external(javascript, "../gleam_stdlib.mjs", "random_uniform")
+fn do_random_uniform() -> Float
+
+/// Returns division of the inputs as a `Result`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > divide(0.0, 1.0)
+/// Ok(1.0)
+/// ```
+///
+/// ```gleam
+/// > divide(1.0, 0.0)
+/// Error(Nil)
+/// ```
+///
+pub fn divide(a: Float, by b: Float) -> Result(Float, Nil) {
+ case b {
+ 0.0 -> Error(Nil)
+ b -> Ok(a /. b)
+ }
+}
+
+/// Adds two floats together.
+///
+/// It's the function equivalent of the `+.` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > add(1.0, 2.0)
+/// 3.0
+/// ```
+///
+/// ```gleam
+/// > import gleam/list
+/// > list.fold([1.0, 2.0, 3.0], 0.0, add)
+/// 6.0
+/// ```
+///
+/// ```gleam
+/// > 3.0 |> add(2.0)
+/// 5.0
+/// ```
+///
+pub fn add(a: Float, b: Float) -> Float {
+ a +. b
+}
+
+/// Multiplies two floats together.
+///
+/// It's the function equivalent of the `*.` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > multiply(2.0, 4.0)
+/// 8.0
+/// ```
+///
+/// ```gleam
+/// import gleam/list
+/// > list.fold([2.0, 3.0, 4.0], 1.0, multiply)
+/// 24.0
+/// ```
+///
+/// ```gleam
+/// > 3.0 |> multiply(2.0)
+/// 6.0
+/// ```
+///
+pub fn multiply(a: Float, b: Float) -> Float {
+ a *. b
+}
+
+/// Subtracts one float from another.
+///
+/// It's the function equivalent of the `-.` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > subtract(3.0, 1.0)
+/// 2.0
+/// ```
+///
+/// ```gleam
+/// > import gleam/list
+/// > list.fold([1.0, 2.0, 3.0], 10.0, subtract)
+/// 4.0
+/// ```
+///
+/// ```gleam
+/// > 3.0 |> subtract(_, 2.0)
+/// 1.0
+/// ```
+///
+/// ```gleam
+/// > 3.0 |> subtract(2.0, _)
+/// -1.0
+/// ```
+///
+pub fn subtract(a: Float, b: Float) -> Float {
+ a -. b
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/function.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/function.gleam
new file mode 100644
index 0000000..daa997d
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/function.gleam
@@ -0,0 +1,162 @@
+/// Takes two functions and chains them together to form one function that
+/// takes the input from the first and returns the output of the second.
+///
+pub fn compose(fun1: fn(a) -> b, fun2: fn(b) -> c) -> fn(a) -> c {
+ fn(a) { fun2(fun1(a)) }
+}
+
+/// Takes a function with `2` arguments (an arity of `2`), and returns the
+/// curried equivalent.
+///
+/// `fn(a, b) -> c` becomes `fn(a) -> fn(b) -> c`.
+///
+/// ## Examples
+///
+/// *Currying* creates a new function that is identical to the given function
+/// except that arguments must now be supplied one by one over several function
+/// calls. It thus is the process of taking a function with `n` arguments
+/// and producing a sequence of `n` single-argument functions. Given:
+///
+/// ```gleam
+/// > fn my_fun(i: Int, s: String) -> String { ... }
+/// ```
+///
+/// …calling `curry2(my_fun)` would return the curried equivalent, like so:
+///
+/// ```gleam
+/// > curry2(my_fun)
+/// fn(Int) -> fn(String) -> String
+/// ```
+///
+/// Currying is useful when you want to partially apply a function with
+/// some arguments and then pass it somewhere else, for example:
+///
+/// ```gleam
+/// > import gleam/list
+/// > let multiply = curry2(fn(x, y) { x * y })
+/// > let doubles = list.map([1, 2, 3], multiply(2))
+/// [2, 4, 6]
+/// ```
+///
+pub fn curry2(fun: fn(a, b) -> value) {
+ fn(a) { fn(b) { fun(a, b) } }
+}
+
+/// Takes a function with `3` arguments (an arity of `3`), and returns the
+/// curried equivalent.
+///
+/// `fn(a, b, c) -> d` becomes `fn(a) -> fn(b) -> fn(c) -> d`.
+///
+/// See [`curry2`](#curry2) for a detailed explanation.
+///
+pub fn curry3(fun: fn(a, b, c) -> value) {
+ fn(a) { fn(b) { fn(c) { fun(a, b, c) } } }
+}
+
+/// Takes a function with `4` arguments (an arity of `4`), and returns the
+/// curried equivalent.
+///
+/// `fn(a, b, c, d) -> e` becomes `fn(a) -> fn(b) -> fn(c) -> fn(d) -> e`.
+///
+/// See [`curry2`](#curry2) for a detailed explanation.
+///
+pub fn curry4(fun: fn(a, b, c, d) -> value) {
+ fn(a) { fn(b) { fn(c) { fn(d) { fun(a, b, c, d) } } } }
+}
+
+/// Takes a function with `5` arguments (an arity of `5`), and returns the
+/// curried equivalent.
+///
+/// `fn(a, b, c, d, e) -> f` becomes
+/// `fn(a) -> fn(b) -> fn(c) -> fn(d) -> fn(e) -> f`.
+///
+/// See [`curry2`](#curry2) for a detailed explanation.
+///
+pub fn curry5(fun: fn(a, b, c, d, e) -> value) {
+ fn(a) { fn(b) { fn(c) { fn(d) { fn(e) { fun(a, b, c, d, e) } } } } }
+}
+
+/// Takes a function with `6` arguments (an arity of `6`), and returns the
+/// curried equivalent.
+///
+/// `fn(a, b, c, d, e, f) -> g` becomes
+/// `fn(a) -> fn(b) -> fn(c) -> fn(d) -> fn(e) -> fn(f) -> g`.
+///
+/// See [`curry2`](#curry2) for a detailed explanation.
+///
+pub fn curry6(fun: fn(a, b, c, d, e, f) -> value) {
+ fn(a) {
+ fn(b) { fn(c) { fn(d) { fn(e) { fn(f) { fun(a, b, c, d, e, f) } } } } }
+ }
+}
+
+/// Takes a function that takes two arguments and returns a new function that
+/// takes the same two arguments, but in reverse order.
+///
+pub fn flip(fun: fn(a, b) -> c) -> fn(b, a) -> c {
+ fn(b, a) { fun(a, b) }
+}
+
+/// Takes a single argument and always returns its input value.
+///
+pub fn identity(x: a) -> a {
+ x
+}
+
+/// Takes a single argument and returns a new function that
+/// ignores its argument and always returns the input value.
+///
+pub fn constant(value: a) -> fn(b) -> a {
+ fn(_) { value }
+}
+
+/// Takes an argument and a single function,
+/// calls that function with that argument
+/// and returns that argument instead of the function return value.
+/// Useful for running synchronous side effects in a pipeline.
+///
+pub fn tap(arg: a, effect: fn(a) -> b) -> a {
+ effect(arg)
+ arg
+}
+
+/// Takes a function with arity one and an argument,
+/// calls that function with the argument and returns the function return value.
+///
+/// Useful for concisely calling functions returned as a part of a pipeline.
+///
+/// ## Example
+///
+/// ```gleam
+/// > let doubler = fn() {
+/// > fn(x: Int) { x * 2 }
+/// > }
+/// >
+/// > doubler()
+/// > |> apply1(2)
+/// 4
+/// ```
+///
+pub fn apply1(fun: fn(a) -> value, arg1: a) -> value {
+ fun(arg1)
+}
+
+/// Takes a function with arity two and two arguments,
+/// calls that function with the arguments
+/// and returns the function return value.
+///
+/// See [`apply1`](#apply1) for more details.
+///
+pub fn apply2(fun: fn(a, b) -> value, arg1: a, arg2: b) -> value {
+ fun(arg1, arg2)
+}
+
+/// Takes a function with arity three and three arguments,
+/// calls that function with the arguments
+/// and returns the function return value.
+///
+/// See [`apply1`](#apply1) for more details.
+///
+pub fn apply3(fun: fn(a, b, c) -> value, arg1: a, arg2: b, arg3: c) -> value {
+ fun(arg1, arg2, arg3)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/int.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/int.gleam
new file mode 100644
index 0000000..d93c16a
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/int.gleam
@@ -0,0 +1,874 @@
+//// Functions for working with integers.
+////
+//// ## Division by zero
+////
+//// In Erlang division by zero results in a crash, however Gleam does not have
+//// partial functions and operators in core so instead division by zero returns
+//// zero, a behaviour taken from Pony, Coq, and Lean.
+////
+//// This may seem unexpected at first, but it is no less mathematically valid
+//// than crashing or returning a special value. Division by zero is undefined
+//// in mathematics.
+
+import gleam/float
+import gleam/order.{type Order}
+
+/// Returns the absolute value of the input.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > absolute_value(-12)
+/// 12
+/// ```
+///
+/// ```gleam
+/// > absolute_value(10)
+/// 10
+/// ```
+///
+pub fn absolute_value(x: Int) -> Int {
+ case x >= 0 {
+ True -> x
+ False -> x * -1
+ }
+}
+
+/// Returns the results of the base being raised to the power of the
+/// exponent, as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > power(2, -1.0)
+/// Ok(0.5)
+/// ```
+///
+/// ```gleam
+/// > power(2, 2.0)
+/// Ok(4.0)
+/// ```
+///
+/// ```gleam
+/// > power(8, 1.5)
+/// Ok(22.627416997969522)
+/// ```
+///
+/// ```gleam
+/// > 4 |> power(of: 2.0)
+/// Ok(16.0)
+/// ```
+///
+/// ```gleam
+/// > power(-1, 0.5)
+/// Error(Nil)
+/// ```
+///
+pub fn power(base: Int, of exponent: Float) -> Result(Float, Nil) {
+ base
+ |> to_float()
+ |> float.power(exponent)
+}
+
+/// Returns the square root of the input as a `Float`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > square_root(4)
+/// Ok(2.0)
+/// ```
+///
+/// ```gleam
+/// > square_root(-16)
+/// Error(Nil)
+/// ```
+///
+pub fn square_root(x: Int) -> Result(Float, Nil) {
+ x
+ |> to_float()
+ |> float.square_root()
+}
+
+/// Parses a given string as an int if possible.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > parse("2")
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > parse("ABC")
+/// Error(Nil)
+/// ```
+///
+pub fn parse(string: String) -> Result(Int, Nil) {
+ do_parse(string)
+}
+
+@external(erlang, "gleam_stdlib", "parse_int")
+@external(javascript, "../gleam_stdlib.mjs", "parse_int")
+fn do_parse(a: String) -> Result(Int, Nil)
+
+/// Parses a given string as an int in a given base if possible.
+/// Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > base_parse("10", 2)
+/// Ok(2)
+///
+/// > base_parse("30", 16)
+/// Ok(48)
+///
+/// > base_parse("1C", 36)
+/// Ok(48)
+///
+/// > base_parse("48", 1)
+/// Error(Nil)
+///
+/// > base_parse("48", 37)
+/// Error(Nil)
+/// ```
+///
+pub fn base_parse(string: String, base: Int) -> Result(Int, Nil) {
+ case base >= 2 && base <= 36 {
+ True -> do_base_parse(string, base)
+ False -> Error(Nil)
+ }
+}
+
+@external(erlang, "gleam_stdlib", "int_from_base_string")
+@external(javascript, "../gleam_stdlib.mjs", "int_from_base_string")
+fn do_base_parse(a: String, b: Int) -> Result(Int, Nil)
+
+/// Prints a given int to a string.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_string(2)
+/// "2"
+/// ```
+///
+pub fn to_string(x: Int) {
+ do_to_string(x)
+}
+
+@external(erlang, "erlang", "integer_to_binary")
+@external(javascript, "../gleam_stdlib.mjs", "to_string")
+fn do_to_string(a: Int) -> String
+
+/// Error value when trying to operate with a base out of the allowed range.
+///
+pub type InvalidBase {
+ InvalidBase
+}
+
+/// Prints a given int to a string using the base number provided.
+/// Supports only bases 2 to 36, for values outside of which this function returns an `Error(InvalidBase)`.
+/// For common bases (2, 8, 16, 36), use the `to_baseN` functions.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_base_string(2, 2)
+/// Ok("10")
+/// ```
+///
+/// ```gleam
+/// > to_base_string(48, 16)
+/// Ok("30")
+/// ```
+///
+/// ```gleam
+/// > to_base_string(48, 36)
+/// Ok("1C")
+/// ```
+///
+/// ```gleam
+/// > to_base_string(48, 1)
+/// Error(InvalidBase)
+/// ```
+///
+/// ```gleam
+/// > to_base_string(48, 37)
+/// Error(InvalidBase)
+/// ```
+///
+pub fn to_base_string(x: Int, base: Int) -> Result(String, InvalidBase) {
+ case base >= 2 && base <= 36 {
+ True -> Ok(do_to_base_string(x, base))
+ False -> Error(InvalidBase)
+ }
+}
+
+@external(erlang, "erlang", "integer_to_binary")
+@external(javascript, "../gleam_stdlib.mjs", "int_to_base_string")
+fn do_to_base_string(a: Int, b: Int) -> String
+
+/// Prints a given int to a string using base-2.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_base2(2)
+/// "10"
+/// ```
+///
+pub fn to_base2(x: Int) -> String {
+ do_to_base_string(x, 2)
+}
+
+/// Prints a given int to a string using base-8.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_base8(15)
+/// "17"
+/// ```
+///
+pub fn to_base8(x: Int) -> String {
+ do_to_base_string(x, 8)
+}
+
+/// Prints a given int to a string using base-16.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_base16(48)
+/// "30"
+/// ```
+///
+pub fn to_base16(x: Int) -> String {
+ do_to_base_string(x, 16)
+}
+
+/// Prints a given int to a string using base-36.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_base36(48)
+/// "1C"
+/// ```
+///
+pub fn to_base36(x: Int) -> String {
+ do_to_base_string(x, 36)
+}
+
+/// Takes an int and returns its value as a float.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_float(5)
+/// 5.0
+/// ```
+///
+/// ```gleam
+/// > to_float(0)
+/// 0.0
+/// ```
+///
+/// ```gleam
+/// > to_float(-3)
+/// -3.0
+/// ```
+///
+pub fn to_float(x: Int) -> Float {
+ do_to_float(x)
+}
+
+@external(erlang, "erlang", "float")
+@external(javascript, "../gleam_stdlib.mjs", "identity")
+fn do_to_float(a: Int) -> Float
+
+/// Restricts an int between a lower and upper bound.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > clamp(40, min: 50, max: 60)
+/// 50
+/// ```
+///
+pub fn clamp(x: Int, min min_bound: Int, max max_bound: Int) -> Int {
+ x
+ |> min(max_bound)
+ |> max(min_bound)
+}
+
+/// Compares two ints, returning an order.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > compare(2, 3)
+/// Lt
+/// ```
+///
+/// ```gleam
+/// > compare(4, 3)
+/// Gt
+/// ```
+///
+/// ```gleam
+/// > compare(3, 3)
+/// Eq
+/// ```
+///
+pub fn compare(a: Int, with b: Int) -> Order {
+ case a == b {
+ True -> order.Eq
+ False ->
+ case a < b {
+ True -> order.Lt
+ False -> order.Gt
+ }
+ }
+}
+
+/// Compares two ints, returning the smaller of the two.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > min(2, 3)
+/// 2
+/// ```
+///
+pub fn min(a: Int, b: Int) -> Int {
+ case a < b {
+ True -> a
+ False -> b
+ }
+}
+
+/// Compares two ints, returning the larger of the two.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > max(2, 3)
+/// 3
+/// ```
+///
+pub fn max(a: Int, b: Int) -> Int {
+ case a > b {
+ True -> a
+ False -> b
+ }
+}
+
+/// Returns whether the value provided is even.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_even(2)
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_even(3)
+/// False
+/// ```
+///
+pub fn is_even(x: Int) -> Bool {
+ x % 2 == 0
+}
+
+/// Returns whether the value provided is odd.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_odd(3)
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_odd(2)
+/// False
+/// ```
+///
+pub fn is_odd(x: Int) -> Bool {
+ x % 2 != 0
+}
+
+/// Returns the negative of the value provided.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > negate(1)
+/// -1
+/// ```
+///
+pub fn negate(x: Int) -> Int {
+ -1 * x
+}
+
+/// Sums a list of ints.
+///
+/// ## Example
+///
+/// ```gleam
+/// > sum([1, 2, 3])
+/// 6
+/// ```
+///
+pub fn sum(numbers: List(Int)) -> Int {
+ numbers
+ |> do_sum(0)
+}
+
+fn do_sum(numbers: List(Int), initial: Int) -> Int {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_sum(rest, x + initial)
+ }
+}
+
+/// Multiplies a list of ints and returns the product.
+///
+/// ## Example
+///
+/// ```gleam
+/// > product([2, 3, 4])
+/// 24
+/// ```
+///
+pub fn product(numbers: List(Int)) -> Int {
+ case numbers {
+ [] -> 1
+ _ -> do_product(numbers, 1)
+ }
+}
+
+fn do_product(numbers: List(Int), initial: Int) -> Int {
+ case numbers {
+ [] -> initial
+ [x, ..rest] -> do_product(rest, x * initial)
+ }
+}
+
+/// Splits an integer into its digit representation in the specified base
+///
+/// ## Examples
+///
+/// ```gleam
+/// > digits(234, 10)
+/// Ok([2,3,4])
+/// ```
+///
+/// ```gleam
+/// > digits(234, 1)
+/// Error(InvalidBase)
+/// ```
+///
+pub fn digits(x: Int, base: Int) -> Result(List(Int), InvalidBase) {
+ case base < 2 {
+ True -> Error(InvalidBase)
+ False -> Ok(do_digits(x, base, []))
+ }
+}
+
+fn do_digits(x: Int, base: Int, acc: List(Int)) -> List(Int) {
+ case absolute_value(x) < base {
+ True -> [x, ..acc]
+ False -> do_digits(x / base, base, [x % base, ..acc])
+ }
+}
+
+/// Joins a list of digits into a single value.
+/// Returns an error if the base is less than 2 or if the list contains a digit greater than or equal to the specified base.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > undigits([2,3,4], 10)
+/// Ok(234)
+/// ```
+///
+/// ```gleam
+/// > undigits([2,3,4], 1)
+/// Error(InvalidBase)
+/// ```
+///
+/// ```gleam
+/// > undigits([2,3,4], 2)
+/// Error(InvalidBase)
+/// ```
+///
+pub fn undigits(numbers: List(Int), base: Int) -> Result(Int, InvalidBase) {
+ case base < 2 {
+ True -> Error(InvalidBase)
+ False -> do_undigits(numbers, base, 0)
+ }
+}
+
+fn do_undigits(
+ numbers: List(Int),
+ base: Int,
+ acc: Int,
+) -> Result(Int, InvalidBase) {
+ case numbers {
+ [] -> Ok(acc)
+ [digit, ..] if digit >= base -> Error(InvalidBase)
+ [digit, ..rest] -> do_undigits(rest, base, acc * base + digit)
+ }
+}
+
+/// Generates a random int between the given minimum and maximum values.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > random(1, 5)
+/// 2
+/// ```
+///
+pub fn random(min: Int, max: Int) -> Int {
+ float.random(to_float(min), to_float(max))
+ |> float.floor()
+ |> float.round()
+}
+
+/// Performs a truncated integer division.
+///
+/// Returns division of the inputs as a `Result`: If the given divisor equals
+/// `0`, this function returns an `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > divide(0, 1)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > divide(1, 0)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > divide(5, 2)
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > divide(-99, 2)
+/// Ok(-49)
+/// ```
+///
+pub fn divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
+ case divisor {
+ 0 -> Error(Nil)
+ divisor -> Ok(dividend / divisor)
+ }
+}
+
+/// Computes the remainder of an integer division of inputs as a `Result`.
+///
+/// Returns division of the inputs as a `Result`: If the given divisor equals
+/// `0`, this function returns an `Error`.
+///
+/// Most the time you will want to use the `%` operator instead of this
+/// function.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > remainder(3, 2)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > remainder(1, 0)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > remainder(10, -1)
+/// Ok(0)
+/// ```
+///
+/// ```gleam
+/// > remainder(13, by: 3)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > remainder(-13, by: 3)
+/// Ok(-1)
+/// ```
+///
+/// ```gleam
+/// > remainder(13, by: -3)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > remainder(-13, by: -3)
+/// Ok(-1)
+/// ```
+///
+pub fn remainder(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
+ case divisor {
+ 0 -> Error(Nil)
+ divisor -> Ok(dividend % divisor)
+ }
+}
+
+/// Computes the modulo of an integer division of inputs as a `Result`.
+///
+/// Returns division of the inputs as a `Result`: If the given divisor equals
+/// `0`, this function returns an `Error`.
+///
+/// Most the time you will want to use the `%` operator instead of this
+/// function.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > modulo(3, 2)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > modulo(1, 0)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > modulo(10, -1)
+/// Ok(0)
+/// ```
+///
+/// ```gleam
+/// > modulo(13, by: 3)
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > modulo(-13, by: 3)
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > modulo(13, by: -3)
+/// Ok(-2)
+/// ```
+///
+/// ```gleam
+/// > modulo(-13, by: -3)
+/// Ok(-1)
+/// ```
+///
+pub fn modulo(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
+ case divisor {
+ 0 -> Error(Nil)
+ _ -> {
+ let remainder = dividend % divisor
+ case remainder * divisor < 0 {
+ True -> Ok(remainder + divisor)
+ False -> Ok(remainder)
+ }
+ }
+ }
+}
+
+/// Performs a *floored* integer division, which means that the result will
+/// always be rounded towards negative infinity.
+///
+/// If you want to perform truncated integer division (rounding towards zero),
+/// use `int.divide()` or the `/` operator instead.
+///
+/// Returns division of the inputs as a `Result`: If the given divisor equals
+/// `0`, this function returns an `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > floor_divide(1, 0)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > floor_divide(5, 2)
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > floor_divide(6, -4)
+/// Ok(-2)
+/// ```
+///
+/// ```gleam
+/// > floor_divide(-99, 2)
+/// Ok(-50)
+/// ```
+///
+pub fn floor_divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
+ case divisor {
+ 0 -> Error(Nil)
+ divisor ->
+ case dividend * divisor < 0 && dividend % divisor != 0 {
+ True -> Ok(dividend / divisor - 1)
+ False -> Ok(dividend / divisor)
+ }
+ }
+}
+
+/// Adds two integers together.
+///
+/// It's the function equivalent of the `+` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > add(1, 2)
+/// 3
+/// ```
+///
+/// ```gleam
+/// import gleam/list
+/// > list.fold([1, 2, 3], 0, add)
+/// 6
+/// ```
+///
+/// ```gleam
+/// > 3 |> add(2)
+/// 5
+/// ```
+///
+pub fn add(a: Int, b: Int) -> Int {
+ a + b
+}
+
+/// Multiplies two integers together.
+///
+/// It's the function equivalent of the `*` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > multiply(2, 4)
+/// 8
+/// ```
+///
+/// ```gleam
+/// import gleam/list
+/// > list.fold([2, 3, 4], 1, multiply)
+/// 24
+/// ```
+///
+/// ```gleam
+/// > 3 |> multiply(2)
+/// 6
+/// ```
+///
+pub fn multiply(a: Int, b: Int) -> Int {
+ a * b
+}
+
+/// Subtracts one int from another.
+///
+/// It's the function equivalent of the `-` operator.
+/// This function is useful in higher order functions or pipes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > subtract(3, 1)
+/// 2.0
+/// ```
+///
+/// ```gleam
+/// import gleam/list
+/// > list.fold([1, 2, 3], 10, subtract)
+/// 4
+/// ```
+///
+/// ```gleam
+/// > 3 |> subtract(2)
+/// 1
+/// ```
+///
+/// ```gleam
+/// > 3 |> subtract(2, _)
+/// -1
+/// ```
+///
+pub fn subtract(a: Int, b: Int) -> Int {
+ a - b
+}
+
+/// Calculates the bitwise AND of its arguments.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "band")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_and")
+pub fn bitwise_and(x: Int, y: Int) -> Int
+
+/// Calculates the bitwise NOT of its argument.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "bnot")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_not")
+pub fn bitwise_not(x: Int) -> Int
+
+/// Calculates the bitwise OR of its arguments.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "bor")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_or")
+pub fn bitwise_or(x: Int, y: Int) -> Int
+
+/// Calculates the bitwise XOR of its arguments.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "bxor")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_exclusive_or")
+pub fn bitwise_exclusive_or(x: Int, y: Int) -> Int
+
+/// Calculates the result of an arithmetic left bitshift.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "bsl")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_left")
+pub fn bitwise_shift_left(x: Int, y: Int) -> Int
+
+/// Calculates the result of an arithmetic right bitshift.
+///
+/// The exact behaviour of this function depends on the target platform.
+/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it
+/// is equivalent to bitwise operations on big-ints.
+///
+@external(erlang, "erlang", "bsr")
+@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_right")
+pub fn bitwise_shift_right(x: Int, y: Int) -> Int
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/io.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/io.gleam
new file mode 100644
index 0000000..0c0a3ee
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/io.gleam
@@ -0,0 +1,117 @@
+import gleam/string
+
+/// Writes a string to standard output.
+///
+/// If you want your output to be printed on its own line see `println`.
+///
+/// ## Example
+///
+/// ```gleam
+/// > io.print("Hi mum")
+/// // -> Hi mum
+/// Nil
+/// ```
+///
+pub fn print(string: String) -> Nil {
+ do_print(string)
+}
+
+@external(erlang, "gleam_stdlib", "print")
+@external(javascript, "../gleam_stdlib.mjs", "print")
+fn do_print(string string: String) -> Nil
+
+/// Writes a string to standard error.
+///
+/// If you want your output to be printed on its own line see `println_error`.
+///
+/// ## Example
+///
+/// ```
+/// > io.print_error("Hi pop")
+/// // -> Hi pop
+/// Nil
+/// ```
+///
+pub fn print_error(string: String) -> Nil {
+ do_print_error(string)
+}
+
+@external(erlang, "gleam_stdlib", "print_error")
+@external(javascript, "../gleam_stdlib.mjs", "print_error")
+fn do_print_error(string string: String) -> Nil
+
+/// Writes a string to standard output, appending a newline to the end.
+///
+/// ## Example
+///
+/// ```gleam
+/// > io.println("Hi mum")
+/// // -> Hi mum
+/// Nil
+/// ```
+///
+pub fn println(string: String) -> Nil {
+ do_println(string)
+}
+
+@external(erlang, "gleam_stdlib", "println")
+@external(javascript, "../gleam_stdlib.mjs", "console_log")
+fn do_println(string string: String) -> Nil
+
+/// Writes a string to standard error, appending a newline to the end.
+///
+/// ## Example
+///
+/// ```gleam
+/// > io.println_error("Hi pop")
+/// // -> Hi mum
+/// Nil
+/// ```
+///
+pub fn println_error(string: String) -> Nil {
+ do_println_error(string)
+}
+
+@external(erlang, "gleam_stdlib", "println_error")
+@external(javascript, "../gleam_stdlib.mjs", "console_error")
+fn do_println_error(string string: String) -> Nil
+
+/// Prints a value to standard error (stderr) yielding Gleam syntax.
+///
+/// The value is returned after being printed so it can be used in pipelines.
+///
+/// ## Example
+///
+/// ```gleam
+/// > debug("Hi mum")
+/// // -> <<"Hi mum">>
+/// "Hi mum"
+/// ```
+///
+/// ```gleam
+/// > debug(Ok(1))
+/// // -> {ok, 1}
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > import list
+/// > [1, 2]
+/// > |> list.map(fn(x) { x + 1 })
+/// > |> debug
+/// > |> list.map(fn(x) { x * 2 })
+/// // -> [2, 3]
+/// [4, 6]
+/// ```
+///
+pub fn debug(term: anything) -> anything {
+ term
+ |> string.inspect
+ |> do_debug_println
+
+ term
+}
+
+@external(erlang, "gleam_stdlib", "println_error")
+@external(javascript, "../gleam_stdlib.mjs", "print_debug")
+fn do_debug_println(string string: String) -> Nil
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/iterator.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/iterator.gleam
new file mode 100644
index 0000000..c57e7fd
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/iterator.gleam
@@ -0,0 +1,1530 @@
+import gleam/result
+import gleam/int
+import gleam/list
+import gleam/dict.{type Dict}
+import gleam/option.{type Option, None, Some}
+import gleam/order
+
+// Internal private representation of an Iterator
+type Action(element) {
+ // Dedicated to Electric Six
+ // https://youtu.be/_30t2dzEgiw?t=162
+ Stop
+ Continue(element, fn() -> Action(element))
+}
+
+/// An iterator is a lazily evaluated sequence of element.
+///
+/// Iterators are useful when working with collections that are too large to
+/// fit in memory (or those that are infinite in size) as they only require the
+/// elements currently being processed to be in memory.
+///
+/// As a lazy data structure no work is done when an iterator is filters,
+/// mapped, etc, instead a new iterator is returned with these transformations
+/// applied to the stream. Once the stream has all the required transformations
+/// applied it can be evaluated using functions such as `fold` and `to_list`.
+///
+pub opaque type Iterator(element) {
+ Iterator(continuation: fn() -> Action(element))
+}
+
+// Public API for iteration
+pub type Step(element, accumulator) {
+ Next(element: element, accumulator: accumulator)
+ Done
+}
+
+// Shortcut for an empty iterator.
+fn stop() -> Action(element) {
+ Stop
+}
+
+// Creating Iterators
+fn do_unfold(
+ initial: acc,
+ f: fn(acc) -> Step(element, acc),
+) -> fn() -> Action(element) {
+ fn() {
+ case f(initial) {
+ Next(x, acc) -> Continue(x, do_unfold(acc, f))
+ Done -> Stop
+ }
+ }
+}
+
+/// Creates an iterator from a given function and accumulator.
+///
+/// The function is called on the accumulator and returns either `Done`,
+/// indicating the iterator has no more elements, or `Next` which contains a
+/// new element and accumulator. The element is yielded by the iterator and the
+/// new accumulator is used with the function to compute the next element in
+/// the sequence.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unfold(from: 5, with: fn(n) {
+/// > case n {
+/// > 0 -> Done
+/// > n -> Next(element: n, accumulator: n - 1)
+/// > }
+/// > })
+/// > |> to_list
+/// [5, 4, 3, 2, 1]
+/// ```
+///
+pub fn unfold(
+ from initial: acc,
+ with f: fn(acc) -> Step(element, acc),
+) -> Iterator(element) {
+ initial
+ |> do_unfold(f)
+ |> Iterator
+}
+
+// TODO: test
+/// Creates an iterator that yields values created by calling a given function
+/// repeatedly.
+///
+pub fn repeatedly(f: fn() -> element) -> Iterator(element) {
+ unfold(Nil, fn(_) { Next(f(), Nil) })
+}
+
+/// Creates an iterator that returns the same value infinitely.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > repeat(10)
+/// > |> take(4)
+/// > |> to_list
+/// [10, 10, 10, 10]
+/// ```
+///
+pub fn repeat(x: element) -> Iterator(element) {
+ repeatedly(fn() { x })
+}
+
+/// Creates an iterator that yields each element from the given list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4])
+/// > |> to_list
+/// [1, 2, 3, 4]
+/// ```
+///
+pub fn from_list(list: List(element)) -> Iterator(element) {
+ let yield = fn(acc) {
+ case acc {
+ [] -> Done
+ [head, ..tail] -> Next(head, tail)
+ }
+ }
+ unfold(list, yield)
+}
+
+// Consuming Iterators
+fn do_transform(
+ continuation: fn() -> Action(a),
+ state: acc,
+ f: fn(acc, a) -> Step(b, acc),
+) -> fn() -> Action(b) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(el, next) ->
+ case f(state, el) {
+ Done -> Stop
+ Next(yield, next_state) ->
+ Continue(yield, do_transform(next, next_state, f))
+ }
+ }
+ }
+}
+
+/// Creates an iterator from an existing iterator
+/// and a stateful function that may short-circuit.
+///
+/// `f` takes arguments `acc` for current state and `el` for current element from underlying iterator,
+/// and returns either `Next` with yielded element and new state value, or `Done` to halt the iterator.
+///
+/// ## Examples
+///
+/// Approximate implementation of `index` in terms of `transform`:
+///
+/// ```gleam
+/// > from_list(["a", "b", "c"])
+/// > |> transform(0, fn(i, el) { Next(#(i, el), i + 1) })
+/// > |> to_list
+/// [#(0, "a"), #(1, "b"), #(2, "c")]
+/// ```
+pub fn transform(
+ over iterator: Iterator(a),
+ from initial: acc,
+ with f: fn(acc, a) -> Step(b, acc),
+) -> Iterator(b) {
+ do_transform(iterator.continuation, initial, f)
+ |> Iterator
+}
+
+fn do_fold(
+ continuation: fn() -> Action(e),
+ f: fn(acc, e) -> acc,
+ accumulator: acc,
+) -> acc {
+ case continuation() {
+ Continue(elem, next) -> do_fold(next, f, f(accumulator, elem))
+ Stop -> accumulator
+ }
+}
+
+/// Reduces an iterator of elements into a single value by calling a given
+/// function on each element in turn.
+///
+/// If called on an iterator of infinite length then this function will never
+/// return.
+///
+/// If you do not care about the end value and only wish to evaluate the
+/// iterator for side effects consider using the `run` function instead.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4]
+/// > |> from_list
+/// > |> fold(from: 0, with: fn(acc, element) { element + acc })
+/// 10
+/// ```
+///
+pub fn fold(
+ over iterator: Iterator(e),
+ from initial: acc,
+ with f: fn(acc, e) -> acc,
+) -> acc {
+ iterator.continuation
+ |> do_fold(f, initial)
+}
+
+// TODO: test
+/// Evaluates all elements emitted by the given iterator. This function is useful for when
+/// you wish to trigger any side effects that would occur when evaluating
+/// the iterator.
+///
+pub fn run(iterator: Iterator(e)) -> Nil {
+ fold(iterator, Nil, fn(_, _) { Nil })
+}
+
+/// Evaluates an iterator and returns all the elements as a list.
+///
+/// If called on an iterator of infinite length then this function will never
+/// return.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3]
+/// > |> from_list
+/// > |> map(fn(x) { x * 2 })
+/// > |> to_list
+/// [2, 4, 6]
+/// ```
+///
+pub fn to_list(iterator: Iterator(element)) -> List(element) {
+ iterator
+ |> fold([], fn(acc, e) { [e, ..acc] })
+ |> list.reverse
+}
+
+/// Eagerly accesses the first value of an iterator, returning a `Next`
+/// that contains the first value and the rest of the iterator.
+///
+/// If called on an empty iterator, `Done` is returned.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Next(first, rest) = [1, 2, 3, 4]
+/// > |> from_list
+/// > |> step
+/// > first
+/// 1
+/// ```
+///
+/// ```gleam
+/// > rest |> to_list
+/// [2, 3, 4]
+/// ```
+///
+/// ```gleam
+/// > empty() |> step
+/// Done
+/// ```
+///
+pub fn step(iterator: Iterator(e)) -> Step(e, Iterator(e)) {
+ case iterator.continuation() {
+ Stop -> Done
+ Continue(e, a) -> Next(e, Iterator(a))
+ }
+}
+
+fn do_take(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) {
+ fn() {
+ case desired > 0 {
+ False -> Stop
+ True ->
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) -> Continue(e, do_take(next, desired - 1))
+ }
+ }
+ }
+}
+
+/// Creates an iterator that only yields the first `desired` elements.
+///
+/// If the iterator does not have enough elements all of them are yielded.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5]
+/// > |> from_list
+/// > |> take(up_to: 3)
+/// > |> to_list
+/// [1, 2, 3]
+/// ```
+///
+/// ```gleam
+/// > [1, 2]
+/// > |> from_list
+/// > |> take(up_to: 3)
+/// > |> to_list
+/// [1, 2]
+/// ```
+///
+pub fn take(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
+ iterator.continuation
+ |> do_take(desired)
+ |> Iterator
+}
+
+fn do_drop(continuation: fn() -> Action(e), desired: Int) -> Action(e) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) ->
+ case desired > 0 {
+ True -> do_drop(next, desired - 1)
+ False -> Continue(e, next)
+ }
+ }
+}
+
+/// Evaluates and discards the first N elements in an iterator, returning a new
+/// iterator.
+///
+/// If the iterator does not have enough elements an empty iterator is
+/// returned.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5]
+/// > |> from_list
+/// > |> drop(up_to: 3)
+/// > |> to_list
+/// [4, 5]
+/// ```
+///
+/// ```gleam
+/// > [1, 2]
+/// > |> from_list
+/// > |> drop(up_to: 3)
+/// > |> to_list
+/// []
+/// ```
+///
+pub fn drop(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) {
+ fn() { do_drop(iterator.continuation, desired) }
+ |> Iterator
+}
+
+fn do_map(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, continuation) -> Continue(f(e), do_map(continuation, f))
+ }
+ }
+}
+
+/// Creates an iterator from an existing iterator and a transformation function.
+///
+/// Each element in the new iterator will be the result of calling the given
+/// function on the elements in the given iterator.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3]
+/// > |> from_list
+/// > |> map(fn(x) { x * 2 })
+/// > |> to_list
+/// [2, 4, 6]
+/// ```
+///
+pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) {
+ iterator.continuation
+ |> do_map(f)
+ |> Iterator
+}
+
+fn do_map2(
+ continuation1: fn() -> Action(a),
+ continuation2: fn() -> Action(b),
+ with fun: fn(a, b) -> c,
+) -> fn() -> Action(c) {
+ fn() {
+ case continuation1() {
+ Stop -> Stop
+ Continue(a, next_a) ->
+ case continuation2() {
+ Stop -> Stop
+ Continue(b, next_b) ->
+ Continue(fun(a, b), do_map2(next_a, next_b, fun))
+ }
+ }
+ }
+}
+
+/// Combines two interators into a single one using the given function.
+///
+/// If an iterator is longer than the other the extra elements are dropped.
+///
+/// This function does not evaluate the elements of the two iterators, the
+/// computation is performed when the resulting iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// let first = from_list([1, 2, 3])
+/// let second = from_list([4, 5, 6])
+/// map2(first, second, fn(x, y) { x + y }) |> to_list
+/// // -> [5, 7, 9]
+/// ```
+///
+/// ```gleam
+/// let first = from_list([1, 2])
+/// let second = from_list(["a", "b", "c"])
+/// map2(first, second, fn(i, x) { #(i, x) }) |> to_list
+/// // -> [#(1, "a"), #(2, "b")]
+/// ```
+///
+pub fn map2(
+ iterator1: Iterator(a),
+ iterator2: Iterator(b),
+ with fun: fn(a, b) -> c,
+) -> Iterator(c) {
+ do_map2(iterator1.continuation, iterator2.continuation, fun)
+ |> Iterator
+}
+
+fn do_append(first: fn() -> Action(a), second: fn() -> Action(a)) -> Action(a) {
+ case first() {
+ Continue(e, first) -> Continue(e, fn() { do_append(first, second) })
+ Stop -> second()
+ }
+}
+
+/// Appends two iterators, producing a new iterator.
+///
+/// This function does not evaluate the elements of the iterators, the
+/// computation is performed when the resulting iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2]
+/// > |> from_list
+/// > |> append([3, 4] |> from_list)
+/// > |> to_list
+/// [1, 2, 3, 4]
+/// ```
+///
+pub fn append(to first: Iterator(a), suffix second: Iterator(a)) -> Iterator(a) {
+ fn() { do_append(first.continuation, second.continuation) }
+ |> Iterator
+}
+
+fn do_flatten(flattened: fn() -> Action(Iterator(a))) -> Action(a) {
+ case flattened() {
+ Stop -> Stop
+ Continue(it, next_iterator) ->
+ do_append(it.continuation, fn() { do_flatten(next_iterator) })
+ }
+}
+
+/// Flattens an iterator of iterators, creating a new iterator.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([[1, 2], [3, 4]])
+/// > |> map(from_list)
+/// > |> flatten
+/// > |> to_list
+/// [1, 2, 3, 4]
+/// ```
+///
+pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) {
+ fn() { do_flatten(iterator.continuation) }
+ |> Iterator
+}
+
+/// Joins a list of iterators into a single iterator.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [[1, 2], [3, 4]]
+/// > |> map(from_list)
+/// > |> concat
+/// > |> to_list
+/// [1, 2, 3, 4]
+/// ```
+///
+pub fn concat(iterators: List(Iterator(a))) -> Iterator(a) {
+ flatten(from_list(iterators))
+}
+
+/// Creates an iterator from an existing iterator and a transformation function.
+///
+/// Each element in the new iterator will be the result of calling the given
+/// function on the elements in the given iterator and then flattening the
+/// results.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2]
+/// > |> from_list
+/// > |> flat_map(fn(x) { from_list([x, x + 1]) })
+/// > |> to_list
+/// [1, 2, 2, 3]
+/// ```
+///
+pub fn flat_map(
+ over iterator: Iterator(a),
+ with f: fn(a) -> Iterator(b),
+) -> Iterator(b) {
+ iterator
+ |> map(f)
+ |> flatten
+}
+
+fn do_filter(
+ continuation: fn() -> Action(e),
+ predicate: fn(e) -> Bool,
+) -> Action(e) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, iterator) ->
+ case predicate(e) {
+ True -> Continue(e, fn() { do_filter(iterator, predicate) })
+ False -> do_filter(iterator, predicate)
+ }
+ }
+}
+
+/// Creates an iterator from an existing iterator and a predicate function.
+///
+/// The new iterator will contain elements from the first iterator for which
+/// the given function returns `True`.
+///
+/// This function does not evaluate the elements of the iterator, the
+/// computation is performed when the iterator is later run.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/int
+/// > [1, 2, 3, 4]
+/// > |> from_list
+/// > |> filter(int.is_even)
+/// > |> to_list
+/// [2, 4]
+/// ```
+///
+pub fn filter(
+ iterator: Iterator(a),
+ keeping predicate: fn(a) -> Bool,
+) -> Iterator(a) {
+ fn() { do_filter(iterator.continuation, predicate) }
+ |> Iterator
+}
+
+/// Creates an iterator that repeats a given iterator infinitely.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2]
+/// > |> from_list
+/// > |> cycle
+/// > |> take(6)
+/// > |> to_list
+/// [1, 2, 1, 2, 1, 2]
+/// ```
+///
+pub fn cycle(iterator: Iterator(a)) -> Iterator(a) {
+ repeat(iterator)
+ |> flatten
+}
+
+/// Creates an iterator of ints, starting at a given start int and stepping by
+/// one to a given end int.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > range(from: 1, to: 5) |> to_list
+/// [1, 2, 3, 4, 5]
+/// ```
+///
+/// ```gleam
+/// > range(from: 1, to: -2) |> to_list
+/// [1, 0, -1, -2]
+/// ```
+///
+/// ```gleam
+/// > range(from: 0, to: 0) |> to_list
+/// [0]
+/// ```
+///
+pub fn range(from start: Int, to stop: Int) -> Iterator(Int) {
+ case int.compare(start, stop) {
+ order.Eq -> once(fn() { start })
+ order.Gt ->
+ unfold(
+ from: start,
+ with: fn(current) {
+ case current < stop {
+ False -> Next(current, current - 1)
+ True -> Done
+ }
+ },
+ )
+
+ order.Lt ->
+ unfold(
+ from: start,
+ with: fn(current) {
+ case current > stop {
+ False -> Next(current, current + 1)
+ True -> Done
+ }
+ },
+ )
+ }
+}
+
+fn do_find(continuation: fn() -> Action(a), f: fn(a) -> Bool) -> Result(a, Nil) {
+ case continuation() {
+ Stop -> Error(Nil)
+ Continue(e, next) ->
+ case f(e) {
+ True -> Ok(e)
+ False -> do_find(next, f)
+ }
+ }
+}
+
+/// Finds the first element in a given iterator for which the given function returns
+/// `True`.
+///
+/// Returns `Error(Nil)` if the function does not return `True` for any of the
+/// elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > find(from_list([1, 2, 3]), fn(x) { x > 2 })
+/// Ok(3)
+/// ```
+///
+/// ```gleam
+/// > find(from_list([1, 2, 3]), fn(x) { x > 4 })
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > find(empty(), fn(_) { True })
+/// Error(Nil)
+/// ```
+///
+pub fn find(
+ in haystack: Iterator(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(a, Nil) {
+ haystack.continuation
+ |> do_find(is_desired)
+}
+
+fn do_index(
+ continuation: fn() -> Action(element),
+ next: Int,
+) -> fn() -> Action(#(Int, element)) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, continuation) ->
+ Continue(#(next, e), do_index(continuation, next + 1))
+ }
+ }
+}
+
+/// Wraps values yielded from an iterator with indices, starting from 0.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list(["a", "b", "c"]) |> index |> to_list
+/// [#(0, "a"), #(1, "b"), #(2, "c")]
+/// ```
+///
+pub fn index(over iterator: Iterator(element)) -> Iterator(#(Int, element)) {
+ iterator.continuation
+ |> do_index(0)
+ |> Iterator
+}
+
+/// Creates an iterator that inifinitely applies a function to a value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > iterate(1, fn(n) { n * 3 }) |> take(5) |> to_list
+/// [1, 3, 9, 27, 81]
+/// ```
+///
+pub fn iterate(
+ from initial: element,
+ with f: fn(element) -> element,
+) -> Iterator(element) {
+ unfold(initial, fn(element) { Next(element, f(element)) })
+}
+
+fn do_take_while(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> fn() -> Action(element) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) ->
+ case predicate(e) {
+ False -> Stop
+ True -> Continue(e, do_take_while(next, predicate))
+ }
+ }
+ }
+}
+
+/// Creates an iterator that yields elements while the predicate returns `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 2, 4])
+/// > |> take_while(satisfying: fn(x) { x < 3 })
+/// > |> to_list
+/// [1, 2]
+/// ```
+///
+pub fn take_while(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Iterator(element) {
+ iterator.continuation
+ |> do_take_while(predicate)
+ |> Iterator
+}
+
+fn do_drop_while(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> Action(element) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) ->
+ case predicate(e) {
+ False -> Continue(e, next)
+ True -> do_drop_while(next, predicate)
+ }
+ }
+}
+
+/// Creates an iterator that drops elements while the predicate returns `True`,
+/// and then yields the remaining elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4, 2, 5])
+/// > |> drop_while(satisfying: fn(x) { x < 4 })
+/// > |> to_list
+/// [4, 2, 5]
+/// ```
+///
+pub fn drop_while(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Iterator(element) {
+ fn() { do_drop_while(iterator.continuation, predicate) }
+ |> Iterator
+}
+
+fn do_scan(
+ continuation: fn() -> Action(element),
+ f: fn(acc, element) -> acc,
+ accumulator: acc,
+) -> fn() -> Action(acc) {
+ fn() {
+ case continuation() {
+ Stop -> Stop
+ Continue(el, next) -> {
+ let accumulated = f(accumulator, el)
+ Continue(accumulated, do_scan(next, f, accumulated))
+ }
+ }
+ }
+}
+
+/// Creates an iterator from an existing iterator and a stateful function.
+///
+/// Specifically, this behaves like `fold`, but yields intermediate results.
+///
+/// ## Examples
+///
+/// ```gleam
+/// // Generate a sequence of partial sums
+/// > from_list([1, 2, 3, 4, 5])
+/// > |> scan(from: 0, with: fn(acc, el) { acc + el })
+/// > |> to_list
+/// [1, 3, 6, 10, 15]
+/// ```
+///
+pub fn scan(
+ over iterator: Iterator(element),
+ from initial: acc,
+ with f: fn(acc, element) -> acc,
+) -> Iterator(acc) {
+ iterator.continuation
+ |> do_scan(f, initial)
+ |> Iterator
+}
+
+fn do_zip(
+ left: fn() -> Action(a),
+ right: fn() -> Action(b),
+) -> fn() -> Action(#(a, b)) {
+ fn() {
+ case left() {
+ Stop -> Stop
+ Continue(el_left, next_left) ->
+ case right() {
+ Stop -> Stop
+ Continue(el_right, next_right) ->
+ Continue(#(el_left, el_right), do_zip(next_left, next_right))
+ }
+ }
+ }
+}
+
+/// Zips two iterators together, emitting values from both
+/// until the shorter one runs out.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list(["a", "b", "c"])
+/// > |> zip(range(20, 30))
+/// > |> to_list
+/// [#("a", 20), #("b", 21), #("c", 22)]
+/// ```
+///
+pub fn zip(left: Iterator(a), right: Iterator(b)) -> Iterator(#(a, b)) {
+ do_zip(left.continuation, right.continuation)
+ |> Iterator
+}
+
+// Result of collecting a single chunk by key
+type Chunk(element, key) {
+ AnotherBy(List(element), key, element, fn() -> Action(element))
+ LastBy(List(element))
+}
+
+fn next_chunk(
+ continuation: fn() -> Action(element),
+ f: fn(element) -> key,
+ previous_key: key,
+ current_chunk: List(element),
+) -> Chunk(element, key) {
+ case continuation() {
+ Stop -> LastBy(list.reverse(current_chunk))
+ Continue(e, next) -> {
+ let key = f(e)
+ case key == previous_key {
+ True -> next_chunk(next, f, key, [e, ..current_chunk])
+ False -> AnotherBy(list.reverse(current_chunk), key, e, next)
+ }
+ }
+ }
+}
+
+fn do_chunk(
+ continuation: fn() -> Action(element),
+ f: fn(element) -> key,
+ previous_key: key,
+ previous_element: element,
+) -> Action(List(element)) {
+ case next_chunk(continuation, f, previous_key, [previous_element]) {
+ LastBy(chunk) -> Continue(chunk, stop)
+ AnotherBy(chunk, key, el, next) ->
+ Continue(chunk, fn() { do_chunk(next, f, key, el) })
+ }
+}
+
+/// Creates an iterator that emits chunks of elements
+/// for which `f` returns the same value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 2, 3, 4, 4, 6, 7, 7])
+/// > |> chunk(by: fn(n) { n % 2 })
+/// > |> to_list
+/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
+/// ```
+///
+pub fn chunk(
+ over iterator: Iterator(element),
+ by f: fn(element) -> key,
+) -> Iterator(List(element)) {
+ fn() {
+ case iterator.continuation() {
+ Stop -> Stop
+ Continue(e, next) -> do_chunk(next, f, f(e), e)
+ }
+ }
+ |> Iterator
+}
+
+// Result of collecting a single sized chunk
+type SizedChunk(element) {
+ Another(List(element), fn() -> Action(element))
+ Last(List(element))
+ NoMore
+}
+
+fn next_sized_chunk(
+ continuation: fn() -> Action(element),
+ left: Int,
+ current_chunk: List(element),
+) -> SizedChunk(element) {
+ case continuation() {
+ Stop ->
+ case current_chunk {
+ [] -> NoMore
+ remaining -> Last(list.reverse(remaining))
+ }
+ Continue(e, next) -> {
+ let chunk = [e, ..current_chunk]
+ case left > 1 {
+ False -> Another(list.reverse(chunk), next)
+ True -> next_sized_chunk(next, left - 1, chunk)
+ }
+ }
+ }
+}
+
+fn do_sized_chunk(
+ continuation: fn() -> Action(element),
+ count: Int,
+) -> fn() -> Action(List(element)) {
+ fn() {
+ case next_sized_chunk(continuation, count, []) {
+ NoMore -> Stop
+ Last(chunk) -> Continue(chunk, stop)
+ Another(chunk, next_element) ->
+ Continue(chunk, do_sized_chunk(next_element, count))
+ }
+ }
+}
+
+/// Creates an iterator that emits chunks of given size.
+///
+/// If the last chunk does not have `count` elements, it is yielded
+/// as a partial chunk, with less than `count` elements.
+///
+/// For any `count` less than 1 this function behaves as if it was set to 1.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4, 5, 6])
+/// > |> sized_chunk(into: 2)
+/// > |> to_list
+/// [[1, 2], [3, 4], [5, 6]]
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4, 5, 6, 7, 8])
+/// > |> sized_chunk(into: 3)
+/// > |> to_list
+/// [[1, 2, 3], [4, 5, 6], [7, 8]]
+/// ```
+///
+pub fn sized_chunk(
+ over iterator: Iterator(element),
+ into count: Int,
+) -> Iterator(List(element)) {
+ iterator.continuation
+ |> do_sized_chunk(count)
+ |> Iterator
+}
+
+fn do_intersperse(
+ continuation: fn() -> Action(element),
+ separator: element,
+) -> Action(element) {
+ case continuation() {
+ Stop -> Stop
+ Continue(e, next) -> {
+ let next_interspersed = fn() { do_intersperse(next, separator) }
+ Continue(separator, fn() { Continue(e, next_interspersed) })
+ }
+ }
+}
+
+/// Creates an iterator that yields the given `elem` element
+/// between elements emitted by the underlying iterator.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty()
+/// > |> intersperse(with: 0)
+/// > |> to_list
+/// []
+///
+/// > from_list([1])
+/// > |> intersperse(with: 0)
+/// > |> to_list
+/// [1]
+///
+/// > from_list([1, 2, 3, 4, 5])
+/// > |> intersperse(with: 0)
+/// > |> to_list
+/// [1, 0, 2, 0, 3, 0, 4, 0, 5]
+/// ```
+///
+pub fn intersperse(
+ over iterator: Iterator(element),
+ with elem: element,
+) -> Iterator(element) {
+ fn() {
+ case iterator.continuation() {
+ Stop -> Stop
+ Continue(e, next) -> Continue(e, fn() { do_intersperse(next, elem) })
+ }
+ }
+ |> Iterator
+}
+
+fn do_any(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> Bool {
+ case continuation() {
+ Stop -> False
+ Continue(e, next) ->
+ case predicate(e) {
+ True -> True
+ False -> do_any(next, predicate)
+ }
+ }
+}
+
+/// Returns `True` if any element emitted by the iterator satisfies the given predicate,
+/// `False` otherwise.
+///
+/// This function short-circuits once it finds a satisfying element.
+///
+/// An empty iterator results in `False`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> any(fn(n) { n % 2 == 0 })
+/// False
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 3, 5, 7, 9]) |> any(fn(n) { n % 2 == 0 })
+/// False
+/// ```
+///
+pub fn any(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Bool {
+ iterator.continuation
+ |> do_any(predicate)
+}
+
+fn do_all(
+ continuation: fn() -> Action(element),
+ predicate: fn(element) -> Bool,
+) -> Bool {
+ case continuation() {
+ Stop -> True
+ Continue(e, next) ->
+ case predicate(e) {
+ True -> do_all(next, predicate)
+ False -> False
+ }
+ }
+}
+
+/// Returns `True` if all elements emitted by the iterator satisfy the given predicate,
+/// `False` otherwise.
+///
+/// This function short-circuits once it finds a non-satisfying element.
+///
+/// An empty iterator results in `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> all(fn(n) { n % 2 == 0 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > from_list([2, 4, 6, 8]) |> all(fn(n) { n % 2 == 0 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > from_list([2, 4, 5, 8]) |> all(fn(n) { n % 2 == 0 })
+/// False
+/// ```
+///
+pub fn all(
+ in iterator: Iterator(element),
+ satisfying predicate: fn(element) -> Bool,
+) -> Bool {
+ iterator.continuation
+ |> do_all(predicate)
+}
+
+fn update_group_with(el: element) -> fn(Option(List(element))) -> List(element) {
+ fn(maybe_group) {
+ case maybe_group {
+ Some(group) -> [el, ..group]
+ None -> [el]
+ }
+ }
+}
+
+fn group_updater(
+ f: fn(element) -> key,
+) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) {
+ fn(groups, elem) {
+ groups
+ |> dict.update(f(elem), update_group_with(elem))
+ }
+}
+
+/// Returns a `Dict(k, List(element))` of elements from the given iterator
+/// grouped with the given key function.
+///
+/// The order within each group is preserved from the iterator.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4, 5, 6]) |> group(by: fn(n) { n % 3 })
+/// dict.from_list([#(0, [3, 6]), #(1, [1, 4]), #(2, [2, 5])])
+/// ```
+///
+pub fn group(
+ in iterator: Iterator(element),
+ by key: fn(element) -> key,
+) -> Dict(key, List(element)) {
+ iterator
+ |> fold(dict.new(), group_updater(key))
+ |> dict.map_values(fn(_, group) { list.reverse(group) })
+}
+
+/// This function acts similar to fold, but does not take an initial state.
+/// Instead, it starts from the first yielded element
+/// and combines it with each subsequent element in turn using the given function.
+/// The function is called as `f(accumulator, current_element)`.
+///
+/// Returns `Ok` to indicate a successful run, and `Error` if called on an empty iterator.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([]) |> reduce(fn(acc, x) { acc + x })
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4, 5]) |> reduce(fn(acc, x) { acc + x })
+/// Ok(15)
+/// ```
+///
+pub fn reduce(
+ over iterator: Iterator(e),
+ with f: fn(e, e) -> e,
+) -> Result(e, Nil) {
+ case iterator.continuation() {
+ Stop -> Error(Nil)
+ Continue(e, next) ->
+ do_fold(next, f, e)
+ |> Ok
+ }
+}
+
+/// Returns the last element in the given iterator.
+///
+/// Returns `Error(Nil)` if the iterator is empty.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> last
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > range(1, 10) |> last
+/// Ok(9)
+/// ```
+///
+pub fn last(iterator: Iterator(element)) -> Result(element, Nil) {
+ iterator
+ |> reduce(fn(_, elem) { elem })
+}
+
+/// Creates an iterator that yields no elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> to_list
+/// []
+/// ```
+///
+pub fn empty() -> Iterator(element) {
+ Iterator(stop)
+}
+
+/// Creates an iterator that yields exactly one element provided by calling the given function.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > once(fn() { 1 }) |> to_list
+/// [1]
+/// ```
+///
+pub fn once(f: fn() -> element) -> Iterator(element) {
+ fn() { Continue(f(), stop) }
+ |> Iterator
+}
+
+/// Creates an iterator that yields the given element exactly once.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > single(1) |> to_list
+/// [1]
+/// ```
+///
+pub fn single(elem: element) -> Iterator(element) {
+ once(fn() { elem })
+}
+
+fn do_interleave(
+ current: fn() -> Action(element),
+ next: fn() -> Action(element),
+) -> Action(element) {
+ case current() {
+ Stop -> next()
+ Continue(e, next_other) ->
+ Continue(e, fn() { do_interleave(next, next_other) })
+ }
+}
+
+/// Creates an iterator that alternates between the two given iterators
+/// until both have run out.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4]) |> interleave(from_list([11, 12, 13, 14])) |> to_list
+/// [1, 11, 2, 12, 3, 13, 4, 14]
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4]) |> interleave(from_list([100])) |> to_list
+/// [1, 100, 2, 3, 4]
+/// ```
+///
+pub fn interleave(
+ left: Iterator(element),
+ with right: Iterator(element),
+) -> Iterator(element) {
+ fn() { do_interleave(left.continuation, right.continuation) }
+ |> Iterator
+}
+
+fn do_fold_until(
+ continuation: fn() -> Action(e),
+ f: fn(acc, e) -> list.ContinueOrStop(acc),
+ accumulator: acc,
+) -> acc {
+ case continuation() {
+ Stop -> accumulator
+ Continue(elem, next) ->
+ case f(accumulator, elem) {
+ list.Continue(accumulator) -> do_fold_until(next, f, accumulator)
+ list.Stop(accumulator) -> accumulator
+ }
+ }
+}
+
+/// Like `fold`, `fold_until` reduces an iterator of elements into a single value by calling a given
+/// function on each element in turn, but uses `list.ContinueOrStop` to determine
+/// whether or not to keep iterating.
+///
+/// If called on an iterator of infinite length then this function will only ever
+/// return if the function returns `list.Stop`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/list
+/// > let f = fn(acc, e) {
+/// > case e {
+/// > _ if e < 4 -> list.Continue(e + acc)
+/// > _ -> list.Stop(acc)
+/// > }
+/// > }
+/// >
+/// > [1, 2, 3, 4]
+/// > |> from_list
+/// > |> fold_until(from: acc, with: f)
+/// 6
+/// ```
+///
+pub fn fold_until(
+ over iterator: Iterator(e),
+ from initial: acc,
+ with f: fn(acc, e) -> list.ContinueOrStop(acc),
+) -> acc {
+ iterator.continuation
+ |> do_fold_until(f, initial)
+}
+
+fn do_try_fold(
+ over continuation: fn() -> Action(a),
+ with f: fn(acc, a) -> Result(acc, err),
+ from accumulator: acc,
+) -> Result(acc, err) {
+ case continuation() {
+ Stop -> Ok(accumulator)
+ Continue(elem, next) -> {
+ use accumulator <- result.try(f(accumulator, elem))
+ do_try_fold(next, f, accumulator)
+ }
+ }
+}
+
+/// A variant of fold that might fail.
+///
+/// The folding function should return `Result(accumulator, error)`.
+/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the iterator.
+/// If the returned value is `Error(error)` try_fold will stop and return that error.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4]
+/// > |> iterator.from_list()
+/// > |> try_fold(0, fn(acc, i) {
+/// > case i < 3 {
+/// > True -> Ok(acc + i)
+/// > False -> Error(Nil)
+/// > }
+/// > })
+/// Error(Nil)
+/// ```
+///
+pub fn try_fold(
+ over iterator: Iterator(e),
+ from initial: acc,
+ with f: fn(acc, e) -> Result(acc, err),
+) -> Result(acc, err) {
+ iterator.continuation
+ |> do_try_fold(f, initial)
+}
+
+/// Returns the first element yielded by the given iterator, if it exists,
+/// or `Error(Nil)` otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3]) |> first
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > empty() |> first
+/// Error(Nil)
+/// ```
+pub fn first(from iterator: Iterator(e)) -> Result(e, Nil) {
+ case iterator.continuation() {
+ Stop -> Error(Nil)
+ Continue(e, _) -> Ok(e)
+ }
+}
+
+/// Returns nth element yielded by the given iterator, where `0` means the first element.
+///
+/// If there are not enough elements in the iterator, `Error(Nil)` is returned.
+///
+/// For any `index` less than `0` this function behaves as if it was set to `0`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4]) |> at(2)
+/// Ok(3)
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4]) |> at(4)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > empty() |> at(0)
+/// Error(Nil)
+/// ```
+///
+pub fn at(in iterator: Iterator(e), get index: Int) -> Result(e, Nil) {
+ iterator
+ |> drop(index)
+ |> first
+}
+
+fn do_length(over continuation: fn() -> Action(e), with length: Int) -> Int {
+ case continuation() {
+ Stop -> length
+ Continue(_, next) -> do_length(next, length + 1)
+ }
+}
+
+/// Counts the number of elements in the given iterator.
+///
+/// This function has to traverse the entire iterator to count its elements,
+/// so it runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> length
+/// 0
+/// ```
+///
+/// ```gleam
+/// > from_list([1, 2, 3, 4]) |> length
+/// 4
+/// ```
+///
+pub fn length(over iterator: Iterator(e)) -> Int {
+ iterator.continuation
+ |> do_length(0)
+}
+
+/// Traverse an iterator, calling a function on each element.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > empty() |> each(io.println)
+/// Nil
+/// ```
+///
+/// ```gleam
+/// > from_list(["Tom", "Malory", "Louis"]) |> each(io.println)
+/// // -> Tom
+/// // -> Malory
+/// // -> Louis
+/// Nil
+/// ```
+///
+pub fn each(over iterator: Iterator(a), with f: fn(a) -> b) -> Nil {
+ iterator
+ |> map(f)
+ |> run
+}
+
+/// Add a new element to the start of an iterator.
+///
+/// This function is for use with `use` expressions, to replicate the behaviour
+/// of the `yield` keyword found in other languages.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > use <- iterator.yield(1)
+/// > use <- iterator.yield(2)
+/// > use <- iterator.yield(3)
+/// > iterator.empty()
+/// iterator.from_list([1, 2, 3])
+/// ```
+///
+pub fn yield(element: a, next: fn() -> Iterator(a)) -> Iterator(a) {
+ Iterator(fn() { Continue(element, next().continuation) })
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/list.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/list.gleam
new file mode 100644
index 0000000..a5cffa9
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/list.gleam
@@ -0,0 +1,2154 @@
+//// Lists are an ordered sequence of elements and are one of the most common
+//// data types in Gleam.
+////
+//// New elements can be added and removed from the front of a list in
+//// constant time, while adding and removing from the end requires traversing
+//// the copying the whole list, so keep this in mind when designing your
+//// programs.
+////
+//// There is a dedicated syntax for prefixing to a list:
+////
+//// ```gleam
+//// let new_list = [1, 2, ..existing_list]
+//// ```
+////
+//// And a matching syntax for getting the first elements of a list:
+////
+//// ```gleam
+//// case list {
+//// [first_element, ..rest] -> first_element
+//// _ -> "this pattern matches when the list is empty"
+//// }
+//// ```
+////
+
+import gleam/int
+import gleam/float
+import gleam/order.{type Order}
+import gleam/pair
+import gleam/dict.{type Dict}
+
+/// An error value returned by the `strict_zip` function.
+///
+pub type LengthMismatch {
+ LengthMismatch
+}
+
+/// Counts the number of elements in a given list.
+///
+/// This function has to traverse the list to determine the number of elements,
+/// so it runs in linear time.
+///
+/// This function is natively implemented by the virtual machine and is highly
+/// optimised.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > length([])
+/// 0
+/// ```
+///
+/// ```gleam
+/// > length([1])
+/// 1
+/// ```
+///
+/// ```gleam
+/// > length([1, 2])
+/// 2
+/// ```
+///
+pub fn length(of list: List(a)) -> Int {
+ do_length(list)
+}
+
+@target(erlang)
+@external(erlang, "erlang", "length")
+fn do_length(a: List(a)) -> Int
+
+@target(javascript)
+fn do_length(list: List(a)) -> Int {
+ do_length_acc(list, 0)
+}
+
+@target(javascript)
+fn do_length_acc(list: List(a), count: Int) -> Int {
+ case list {
+ [_, ..list] -> do_length_acc(list, count + 1)
+ _ -> count
+ }
+}
+
+/// Creates a new list from a given list containing the same elements but in the
+/// opposite order.
+///
+/// This function has to traverse the list to create the new reversed list, so
+/// it runs in linear time.
+///
+/// This function is natively implemented by the virtual machine and is highly
+/// optimised.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > reverse([])
+/// []
+/// ```
+///
+/// ```gleam
+/// > reverse([1])
+/// [1]
+/// ```
+///
+/// ```gleam
+/// > reverse([1, 2])
+/// [2, 1]
+/// ```
+///
+pub fn reverse(xs: List(a)) -> List(a) {
+ do_reverse(xs)
+}
+
+@target(erlang)
+@external(erlang, "lists", "reverse")
+fn do_reverse(a: List(a)) -> List(a)
+
+@target(javascript)
+fn do_reverse(list) {
+ do_reverse_acc(list, [])
+}
+
+@target(javascript)
+fn do_reverse_acc(remaining, accumulator) {
+ case remaining {
+ [] -> accumulator
+ [item, ..rest] -> do_reverse_acc(rest, [item, ..accumulator])
+ }
+}
+
+/// Determines whether or not the list is empty.
+///
+/// This function runs in constant time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_empty([])
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_empty([1])
+/// False
+/// ```
+///
+/// ```gleam
+/// > is_empty([1, 1])
+/// False
+/// ```
+///
+pub fn is_empty(list: List(a)) -> Bool {
+ list == []
+}
+
+/// Determines whether or not a given element exists within a given list.
+///
+/// This function traverses the list to find the element, so it runs in linear
+/// time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [] |> contains(any: 0)
+/// False
+/// ```
+///
+/// ```gleam
+/// > [0] |> contains(any: 0)
+/// True
+/// ```
+///
+/// ```gleam
+/// > [1] |> contains(any: 0)
+/// False
+/// ```
+///
+/// ```gleam
+/// > [1, 1] |> contains(any: 0)
+/// False
+/// ```
+///
+/// ```gleam
+/// > [1, 0] |> contains(any: 0)
+/// True
+/// ```
+///
+pub fn contains(list: List(a), any elem: a) -> Bool {
+ case list {
+ [] -> False
+ [first, ..] if first == elem -> True
+ [_, ..rest] -> contains(rest, elem)
+ }
+}
+
+/// Gets the first element from the start of the list, if there is one.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > first([])
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > first([0])
+/// Ok(0)
+/// ```
+///
+/// ```gleam
+/// > first([1, 2])
+/// Ok(1)
+/// ```
+///
+pub fn first(list: List(a)) -> Result(a, Nil) {
+ case list {
+ [] -> Error(Nil)
+ [x, ..] -> Ok(x)
+ }
+}
+
+/// Returns the list minus the first element. If the list is empty, `Error(Nil)` is
+/// returned.
+///
+/// This function runs in constant time and does not make a copy of the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > rest([])
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > rest([0])
+/// Ok([])
+/// ```
+///
+/// ```gleam
+/// > rest([1, 2])
+/// Ok([2])
+/// ```
+///
+pub fn rest(list: List(a)) -> Result(List(a), Nil) {
+ case list {
+ [] -> Error(Nil)
+ [_, ..xs] -> Ok(xs)
+ }
+}
+
+fn update_group(
+ f: fn(element) -> key,
+) -> fn(Dict(key, List(element)), element) -> Dict(key, List(element)) {
+ fn(groups, elem) {
+ case dict.get(groups, f(elem)) {
+ Ok(existing) -> dict.insert(groups, f(elem), [elem, ..existing])
+ Error(_) -> dict.insert(groups, f(elem), [elem])
+ }
+ }
+}
+
+/// Takes a list and groups the values by a key
+/// which is built from a key function.
+///
+/// Does not preserve the initial value order.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [Ok(3), Error("Wrong"), Ok(200), Ok(73)]
+/// |> group(by: fn(i) {
+/// case i {
+/// Ok(_) -> "Successful"
+/// Error(_) -> "Failed"
+/// }
+/// })
+/// |> dict.to_list
+///
+/// [
+/// #("Failed", [Error("Wrong")]),
+/// #("Successful", [Ok(73), Ok(200), Ok(3)])
+/// ]
+///
+/// > group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 })
+/// |> dict.to_list
+/// [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])]
+/// ```
+///
+pub fn group(list: List(v), by key: fn(v) -> k) -> Dict(k, List(v)) {
+ fold(list, dict.new(), update_group(key))
+}
+
+fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let new_acc = case fun(x) {
+ True -> [x, ..acc]
+ False -> acc
+ }
+ do_filter(xs, fun, new_acc)
+ }
+ }
+}
+
+/// Returns a new list containing only the elements from the first list for
+/// which the given functions returns `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > filter([2, 4, 6, 1], fn(x) { x > 2 })
+/// [4, 6]
+/// ```
+///
+/// ```gleam
+/// > filter([2, 4, 6, 1], fn(x) { x > 6 })
+/// []
+/// ```
+///
+pub fn filter(list: List(a), keeping predicate: fn(a) -> Bool) -> List(a) {
+ do_filter(list, predicate, [])
+}
+
+fn do_filter_map(
+ list: List(a),
+ fun: fn(a) -> Result(b, e),
+ acc: List(b),
+) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let new_acc = case fun(x) {
+ Ok(x) -> [x, ..acc]
+ Error(_) -> acc
+ }
+ do_filter_map(xs, fun, new_acc)
+ }
+ }
+}
+
+/// Returns a new list containing only the elements from the first list for
+/// which the given functions returns `Ok(_)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > filter_map([2, 4, 6, 1], Error)
+/// []
+/// ```
+///
+/// ```gleam
+/// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })
+/// [3, 5, 7, 2]
+/// ```
+///
+pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) {
+ do_filter_map(list, fun, [])
+}
+
+fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc])
+ }
+}
+
+/// Returns a new list containing only the elements of the first list after the
+/// function has been applied to each one.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map([2, 4, 6], fn(x) { x * 2 })
+/// [4, 8, 12]
+/// ```
+///
+pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) {
+ do_map(list, fun, [])
+}
+
+/// Combines two lists into a single list using the given function.
+///
+/// If a list is longer than the other the extra elements are dropped.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y })
+/// [5, 7, 9]
+/// ```
+///
+/// ```gleam
+/// > map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) })
+/// [#(1, "a"), #(2, "b")]
+/// ```
+///
+pub fn map2(list1: List(a), list2: List(b), with fun: fn(a, b) -> c) -> List(c) {
+ do_map2(list1, list2, fun, [])
+}
+
+fn do_map2(
+ list1: List(a),
+ list2: List(b),
+ fun: fn(a, b) -> c,
+ acc: List(c),
+) -> List(c) {
+ case list1, list2 {
+ [], _ | _, [] -> reverse(acc)
+ [a, ..as_], [b, ..bs] -> do_map2(as_, bs, fun, [fun(a, b), ..acc])
+ }
+}
+
+/// Similar to `map` but also lets you pass around an accumulated value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map_fold(
+/// over: [1, 2, 3],
+/// from: 100,
+/// with: fn(memo, i) { #(memo + i, i * 2) }
+/// )
+/// #(106, [2, 4, 6])
+/// ```
+///
+pub fn map_fold(
+ over list: List(a),
+ from acc: acc,
+ with fun: fn(acc, a) -> #(acc, b),
+) -> #(acc, List(b)) {
+ fold(
+ over: list,
+ from: #(acc, []),
+ with: fn(acc, item) {
+ let #(current_acc, items) = acc
+ let #(next_acc, next_item) = fun(current_acc, item)
+ #(next_acc, [next_item, ..items])
+ },
+ )
+ |> pair.map_second(reverse)
+}
+
+fn do_index_map(
+ list: List(a),
+ fun: fn(Int, a) -> b,
+ index: Int,
+ acc: List(b),
+) -> List(b) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> {
+ let acc = [fun(index, x), ..acc]
+ do_index_map(xs, fun, index + 1, acc)
+ }
+ }
+}
+
+/// Returns a new list containing only the elements of the first list after the
+/// function has been applied to each one and their index.
+///
+/// The index starts at 0, so the first element is 0, the second is 1, and so
+/// on.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > index_map(["a", "b"], fn(i, x) { #(i, x) })
+/// [#(0, "a"), #(1, "b")]
+/// ```
+///
+pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) {
+ do_index_map(list, fun, 0, [])
+}
+
+fn do_try_map(
+ list: List(a),
+ fun: fn(a) -> Result(b, e),
+ acc: List(b),
+) -> Result(List(b), e) {
+ case list {
+ [] -> Ok(reverse(acc))
+ [x, ..xs] ->
+ case fun(x) {
+ Ok(y) -> do_try_map(xs, fun, [y, ..acc])
+ Error(error) -> Error(error)
+ }
+ }
+}
+
+/// Takes a function that returns a `Result` and applies it to each element in a
+/// given list in turn.
+///
+/// If the function returns `Ok(new_value)` for all elements in the list then a
+/// list of the new values is returned.
+///
+/// If the function returns `Error(reason)` for any of the elements then it is
+/// returned immediately. None of the elements in the list are processed after
+/// one returns an `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > try_map([1, 2, 3], fn(x) { Ok(x + 2) })
+/// Ok([3, 4, 5])
+/// ```
+///
+/// ```gleam
+/// > try_map([1, 2, 3], fn(_) { Error(0) })
+/// Error(0)
+/// ```
+///
+/// ```gleam
+/// > try_map([[1], [2, 3]], first)
+/// Ok([1, 2])
+/// ```
+///
+/// ```gleam
+/// > try_map([[1], [], [2]], first)
+/// Error(Nil)
+/// ```
+///
+pub fn try_map(
+ over list: List(a),
+ with fun: fn(a) -> Result(b, e),
+) -> Result(List(b), e) {
+ do_try_map(list, fun, [])
+}
+
+/// Returns a list that is the given list with up to the given number of
+/// elements removed from the front of the list.
+///
+/// If the element has less than the number of elements an empty list is
+/// returned.
+///
+/// This function runs in linear time but does not copy the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > drop([1, 2, 3, 4], 2)
+/// [3, 4]
+/// ```
+///
+/// ```gleam
+/// > drop([1, 2, 3, 4], 9)
+/// []
+/// ```
+///
+pub fn drop(from list: List(a), up_to n: Int) -> List(a) {
+ case n <= 0 {
+ True -> list
+ False ->
+ case list {
+ [] -> []
+ [_, ..xs] -> drop(xs, n - 1)
+ }
+ }
+}
+
+fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) {
+ case n <= 0 {
+ True -> reverse(acc)
+ False ->
+ case list {
+ [] -> reverse(acc)
+ [x, ..xs] -> do_take(xs, n - 1, [x, ..acc])
+ }
+ }
+}
+
+/// Returns a list containing the first given number of elements from the given
+/// list.
+///
+/// If the element has less than the number of elements then the full list is
+/// returned.
+///
+/// This function runs in linear time but does not copy the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > take([1, 2, 3, 4], 2)
+/// [1, 2]
+/// ```
+///
+/// ```gleam
+/// > take([1, 2, 3, 4], 9)
+/// [1, 2, 3, 4]
+/// ```
+///
+pub fn take(from list: List(a), up_to n: Int) -> List(a) {
+ do_take(list, n, [])
+}
+
+/// Returns a new empty list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new()
+/// []
+/// ```
+///
+pub fn new() -> List(a) {
+ []
+}
+
+/// Joins one list onto the end of another.
+///
+/// This function runs in linear time, and it traverses and copies the first
+/// list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > append([1, 2], [3])
+/// [1, 2, 3]
+/// ```
+///
+pub fn append(first: List(a), second: List(a)) -> List(a) {
+ do_append(first, second)
+}
+
+@target(erlang)
+@external(erlang, "lists", "append")
+fn do_append(a: List(a), b: List(a)) -> List(a)
+
+@target(javascript)
+fn do_append(first: List(a), second: List(a)) -> List(a) {
+ do_append_acc(reverse(first), second)
+}
+
+@target(javascript)
+fn do_append_acc(first: List(a), second: List(a)) -> List(a) {
+ case first {
+ [] -> second
+ [item, ..rest] -> do_append_acc(rest, [item, ..second])
+ }
+}
+
+/// Prefixes an item to a list. This can also be done using the dedicated
+/// syntax instead
+///
+/// ```gleam
+/// let new_list = [1, ..existing_list]
+/// ```
+///
+pub fn prepend(to list: List(a), this item: a) -> List(a) {
+ [item, ..list]
+}
+
+// Reverses a list and prepends it to another list
+fn reverse_and_prepend(list prefix: List(a), to suffix: List(a)) -> List(a) {
+ case prefix {
+ [] -> suffix
+ [first, ..rest] -> reverse_and_prepend(list: rest, to: [first, ..suffix])
+ }
+}
+
+fn do_concat(lists: List(List(a)), acc: List(a)) -> List(a) {
+ case lists {
+ [] -> reverse(acc)
+ [list, ..further_lists] ->
+ do_concat(further_lists, reverse_and_prepend(list: list, to: acc))
+ }
+}
+
+/// Joins a list of lists into a single list.
+///
+/// This function traverses all elements twice.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > concat([[1], [2, 3], []])
+/// [1, 2, 3]
+/// ```
+///
+pub fn concat(lists: List(List(a))) -> List(a) {
+ do_concat(lists, [])
+}
+
+/// This is the same as `concat`: it joins a list of lists into a single
+/// list.
+///
+/// This function traverses all elements twice.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > flatten([[1], [2, 3], []])
+/// [1, 2, 3]
+/// ```
+///
+pub fn flatten(lists: List(List(a))) -> List(a) {
+ do_concat(lists, [])
+}
+
+/// Maps the list with the given function into a list of lists, and then flattens it.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > flat_map([2, 4, 6], fn(x) { [x, x + 1] })
+/// [2, 3, 4, 5, 6, 7]
+/// ```
+///
+pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) {
+ map(list, fun)
+ |> concat
+}
+
+/// Reduces a list of elements into a single value by calling a given function
+/// on each element, going from left to right.
+///
+/// `fold([1, 2, 3], 0, add)` is the equivalent of
+/// `add(add(add(0, 1), 2), 3)`.
+///
+/// This function runs in linear time.
+///
+pub fn fold(
+ over list: List(a),
+ from initial: acc,
+ with fun: fn(acc, a) -> acc,
+) -> acc {
+ case list {
+ [] -> initial
+ [x, ..rest] -> fold(rest, fun(initial, x), fun)
+ }
+}
+
+/// Reduces a list of elements into a single value by calling a given function
+/// on each element, going from right to left.
+///
+/// `fold_right([1, 2, 3], 0, add)` is the equivalent of
+/// `add(add(add(0, 3), 2), 1)`.
+///
+/// This function runs in linear time.
+///
+/// Unlike `fold` this function is not tail recursive. Where possible use
+/// `fold` instead as it will use less memory.
+///
+pub fn fold_right(
+ over list: List(a),
+ from initial: acc,
+ with fun: fn(acc, a) -> acc,
+) -> acc {
+ case list {
+ [] -> initial
+ [x, ..rest] -> fun(fold_right(rest, initial, fun), x)
+ }
+}
+
+fn do_index_fold(
+ over: List(a),
+ acc: acc,
+ with: fn(acc, a, Int) -> acc,
+ index: Int,
+) -> acc {
+ case over {
+ [] -> acc
+ [first, ..rest] ->
+ do_index_fold(rest, with(acc, first, index), with, index + 1)
+ }
+}
+
+/// Like fold but the folding function also receives the index of the current element.
+///
+/// ## Examples
+///
+/// ```gleam
+/// ["a", "b", "c"]
+/// |> index_fold([], fn(acc, item, index) { ... })
+/// ```
+///
+pub fn index_fold(
+ over over: List(a),
+ from initial: acc,
+ with fun: fn(acc, a, Int) -> acc,
+) -> acc {
+ do_index_fold(over, initial, fun, 0)
+}
+
+/// A variant of fold that might fail.
+///
+/// The folding function should return `Result(accumulator, error)`.
+/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.
+/// If the returned value is `Error(error)` try_fold will stop and return that error.
+///
+/// ## Examples
+///
+/// ```gleam
+/// [1, 2, 3, 4]
+/// |> try_fold(0, fn(acc, i) {
+/// case i < 3 {
+/// True -> Ok(acc + i)
+/// False -> Error(Nil)
+/// }
+/// })
+/// ```
+///
+pub fn try_fold(
+ over collection: List(a),
+ from accumulator: acc,
+ with fun: fn(acc, a) -> Result(acc, e),
+) -> Result(acc, e) {
+ case collection {
+ [] -> Ok(accumulator)
+ [first, ..rest] ->
+ case fun(accumulator, first) {
+ Ok(result) -> try_fold(rest, result, fun)
+ Error(_) as error -> error
+ }
+ }
+}
+
+pub type ContinueOrStop(a) {
+ Continue(a)
+ Stop(a)
+}
+
+/// A variant of fold that allows to stop folding earlier.
+///
+/// The folding function should return `ContinueOrStop(accumulator)`.
+/// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.
+/// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.
+///
+/// ## Examples
+///
+/// ```gleam
+/// [1, 2, 3, 4]
+/// |> fold_until(0, fn(acc, i) {
+/// case i < 3 {
+/// True -> Continue(acc + i)
+/// False -> Stop(acc)
+/// }
+/// })
+/// ```
+///
+pub fn fold_until(
+ over collection: List(a),
+ from accumulator: acc,
+ with fun: fn(acc, a) -> ContinueOrStop(acc),
+) -> acc {
+ case collection {
+ [] -> accumulator
+ [first, ..rest] ->
+ case fun(accumulator, first) {
+ Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun)
+ Stop(b) -> b
+ }
+ }
+}
+
+/// Finds the first element in a given list for which the given function returns
+/// `True`.
+///
+/// Returns `Error(Nil)` if no such element is found.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > find([1, 2, 3], fn(x) { x > 2 })
+/// Ok(3)
+/// ```
+///
+/// ```gleam
+/// > find([1, 2, 3], fn(x) { x > 4 })
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > find([], fn(_) { True })
+/// Error(Nil)
+/// ```
+///
+pub fn find(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(a, Nil) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case is_desired(x) {
+ True -> Ok(x)
+ _ -> find(in: rest, one_that: is_desired)
+ }
+ }
+}
+
+/// Finds the first element in a given list for which the given function returns
+/// `Ok(new_value)`, then returns the wrapped `new_value`.
+///
+/// Returns `Error(Nil)` if no such element is found.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > find_map([[], [2], [3]], first)
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > find_map([[], []], first)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > find_map([], first)
+/// Error(Nil)
+/// ```
+///
+pub fn find_map(
+ in haystack: List(a),
+ with fun: fn(a) -> Result(b, c),
+) -> Result(b, Nil) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case fun(x) {
+ Ok(x) -> Ok(x)
+ _ -> find_map(in: rest, with: fun)
+ }
+ }
+}
+
+/// Returns `True` if the given function returns `True` for all the elements in
+/// the given list. If the function returns `False` for any of the elements it
+/// immediately returns `False` without checking the rest of the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > all([], fn(x) { x > 3 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > all([4, 5], fn(x) { x > 3 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > all([4, 3], fn(x) { x > 3 })
+/// False
+/// ```
+///
+pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
+ case list {
+ [] -> True
+ [first, ..rest] ->
+ case predicate(first) {
+ True -> all(rest, predicate)
+ False -> False
+ }
+ }
+}
+
+/// Returns `True` if the given function returns `True` for any the elements in
+/// the given list. If the function returns `True` for any of the elements it
+/// immediately returns `True` without checking the rest of the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > any([], fn(x) { x > 3 })
+/// False
+/// ```
+///
+/// ```gleam
+/// > any([4, 5], fn(x) { x > 3 })
+/// True
+/// ```
+///
+/// ```gleam
+/// > any([4, 3], fn(x) { x > 4 })
+/// False
+/// ```
+///
+/// ```gleam
+/// > any([3, 4], fn(x) { x > 3 })
+/// True
+/// ```
+///
+pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool {
+ case list {
+ [] -> False
+ [first, ..rest] ->
+ case predicate(first) {
+ True -> True
+ False -> any(rest, predicate)
+ }
+ }
+}
+
+fn do_zip(xs: List(a), ys: List(b), acc: List(#(a, b))) -> List(#(a, b)) {
+ case xs, ys {
+ [x, ..xs], [y, ..ys] -> do_zip(xs, ys, [#(x, y), ..acc])
+ _, _ -> reverse(acc)
+ }
+}
+
+/// Takes two lists and returns a single list of 2-element tuples.
+///
+/// If one of the lists is longer than the other, the remaining elements from
+/// the longer list are not used.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > zip([], [])
+/// []
+/// ```
+///
+/// ```gleam
+/// > zip([1, 2], [3])
+/// [#(1, 3)]
+/// ```
+///
+/// ```gleam
+/// > zip([1], [3, 4])
+/// [#(1, 3)]
+/// ```
+///
+/// ```gleam
+/// > zip([1, 2], [3, 4])
+/// [#(1, 3), #(2, 4)]
+/// ```
+///
+pub fn zip(list: List(a), with other: List(b)) -> List(#(a, b)) {
+ do_zip(list, other, [])
+}
+
+/// Takes two lists and returns a single list of 2-element tuples.
+///
+/// If one of the lists is longer than the other, an `Error` is returned.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > strict_zip([], [])
+/// Ok([])
+/// ```
+///
+/// ```gleam
+/// > strict_zip([1, 2], [3])
+/// Error(LengthMismatch)
+/// ```
+///
+/// ```gleam
+/// > strict_zip([1], [3, 4])
+/// Error(LengthMismatch)
+/// ```
+///
+/// ```gleam
+/// > strict_zip([1, 2], [3, 4])
+/// Ok([#(1, 3), #(2, 4)])
+/// ```
+///
+pub fn strict_zip(
+ list: List(a),
+ with other: List(b),
+) -> Result(List(#(a, b)), LengthMismatch) {
+ case length(of: list) == length(of: other) {
+ True -> Ok(zip(list, other))
+ False -> Error(LengthMismatch)
+ }
+}
+
+fn do_unzip(input, xs, ys) {
+ case input {
+ [] -> #(reverse(xs), reverse(ys))
+ [#(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys])
+ }
+}
+
+/// Takes a single list of 2-element tuples and returns two lists.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unzip([#(1, 2), #(3, 4)])
+/// #([1, 3], [2, 4])
+/// ```
+///
+/// ```gleam
+/// > unzip([])
+/// #([], [])
+/// ```
+///
+pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) {
+ do_unzip(input, [], [])
+}
+
+fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc])
+ }
+}
+
+/// Inserts a given value between each existing element in a given list.
+///
+/// This function runs in linear time and copies the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > intersperse([1, 1, 1], 2)
+/// [1, 2, 1, 2, 1]
+/// ```
+///
+/// ```gleam
+/// > intersperse([], 2)
+/// []
+/// ```
+///
+pub fn intersperse(list: List(a), with elem: a) -> List(a) {
+ case list {
+ [] | [_] -> list
+ [x, ..rest] -> do_intersperse(rest, elem, [x])
+ }
+}
+
+/// Returns the element in the Nth position in the list, with 0 being the first
+/// position.
+///
+/// `Error(Nil)` is returned if the list is not long enough for the given index
+/// or if the index is less than 0.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > at([1, 2, 3], 1)
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > at([1, 2, 3], 5)
+/// Error(Nil)
+/// ```
+///
+pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) {
+ case index >= 0 {
+ True ->
+ list
+ |> drop(index)
+ |> first
+ False -> Error(Nil)
+ }
+}
+
+/// Removes any duplicate elements from a given list.
+///
+/// This function returns in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unique([1, 1, 1, 4, 7, 3, 3, 4])
+/// [1, 4, 7, 3]
+/// ```
+///
+pub fn unique(list: List(a)) -> List(a) {
+ case list {
+ [] -> []
+ [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))]
+ }
+}
+
+/// Merge lists `a` and `b` in ascending order
+/// but only up to `na` and `nb` number of items respectively.
+///
+fn merge_up(
+ na: Int,
+ nb: Int,
+ a: List(a),
+ b: List(a),
+ acc: List(a),
+ compare: fn(a, a) -> Order,
+) {
+ case na, nb, a, b {
+ 0, 0, _, _ -> acc
+ _, 0, [ax, ..ar], _ -> merge_up(na - 1, nb, ar, b, [ax, ..acc], compare)
+ 0, _, _, [bx, ..br] -> merge_up(na, nb - 1, a, br, [bx, ..acc], compare)
+ _, _, [ax, ..ar], [bx, ..br] ->
+ case compare(ax, bx) {
+ order.Gt -> merge_up(na, nb - 1, a, br, [bx, ..acc], compare)
+ _ -> merge_up(na - 1, nb, ar, b, [ax, ..acc], compare)
+ }
+ _, _, _, _ -> acc
+ }
+}
+
+/// Merge lists `a` and `b` in descending order
+/// but only up to `na` and `nb` number of items respectively.
+///
+fn merge_down(
+ na: Int,
+ nb: Int,
+ a: List(a),
+ b: List(a),
+ acc: List(a),
+ compare: fn(a, a) -> Order,
+) {
+ case na, nb, a, b {
+ 0, 0, _, _ -> acc
+ _, 0, [ax, ..ar], _ -> merge_down(na - 1, nb, ar, b, [ax, ..acc], compare)
+ 0, _, _, [bx, ..br] -> merge_down(na, nb - 1, a, br, [bx, ..acc], compare)
+ _, _, [ax, ..ar], [bx, ..br] ->
+ case compare(bx, ax) {
+ order.Lt -> merge_down(na - 1, nb, ar, b, [ax, ..acc], compare)
+ _ -> merge_down(na, nb - 1, a, br, [bx, ..acc], compare)
+ }
+ _, _, _, _ -> acc
+ }
+}
+
+/// Merge sort that alternates merging in ascending and descending order
+/// because the merge process also reverses the list.
+///
+/// Some copying is avoided by merging only a subset of the lists
+/// instead of creating and merging new smaller lists.
+///
+fn merge_sort(
+ l: List(a),
+ ln: Int,
+ compare: fn(a, a) -> Order,
+ down: Bool,
+) -> List(a) {
+ let n = ln / 2
+ let a = l
+ let b = drop(l, n)
+ case ln < 3 {
+ True ->
+ case down {
+ True -> merge_down(n, ln - n, a, b, [], compare)
+ False -> merge_up(n, ln - n, a, b, [], compare)
+ }
+ False ->
+ case down {
+ True ->
+ merge_down(
+ n,
+ ln - n,
+ merge_sort(a, n, compare, False),
+ merge_sort(b, ln - n, compare, False),
+ [],
+ compare,
+ )
+ False ->
+ merge_up(
+ n,
+ ln - n,
+ merge_sort(a, n, compare, True),
+ merge_sort(b, ln - n, compare, True),
+ [],
+ compare,
+ )
+ }
+ }
+}
+
+/// Sorts from smallest to largest based upon the ordering specified by a given
+/// function.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/int
+/// > list.sort([4, 3, 6, 5, 4, 1, 2], by: int.compare)
+/// [1, 2, 3, 4, 4, 5, 6]
+/// ```
+///
+pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) {
+ merge_sort(list, length(list), compare, True)
+}
+
+/// Creates a list of ints ranging from a given start and finish.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > range(0, 0)
+/// [0]
+/// ```
+///
+/// ```gleam
+/// > range(0, 5)
+/// [0, 1, 2, 3, 4, 5]
+/// ```
+///
+/// ```gleam
+/// > range(1, -5)
+/// [1, 0, -1, -2, -3, -4, -5]
+/// ```
+///
+pub fn range(from start: Int, to stop: Int) -> List(Int) {
+ tail_recursive_range(start, stop, [])
+}
+
+fn tail_recursive_range(start: Int, stop: Int, acc: List(Int)) -> List(Int) {
+ case int.compare(start, stop) {
+ order.Eq -> [stop, ..acc]
+ order.Gt -> tail_recursive_range(start, stop + 1, [stop, ..acc])
+ order.Lt -> tail_recursive_range(start, stop - 1, [stop, ..acc])
+ }
+}
+
+fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) {
+ case times <= 0 {
+ True -> acc
+ False -> do_repeat(a, times - 1, [a, ..acc])
+ }
+}
+
+/// Builds a list of a given value a given number of times.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > repeat("a", times: 0)
+/// []
+/// ```
+///
+/// ```gleam
+/// > repeat("a", times: 5)
+/// ["a", "a", "a", "a", "a"]
+/// ```
+///
+pub fn repeat(item a: a, times times: Int) -> List(a) {
+ do_repeat(a, times, [])
+}
+
+fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) {
+ case n <= 0 {
+ True -> #(reverse(taken), list)
+ False ->
+ case list {
+ [] -> #(reverse(taken), [])
+ [x, ..xs] -> do_split(xs, n - 1, [x, ..taken])
+ }
+ }
+}
+
+/// Splits a list in two before the given index.
+///
+/// If the list is not long enough to have the given index the before list will
+/// be the input list, and the after list will be empty.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > split([6, 7, 8, 9], 0)
+/// #([], [6, 7, 8, 9])
+/// ```
+///
+/// ```gleam
+/// > split([6, 7, 8, 9], 2)
+/// #([6, 7], [8, 9])
+/// ```
+///
+/// ```gleam
+/// > split([6, 7, 8, 9], 4)
+/// #([6, 7, 8, 9], [])
+/// ```
+///
+pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) {
+ do_split(list, index, [])
+}
+
+fn do_split_while(
+ list: List(a),
+ f: fn(a) -> Bool,
+ acc: List(a),
+) -> #(List(a), List(a)) {
+ case list {
+ [] -> #(reverse(acc), [])
+ [x, ..xs] ->
+ case f(x) {
+ False -> #(reverse(acc), list)
+ _ -> do_split_while(xs, f, [x, ..acc])
+ }
+ }
+}
+
+/// Splits a list in two before the first element that a given function returns
+/// `False` for.
+///
+/// If the function returns `True` for all elements the first list will be the
+/// input list, and the second list will be empty.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })
+/// #([1, 2, 3], [4, 5])
+/// ```
+///
+/// ```gleam
+/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })
+/// #([1, 2, 3, 4, 5], [])
+/// ```
+///
+pub fn split_while(
+ list list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> #(List(a), List(a)) {
+ do_split_while(list, predicate, [])
+}
+
+/// Given a list of 2-element tuples, finds the first tuple that has a given
+/// key as the first element and returns the second element.
+///
+/// If no tuple is found with the given key then `Error(Nil)` is returned.
+///
+/// This function may be useful for interacting with Erlang code where lists of
+/// tuples are common.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > key_find([#("a", 0), #("b", 1)], "a")
+/// Ok(0)
+/// ```
+///
+/// ```gleam
+/// > key_find([#("a", 0), #("b", 1)], "b")
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > key_find([#("a", 0), #("b", 1)], "c")
+/// Error(Nil)
+/// ```
+///
+pub fn key_find(
+ in keyword_list: List(#(k, v)),
+ find desired_key: k,
+) -> Result(v, Nil) {
+ find_map(
+ keyword_list,
+ fn(keyword) {
+ let #(key, value) = keyword
+ case key == desired_key {
+ True -> Ok(value)
+ False -> Error(Nil)
+ }
+ },
+ )
+}
+
+/// Given a list of 2-element tuples, finds all tuples that have a given
+/// key as the first element and returns the second element.
+///
+/// This function may be useful for interacting with Erlang code where lists of
+/// tuples are common.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > key_filter([#("a", 0), #("b", 1), #("a", 2)], "a")
+/// [0, 2]
+/// ```
+///
+/// ```gleam
+/// > key_filter([#("a", 0), #("b", 1)], "c")
+/// []
+/// ```
+///
+pub fn key_filter(
+ in keyword_list: List(#(k, v)),
+ find desired_key: k,
+) -> List(v) {
+ filter_map(
+ keyword_list,
+ fn(keyword) {
+ let #(key, value) = keyword
+ case key == desired_key {
+ True -> Ok(value)
+ False -> Error(Nil)
+ }
+ },
+ )
+}
+
+fn do_pop(haystack, predicate, checked) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case predicate(x) {
+ True -> Ok(#(x, append(reverse(checked), rest)))
+ False -> do_pop(rest, predicate, [x, ..checked])
+ }
+ }
+}
+
+/// Removes the first element in a given list for which the predicate function returns `True`.
+///
+/// Returns `Error(Nil)` if no such element is found.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > pop([1, 2, 3], fn(x) { x > 2 })
+/// Ok(#(3, [1, 2]))
+/// ```
+///
+/// ```gleam
+/// > pop([1, 2, 3], fn(x) { x > 4 })
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > pop([], fn(_) { True })
+/// Error(Nil)
+/// ```
+///
+pub fn pop(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Bool,
+) -> Result(#(a, List(a)), Nil) {
+ do_pop(haystack, is_desired, [])
+}
+
+fn do_pop_map(haystack, mapper, checked) {
+ case haystack {
+ [] -> Error(Nil)
+ [x, ..rest] ->
+ case mapper(x) {
+ Ok(y) -> Ok(#(y, append(reverse(checked), rest)))
+ Error(_) -> do_pop_map(rest, mapper, [x, ..checked])
+ }
+ }
+}
+
+/// Removes the first element in a given list for which the given function returns
+/// `Ok(new_value)`, then returns the wrapped `new_value` as well as list with the value removed.
+///
+/// Returns `Error(Nil)` if no such element is found.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > pop_map([[], [2], [3]], first)
+/// Ok(#(2, [[], [3]]))
+/// ```
+///
+/// ```gleam
+/// > pop_map([[], []], first)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > pop_map([], first)
+/// Error(Nil)
+/// ```
+///
+pub fn pop_map(
+ in haystack: List(a),
+ one_that is_desired: fn(a) -> Result(b, c),
+) -> Result(#(b, List(a)), Nil) {
+ do_pop_map(haystack, is_desired, [])
+}
+
+/// Given a list of 2-element tuples, finds the first tuple that has a given
+/// key as the first element. This function will return the second element
+/// of the found tuple and list with tuple removed.
+///
+/// If no tuple is found with the given key then `Error(Nil)` is returned.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > key_pop([#("a", 0), #("b", 1)], "a")
+/// Ok(#(0, [#("b", 1)]))
+/// ```
+///
+/// ```gleam
+/// > key_pop([#("a", 0), #("b", 1)], "b")
+/// Ok(#(1, [#("a", 0)]))
+/// ```
+///
+/// ```gleam
+/// > key_pop([#("a", 0), #("b", 1)], "c")
+/// Error(Nil)
+/// ```
+///
+pub fn key_pop(
+ haystack: List(#(k, v)),
+ key: k,
+) -> Result(#(v, List(#(k, v))), Nil) {
+ pop_map(
+ haystack,
+ fn(entry) {
+ let #(k, v) = entry
+ case k {
+ k if k == key -> Ok(v)
+ _ -> Error(Nil)
+ }
+ },
+ )
+}
+
+/// Given a list of 2-element tuples, inserts a key and value into the list.
+///
+/// If there was already a tuple with the key then it is replaced, otherwise it
+/// is added to the end of the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > key_set([#(5, 0), #(4, 1)], 4, 100)
+/// [#(5, 0), #(4, 100)]
+/// ```
+///
+/// ```gleam
+/// > key_set([#(5, 0), #(4, 1)], 1, 100)
+/// [#(5, 0), #(4, 1), #(1, 100)]
+/// ```
+///
+pub fn key_set(list: List(#(a, b)), key: a, value: b) -> List(#(a, b)) {
+ case list {
+ [] -> [#(key, value)]
+ [#(k, _), ..rest] if k == key -> [#(key, value), ..rest]
+ [first, ..rest] -> [first, ..key_set(rest, key, value)]
+ }
+}
+
+/// Calls a function for each element in a list, discarding the return value.
+///
+/// Useful for calling a side effect for every item of a list.
+///
+/// ```gleam
+/// > list.each([1, 2, 3], io.println)
+/// Nil
+/// ```
+///
+pub fn each(list: List(a), f: fn(a) -> b) -> Nil {
+ case list {
+ [] -> Nil
+ [x, ..xs] -> {
+ f(x)
+ each(xs, f)
+ }
+ }
+}
+
+/// Calls a `Result` returning function for each element in a list, discarding
+/// the return value. If the function returns `Error` then the iteration is
+/// stopped and the error is returned.
+///
+/// Useful for calling a side effect for every item of a list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > try_each(
+/// > over: [1, 2, 3],
+/// > with: function_that_might_fail,
+/// > )
+/// Ok(Nil)
+/// ```
+///
+pub fn try_each(
+ over list: List(a),
+ with fun: fn(a) -> Result(b, e),
+) -> Result(Nil, e) {
+ case list {
+ [] -> Ok(Nil)
+ [x, ..xs] ->
+ case fun(x) {
+ Ok(_) -> try_each(over: xs, with: fun)
+ Error(e) -> Error(e)
+ }
+ }
+}
+
+fn do_partition(list, categorise, trues, falses) {
+ case list {
+ [] -> #(reverse(trues), reverse(falses))
+ [x, ..xs] ->
+ case categorise(x) {
+ True -> do_partition(xs, categorise, [x, ..trues], falses)
+ False -> do_partition(xs, categorise, trues, [x, ..falses])
+ }
+ }
+}
+
+/// Partitions a list into a tuple/pair of lists
+/// by a given categorisation function.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5] |> list.partition(int.is_odd)
+/// #([1, 3, 5], [2, 4])
+/// ```
+///
+pub fn partition(
+ list: List(a),
+ with categorise: fn(a) -> Bool,
+) -> #(List(a), List(a)) {
+ do_partition(list, categorise, [], [])
+}
+
+/// Returns all the permutations of a list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > permutations([1, 2])
+/// [[1, 2], [2, 1]]
+/// ```
+///
+pub fn permutations(l: List(a)) -> List(List(a)) {
+ case l {
+ [] -> [[]]
+ _ ->
+ l
+ |> index_map(fn(i_idx, i) {
+ l
+ |> index_fold(
+ [],
+ fn(acc, j, j_idx) {
+ case i_idx == j_idx {
+ True -> acc
+ False -> [j, ..acc]
+ }
+ },
+ )
+ |> reverse
+ |> permutations
+ |> map(fn(permutation) { [i, ..permutation] })
+ })
+ |> concat
+ }
+}
+
+fn do_window(acc: List(List(a)), l: List(a), n: Int) -> List(List(a)) {
+ let window = take(l, n)
+
+ case length(window) == n {
+ True -> do_window([window, ..acc], drop(l, 1), n)
+ False -> acc
+ }
+}
+
+/// Returns a list of sliding windows.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > window([1,2,3,4,5], 3)
+/// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
+/// ```
+///
+/// ```gleam
+/// > window([1, 2], 4)
+/// []
+/// ```
+///
+pub fn window(l: List(a), by n: Int) -> List(List(a)) {
+ do_window([], l, n)
+ |> reverse
+}
+
+/// Returns a list of tuples containing two contiguous elements.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > window_by_2([1,2,3,4])
+/// [#(1, 2), #(2, 3), #(3, 4)]
+/// ```
+///
+/// ```gleam
+/// > window_by_2([1])
+/// []
+/// ```
+///
+pub fn window_by_2(l: List(a)) -> List(#(a, a)) {
+ zip(l, drop(l, 1))
+}
+
+/// Drops the first elements in a given list for which the predicate function returns `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > drop_while([1, 2, 3, 4], fn (x) { x < 3 })
+/// [3, 4]
+/// ```
+///
+pub fn drop_while(
+ in list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> List(a) {
+ case list {
+ [] -> []
+ [x, ..xs] ->
+ case predicate(x) {
+ True -> drop_while(xs, predicate)
+ False -> [x, ..xs]
+ }
+ }
+}
+
+fn do_take_while(
+ list: List(a),
+ predicate: fn(a) -> Bool,
+ acc: List(a),
+) -> List(a) {
+ case list {
+ [] -> reverse(acc)
+ [first, ..rest] ->
+ case predicate(first) {
+ True -> do_take_while(rest, predicate, [first, ..acc])
+ False -> reverse(acc)
+ }
+ }
+}
+
+/// Takes the first elements in a given list for which the predicate function returns `True`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > take_while([1, 2, 3, 2, 4], fn (x) { x < 3 })
+/// [1, 2]
+/// ```
+///
+pub fn take_while(
+ in list: List(a),
+ satisfying predicate: fn(a) -> Bool,
+) -> List(a) {
+ do_take_while(list, predicate, [])
+}
+
+fn do_chunk(
+ list: List(a),
+ f: fn(a) -> key,
+ previous_key: key,
+ current_chunk: List(a),
+ acc: List(List(a)),
+) -> List(List(a)) {
+ case list {
+ [first, ..rest] -> {
+ let key = f(first)
+ case key == previous_key {
+ False -> {
+ let new_acc = [reverse(current_chunk), ..acc]
+ do_chunk(rest, f, key, [first], new_acc)
+ }
+ _true -> do_chunk(rest, f, key, [first, ..current_chunk], acc)
+ }
+ }
+ _empty -> reverse([reverse(current_chunk), ..acc])
+ }
+}
+
+/// Returns a list of chunks in which
+/// the return value of calling `f` on each element is the same.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })
+/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
+/// ```
+///
+pub fn chunk(in list: List(a), by f: fn(a) -> key) -> List(List(a)) {
+ case list {
+ [] -> []
+ [first, ..rest] -> do_chunk(rest, f, f(first), [first], [])
+ }
+}
+
+fn do_sized_chunk(
+ list: List(a),
+ count: Int,
+ left: Int,
+ current_chunk: List(a),
+ acc: List(List(a)),
+) -> List(List(a)) {
+ case list {
+ [] ->
+ case current_chunk {
+ [] -> reverse(acc)
+ remaining -> reverse([reverse(remaining), ..acc])
+ }
+ [first, ..rest] -> {
+ let chunk = [first, ..current_chunk]
+ case left > 1 {
+ False -> do_sized_chunk(rest, count, count, [], [reverse(chunk), ..acc])
+ True -> do_sized_chunk(rest, count, left - 1, chunk, acc)
+ }
+ }
+ }
+}
+
+/// Returns a list of chunks containing `count` elements each.
+///
+/// If the last chunk does not have `count` elements, it is instead
+/// a partial chunk, with less than `count` elements.
+///
+/// For any `count` less than 1 this function behaves as if it was set to 1.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2)
+/// [[1, 2], [3, 4], [5, 6]]
+/// ```
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3)
+/// [[1, 2, 3], [4, 5, 6], [7, 8]]
+/// ```
+///
+pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) {
+ do_sized_chunk(list, count, count, [], [])
+}
+
+/// This function acts similar to fold, but does not take an initial state.
+/// Instead, it starts from the first element in the list
+/// and combines it with each subsequent element in turn using the given
+/// function. The function is called as `fun(accumulator, current_element)`.
+///
+/// Returns `Ok` to indicate a successful run, and `Error` if called on an
+/// empty list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [] |> reduce(fn(acc, x) { acc + x })
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x })
+/// Ok(15)
+/// ```
+///
+pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) {
+ case list {
+ [] -> Error(Nil)
+ [first, ..rest] -> Ok(fold(rest, first, fun))
+ }
+}
+
+fn do_scan(
+ list: List(a),
+ accumulator: acc,
+ accumulated: List(acc),
+ fun: fn(acc, a) -> acc,
+) -> List(acc) {
+ case list {
+ [] -> reverse(accumulated)
+ [x, ..xs] -> {
+ let next = fun(accumulator, x)
+ do_scan(xs, next, [next, ..accumulated], fun)
+ }
+ }
+}
+
+/// Similar to `fold`, but yields the state of the accumulator at each stage.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i })
+/// [101, 103, 106]
+/// ```
+///
+pub fn scan(
+ over list: List(a),
+ from initial: acc,
+ with fun: fn(acc, a) -> acc,
+) -> List(acc) {
+ do_scan(list, initial, [], fun)
+}
+
+/// Returns the last element in the given list.
+///
+/// Returns `Error(Nil)` if the list is empty.
+///
+/// This function runs in linear time.
+/// For a collection oriented around performant access at either end,
+/// see `gleam/queue.Queue`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > last([])
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > last([1, 2, 3, 4, 5])
+/// Ok(5)
+/// ```
+///
+pub fn last(list: List(a)) -> Result(a, Nil) {
+ list
+ |> reduce(fn(_, elem) { elem })
+}
+
+/// Return unique combinations of elements in the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > combinations([1, 2, 3], 2)
+/// [[1, 2], [1, 3], [2, 3]]
+/// ```
+///
+/// ```gleam
+/// > combinations([1, 2, 3, 4], 3)
+/// [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
+/// ```
+///
+pub fn combinations(items: List(a), by n: Int) -> List(List(a)) {
+ case n {
+ 0 -> [[]]
+ _ ->
+ case items {
+ [] -> []
+ [x, ..xs] -> {
+ let first_combinations =
+ map(combinations(xs, n - 1), with: fn(com) { [x, ..com] })
+ |> reverse
+ fold(
+ first_combinations,
+ combinations(xs, n),
+ fn(acc, c) { [c, ..acc] },
+ )
+ }
+ }
+ }
+}
+
+fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) {
+ case items {
+ [] -> []
+ [x, ..xs] -> {
+ let first_combinations = map(xs, with: fn(other) { #(x, other) })
+ [first_combinations, ..do_combination_pairs(xs)]
+ }
+ }
+}
+
+/// Return unique pair combinations of elements in the list
+///
+/// ## Examples
+///
+/// ```gleam
+/// > combination_pairs([1, 2, 3])
+/// [#(1, 2), #(1, 3), #(2, 3)]
+/// ```
+///
+pub fn combination_pairs(items: List(a)) -> List(#(a, a)) {
+ do_combination_pairs(items)
+ |> concat
+}
+
+/// Make a list alternating the elements from the given lists
+///
+/// ## Examples
+///
+/// ```gleam
+/// > list.interleave([[1, 2], [101, 102], [201, 202]])
+/// [1, 101, 201, 2, 102, 202]
+/// ```
+///
+pub fn interleave(list: List(List(a))) -> List(a) {
+ transpose(list)
+ |> concat
+}
+
+/// Transpose rows and columns of the list of lists.
+///
+/// Notice: This function is not tail recursive,
+/// and thus may exceed stack size if called,
+/// with large lists (on target JavaScript).
+///
+/// ## Examples
+///
+/// ```gleam
+/// > transpose([[1, 2, 3], [101, 102, 103]])
+/// [[1, 101], [2, 102], [3, 103]]
+/// ```
+///
+pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) {
+ let take_first = fn(list) {
+ case list {
+ [] -> []
+ [f] -> [f]
+ [f, ..] -> [f]
+ }
+ }
+
+ case list_of_list {
+ [] -> []
+ [[], ..xss] -> transpose(xss)
+ rows -> {
+ let firsts =
+ rows
+ |> map(take_first)
+ |> concat
+ let rest = transpose(map(rows, drop(_, 1)))
+ [firsts, ..rest]
+ }
+ }
+}
+
+fn do_shuffle_pair_unwrap(list: List(#(Float, a)), acc: List(a)) -> List(a) {
+ case list {
+ [] -> acc
+ [elem_pair, ..enumerable] ->
+ do_shuffle_pair_unwrap(enumerable, [elem_pair.1, ..acc])
+ }
+}
+
+fn do_shuffle_by_pair_indexes(
+ list_of_pairs: List(#(Float, a)),
+) -> List(#(Float, a)) {
+ sort(
+ list_of_pairs,
+ fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order {
+ float.compare(a_pair.0, b_pair.0)
+ },
+ )
+}
+
+/// Takes a list, randomly sorts all items and returns the shuffled list.
+///
+/// This function uses Erlang's `:rand` module or Javascript's
+/// `Math.random()` to calculate the index shuffling.
+///
+/// ## Example
+///
+/// ```gleam
+/// > range(1, 10)
+/// > |> shuffle()
+/// [1, 6, 9, 10, 3, 8, 4, 2, 7, 5]
+/// ```
+///
+pub fn shuffle(list: List(a)) -> List(a) {
+ list
+ |> fold(from: [], with: fn(acc, a) { [#(float.random(0.0, 1.0), a), ..acc] })
+ |> do_shuffle_by_pair_indexes()
+ |> do_shuffle_pair_unwrap([])
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/map.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/map.gleam
new file mode 100644
index 0000000..1f8b228
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/map.gleam
@@ -0,0 +1,127 @@
+import gleam/option.{type Option}
+import gleam/dict
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub type Map(key, value) =
+ dict.Dict(key, value)
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn size(map) -> Int {
+ dict.size(map)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn to_list(map) -> List(#(key, value)) {
+ dict.to_list(map)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn from_list(list: List(#(k, v))) {
+ dict.from_list(list)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn has_key(map, key: k) -> Bool {
+ dict.has_key(map, key)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn new() {
+ dict.new()
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn get(from, get: key) -> Result(value, Nil) {
+ dict.get(from, get)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn insert(into map, for key: k, insert value: v) {
+ dict.insert(map, key, value)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn map_values(in map, with fun: fn(k, v) -> w) {
+ dict.map_values(map, fun)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn keys(map) -> List(keys) {
+ dict.keys(map)
+}
+
+@target(javascript)
+fn reverse_and_concat(remaining, accumulator) {
+ case remaining {
+ [] -> accumulator
+ [item, ..rest] -> reverse_and_concat(rest, [item, ..accumulator])
+ }
+}
+
+@target(javascript)
+fn do_keys_acc(list: List(#(k, v)), acc: List(k)) -> List(k) {
+ case list {
+ [] -> reverse_and_concat(acc, [])
+ [x, ..xs] -> do_keys_acc(xs, [x.0, ..acc])
+ }
+}
+
+@target(javascript)
+fn do_keys(map) -> List(k) {
+ let list_of_pairs =
+ map
+ |> to_list
+ do_keys_acc(list_of_pairs, [])
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn values(map) -> List(values) {
+ dict.values(map)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn filter(in map, keeping predicate: fn(k, v) -> Bool) {
+ dict.filter(map, predicate)
+}
+
+@target(javascript)
+fn do_filter(f: fn(key, value) -> Bool, map) {
+ let insert = fn(map, k, v) {
+ case f(k, v) {
+ True -> insert(map, k, v)
+ _ -> map
+ }
+ }
+ map
+ |> fold(from: new(), with: insert)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn take(from map, keeping desired_keys: List(k)) {
+ dict.take(map, desired_keys)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn merge(into map, from new_entries) {
+ dict.merge(map, new_entries)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn delete(from map, delete key: k) {
+ dict.delete(map, key)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn drop(from map, drop disallowed_keys: List(k)) {
+ dict.drop(map, disallowed_keys)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn update(in map, update key: k, with fun: fn(Option(v)) -> v) {
+ dict.update(map, key, fun)
+}
+
+@deprecated("Please use the `gleam/dict` module instead")
+pub fn fold(over map, from initial: acc, with fun: fn(acc, k, v) -> acc) -> acc {
+ dict.fold(map, initial, fun)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/option.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/option.gleam
new file mode 100644
index 0000000..6015c0f
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/option.gleam
@@ -0,0 +1,346 @@
+/// `Option` represents a value that may be present or not. `Some` means the value is
+/// present, `None` means the value is not.
+///
+/// This is Gleam's alternative to having a value that could be Null, as is
+/// possible in some other languages.
+///
+pub type Option(a) {
+ Some(a)
+ None
+}
+
+fn do_all(list: List(Option(a)), acc: List(a)) -> Option(List(a)) {
+ case list {
+ [] -> Some(acc)
+ [x, ..rest] -> {
+ let accumulate = fn(acc, item) {
+ case acc, item {
+ Some(values), Some(value) -> Some([value, ..values])
+ _, _ -> None
+ }
+ }
+ accumulate(do_all(rest, acc), x)
+ }
+ }
+}
+
+/// Combines a list of `Option`s into a single `Option`.
+/// If all elements in the list are `Some` then returns a `Some` holding the list of values.
+/// If any element is `None` then returns`None`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > all([Some(1), Some(2)])
+/// Some([1, 2])
+/// ```
+///
+/// ```gleam
+/// > all([Some(1), None])
+/// None
+/// ```
+///
+pub fn all(list: List(Option(a))) -> Option(List(a)) {
+ do_all(list, [])
+}
+
+/// Checks whether the `Option` is a `Some` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_some(Some(1))
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_some(None)
+/// False
+/// ```
+///
+pub fn is_some(option: Option(a)) -> Bool {
+ option != None
+}
+
+/// Checks whether the `Option` is a `None` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_none(Some(1))
+/// False
+/// ```
+///
+/// ```gleam
+/// > is_none(None)
+/// True
+/// ```
+///
+pub fn is_none(option: Option(a)) -> Bool {
+ option == None
+}
+
+/// Converts an `Option` type to a `Result` type.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_result(Some(1), "some_error")
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > to_result(None, "some_error")
+/// Error("some_error")
+/// ```
+///
+pub fn to_result(option: Option(a), e) -> Result(a, e) {
+ case option {
+ Some(a) -> Ok(a)
+ _ -> Error(e)
+ }
+}
+
+/// Converts a `Result` type to an `Option` type.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_result(Ok(1))
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > from_result(Error("some_error"))
+/// None
+/// ```
+///
+pub fn from_result(result: Result(a, e)) -> Option(a) {
+ case result {
+ Ok(a) -> Some(a)
+ _ -> None
+ }
+}
+
+/// Extracts the value from an `Option`, returning a default value if there is none.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unwrap(Some(1), 0)
+/// 1
+/// ```
+///
+/// ```gleam
+/// > unwrap(None, 0)
+/// 0
+/// ```
+///
+pub fn unwrap(option: Option(a), or default: a) -> a {
+ case option {
+ Some(x) -> x
+ None -> default
+ }
+}
+
+/// Extracts the value from an `Option`, evaluating the default function if the option is `None`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > lazy_unwrap(Some(1), fn() { 0 })
+/// 1
+/// ```
+///
+/// ```gleam
+/// > lazy_unwrap(None, fn() { 0 })
+/// 0
+/// ```
+///
+pub fn lazy_unwrap(option: Option(a), or default: fn() -> a) -> a {
+ case option {
+ Some(x) -> x
+ None -> default()
+ }
+}
+
+/// Updates a value held within the `Some` of an `Option` by calling a given function
+/// on it.
+///
+/// If the `Option` is a `None` rather than `Some`, the function is not called and the
+/// `Option` stays the same.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map(over: Some(1), with: fn(x) { x + 1 })
+/// Some(2)
+/// ```
+///
+/// ```gleam
+/// > map(over: None, with: fn(x) { x + 1 })
+/// None
+/// ```
+///
+pub fn map(over option: Option(a), with fun: fn(a) -> b) -> Option(b) {
+ case option {
+ Some(x) -> Some(fun(x))
+ None -> None
+ }
+}
+
+/// Merges a nested `Option` into a single layer.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > flatten(Some(Some(1)))
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > flatten(Some(None))
+/// None
+/// ```
+///
+/// ```gleam
+/// > flatten(None)
+/// None
+/// ```
+///
+pub fn flatten(option: Option(Option(a))) -> Option(a) {
+ case option {
+ Some(x) -> x
+ None -> None
+ }
+}
+
+/// Updates a value held within the `Some` of an `Option` by calling a given function
+/// on it, where the given function also returns an `Option`. The two options are
+/// then merged together into one `Option`.
+///
+/// If the `Option` is a `None` rather than `Some` the function is not called and the
+/// option stays the same.
+///
+/// This function is the equivalent of calling `map` followed by `flatten`, and
+/// it is useful for chaining together multiple functions that return `Option`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > then(Some(1), fn(x) { Some(x + 1) })
+/// Some(2)
+/// ```
+///
+/// ```gleam
+/// > then(Some(1), fn(x) { Some(#("a", x)) })
+/// Some(#("a", 1))
+/// ```
+///
+/// ```gleam
+/// > then(Some(1), fn(_) { None })
+/// None
+/// ```
+///
+/// ```gleam
+/// > then(None, fn(x) { Some(x + 1) })
+/// None
+/// ```
+///
+pub fn then(option: Option(a), apply fun: fn(a) -> Option(b)) -> Option(b) {
+ case option {
+ Some(x) -> fun(x)
+ None -> None
+ }
+}
+
+/// Returns the first value if it is `Some`, otherwise returns the second value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > or(Some(1), Some(2))
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > or(Some(1), None)
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > or(None, Some(2))
+/// Some(2)
+/// ```
+///
+/// ```gleam
+/// > or(None, None)
+/// None
+/// ```
+///
+pub fn or(first: Option(a), second: Option(a)) -> Option(a) {
+ case first {
+ Some(_) -> first
+ None -> second
+ }
+}
+
+/// Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > lazy_or(Some(1), fn() { Some(2) })
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(Some(1), fn() { None })
+/// Some(1)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(None, fn() { Some(2) })
+/// Some(2)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(None, fn() { None })
+/// None
+/// ```
+///
+pub fn lazy_or(first: Option(a), second: fn() -> Option(a)) -> Option(a) {
+ case first {
+ Some(_) -> first
+ None -> second()
+ }
+}
+
+fn do_values(list: List(Option(a)), acc: List(a)) -> List(a) {
+ case list {
+ [] -> acc
+ [x, ..xs] -> {
+ let accumulate = fn(acc, item) {
+ case item {
+ Some(value) -> [value, ..acc]
+ None -> acc
+ }
+ }
+ accumulate(do_values(xs, acc), x)
+ }
+ }
+}
+
+/// Given a list of `Option`s,
+/// returns only the values inside `Some`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > values([Some(1), None, Some(3)])
+/// [1, 3]
+/// ```
+///
+pub fn values(options: List(Option(a))) -> List(a) {
+ do_values(options, [])
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/order.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/order.gleam
new file mode 100644
index 0000000..12ce011
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/order.gleam
@@ -0,0 +1,133 @@
+/// Represents the result of a single comparison to determine the precise
+/// ordering of two values.
+///
+pub type Order {
+ /// Less-than
+ Lt
+
+ /// Equal
+ Eq
+
+ /// Greater than
+ Gt
+}
+
+/// Inverts an order, so less-than becomes greater-than and greater-than
+/// becomes less-than.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > negate(Lt)
+/// Gt
+/// ```
+///
+/// ```gleam
+/// > negate(Eq)
+/// Eq
+/// ```
+///
+/// ```gleam
+/// > negate(Lt)
+/// Gt
+/// ```
+///
+pub fn negate(order: Order) -> Order {
+ case order {
+ Lt -> Gt
+ Eq -> Eq
+ Gt -> Lt
+ }
+}
+
+/// Produces a numeric representation of the order.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_int(Lt)
+/// -1
+/// ```
+///
+/// ```gleam
+/// > to_int(Eq)
+/// 0
+/// ```
+///
+/// ```gleam
+/// > to_int(Gt)
+/// 1
+/// ```
+///
+pub fn to_int(order: Order) -> Int {
+ case order {
+ Lt -> -1
+ Eq -> 0
+ Gt -> 1
+ }
+}
+
+/// Compares two `Order` values to one another, producing a new `Order`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > compare(Eq, with: Lt)
+/// Gt
+/// ```
+///
+pub fn compare(a: Order, with b: Order) -> Order {
+ case a, b {
+ x, y if x == y -> Eq
+ Lt, _ | Eq, Gt -> Lt
+ _, _ -> Gt
+ }
+}
+
+/// Returns the largest of two orders given that `Gt > Eq > Lt`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > max(Eq, Lt)
+/// Eq
+/// ```
+///
+pub fn max(a: Order, b: Order) -> Order {
+ case a, b {
+ Gt, _ -> Gt
+ Eq, Lt -> Eq
+ _, _ -> b
+ }
+}
+
+/// Returns the smallest of two orders given that `Gt > Eq > Lt`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > min(Eq, Lt)
+/// Lt
+/// ```
+///
+pub fn min(a: Order, b: Order) -> Order {
+ case a, b {
+ Lt, _ -> Lt
+ Eq, Gt -> Eq
+ _, _ -> b
+ }
+}
+
+/// Inverts an ordering function, so less-than becomes greater-than and greater-than
+/// becomes less-than.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > list.sort([1, 5, 4], by: reverse(int.compare))
+/// [5, 4, 1]
+/// ```
+///
+pub fn reverse(orderer: fn(a, a) -> Order) -> fn(a, a) -> Order {
+ fn(a, b) { orderer(b, a) }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/pair.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/pair.gleam
new file mode 100644
index 0000000..894e6a8
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/pair.gleam
@@ -0,0 +1,85 @@
+/// Returns the first element in a pair.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > first(#(1, 2))
+/// 1
+/// ```
+///
+pub fn first(pair: #(a, b)) -> a {
+ let #(a, _) = pair
+ a
+}
+
+/// Returns the second element in a pair.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > second(#(1, 2))
+/// 2
+/// ```
+///
+pub fn second(pair: #(a, b)) -> b {
+ let #(_, a) = pair
+ a
+}
+
+/// Returns a new pair with the elements swapped.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > swap(#(1, 2))
+/// #(2, 1)
+/// ```
+///
+pub fn swap(pair: #(a, b)) -> #(b, a) {
+ let #(a, b) = pair
+ #(b, a)
+}
+
+/// Returns a new pair with the first element having had `with` applied to
+/// it.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > #(1, 2) |> map_first(fn(n) { n * 2 })
+/// #(2, 2)
+/// ```
+///
+pub fn map_first(of pair: #(a, b), with fun: fn(a) -> c) -> #(c, b) {
+ let #(a, b) = pair
+ #(fun(a), b)
+}
+
+/// Returns a new pair with the second element having had `with` applied to
+/// it.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > #(1, 2) |> map_second(fn(n) { n * 2 })
+/// #(1, 4)
+/// ```
+///
+pub fn map_second(of pair: #(a, b), with fun: fn(b) -> c) -> #(a, c) {
+ let #(a, b) = pair
+ #(a, fun(b))
+}
+
+/// Returns a new pair with the given elements. This can also be done using the dedicated
+/// syntax instead: `new(1, 2) == #(1, 2)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new(1, 2)
+/// #(1, 2)
+/// ```
+///
+pub fn new(first: a, second: b) -> #(a, b) {
+ #(first, second)
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/queue.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/queue.gleam
new file mode 100644
index 0000000..5bf60c8
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/queue.gleam
@@ -0,0 +1,292 @@
+import gleam/list
+
+/// A queue is an ordered collection of elements. It is similar to a list, but
+/// unlike a list elements can be added to or removed from either the front or
+/// the back in a performant fashion.
+///
+/// The internal representation may be different for two queues with the same
+/// elements in the same order if the queues were constructed in different
+/// ways. This is the price paid for a queue's fast access at both the front
+/// and the back.
+///
+/// Because of unpredictable internal representation the equality operator `==`
+/// may return surprising results, and the `is_equal` and `is_logically_equal`
+/// functions are the recommended way to test queues for equality.
+///
+pub opaque type Queue(element) {
+ Queue(in: List(element), out: List(element))
+}
+
+/// Creates a fresh queue that contains no values.
+///
+pub fn new() -> Queue(a) {
+ Queue(in: [], out: [])
+}
+
+/// Converts a list of elements into a queue of the same elements in the same
+/// order. The first element in the list becomes the front element in the queue.
+///
+/// This function runs in constant time.
+///
+/// # Examples
+///
+/// ```gleam
+/// > [1, 2, 3] |> from_list |> length
+/// 3
+/// ```
+///
+pub fn from_list(list: List(a)) -> Queue(a) {
+ Queue(in: [], out: list)
+}
+
+/// Converts a queue of elements into a list of the same elements in the same
+/// order. The front element in the queue becomes the first element in the list.
+///
+/// This function runs in linear time.
+///
+/// # Examples
+///
+/// ```gleam
+/// > new() |> push_back(1) |> push_back(2) |> to_list
+/// [1, 2]
+/// ```
+///
+pub fn to_list(queue: Queue(a)) -> List(a) {
+ queue.out
+ |> list.append(list.reverse(queue.in))
+}
+
+/// Determines whether or not the queue is empty.
+///
+/// This function runs in constant time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [] |> from_list |> is_empty
+/// True
+/// ```
+///
+/// ```gleam
+/// > [1] |> from_list |> is_empty
+/// False
+/// ```
+///
+/// ```gleam
+/// > [1, 2] |> from_list |> is_empty
+/// False
+/// ```
+///
+pub fn is_empty(queue: Queue(a)) -> Bool {
+ queue.in == [] && queue.out == []
+}
+
+/// Counts the number of elements in a given queue.
+///
+/// This function has to traverse the queue to determine the number of elements,
+/// so it runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > length(from_list([]))
+/// 0
+/// ```
+///
+/// ```gleam
+/// > length(from_list([1]))
+/// 1
+/// ```
+///
+/// ```gleam
+/// > length(from_list([1, 2]))
+/// 2
+/// ```
+///
+pub fn length(queue: Queue(a)) -> Int {
+ list.length(queue.in) + list.length(queue.out)
+}
+
+/// Pushes an element onto the back of the queue.
+///
+/// # Examples
+///
+/// ```gleam
+/// > [1, 2] |> from_list |> push_back(3) |> to_list
+/// [1, 2, 3]
+/// ```
+///
+pub fn push_back(onto queue: Queue(a), this item: a) -> Queue(a) {
+ Queue(in: [item, ..queue.in], out: queue.out)
+}
+
+/// Pushes an element onto the front of the queue.
+///
+/// # Examples
+///
+/// ```gleam
+/// > [0, 0] |> from_list |> push_front(1) |> to_list
+/// [1, 0, 0]
+/// ```
+///
+pub fn push_front(onto queue: Queue(a), this item: a) -> Queue(a) {
+ Queue(in: queue.in, out: [item, ..queue.out])
+}
+
+/// Gets the last element from the queue, returning the
+/// element and a new queue without that element.
+///
+/// This function typically runs in constant time, but will occasionally run in
+/// linear time.
+///
+/// # Examples
+///
+/// ```gleam
+/// > new()
+/// > |> push_back(0)
+/// > |> push_back(1)
+/// > |> pop_back()
+/// Ok(#(1, push_front(new(), 0)))
+/// ```
+///
+/// ```gleam
+/// > new()
+/// > |> push_front(0)
+/// > |> pop_back()
+/// Ok(#(0, new()))
+/// ```
+///
+/// ```gleam
+/// > new()
+/// > |> pop_back()
+/// Error(Nil)
+/// ```
+///
+pub fn pop_back(from queue: Queue(a)) -> Result(#(a, Queue(a)), Nil) {
+ case queue {
+ Queue(in: [], out: []) -> Error(Nil)
+ Queue(in: [], out: out) -> pop_back(Queue(in: list.reverse(out), out: []))
+ Queue(in: [first, ..rest], out: out) -> {
+ let queue = Queue(in: rest, out: out)
+ Ok(#(first, queue))
+ }
+ }
+}
+
+/// Gets the first element from the queue, returning the
+/// element and a new queue without that element.
+///
+/// This function typically runs in constant time, but will occasionally run in
+/// linear time.
+///
+/// # Examples
+///
+/// ```gleam
+/// > queue.new()
+/// > |> queue.push_front(1)
+/// > |> queue.push_front(0)
+/// > |> queue.pop_front()
+/// Ok(#(0, queue.push_back(queue.new(), 1)))
+/// ```
+///
+/// ```gleam
+/// > queue.new()
+/// > |> queue.push_back(0)
+/// > |> queue.pop_front()
+/// Ok(#(0, queue.new()))
+/// ```
+///
+/// ```gleam
+/// > queue.new()
+/// > |> queue.pop_back()
+/// Error(Nil)
+/// ```
+///
+pub fn pop_front(from queue: Queue(a)) -> Result(#(a, Queue(a)), Nil) {
+ case queue {
+ Queue(in: [], out: []) -> Error(Nil)
+ Queue(in: in, out: []) -> pop_front(Queue(in: [], out: list.reverse(in)))
+ Queue(in: in, out: [first, ..rest]) -> {
+ let queue = Queue(in: in, out: rest)
+ Ok(#(first, queue))
+ }
+ }
+}
+
+/// Creates a new queue from a given queue containing the same elements, but in
+/// the opposite order.
+///
+/// This function runs in constant time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > [] |> from_list |> reverse |> to_list
+/// []
+/// ```
+///
+/// ```gleam
+/// > [1] |> from_list |> reverse |> to_list
+/// [1]
+/// ```
+///
+/// ```gleam
+/// > [1, 2] |> from_list |> reverse |> to_list
+/// [2, 1]
+/// ```
+///
+pub fn reverse(queue: Queue(a)) -> Queue(a) {
+ Queue(in: queue.out, out: queue.in)
+}
+
+fn check_equal(
+ xs: List(t),
+ x_tail: List(t),
+ ys: List(t),
+ y_tail: List(t),
+ eq: fn(t, t) -> Bool,
+) -> Bool {
+ case xs, x_tail, ys, y_tail {
+ [], [], [], [] -> True
+ [x, ..xs], _, [y, ..ys], _ ->
+ case eq(x, y) {
+ False -> False
+ True -> check_equal(xs, x_tail, ys, y_tail, eq)
+ }
+ [], [_, ..], _, _ -> check_equal(list.reverse(x_tail), [], ys, y_tail, eq)
+ _, _, [], [_, ..] -> check_equal(xs, x_tail, list.reverse(y_tail), [], eq)
+ _, _, _, _ -> False
+ }
+}
+
+/// Checks whether two queues have equal elements in the same order, where the
+/// equality of elements is determined by a given equality checking function.
+///
+/// This function is useful as the internal representation may be different for
+/// two queues with the same elements in the same order depending on how they
+/// were constructed, so the equality operator `==` may return surprising
+/// results.
+///
+/// This function runs in linear time multiplied by the time taken by the
+/// element equality checking function.
+///
+pub fn is_logically_equal(
+ a: Queue(t),
+ to b: Queue(t),
+ checking element_is_equal: fn(t, t) -> Bool,
+) -> Bool {
+ check_equal(a.out, a.in, b.out, b.in, element_is_equal)
+}
+
+/// Checks whether two queues have the same elements in the same order.
+///
+/// This function is useful as the internal representation may be different for
+/// two queues with the same elements in the same order depending on how they
+/// were constructed, so the equality operator `==` may return surprising
+/// results.
+///
+/// This function runs in linear time.
+///
+pub fn is_equal(a: Queue(t), to b: Queue(t)) -> Bool {
+ check_equal(a.out, a.in, b.out, b.in, fn(a, b) { a == b })
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/regex.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/regex.gleam
new file mode 100644
index 0000000..9ffda78
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/regex.gleam
@@ -0,0 +1,214 @@
+//// This module contains regular expression matching functions for strings.
+//// The matching algorithms of the library are based on the PCRE library, but not
+//// all of the PCRE library is interfaced and some parts of the library go beyond
+//// what PCRE offers. Currently PCRE version 8.40 (release date 2017-01-11) is used.
+
+import gleam/option.{type Option}
+
+pub type Regex
+
+/// The details about a particular match:
+///
+pub type Match {
+ Match(
+ /// The full string of the match.
+ content: String,
+ /// A `Regex` can have subpatterns, sup-parts that are in parentheses.
+ submatches: List(Option(String)),
+ )
+}
+
+/// When a regular expression fails to compile:
+///
+pub type CompileError {
+ CompileError(
+ /// The problem encountered that caused the compilation to fail
+ error: String,
+ /// The byte index into the string to where the problem was found
+ /// This value may not be correct in JavaScript environments.
+ byte_index: Int,
+ )
+}
+
+pub type Options {
+ Options(case_insensitive: Bool, multi_line: Bool)
+}
+
+/// Creates a `Regex` with some additional options.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let options = Options(case_insensitive: False, multi_line: True)
+/// > let assert Ok(re) = compile("^[0-9]", with: options)
+/// > check(re, "abc\n123")
+/// True
+/// ```
+///
+/// ```gleam
+/// > let options = Options(case_insensitive: True, multi_line: False)
+/// > let assert Ok(re) = compile("[A-Z]", with: options)
+/// > check(re, "abc123")
+/// True
+/// ```
+///
+pub fn compile(
+ pattern: String,
+ with options: Options,
+) -> Result(Regex, CompileError) {
+ do_compile(pattern, options)
+}
+
+@external(erlang, "gleam_stdlib", "compile_regex")
+@external(javascript, "../gleam_stdlib.mjs", "compile_regex")
+fn do_compile(a: String, with with: Options) -> Result(Regex, CompileError)
+
+/// Creates a new `Regex`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Ok(re) = from_string("[0-9]")
+/// > check(re, "abc123")
+/// True
+/// ```
+///
+/// ```gleam
+/// > check(re, "abcxyz")
+/// False
+/// ```
+///
+/// ```gleam
+/// > from_string("[0-9")
+/// Error(
+/// CompileError(
+/// error: "missing terminating ] for character class",
+/// byte_index: 4
+/// )
+/// )
+/// ```
+///
+pub fn from_string(pattern: String) -> Result(Regex, CompileError) {
+ compile(pattern, Options(case_insensitive: False, multi_line: False))
+}
+
+/// Returns a boolean indicating whether there was a match or not.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Ok(re) = from_string("^f.o.?")
+/// > check(with: re, content: "foo")
+/// True
+/// ```
+///
+/// ```gleam
+/// > check(with: re, content: "boo")
+/// False
+/// ```
+///
+pub fn check(with regex: Regex, content content: String) -> Bool {
+ do_check(regex, content)
+}
+
+@external(erlang, "gleam_stdlib", "regex_check")
+@external(javascript, "../gleam_stdlib.mjs", "regex_check")
+fn do_check(a: Regex, b: String) -> Bool
+
+/// Splits a string.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Ok(re) = from_string(" *, *")
+/// > split(with: re, content: "foo,32, 4, 9 ,0")
+/// ["foo", "32", "4", "9", "0"]
+/// ```
+///
+pub fn split(with regex: Regex, content string: String) -> List(String) {
+ do_split(regex, string)
+}
+
+@target(erlang)
+@external(erlang, "gleam_stdlib", "regex_split")
+fn do_split(a: Regex, b: String) -> List(String)
+
+@target(javascript)
+fn do_split(regex, string) -> List(String) {
+ js_split(string, regex)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "split")
+fn js_split(a: String, b: Regex) -> List(String)
+
+/// Collects all matches of the regular expression.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Ok(re) = from_string("[oi]n a (\\w+)")
+/// > scan(with: re, content: "I am on a boat in a lake.")
+/// [
+/// Match(
+/// content: "on a boat",
+/// submatches: [Some("boat")]
+/// ),
+/// Match(
+/// content: "in a lake",
+/// submatches: [Some("lake")]
+/// )
+/// ]
+/// ```
+///
+/// ```gleam
+/// > let assert Ok(re) = regex.from_string("([+|\\-])?(\\d+)(\\w+)?")
+/// > scan(with: re, content: "-36")
+/// [
+/// Match(
+/// content: "-36",
+/// submatches: [Some("-"), Some("36")]
+/// )
+/// ]
+///
+/// > scan(with: re, content: "36")
+/// [
+/// Match(
+/// content: "36",
+/// submatches: [None, Some("36")]
+/// )
+/// ]
+/// ```
+///
+/// ```gleam
+/// > let assert Ok(re) = regex.from_string("var\\s*(\\w+)\\s*(int|string)?\\s*=\\s*(.*)")
+/// > scan(with: re, content: "var age = 32")
+/// [
+/// Match(
+/// content: "var age = 32",
+/// submatches: [Some("age"), None, Some("32")]
+/// )
+/// ]
+/// ```
+///
+/// ```gleam
+/// > let assert Ok(re) = regex.from_string("let (\\w+) = (\\w+)")
+/// > scan(with: re, content: "let age = 32")
+/// [
+/// Match(
+/// content: "let age = 32",
+/// submatches: [Some("age"), Some("32")]
+/// )
+/// ]
+///
+/// > scan(with: re, content: "const age = 32")
+/// []
+/// ```
+///
+pub fn scan(with regex: Regex, content string: String) -> List(Match) {
+ do_scan(regex, string)
+}
+
+@external(erlang, "gleam_stdlib", "regex_scan")
+@external(javascript, "../gleam_stdlib.mjs", "regex_scan")
+fn do_scan(a: Regex, b: String) -> List(Match)
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/result.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/result.gleam
new file mode 100644
index 0000000..fb6dddb
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/result.gleam
@@ -0,0 +1,482 @@
+//// Result represents the result of something that may succeed or not.
+//// `Ok` means it was successful, `Error` means it was not successful.
+
+import gleam/list
+
+/// Checks whether the result is an `Ok` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_ok(Ok(1))
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_ok(Error(Nil))
+/// False
+/// ```
+///
+pub fn is_ok(result: Result(a, e)) -> Bool {
+ case result {
+ Error(_) -> False
+ Ok(_) -> True
+ }
+}
+
+/// Checks whether the result is an `Error` value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_error(Ok(1))
+/// False
+/// ```
+///
+/// ```gleam
+/// > is_error(Error(Nil))
+/// True
+/// ```
+///
+pub fn is_error(result: Result(a, e)) -> Bool {
+ case result {
+ Ok(_) -> False
+ Error(_) -> True
+ }
+}
+
+/// Updates a value held within the `Ok` of a result by calling a given function
+/// on it.
+///
+/// If the result is an `Error` rather than `Ok` the function is not called and the
+/// result stays the same.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map(over: Ok(1), with: fn(x) { x + 1 })
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > map(over: Error(1), with: fn(x) { x + 1 })
+/// Error(1)
+/// ```
+///
+pub fn map(over result: Result(a, e), with fun: fn(a) -> b) -> Result(b, e) {
+ case result {
+ Ok(x) -> Ok(fun(x))
+ Error(e) -> Error(e)
+ }
+}
+
+/// Updates a value held within the `Error` of a result by calling a given function
+/// on it.
+///
+/// If the result is `Ok` rather than `Error` the function is not called and the
+/// result stays the same.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > map_error(over: Error(1), with: fn(x) { x + 1 })
+/// Error(2)
+/// ```
+///
+/// ```gleam
+/// > map_error(over: Ok(1), with: fn(x) { x + 1 })
+/// Ok(1)
+/// ```
+///
+pub fn map_error(
+ over result: Result(a, e),
+ with fun: fn(e) -> f,
+) -> Result(a, f) {
+ case result {
+ Ok(x) -> Ok(x)
+ Error(error) -> Error(fun(error))
+ }
+}
+
+/// Merges a nested `Result` into a single layer.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > flatten(Ok(Ok(1)))
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > flatten(Ok(Error("")))
+/// Error("")
+/// ```
+///
+/// ```gleam
+/// > flatten(Error(Nil))
+/// Error(Nil)
+/// ```
+///
+pub fn flatten(result: Result(Result(a, e), e)) -> Result(a, e) {
+ case result {
+ Ok(x) -> x
+ Error(error) -> Error(error)
+ }
+}
+
+/// "Updates" an `Ok` result by passing its value to a function that yields a result,
+/// and returning the yielded result. (This may "replace" the `Ok` with an `Error`.)
+///
+/// If the input is an `Error` rather than an `Ok`, the function is not called and
+/// the original `Error` is returned.
+///
+/// This function is the equivalent of calling `map` followed by `flatten`, and
+/// it is useful for chaining together multiple functions that may fail.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > try(Ok(1), fn(x) { Ok(x + 1) })
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > try(Ok(1), fn(x) { Ok(#("a", x)) })
+/// Ok(#("a", 1))
+/// ```
+///
+/// ```gleam
+/// > try(Ok(1), fn(_) { Error("Oh no") })
+/// Error("Oh no")
+/// ```
+///
+/// ```gleam
+/// > try(Error(Nil), fn(x) { Ok(x + 1) })
+/// Error(Nil)
+/// ```
+///
+pub fn try(
+ result: Result(a, e),
+ apply fun: fn(a) -> Result(b, e),
+) -> Result(b, e) {
+ case result {
+ Ok(x) -> fun(x)
+ Error(e) -> Error(e)
+ }
+}
+
+/// An alias for `try`. See the documentation for that function for more information.
+///
+pub fn then(
+ result: Result(a, e),
+ apply fun: fn(a) -> Result(b, e),
+) -> Result(b, e) {
+ try(result, fun)
+}
+
+/// Extracts the `Ok` value from a result, returning a default value if the result
+/// is an `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unwrap(Ok(1), 0)
+/// 1
+/// ```
+///
+/// ```gleam
+/// > unwrap(Error(""), 0)
+/// 0
+/// ```
+///
+pub fn unwrap(result: Result(a, e), or default: a) -> a {
+ case result {
+ Ok(v) -> v
+ Error(_) -> default
+ }
+}
+
+/// Extracts the `Ok` value from a result, evaluating the default function if the result
+/// is an `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > lazy_unwrap(Ok(1), fn() { 0 })
+/// 1
+/// ```
+///
+/// ```gleam
+/// > lazy_unwrap(Error(""), fn() { 0 })
+/// 0
+/// ```
+///
+pub fn lazy_unwrap(result: Result(a, e), or default: fn() -> a) -> a {
+ case result {
+ Ok(v) -> v
+ Error(_) -> default()
+ }
+}
+
+/// Extracts the `Error` value from a result, returning a default value if the result
+/// is an `Ok`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unwrap_error(Error(1), 0)
+/// 1
+/// ```
+///
+/// ```gleam
+/// > unwrap_error(Ok(""), 0)
+/// 0
+/// ```
+///
+pub fn unwrap_error(result: Result(a, e), or default: e) -> e {
+ case result {
+ Ok(_) -> default
+ Error(e) -> e
+ }
+}
+
+/// Extracts the inner value from a result. Both the value and error must be of
+/// the same type.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > unwrap_both(Error(1))
+/// 1
+/// ```
+///
+/// ```gleam
+/// > unwrap_both(Ok(2))
+/// 2
+/// ```
+///
+pub fn unwrap_both(result: Result(a, a)) -> a {
+ case result {
+ Ok(a) -> a
+ Error(a) -> a
+ }
+}
+
+/// Transforms any error into `Error(Nil)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > nil_error(Error(1))
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > nil_error(Ok(1))
+/// Ok(1)
+/// ```
+///
+pub fn nil_error(result: Result(a, e)) -> Result(a, Nil) {
+ map_error(result, fn(_) { Nil })
+}
+
+/// Returns the first value if it is `Ok`, otherwise returns the second value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > or(Ok(1), Ok(2))
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > or(Ok(1), Error("Error 2"))
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > or(Error("Error 1"), Ok(2))
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > or(Error("Error 1"), Error("Error 2"))
+/// Error("Error 2")
+/// ```
+///
+pub fn or(first: Result(a, e), second: Result(a, e)) -> Result(a, e) {
+ case first {
+ Ok(_) -> first
+ Error(_) -> second
+ }
+}
+
+/// Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > lazy_or(Ok(1), fn() { Ok(2) })
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(Ok(1), fn() { Error("Error 2") })
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(Error("Error 1"), fn() { Ok(2) })
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > lazy_or(Error("Error 1"), fn() { Error("Error 2") })
+/// Error("Error 2")
+/// ```
+///
+pub fn lazy_or(
+ first: Result(a, e),
+ second: fn() -> Result(a, e),
+) -> Result(a, e) {
+ case first {
+ Ok(_) -> first
+ Error(_) -> second()
+ }
+}
+
+/// Combines a list of results into a single result.
+/// If all elements in the list are `Ok` then returns an `Ok` holding the list of values.
+/// If any element is `Error` then returns the first error.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > all([Ok(1), Ok(2)])
+/// Ok([1, 2])
+/// ```
+///
+/// ```gleam
+/// > all([Ok(1), Error("e")])
+/// Error("e")
+/// ```
+///
+pub fn all(results: List(Result(a, e))) -> Result(List(a), e) {
+ list.try_map(results, fn(x) { x })
+}
+
+/// Given a list of results, returns a pair where the first element is a list
+/// of all the values inside `Ok` and the second element is a list with all the
+/// values inside `Error`. The values in both lists appear in reverse order with
+/// respect to their position in the original list of results.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > partition([Ok(1), Error("a"), Error("b"), Ok(2)])
+/// #([2, 1], ["b", "a"])
+/// ```
+///
+pub fn partition(results: List(Result(a, e))) -> #(List(a), List(e)) {
+ do_partition(results, [], [])
+}
+
+fn do_partition(results: List(Result(a, e)), oks: List(a), errors: List(e)) {
+ case results {
+ [] -> #(oks, errors)
+ [Ok(a), ..rest] -> do_partition(rest, [a, ..oks], errors)
+ [Error(e), ..rest] -> do_partition(rest, oks, [e, ..errors])
+ }
+}
+
+/// Replace the value within a result
+///
+/// ## Examples
+///
+/// ```gleam
+/// > replace(Ok(1), Nil)
+/// Ok(Nil)
+/// ```
+///
+/// ```gleam
+/// > replace(Error(1), Nil)
+/// Error(1)
+/// ```
+///
+pub fn replace(result: Result(a, e), value: b) -> Result(b, e) {
+ case result {
+ Ok(_) -> Ok(value)
+ Error(error) -> Error(error)
+ }
+}
+
+/// Replace the error within a result
+///
+/// ## Examples
+///
+/// ```gleam
+/// > replace_error(Error(1), Nil)
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > replace_error(Ok(1), Nil)
+/// Ok(1)
+/// ```
+///
+pub fn replace_error(result: Result(a, e1), error: e2) -> Result(a, e2) {
+ case result {
+ Ok(x) -> Ok(x)
+ Error(_) -> Error(error)
+ }
+}
+
+/// Given a list of results, returns only the values inside `Ok`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > values([Ok(1), Error("a"), Ok(3)])
+/// [1, 3]
+/// ```
+///
+pub fn values(results: List(Result(a, e))) -> List(a) {
+ list.filter_map(results, fn(r) { r })
+}
+
+/// Updates a value held within the `Error` of a result by calling a given function
+/// on it, where the given function also returns a result. The two results are
+/// then merged together into one result.
+///
+/// If the result is an `Ok` rather than `Error` the function is not called and the
+/// result stays the same.
+///
+/// This function is useful for chaining together computations that may fail
+/// and trying to recover from possible errors.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > Ok(1) |> try_recover(with: fn(_) { Error("failed to recover") })
+/// Ok(1)
+/// ```
+///
+/// ```gleam
+/// > Error(1) |> try_recover(with: fn(error) { Ok(error + 1) })
+/// Ok(2)
+/// ```
+///
+/// ```gleam
+/// > Error(1) |> try_recover(with: fn(error) { Error("failed to recover") })
+/// Error("failed to recover")
+/// ```
+///
+pub fn try_recover(
+ result: Result(a, e),
+ with fun: fn(e) -> Result(a, f),
+) -> Result(a, f) {
+ case result {
+ Ok(value) -> Ok(value)
+ Error(error) -> fun(error)
+ }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/set.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/set.gleam
new file mode 100644
index 0000000..df8d500
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/set.gleam
@@ -0,0 +1,264 @@
+import gleam/list
+import gleam/dict.{type Dict}
+import gleam/result
+
+// A list is used as the map value as an empty list has the smallest
+// representation in Erlang's binary format
+@target(erlang)
+type Token =
+ List(Nil)
+
+@target(erlang)
+const token = []
+
+@target(javascript)
+type Token =
+ Nil
+
+@target(javascript)
+const token = Nil
+
+/// A set is a collection of unique members of the same type.
+///
+/// It is implemented using the `gleam/map` module, so inserts and lookups have
+/// logarithmic time complexity.
+///
+pub opaque type Set(member) {
+ Set(map: Dict(member, Token))
+}
+
+/// Creates a new empty set.
+///
+pub fn new() -> Set(member) {
+ Set(dict.new())
+}
+
+/// Gets the number of members in a set.
+///
+/// This function runs in constant time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new()
+/// > |> insert(1)
+/// > |> insert(2)
+/// > |> size
+/// 2
+/// ```
+///
+pub fn size(set: Set(member)) -> Int {
+ dict.size(set.map)
+}
+
+/// Inserts an member into the set.
+///
+/// This function runs in logarithmic time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new()
+/// > |> insert(1)
+/// > |> insert(2)
+/// > |> size
+/// 2
+/// ```
+///
+pub fn insert(into set: Set(member), this member: member) -> Set(member) {
+ Set(map: dict.insert(set.map, member, token))
+}
+
+/// Checks whether a set contains a given member.
+///
+/// This function runs in logarithmic time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new()
+/// > |> insert(2)
+/// > |> contains(2)
+/// True
+/// ```
+///
+/// ```gleam
+/// > new()
+/// > |> insert(2)
+/// > |> contains(1)
+/// False
+/// ```
+///
+pub fn contains(in set: Set(member), this member: member) -> Bool {
+ set.map
+ |> dict.get(member)
+ |> result.is_ok
+}
+
+/// Removes a member from a set. If the set does not contain the member then
+/// the set is returned unchanged.
+///
+/// This function runs in logarithmic time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new()
+/// > |> insert(2)
+/// > |> delete(2)
+/// > |> contains(1)
+/// False
+/// ```
+///
+pub fn delete(from set: Set(member), this member: member) -> Set(member) {
+ Set(map: dict.delete(set.map, member))
+}
+
+/// Converts the set into a list of the contained members.
+///
+/// The list has no specific ordering, any unintentional ordering may change in
+/// future versions of Gleam or Erlang.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > new() |> insert(2) |> to_list
+/// [2]
+/// ```
+///
+pub fn to_list(set: Set(member)) -> List(member) {
+ dict.keys(set.map)
+}
+
+/// Creates a new set of the members in a given list.
+///
+/// This function runs in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/list
+/// > [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort
+/// [1, 3, 3, 4]
+/// ```
+///
+pub fn from_list(members: List(member)) -> Set(member) {
+ let map =
+ list.fold(
+ over: members,
+ from: dict.new(),
+ with: fn(m, k) { dict.insert(m, k, token) },
+ )
+ Set(map)
+}
+
+/// Combines all entries into a single value by calling a given function on each
+/// one.
+///
+/// Sets are not ordered so the values are not returned in any specific order.
+/// Do not write code that relies on the order entries are used by this
+/// function as it may change in later versions of Gleam or Erlang.
+///
+/// # Examples
+///
+/// ```gleam
+/// > from_list([1, 3, 9])
+/// > |> fold(0, fn(member, accumulator) { accumulator + member })
+/// 13
+/// ```
+///
+pub fn fold(
+ over set: Set(member),
+ from initial: acc,
+ with reducer: fn(acc, member) -> acc,
+) -> acc {
+ dict.fold(over: set.map, from: initial, with: fn(a, k, _) { reducer(a, k) })
+}
+
+/// Creates a new set from an existing set, minus any members that a given
+/// function returns `False` for.
+///
+/// This function runs in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > import gleam/int
+/// > from_list([1, 4, 6, 3, 675, 44, 67])
+/// > |> filter(for: int.is_even)
+/// > |> to_list
+/// [4, 6, 44]
+/// ```
+///
+pub fn filter(
+ in set: Set(member),
+ keeping predicate: fn(member) -> Bool,
+) -> Set(member) {
+ Set(dict.filter(in: set.map, keeping: fn(m, _) { predicate(m) }))
+}
+
+pub fn drop(from set: Set(member), drop disallowed: List(member)) -> Set(member) {
+ list.fold(over: disallowed, from: set, with: delete)
+}
+
+/// Creates a new map from a given map, only including any members which are in
+/// a given list.
+///
+/// This function runs in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_list([1, 2, 3])
+/// > |> take([1, 3, 5])
+/// > |> to_list
+/// [1, 3]
+/// ```
+///
+pub fn take(from set: Set(member), keeping desired: List(member)) -> Set(member) {
+ Set(dict.take(from: set.map, keeping: desired))
+}
+
+fn order(first: Set(member), second: Set(member)) -> #(Set(member), Set(member)) {
+ case dict.size(first.map) > dict.size(second.map) {
+ True -> #(first, second)
+ False -> #(second, first)
+ }
+}
+
+/// Creates a new set that contains all members of both given sets.
+///
+/// This function runs in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > union(from_list([1, 2]), from_list([2, 3])) |> to_list
+/// [1, 2, 3]
+/// ```
+///
+pub fn union(of first: Set(member), and second: Set(member)) -> Set(member) {
+ let #(larger, smaller) = order(first, second)
+ fold(over: smaller, from: larger, with: insert)
+}
+
+/// Creates a new set that contains members that are present in both given sets.
+///
+/// This function runs in loglinear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > intersection(from_list([1, 2]), from_list([2, 3])) |> to_list
+/// [2]
+/// ```
+///
+pub fn intersection(
+ of first: Set(member),
+ and second: Set(member),
+) -> Set(member) {
+ let #(larger, smaller) = order(first, second)
+ take(from: larger, keeping: to_list(smaller))
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/string.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/string.gleam
new file mode 100644
index 0000000..d4496f3
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/string.gleam
@@ -0,0 +1,906 @@
+//// Strings in Gleam are UTF-8 binaries. They can be written in your code as
+//// text surrounded by `"double quotes"`.
+
+import gleam/iterator.{type Iterator}
+import gleam/list
+import gleam/option.{type Option, None, Some}
+import gleam/order
+import gleam/string_builder.{type StringBuilder}
+
+/// Determines if a `String` is empty.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_empty("")
+/// True
+/// ```
+///
+/// ```gleam
+/// > is_empty("the world")
+/// False
+/// ```
+///
+pub fn is_empty(str: String) -> Bool {
+ str == ""
+}
+
+/// Gets the number of grapheme clusters in a given `String`.
+///
+/// This function has to iterate across the whole string to count the number of
+/// graphemes, so it runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > length("Gleam")
+/// 5
+/// ```
+///
+/// ```gleam
+/// > length("ß↑e̊")
+/// 3
+/// ```
+///
+/// ```gleam
+/// > length("")
+/// 0
+/// ```
+///
+pub fn length(string: String) -> Int {
+ do_length(string)
+}
+
+@external(erlang, "string", "length")
+@external(javascript, "../gleam_stdlib.mjs", "string_length")
+fn do_length(a: String) -> Int
+
+/// Reverses a `String`.
+///
+/// This function has to iterate across the whole `String` so it runs in linear
+/// time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > reverse("stressed")
+/// "desserts"
+/// ```
+///
+pub fn reverse(string: String) -> String {
+ do_reverse(string)
+}
+
+@target(erlang)
+fn do_reverse(string: String) -> String {
+ string
+ |> string_builder.from_string
+ |> string_builder.reverse
+ |> string_builder.to_string
+}
+
+@target(javascript)
+fn do_reverse(string: String) -> String {
+ string
+ |> to_graphemes
+ |> list.reverse
+ |> concat
+}
+
+/// Creates a new `String` by replacing all occurrences of a given substring.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > replace("www.example.com", each: ".", with: "-")
+/// "www-example-com"
+/// ```
+///
+/// ```gleam
+/// > replace("a,b,c,d,e", each: ",", with: "/")
+/// "a/b/c/d/e"
+/// ```
+///
+pub fn replace(
+ in string: String,
+ each pattern: String,
+ with substitute: String,
+) -> String {
+ string
+ |> string_builder.from_string
+ |> string_builder.replace(each: pattern, with: substitute)
+ |> string_builder.to_string
+}
+
+/// Creates a new `String` with all the graphemes in the input `String` converted to
+/// lowercase.
+///
+/// Useful for case-insensitive comparisons.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > lowercase("X-FILES")
+/// "x-files"
+/// ```
+///
+pub fn lowercase(string: String) -> String {
+ do_lowercase(string)
+}
+
+@external(erlang, "string", "lowercase")
+@external(javascript, "../gleam_stdlib.mjs", "lowercase")
+fn do_lowercase(a: String) -> String
+
+/// Creates a new `String` with all the graphemes in the input `String` converted to
+/// uppercase.
+///
+/// Useful for case-insensitive comparisons and VIRTUAL YELLING.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > uppercase("skinner")
+/// "SKINNER"
+/// ```
+///
+pub fn uppercase(string: String) -> String {
+ do_uppercase(string)
+}
+
+@external(erlang, "string", "uppercase")
+@external(javascript, "../gleam_stdlib.mjs", "uppercase")
+fn do_uppercase(a: String) -> String
+
+/// Compares two `String`s to see which is "larger" by comparing their graphemes.
+///
+/// This does not compare the size or length of the given `String`s.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > compare("Anthony", "Anthony")
+/// order.Eq
+/// ```
+///
+/// ```gleam
+/// > compare("A", "B")
+/// order.Lt
+/// ```
+///
+pub fn compare(a: String, b: String) -> order.Order {
+ case a == b {
+ True -> order.Eq
+ _ ->
+ case less_than(a, b) {
+ True -> order.Lt
+ _ -> order.Gt
+ }
+ }
+}
+
+@external(erlang, "gleam_stdlib", "less_than")
+@external(javascript, "../gleam_stdlib.mjs", "less_than")
+fn less_than(a: String, b: String) -> Bool
+
+/// Takes a substring given a start grapheme index and a length. Negative indexes
+/// are taken starting from the *end* of the list.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > slice(from: "gleam", at_index: 1, length: 2)
+/// "le"
+/// ```
+///
+/// ```gleam
+/// > slice(from: "gleam", at_index: 1, length: 10)
+/// "leam"
+/// ```
+///
+/// ```gleam
+/// > slice(from: "gleam", at_index: 10, length: 3)
+/// ""
+/// ```
+///
+/// ```gleam
+/// > slice(from: "gleam", at_index: -2, length: 2)
+/// "am"
+/// ```
+///
+/// ```gleam
+/// > slice(from: "gleam", at_index: -12, length: 2)
+/// ""
+/// ```
+///
+pub fn slice(from string: String, at_index idx: Int, length len: Int) -> String {
+ case len < 0 {
+ True -> ""
+ False ->
+ case idx < 0 {
+ True -> {
+ let translated_idx = length(string) + idx
+ case translated_idx < 0 {
+ True -> ""
+ False -> do_slice(string, translated_idx, len)
+ }
+ }
+ False -> do_slice(string, idx, len)
+ }
+ }
+}
+
+@target(erlang)
+@external(erlang, "string", "slice")
+fn do_slice(a: String, b: Int, c: Int) -> String
+
+@target(javascript)
+fn do_slice(string: String, idx: Int, len: Int) -> String {
+ string
+ |> to_graphemes
+ |> list.drop(idx)
+ |> list.take(len)
+ |> concat
+}
+
+/// Drops contents of the first `String` that occur before the second `String`.
+/// If the `from` string does not contain the `before` string, `from` is returned unchanged.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > crop(from: "The Lone Gunmen", before: "Lone")
+/// "Lone Gunmen"
+/// ```
+///
+@external(erlang, "gleam_stdlib", "crop_string")
+@external(javascript, "../gleam_stdlib.mjs", "crop_string")
+pub fn crop(from string: String, before substring: String) -> String
+
+/// Drops *n* graphemes from the left side of a `String`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > drop_left(from: "The Lone Gunmen", up_to: 2)
+/// "e Lone Gunmen"
+/// ```
+///
+pub fn drop_left(from string: String, up_to num_graphemes: Int) -> String {
+ case num_graphemes < 0 {
+ True -> string
+ False -> slice(string, num_graphemes, length(string) - num_graphemes)
+ }
+}
+
+/// Drops *n* graphemes from the right side of a `String`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > drop_right(from: "Cigarette Smoking Man", up_to: 2)
+/// "Cigarette Smoking M"
+/// ```
+///
+pub fn drop_right(from string: String, up_to num_graphemes: Int) -> String {
+ case num_graphemes < 0 {
+ True -> string
+ False -> slice(string, 0, length(string) - num_graphemes)
+ }
+}
+
+/// Checks if the first `String` contains the second.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > contains(does: "theory", contain: "ory")
+/// True
+/// ```
+///
+/// ```gleam
+/// > contains(does: "theory", contain: "the")
+/// True
+/// ```
+///
+/// ```gleam
+/// > contains(does: "theory", contain: "THE")
+/// False
+/// ```
+///
+@external(erlang, "gleam_stdlib", "contains_string")
+@external(javascript, "../gleam_stdlib.mjs", "contains_string")
+pub fn contains(does haystack: String, contain needle: String) -> Bool
+
+/// Checks whether the first `String` starts with the second one.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > starts_with("theory", "ory")
+/// False
+/// ```
+///
+pub fn starts_with(string: String, prefix: String) -> Bool {
+ do_starts_with(string, prefix)
+}
+
+@external(erlang, "gleam_stdlib", "string_starts_with")
+@external(javascript, "../gleam_stdlib.mjs", "starts_with")
+fn do_starts_with(a: String, b: String) -> Bool
+
+/// Checks whether the first `String` ends with the second one.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > ends_with("theory", "ory")
+/// True
+/// ```
+///
+pub fn ends_with(string: String, suffix: String) -> Bool {
+ do_ends_with(string, suffix)
+}
+
+@external(erlang, "gleam_stdlib", "string_ends_with")
+@external(javascript, "../gleam_stdlib.mjs", "ends_with")
+fn do_ends_with(a: String, b: String) -> Bool
+
+/// Creates a list of `String`s by splitting a given string on a given substring.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > split("home/gleam/desktop/", on: "/")
+/// ["home", "gleam", "desktop", ""]
+/// ```
+///
+pub fn split(x: String, on substring: String) -> List(String) {
+ case substring {
+ "" -> to_graphemes(x)
+ _ ->
+ x
+ |> string_builder.from_string
+ |> string_builder.split(on: substring)
+ |> list.map(with: string_builder.to_string)
+ }
+}
+
+/// Splits a `String` a single time on the given substring.
+///
+/// Returns an `Error` if substring not present.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > split_once("home/gleam/desktop/", on: "/")
+/// Ok(#("home", "gleam/desktop/"))
+/// ```
+///
+/// ```gleam
+/// > split_once("home/gleam/desktop/", on: "?")
+/// Error(Nil)
+/// ```
+///
+pub fn split_once(
+ x: String,
+ on substring: String,
+) -> Result(#(String, String), Nil) {
+ do_split_once(x, substring)
+}
+
+@target(erlang)
+@external(erlang, "string", "split")
+fn erl_split(a: String, b: String) -> List(String)
+
+@target(erlang)
+fn do_split_once(x: String, substring: String) -> Result(#(String, String), Nil) {
+ case erl_split(x, substring) {
+ [first, rest] -> Ok(#(first, rest))
+ _ -> Error(Nil)
+ }
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "split_once")
+fn do_split_once(
+ x x: String,
+ substring substring: String,
+) -> Result(#(String, String), Nil)
+
+/// Creates a new `String` by joining two `String`s together.
+///
+/// This function copies both `String`s and runs in linear time. If you find
+/// yourself joining `String`s frequently consider using the [`string_builder`](../gleam/string_builder.html)
+/// module as it can append `String`s much faster!
+///
+/// ## Examples
+///
+/// ```gleam
+/// > append(to: "butter", suffix: "fly")
+/// "butterfly"
+/// ```
+///
+pub fn append(to first: String, suffix second: String) -> String {
+ first
+ |> string_builder.from_string
+ |> string_builder.append(second)
+ |> string_builder.to_string
+}
+
+/// Creates a new `String` by joining many `String`s together.
+///
+/// This function copies both `String`s and runs in linear time. If you find
+/// yourself joining `String`s frequently consider using the [`string_builder`](../gleam/string_builder.html)
+/// module as it can append `String`s much faster!
+///
+/// ## Examples
+///
+/// ```gleam
+/// > concat(["never", "the", "less"])
+/// "nevertheless"
+/// ```
+///
+pub fn concat(strings: List(String)) -> String {
+ strings
+ |> string_builder.from_strings
+ |> string_builder.to_string
+}
+
+/// Creates a new `String` by repeating a `String` a given number of times.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > repeat("ha", times: 3)
+/// "hahaha"
+/// ```
+///
+pub fn repeat(string: String, times times: Int) -> String {
+ iterator.repeat(string)
+ |> iterator.take(times)
+ |> iterator.to_list
+ |> concat
+}
+
+/// Joins many `String`s together with a given separator.
+///
+/// This function runs in linear time.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > join(["home","evan","Desktop"], with: "/")
+/// "home/evan/Desktop"
+/// ```
+///
+pub fn join(strings: List(String), with separator: String) -> String {
+ do_join(strings, separator)
+}
+
+@target(erlang)
+fn do_join(strings: List(String), separator: String) -> String {
+ strings
+ |> list.intersperse(with: separator)
+ |> concat
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "join")
+fn do_join(strings strings: List(String), string string: String) -> String
+
+/// Pads a `String` on the left until it has at least given number of graphemes.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > pad_left("121", to: 5, with: ".")
+/// "..121"
+/// ```
+///
+/// ```gleam
+/// > pad_left("121", to: 3, with: ".")
+/// "121"
+/// ```
+///
+/// ```gleam
+/// > pad_left("121", to: 2, with: ".")
+/// "121"
+/// ```
+///
+pub fn pad_left(string: String, to desired_length: Int, with pad_string: String) {
+ let current_length = length(string)
+ let to_pad_length = desired_length - current_length
+ padding(to_pad_length, pad_string)
+ |> iterator.append(iterator.single(string))
+ |> iterator.to_list
+ |> concat
+}
+
+/// Pads a `String` on the right until it has a given length.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > pad_right("123", to: 5, with: ".")
+/// "123.."
+/// ```
+///
+/// ```gleam
+/// > pad_right("123", to: 3, with: ".")
+/// "123"
+/// ```
+///
+/// ```gleam
+/// > pad_right("123", to: 2, with: ".")
+/// "123"
+/// ```
+///
+pub fn pad_right(
+ string: String,
+ to desired_length: Int,
+ with pad_string: String,
+) {
+ let current_length = length(string)
+ let to_pad_length = desired_length - current_length
+ iterator.single(string)
+ |> iterator.append(padding(to_pad_length, pad_string))
+ |> iterator.to_list
+ |> concat
+}
+
+fn padding(size: Int, pad_string: String) -> Iterator(String) {
+ let pad_length = length(pad_string)
+ let num_pads = size / pad_length
+ let extra = size % pad_length
+ iterator.repeat(pad_string)
+ |> iterator.take(num_pads)
+ |> iterator.append(iterator.single(slice(pad_string, 0, extra)))
+}
+
+/// Removes whitespace on both sides of a `String`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > trim(" hats \n")
+/// "hats"
+/// ```
+///
+pub fn trim(string: String) -> String {
+ do_trim(string)
+}
+
+@target(erlang)
+fn do_trim(string: String) -> String {
+ erl_trim(string, Both)
+}
+
+@target(erlang)
+type Direction {
+ Leading
+ Trailing
+ Both
+}
+
+@target(erlang)
+@external(erlang, "string", "trim")
+fn erl_trim(a: String, b: Direction) -> String
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "trim")
+fn do_trim(string string: String) -> String
+
+/// Removes whitespace on the left of a `String`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > trim_left(" hats \n")
+/// "hats \n"
+/// ```
+///
+pub fn trim_left(string: String) -> String {
+ do_trim_left(string)
+}
+
+@target(erlang)
+fn do_trim_left(string: String) -> String {
+ erl_trim(string, Leading)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "trim_left")
+fn do_trim_left(string string: String) -> String
+
+/// Removes whitespace on the right of a `String`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > trim_right(" hats \n")
+/// " hats"
+/// ```
+///
+pub fn trim_right(string: String) -> String {
+ do_trim_right(string)
+}
+
+@target(erlang)
+fn do_trim_right(string: String) -> String {
+ erl_trim(string, Trailing)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "trim_right")
+fn do_trim_right(string string: String) -> String
+
+/// Splits a non-empty `String` into its first element (head) and rest (tail).
+/// This lets you pattern match on `String`s exactly as you would with lists.
+///
+/// Note on JavaScript using the function to iterate over a string will likely
+/// be slower than using `to_graphemes` due to string slicing being more
+/// expensive on JavaScript than Erlang.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > pop_grapheme("gleam")
+/// Ok(#("g", "leam"))
+/// ```
+///
+/// ```gleam
+/// > pop_grapheme("")
+/// Error(Nil)
+/// ```
+///
+pub fn pop_grapheme(string: String) -> Result(#(String, String), Nil) {
+ do_pop_grapheme(string)
+}
+
+@external(erlang, "gleam_stdlib", "string_pop_grapheme")
+@external(javascript, "../gleam_stdlib.mjs", "pop_grapheme")
+fn do_pop_grapheme(string string: String) -> Result(#(String, String), Nil)
+
+/// Converts a `String` to a list of
+/// [graphemes](https://en.wikipedia.org/wiki/Grapheme).
+///
+/// ```gleam
+/// > to_graphemes("abc")
+/// ["a", "b", "c"]
+/// ```
+///
+@external(javascript, "../gleam_stdlib.mjs", "graphemes")
+pub fn to_graphemes(string: String) -> List(String) {
+ do_to_graphemes(string, [])
+ |> list.reverse
+}
+
+fn do_to_graphemes(string: String, acc: List(String)) -> List(String) {
+ case pop_grapheme(string) {
+ Ok(#(grapheme, rest)) -> do_to_graphemes(rest, [grapheme, ..acc])
+ _ -> acc
+ }
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "codepoint")
+fn unsafe_int_to_utf_codepoint(a: Int) -> UtfCodepoint
+
+/// Converts a `String` to a `List` of `UtfCodepoint`.
+///
+/// See <https://en.wikipedia.org/wiki/Code_point> and
+/// <https://en.wikipedia.org/wiki/Unicode#Codespace_and_Code_Points> for an
+/// explanation on code points.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > "a" |> to_utf_codepoints
+/// [UtfCodepoint(97)]
+/// ```
+///
+/// ```gleam
+/// // Semantically the same as:
+/// // ["🏳", "️", "‍", "🌈"] or:
+/// // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow]
+/// > "🏳️‍🌈" |> to_utf_codepoints
+/// [UtfCodepoint(127987), UtfCodepoint(65039), UtfCodepoint(8205), UtfCodepoint(127752)]
+/// ```
+///
+pub fn to_utf_codepoints(string: String) -> List(UtfCodepoint) {
+ do_to_utf_codepoints(string)
+}
+
+@target(erlang)
+fn do_to_utf_codepoints(string: String) -> List(UtfCodepoint) {
+ do_to_utf_codepoints_impl(<<string:utf8>>, [])
+ |> list.reverse
+}
+
+@target(erlang)
+fn do_to_utf_codepoints_impl(
+ bit_array: BitArray,
+ acc: List(UtfCodepoint),
+) -> List(UtfCodepoint) {
+ case bit_array {
+ <<first:utf8_codepoint, rest:bytes>> ->
+ do_to_utf_codepoints_impl(rest, [first, ..acc])
+ _ -> acc
+ }
+}
+
+@target(javascript)
+fn do_to_utf_codepoints(string: String) -> List(UtfCodepoint) {
+ string
+ |> string_to_codepoint_integer_list
+ |> list.map(unsafe_int_to_utf_codepoint)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "string_to_codepoint_integer_list")
+fn string_to_codepoint_integer_list(a: String) -> List(Int)
+
+/// Converts a `List` of `UtfCodepoint`s to a `String`.
+///
+/// See <https://en.wikipedia.org/wiki/Code_point> and
+/// <https://en.wikipedia.org/wiki/Unicode#Codespace_and_Code_Points> for an
+/// explanation on code points.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > {
+/// > let assert #(Ok(a), Ok(b), Ok(c)) = #(
+/// > utf_codepoint(97),
+/// > utf_codepoint(98),
+/// > utf_codepoint(99),
+/// > )
+/// > [a, b, c]
+/// > }
+/// > |> from_utf_codepoints
+/// "abc"
+/// ```
+///
+@external(erlang, "gleam_stdlib", "utf_codepoint_list_to_string")
+@external(javascript, "../gleam_stdlib.mjs", "utf_codepoint_list_to_string")
+pub fn from_utf_codepoints(utf_codepoints: List(UtfCodepoint)) -> String
+
+/// Converts an integer to a `UtfCodepoint`.
+///
+/// Returns an `Error` if the integer does not represent a valid UTF codepoint.
+///
+pub fn utf_codepoint(value: Int) -> Result(UtfCodepoint, Nil) {
+ case value {
+ i if i > 1_114_111 -> Error(Nil)
+ 65_534 | 65_535 -> Error(Nil)
+ i if i >= 55_296 && i <= 57_343 -> Error(Nil)
+ i -> Ok(unsafe_int_to_utf_codepoint(i))
+ }
+}
+
+/// Converts an UtfCodepoint to its ordinal code point value.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert [utf_codepoint, ..] = to_utf_codepoints("💜")
+/// > utf_codepoint_to_int(utf_codepoint)
+/// 128156
+/// ```
+///
+pub fn utf_codepoint_to_int(cp: UtfCodepoint) -> Int {
+ do_utf_codepoint_to_int(cp)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "utf_codepoint_to_int")
+fn do_utf_codepoint_to_int(cp cp: UtfCodepoint) -> Int
+
+/// Converts a `String` into `Option(String)` where an empty `String` becomes
+/// `None`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > to_option("")
+/// None
+/// ```
+///
+/// ```gleam
+/// > to_option("hats")
+/// Some("hats")
+/// ```
+///
+pub fn to_option(s: String) -> Option(String) {
+ case s {
+ "" -> None
+ _ -> Some(s)
+ }
+}
+
+/// Returns the first grapheme cluster in a given `String` and wraps it in a
+/// `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.
+/// Otherwise, it returns `Ok(String)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > first("")
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > first("icecream")
+/// Ok("i")
+/// ```
+///
+pub fn first(s: String) -> Result(String, Nil) {
+ case pop_grapheme(s) {
+ Ok(#(first, _)) -> Ok(first)
+ Error(e) -> Error(e)
+ }
+}
+
+/// Returns the last grapheme cluster in a given `String` and wraps it in a
+/// `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.
+/// Otherwise, it returns `Ok(String)`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > last("")
+/// Error(Nil)
+/// ```
+///
+/// ```gleam
+/// > last("icecream")
+/// Ok("m")
+/// ```
+///
+pub fn last(s: String) -> Result(String, Nil) {
+ case pop_grapheme(s) {
+ Ok(#(first, "")) -> Ok(first)
+ Ok(#(_, rest)) -> Ok(slice(rest, -1, 1))
+ Error(e) -> Error(e)
+ }
+}
+
+/// Creates a new `String` with the first grapheme in the input `String`
+/// converted to uppercase and the remaining graphemes to lowercase.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > capitalise("mamouna")
+/// "Mamouna"
+/// ```
+///
+pub fn capitalise(s: String) -> String {
+ case pop_grapheme(s) {
+ Ok(#(first, rest)) -> append(to: uppercase(first), suffix: lowercase(rest))
+ _ -> ""
+ }
+}
+
+/// Returns a `String` representation of a term in Gleam syntax.
+///
+pub fn inspect(term: anything) -> String {
+ do_inspect(term)
+ |> string_builder.to_string
+}
+
+@external(erlang, "gleam_stdlib", "inspect")
+@external(javascript, "../gleam_stdlib.mjs", "inspect")
+fn do_inspect(term term: anything) -> StringBuilder
+
+/// Returns the number of bytes in a `String`.
+///
+/// This function runs in constant time on Erlang and in linear time on
+/// JavaScript.
+///
+@external(erlang, "erlang", "byte_size")
+@external(javascript, "../gleam_stdlib.mjs", "byte_size")
+pub fn byte_size(string: String) -> Int
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/string_builder.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/string_builder.gleam
new file mode 100644
index 0000000..5792ca8
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/string_builder.gleam
@@ -0,0 +1,298 @@
+import gleam/list
+
+/// `StringBuilder` is a type used for efficiently building strings.
+///
+/// When we append one string to another the strings must be copied to a
+/// new location in memory so that they can sit together. This behaviour
+/// enables efficient reading of the string but copying can be expensive,
+/// especially if we want to join many strings together.
+///
+/// `StringBuilder` is different in that it can be joined together in constant time
+/// using minimal memory, and then can be efficiently converted to a string
+/// using the `to_string` function.
+///
+/// On Erlang this type is compatible with Erlang's iodata. On JavaScript this
+/// type is compatible with normal strings.
+///
+pub type StringBuilder
+
+/// Create an empty `StringBuilder`. Useful as the start of a pipe chaining many
+/// builders together.
+///
+pub fn new() -> StringBuilder {
+ do_from_strings([])
+}
+
+/// Prepends a `String` onto the start of some `StringBuilder`.
+///
+/// Runs in constant time.
+///
+pub fn prepend(
+ to builder: StringBuilder,
+ prefix prefix: String,
+) -> StringBuilder {
+ append_builder(from_string(prefix), builder)
+}
+
+/// Appends a `String` onto the end of some `StringBuilder`.
+///
+/// Runs in constant time.
+///
+pub fn append(to builder: StringBuilder, suffix second: String) -> StringBuilder {
+ append_builder(builder, from_string(second))
+}
+
+/// Prepends some `StringBuilder` onto the start of another.
+///
+/// Runs in constant time.
+///
+pub fn prepend_builder(
+ to builder: StringBuilder,
+ prefix prefix: StringBuilder,
+) -> StringBuilder {
+ do_append(prefix, builder)
+}
+
+/// Appends some `StringBuilder` onto the end of another.
+///
+/// Runs in constant time.
+///
+pub fn append_builder(
+ to builder: StringBuilder,
+ suffix suffix: StringBuilder,
+) -> StringBuilder {
+ do_append(builder, suffix)
+}
+
+@external(erlang, "gleam_stdlib", "iodata_append")
+@external(javascript, "../gleam_stdlib.mjs", "add")
+fn do_append(a: StringBuilder, b: StringBuilder) -> StringBuilder
+
+/// Converts a list of strings into a builder.
+///
+/// Runs in constant time.
+///
+pub fn from_strings(strings: List(String)) -> StringBuilder {
+ do_from_strings(strings)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "concat")
+fn do_from_strings(a: List(String)) -> StringBuilder
+
+/// Joins a list of builders into a single builder.
+///
+/// Runs in constant time.
+///
+pub fn concat(builders: List(StringBuilder)) -> StringBuilder {
+ do_concat(builders)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "concat")
+fn do_concat(a: List(StringBuilder)) -> StringBuilder
+
+/// Converts a string into a builder.
+///
+/// Runs in constant time.
+///
+pub fn from_string(string: String) -> StringBuilder {
+ do_from_string(string)
+}
+
+@external(erlang, "gleam_stdlib", "identity")
+@external(javascript, "../gleam_stdlib.mjs", "identity")
+fn do_from_string(a: String) -> StringBuilder
+
+/// Turns an `StringBuilder` into a `String`
+///
+/// This function is implemented natively by the virtual machine and is highly
+/// optimised.
+///
+pub fn to_string(builder: StringBuilder) -> String {
+ do_to_string(builder)
+}
+
+@external(erlang, "unicode", "characters_to_binary")
+@external(javascript, "../gleam_stdlib.mjs", "identity")
+fn do_to_string(a: StringBuilder) -> String
+
+/// Returns the size of the `StringBuilder` in bytes.
+///
+pub fn byte_size(builder: StringBuilder) -> Int {
+ do_byte_size(builder)
+}
+
+@external(erlang, "erlang", "iolist_size")
+@external(javascript, "../gleam_stdlib.mjs", "length")
+fn do_byte_size(a: StringBuilder) -> Int
+
+/// Joins the given builders into a new builder separated with the given string
+///
+pub fn join(builders: List(StringBuilder), with sep: String) -> StringBuilder {
+ builders
+ |> list.intersperse(from_string(sep))
+ |> concat
+}
+
+/// Converts a builder to a new builder where the contents have been
+/// lowercased.
+///
+pub fn lowercase(builder: StringBuilder) -> StringBuilder {
+ do_lowercase(builder)
+}
+
+@external(erlang, "string", "lowercase")
+@external(javascript, "../gleam_stdlib.mjs", "lowercase")
+fn do_lowercase(a: StringBuilder) -> StringBuilder
+
+/// Converts a builder to a new builder where the contents have been
+/// uppercased.
+///
+pub fn uppercase(builder: StringBuilder) -> StringBuilder {
+ do_uppercase(builder)
+}
+
+@external(erlang, "string", "uppercase")
+@external(javascript, "../gleam_stdlib.mjs", "uppercase")
+fn do_uppercase(a: StringBuilder) -> StringBuilder
+
+/// Converts a builder to a new builder with the contents reversed.
+///
+pub fn reverse(builder: StringBuilder) -> StringBuilder {
+ do_reverse(builder)
+}
+
+@target(erlang)
+@external(erlang, "string", "reverse")
+fn do_reverse(a: StringBuilder) -> StringBuilder
+
+@target(javascript)
+fn do_reverse(builder: StringBuilder) -> StringBuilder {
+ builder
+ |> to_string
+ |> do_to_graphemes
+ |> list.reverse
+ |> from_strings
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "graphemes")
+fn do_to_graphemes(string string: String) -> List(String)
+
+/// Splits a builder on a given pattern into a list of builders.
+///
+pub fn split(iodata: StringBuilder, on pattern: String) -> List(StringBuilder) {
+ do_split(iodata, pattern)
+}
+
+@target(erlang)
+type Direction {
+ All
+}
+
+@target(erlang)
+@external(erlang, "string", "split")
+fn erl_split(a: StringBuilder, b: String, c: Direction) -> List(StringBuilder)
+
+@target(erlang)
+fn do_split(iodata: StringBuilder, pattern: String) -> List(StringBuilder) {
+ erl_split(iodata, pattern, All)
+}
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "split")
+fn do_split(
+ builder builder: StringBuilder,
+ pattern pattern: String,
+) -> List(StringBuilder)
+
+/// Replaces all instances of a pattern with a given string substitute.
+///
+pub fn replace(
+ in builder: StringBuilder,
+ each pattern: String,
+ with substitute: String,
+) -> StringBuilder {
+ do_replace(builder, pattern, substitute)
+}
+
+@target(erlang)
+fn do_replace(
+ iodata: StringBuilder,
+ pattern: String,
+ substitute: String,
+) -> StringBuilder {
+ erl_replace(iodata, pattern, substitute, All)
+}
+
+@target(erlang)
+@external(erlang, "string", "replace")
+fn erl_replace(
+ a: StringBuilder,
+ b: String,
+ c: String,
+ d: Direction,
+) -> StringBuilder
+
+@target(javascript)
+@external(javascript, "../gleam_stdlib.mjs", "string_replace")
+fn do_replace(a: StringBuilder, b: String, c: String) -> StringBuilder
+
+/// Compares two builders to determine if they have the same textual content.
+///
+/// Comparing two iodata using the `==` operator may return `False` even if they
+/// have the same content as they may have been build in different ways, so
+/// using this function is often preferred.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_strings(["a", "b"]) == from_string("ab")
+/// False
+/// ```
+///
+/// ```gleam
+/// > is_equal(from_strings(["a", "b"]), from_string("ab"))
+/// True
+/// ```
+///
+pub fn is_equal(a: StringBuilder, b: StringBuilder) -> Bool {
+ do_is_equal(a, b)
+}
+
+@external(erlang, "string", "equal")
+@external(javascript, "../gleam_stdlib.mjs", "equal")
+fn do_is_equal(a: StringBuilder, b: StringBuilder) -> Bool
+
+/// Inspects a builder to determine if it is equivalent to an empty string.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > from_string("ok") |> is_empty
+/// False
+/// ```
+///
+/// ```gleam
+/// > from_string("") |> is_empty
+/// True
+/// ```
+///
+/// ```gleam
+/// > from_strings([]) |> is_empty
+/// True
+/// ```
+///
+pub fn is_empty(builder: StringBuilder) -> Bool {
+ do_is_empty(builder)
+}
+
+@target(erlang)
+@external(erlang, "string", "is_empty")
+fn do_is_empty(a: StringBuilder) -> Bool
+
+@target(javascript)
+fn do_is_empty(builder: StringBuilder) -> Bool {
+ from_string("") == builder
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/uri.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/uri.gleam
new file mode 100644
index 0000000..11f6ea6
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/uri.gleam
@@ -0,0 +1,462 @@
+//// Utilities for working with URIs
+////
+//// This module provides functions for working with URIs (for example, parsing
+//// URIs or encoding query strings). The functions in this module are implemented
+//// according to [RFC 3986](https://tools.ietf.org/html/rfc3986).
+////
+//// Query encoding (Form encoding) is defined in the
+//// [W3C specification](https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data).
+
+import gleam/int
+import gleam/list
+import gleam/option.{type Option, None, Some}
+import gleam/string
+import gleam/string_builder.{type StringBuilder}
+@target(javascript)
+import gleam/pair
+@target(javascript)
+import gleam/regex
+@target(javascript)
+import gleam/result
+
+/// Type representing holding the parsed components of an URI.
+/// All components of a URI are optional, except the path.
+///
+pub type Uri {
+ Uri(
+ scheme: Option(String),
+ userinfo: Option(String),
+ host: Option(String),
+ port: Option(Int),
+ path: String,
+ query: Option(String),
+ fragment: Option(String),
+ )
+}
+
+/// Parses a compliant URI string into the `Uri` Type.
+/// If the string is not a valid URI string then an error is returned.
+///
+/// The opposite operation is `uri.to_string`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > parse("https://example.com:1234/a/b?query=true#fragment")
+/// Ok(
+/// Uri(
+/// scheme: Some("https"),
+/// userinfo: None,
+/// host: Some("example.com"),
+/// port: Some(1234),
+/// path: "/a/b",
+/// query: Some("query=true"),
+/// fragment: Some("fragment")
+/// )
+/// )
+/// ```
+///
+pub fn parse(uri_string: String) -> Result(Uri, Nil) {
+ do_parse(uri_string)
+}
+
+@target(erlang)
+@external(erlang, "gleam_stdlib", "uri_parse")
+fn do_parse(a: String) -> Result(Uri, Nil)
+
+@target(javascript)
+fn do_parse(uri_string: String) -> Result(Uri, Nil) {
+ // From https://tools.ietf.org/html/rfc3986#appendix-B
+ let pattern =
+ // 12 3 4 5 6 7 8
+ "^(([a-z][a-z0-9\\+\\-\\.]*):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#.*)?"
+ let matches =
+ pattern
+ |> regex_submatches(uri_string)
+ |> pad_list(8)
+
+ let #(scheme, authority, path, query, fragment) = case matches {
+ [
+ _scheme_with_colon,
+ scheme,
+ authority_with_slashes,
+ _authority,
+ path,
+ query_with_question_mark,
+ _query,
+ fragment,
+ ] -> #(
+ scheme,
+ authority_with_slashes,
+ path,
+ query_with_question_mark,
+ fragment,
+ )
+ _ -> #(None, None, None, None, None)
+ }
+
+ let scheme = noneify_empty_string(scheme)
+ let path = option.unwrap(path, "")
+ let query = noneify_query(query)
+ let #(userinfo, host, port) = split_authority(authority)
+ let fragment =
+ fragment
+ |> option.to_result(Nil)
+ |> result.try(string.pop_grapheme)
+ |> result.map(pair.second)
+ |> option.from_result
+ let scheme =
+ scheme
+ |> noneify_empty_string
+ |> option.map(string.lowercase)
+ Ok(Uri(
+ scheme: scheme,
+ userinfo: userinfo,
+ host: host,
+ port: port,
+ path: path,
+ query: query,
+ fragment: fragment,
+ ))
+}
+
+@target(javascript)
+fn regex_submatches(pattern: String, string: String) -> List(Option(String)) {
+ pattern
+ |> regex.compile(regex.Options(case_insensitive: True, multi_line: False))
+ |> result.nil_error
+ |> result.map(regex.scan(_, string))
+ |> result.try(list.first)
+ |> result.map(fn(m: regex.Match) { m.submatches })
+ |> result.unwrap([])
+}
+
+@target(javascript)
+fn noneify_query(x: Option(String)) -> Option(String) {
+ case x {
+ None -> None
+ Some(x) ->
+ case string.pop_grapheme(x) {
+ Ok(#("?", query)) -> Some(query)
+ _ -> None
+ }
+ }
+}
+
+@target(javascript)
+fn noneify_empty_string(x: Option(String)) -> Option(String) {
+ case x {
+ Some("") | None -> None
+ Some(_) -> x
+ }
+}
+
+// Split an authority into its userinfo, host and port parts.
+@target(javascript)
+fn split_authority(
+ authority: Option(String),
+) -> #(Option(String), Option(String), Option(Int)) {
+ case option.unwrap(authority, "") {
+ "" -> #(None, None, None)
+ "//" -> #(None, Some(""), None)
+ authority -> {
+ let matches =
+ "^(//)?((.*)@)?(\\[[a-zA-Z0-9:.]*\\]|[^:]*)(:(\\d*))?"
+ |> regex_submatches(authority)
+ |> pad_list(6)
+ case matches {
+ [_, _, userinfo, host, _, port] -> {
+ let userinfo = noneify_empty_string(userinfo)
+ let host = noneify_empty_string(host)
+ let port =
+ port
+ |> option.unwrap("")
+ |> int.parse
+ |> option.from_result
+ #(userinfo, host, port)
+ }
+ _ -> #(None, None, None)
+ }
+ }
+ }
+}
+
+@target(javascript)
+fn pad_list(list: List(Option(a)), size: Int) -> List(Option(a)) {
+ list
+ |> list.append(list.repeat(None, extra_required(list, size)))
+}
+
+@target(javascript)
+fn extra_required(list: List(a), remaining: Int) -> Int {
+ case list {
+ _ if remaining == 0 -> 0
+ [] -> remaining
+ [_, ..xs] -> extra_required(xs, remaining - 1)
+ }
+}
+
+/// Parses an urlencoded query string into a list of key value pairs.
+/// Returns an error for invalid encoding.
+///
+/// The opposite operation is `uri.query_to_string`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > parse_query("a=1&b=2")
+/// Ok([#("a", "1"), #("b", "2")])
+/// ```
+///
+pub fn parse_query(query: String) -> Result(List(#(String, String)), Nil) {
+ do_parse_query(query)
+}
+
+@external(erlang, "gleam_stdlib", "parse_query")
+@external(javascript, "../gleam_stdlib.mjs", "parse_query")
+fn do_parse_query(a: String) -> Result(List(#(String, String)), Nil)
+
+/// Encodes a list of key value pairs as a URI query string.
+///
+/// The opposite operation is `uri.parse_query`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > query_to_string([#("a", "1"), #("b", "2")])
+/// "a=1&b=2"
+/// ```
+///
+pub fn query_to_string(query: List(#(String, String))) -> String {
+ query
+ |> list.map(query_pair)
+ |> list.intersperse(string_builder.from_string("&"))
+ |> string_builder.concat
+ |> string_builder.to_string
+}
+
+fn query_pair(pair: #(String, String)) -> StringBuilder {
+ string_builder.from_strings([
+ percent_encode(pair.0),
+ "=",
+ percent_encode(pair.1),
+ ])
+}
+
+/// Encodes a string into a percent encoded representation.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > percent_encode("100% great")
+/// "100%25%20great"
+/// ```
+///
+pub fn percent_encode(value: String) -> String {
+ do_percent_encode(value)
+}
+
+@external(erlang, "gleam_stdlib", "percent_encode")
+@external(javascript, "../gleam_stdlib.mjs", "percent_encode")
+fn do_percent_encode(a: String) -> String
+
+/// Decodes a percent encoded string.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > percent_decode("100%25+great")
+/// Ok("100% great")
+/// ```
+///
+pub fn percent_decode(value: String) -> Result(String, Nil) {
+ do_percent_decode(value)
+}
+
+@external(erlang, "gleam_stdlib", "percent_decode")
+@external(javascript, "../gleam_stdlib.mjs", "percent_decode")
+fn do_percent_decode(a: String) -> Result(String, Nil)
+
+fn do_remove_dot_segments(
+ input: List(String),
+ accumulator: List(String),
+) -> List(String) {
+ case input {
+ [] -> list.reverse(accumulator)
+ [segment, ..rest] -> {
+ let accumulator = case segment, accumulator {
+ "", accumulator -> accumulator
+ ".", accumulator -> accumulator
+ "..", [] -> []
+ "..", [_, ..accumulator] -> accumulator
+ segment, accumulator -> [segment, ..accumulator]
+ }
+ do_remove_dot_segments(rest, accumulator)
+ }
+ }
+}
+
+fn remove_dot_segments(input: List(String)) -> List(String) {
+ do_remove_dot_segments(input, [])
+}
+
+/// Splits the path section of a URI into it's constituent segments.
+///
+/// Removes empty segments and resolves dot-segments as specified in
+/// [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > path_segments("/users/1")
+/// ["users" ,"1"]
+/// ```
+///
+pub fn path_segments(path: String) -> List(String) {
+ remove_dot_segments(string.split(path, "/"))
+}
+
+/// Encodes a `Uri` value as a URI string.
+///
+/// The opposite operation is `uri.parse`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let uri = Uri(Some("http"), None, Some("example.com"), ...)
+/// > to_string(uri)
+/// "http://example.com"
+/// ```
+///
+pub fn to_string(uri: Uri) -> String {
+ let parts = case uri.fragment {
+ Some(fragment) -> ["#", fragment]
+ _ -> []
+ }
+ let parts = case uri.query {
+ Some(query) -> ["?", query, ..parts]
+ _ -> parts
+ }
+ let parts = [uri.path, ..parts]
+ let parts = case uri.host, string.starts_with(uri.path, "/") {
+ Some(host), False if host != "" -> ["/", ..parts]
+ _, _ -> parts
+ }
+ let parts = case uri.host, uri.port {
+ Some(_), Some(port) -> [":", int.to_string(port), ..parts]
+ _, _ -> parts
+ }
+ let parts = case uri.scheme, uri.userinfo, uri.host {
+ Some(s), Some(u), Some(h) -> [s, "://", u, "@", h, ..parts]
+ Some(s), None, Some(h) -> [s, "://", h, ..parts]
+ Some(s), Some(_), None | Some(s), None, None -> [s, ":", ..parts]
+ None, None, Some(h) -> ["//", h, ..parts]
+ _, _, _ -> parts
+ }
+ string.concat(parts)
+}
+
+/// Fetches the origin of a URI.
+///
+/// Returns the origin of a uri as defined in
+/// [RFC 6454](https://tools.ietf.org/html/rfc6454)
+///
+/// The supported URI schemes are `http` and `https`.
+/// URLs without a scheme will return `Error`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > let assert Ok(uri) = parse("http://example.com/path?foo#bar")
+/// > origin(uri)
+/// Ok("http://example.com")
+/// ```
+///
+pub fn origin(uri: Uri) -> Result(String, Nil) {
+ let Uri(scheme: scheme, host: host, port: port, ..) = uri
+ case scheme {
+ Some("https") if port == Some(443) -> {
+ let origin = Uri(scheme, None, host, None, "", None, None)
+ Ok(to_string(origin))
+ }
+ Some("http") if port == Some(80) -> {
+ let origin = Uri(scheme, None, host, None, "", None, None)
+ Ok(to_string(origin))
+ }
+ Some(s) if s == "http" || s == "https" -> {
+ let origin = Uri(scheme, None, host, port, "", None, None)
+ Ok(to_string(origin))
+ }
+ _ -> Error(Nil)
+ }
+}
+
+fn drop_last(elements: List(a)) -> List(a) {
+ list.take(from: elements, up_to: list.length(elements) - 1)
+}
+
+fn join_segments(segments: List(String)) -> String {
+ string.join(["", ..segments], "/")
+}
+
+/// Resolves a URI with respect to the given base URI.
+///
+/// The base URI must be an absolute URI or this function will return an error.
+/// The algorithm for merging uris is described in
+/// [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2).
+///
+pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) {
+ case base {
+ Uri(scheme: Some(_), host: Some(_), ..) ->
+ case relative {
+ Uri(host: Some(_), ..) -> {
+ let path =
+ string.split(relative.path, "/")
+ |> remove_dot_segments()
+ |> join_segments()
+ let resolved =
+ Uri(
+ option.or(relative.scheme, base.scheme),
+ None,
+ relative.host,
+ option.or(relative.port, base.port),
+ path,
+ relative.query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ _ -> {
+ let #(new_path, new_query) = case relative.path {
+ "" -> #(base.path, option.or(relative.query, base.query))
+ _ -> {
+ let path_segments = case string.starts_with(relative.path, "/") {
+ True -> string.split(relative.path, "/")
+ False ->
+ string.split(base.path, "/")
+ |> drop_last()
+ |> list.append(string.split(relative.path, "/"))
+ }
+ let path =
+ path_segments
+ |> remove_dot_segments()
+ |> join_segments()
+ #(path, relative.query)
+ }
+ }
+ let resolved =
+ Uri(
+ base.scheme,
+ None,
+ base.host,
+ base.port,
+ new_path,
+ new_query,
+ relative.fragment,
+ )
+ Ok(resolved)
+ }
+ }
+ _ -> Error(Nil)
+ }
+}
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@base.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@base.erl
new file mode 100644
index 0000000..65bc3f6
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@base.erl
@@ -0,0 +1,20 @@
+-module(gleam@base).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([encode64/2, decode64/1, url_encode64/2, url_decode64/1]).
+
+-spec encode64(bitstring(), boolean()) -> binary().
+encode64(Input, Padding) ->
+ gleam@bit_array:base64_encode(Input, Padding).
+
+-spec decode64(binary()) -> {ok, bitstring()} | {error, nil}.
+decode64(Encoded) ->
+ gleam@bit_array:base64_decode(Encoded).
+
+-spec url_encode64(bitstring(), boolean()) -> binary().
+url_encode64(Input, Padding) ->
+ gleam@bit_array:base64_url_encode(Input, Padding).
+
+-spec url_decode64(binary()) -> {ok, bitstring()} | {error, nil}.
+url_decode64(Encoded) ->
+ gleam@bit_array:base64_url_decode(Encoded).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_array.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_array.erl
new file mode 100644
index 0000000..ba18dfa
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_array.erl
@@ -0,0 +1,102 @@
+-module(gleam@bit_array).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([from_string/1, byte_size/1, slice/3, is_utf8/1, to_string/1, concat/1, append/2, base64_encode/2, base64_decode/1, base64_url_encode/2, base64_url_decode/1, base16_encode/1, base16_decode/1]).
+
+-spec from_string(binary()) -> bitstring().
+from_string(X) ->
+ gleam_stdlib:identity(X).
+
+-spec byte_size(bitstring()) -> integer().
+byte_size(X) ->
+ erlang:byte_size(X).
+
+-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} |
+ {error, nil}.
+slice(String, Position, Length) ->
+ gleam_stdlib:bit_array_slice(String, Position, Length).
+
+-spec do_is_utf8(bitstring()) -> boolean().
+do_is_utf8(Bits) ->
+ case Bits of
+ <<>> ->
+ true;
+
+ <<_/utf8, Rest/binary>> ->
+ do_is_utf8(Rest);
+
+ _ ->
+ false
+ end.
+
+-spec is_utf8(bitstring()) -> boolean().
+is_utf8(Bits) ->
+ do_is_utf8(Bits).
+
+-spec do_to_string(bitstring()) -> {ok, binary()} | {error, nil}.
+do_to_string(Bits) ->
+ case is_utf8(Bits) of
+ true ->
+ {ok, gleam_stdlib:identity(Bits)};
+
+ false ->
+ {error, nil}
+ end.
+
+-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}.
+to_string(Bits) ->
+ do_to_string(Bits).
+
+-spec concat(list(bitstring())) -> bitstring().
+concat(Bit_arrays) ->
+ gleam_stdlib:bit_array_concat(Bit_arrays).
+
+-spec append(bitstring(), bitstring()) -> bitstring().
+append(First, Second) ->
+ gleam_stdlib:bit_array_concat([First, Second]).
+
+-spec base64_encode(bitstring(), boolean()) -> binary().
+base64_encode(Input, Padding) ->
+ Encoded = base64:encode(Input),
+ case Padding of
+ true ->
+ Encoded;
+
+ false ->
+ gleam@string:replace(Encoded, <<"="/utf8>>, <<""/utf8>>)
+ end.
+
+-spec base64_decode(binary()) -> {ok, bitstring()} | {error, nil}.
+base64_decode(Encoded) ->
+ Padded = case erlang:byte_size(gleam_stdlib:identity(Encoded)) rem 4 of
+ 0 ->
+ Encoded;
+
+ N ->
+ gleam@string:append(
+ Encoded,
+ gleam@string:repeat(<<"="/utf8>>, 4 - N)
+ )
+ end,
+ gleam_stdlib:base_decode64(Padded).
+
+-spec base64_url_encode(bitstring(), boolean()) -> binary().
+base64_url_encode(Input, Padding) ->
+ _pipe = base64_encode(Input, Padding),
+ _pipe@1 = gleam@string:replace(_pipe, <<"+"/utf8>>, <<"-"/utf8>>),
+ gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"_"/utf8>>).
+
+-spec base64_url_decode(binary()) -> {ok, bitstring()} | {error, nil}.
+base64_url_decode(Encoded) ->
+ _pipe = Encoded,
+ _pipe@1 = gleam@string:replace(_pipe, <<"-"/utf8>>, <<"+"/utf8>>),
+ _pipe@2 = gleam@string:replace(_pipe@1, <<"_"/utf8>>, <<"/"/utf8>>),
+ base64_decode(_pipe@2).
+
+-spec base16_encode(bitstring()) -> binary().
+base16_encode(Input) ->
+ binary:encode_hex(Input).
+
+-spec base16_decode(binary()) -> {ok, bitstring()} | {error, nil}.
+base16_decode(Input) ->
+ gleam_stdlib:base16_decode(Input).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_builder.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_builder.erl
new file mode 100644
index 0000000..284c6d4
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_builder.erl
@@ -0,0 +1,66 @@
+-module(gleam@bit_builder).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([new/0, prepend/2, append/2, prepend_builder/2, append_builder/2, prepend_string/2, append_string/2, concat/1, concat_bit_strings/1, from_string/1, from_string_builder/1, from_bit_string/1, to_bit_string/1, byte_size/1]).
+
+-spec new() -> gleam@bytes_builder:bytes_builder().
+new() ->
+ gleam@bytes_builder:new().
+
+-spec prepend(gleam@bytes_builder:bytes_builder(), bitstring()) -> gleam@bytes_builder:bytes_builder().
+prepend(To, Prefix) ->
+ gleam@bytes_builder:prepend(To, Prefix).
+
+-spec append(gleam@bytes_builder:bytes_builder(), bitstring()) -> gleam@bytes_builder:bytes_builder().
+append(To, Suffix) ->
+ gleam@bytes_builder:append(To, Suffix).
+
+-spec prepend_builder(
+ gleam@bytes_builder:bytes_builder(),
+ gleam@bytes_builder:bytes_builder()
+) -> gleam@bytes_builder:bytes_builder().
+prepend_builder(To, Prefix) ->
+ gleam@bytes_builder:prepend_builder(To, Prefix).
+
+-spec append_builder(
+ gleam@bytes_builder:bytes_builder(),
+ gleam@bytes_builder:bytes_builder()
+) -> gleam@bytes_builder:bytes_builder().
+append_builder(First, Second) ->
+ gleam_stdlib:iodata_append(First, Second).
+
+-spec prepend_string(gleam@bytes_builder:bytes_builder(), binary()) -> gleam@bytes_builder:bytes_builder().
+prepend_string(To, Prefix) ->
+ gleam@bytes_builder:prepend_string(To, Prefix).
+
+-spec append_string(gleam@bytes_builder:bytes_builder(), binary()) -> gleam@bytes_builder:bytes_builder().
+append_string(To, Suffix) ->
+ gleam@bytes_builder:append_string(To, Suffix).
+
+-spec concat(list(gleam@bytes_builder:bytes_builder())) -> gleam@bytes_builder:bytes_builder().
+concat(Builders) ->
+ gleam_stdlib:identity(Builders).
+
+-spec concat_bit_strings(list(bitstring())) -> gleam@bytes_builder:bytes_builder().
+concat_bit_strings(Bits) ->
+ gleam_stdlib:identity(Bits).
+
+-spec from_string(binary()) -> gleam@bytes_builder:bytes_builder().
+from_string(String) ->
+ gleam_stdlib:wrap_list(String).
+
+-spec from_string_builder(gleam@string_builder:string_builder()) -> gleam@bytes_builder:bytes_builder().
+from_string_builder(Builder) ->
+ gleam_stdlib:wrap_list(Builder).
+
+-spec from_bit_string(bitstring()) -> gleam@bytes_builder:bytes_builder().
+from_bit_string(Bits) ->
+ gleam_stdlib:wrap_list(Bits).
+
+-spec to_bit_string(gleam@bytes_builder:bytes_builder()) -> bitstring().
+to_bit_string(Builder) ->
+ erlang:list_to_bitstring(Builder).
+
+-spec byte_size(gleam@bytes_builder:bytes_builder()) -> integer().
+byte_size(Builder) ->
+ erlang:iolist_size(Builder).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_string.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_string.erl
new file mode 100644
index 0000000..7dabaa3
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@bit_string.erl
@@ -0,0 +1,33 @@
+-module(gleam@bit_string).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([from_string/1, byte_size/1, append/2, slice/3, is_utf8/1, to_string/1, concat/1]).
+
+-spec from_string(binary()) -> bitstring().
+from_string(X) ->
+ gleam_stdlib:identity(X).
+
+-spec byte_size(bitstring()) -> integer().
+byte_size(X) ->
+ erlang:byte_size(X).
+
+-spec append(bitstring(), bitstring()) -> bitstring().
+append(First, Second) ->
+ gleam@bit_array:append(First, Second).
+
+-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} |
+ {error, nil}.
+slice(String, Position, Length) ->
+ gleam_stdlib:bit_array_slice(String, Position, Length).
+
+-spec is_utf8(bitstring()) -> boolean().
+is_utf8(Bits) ->
+ gleam@bit_array:is_utf8(Bits).
+
+-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}.
+to_string(Bits) ->
+ gleam@bit_array:to_string(Bits).
+
+-spec concat(list(bitstring())) -> bitstring().
+concat(Bit_strings) ->
+ gleam_stdlib:bit_array_concat(Bit_strings).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@bool.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@bool.erl
new file mode 100644
index 0000000..cd55358
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@bool.erl
@@ -0,0 +1,162 @@
+-module(gleam@bool).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export(['and'/2, 'or'/2, negate/1, nor/2, nand/2, exclusive_or/2, exclusive_nor/2, compare/2, max/2, min/2, to_int/1, to_string/1, guard/3, lazy_guard/3]).
+
+-spec 'and'(boolean(), boolean()) -> boolean().
+'and'(A, B) ->
+ A andalso B.
+
+-spec 'or'(boolean(), boolean()) -> boolean().
+'or'(A, B) ->
+ A orelse B.
+
+-spec negate(boolean()) -> boolean().
+negate(Bool) ->
+ case Bool of
+ true ->
+ false;
+
+ false ->
+ true
+ end.
+
+-spec nor(boolean(), boolean()) -> boolean().
+nor(A, B) ->
+ case {A, B} of
+ {false, false} ->
+ true;
+
+ {false, true} ->
+ false;
+
+ {true, false} ->
+ false;
+
+ {true, true} ->
+ false
+ end.
+
+-spec nand(boolean(), boolean()) -> boolean().
+nand(A, B) ->
+ case {A, B} of
+ {false, false} ->
+ true;
+
+ {false, true} ->
+ true;
+
+ {true, false} ->
+ true;
+
+ {true, true} ->
+ false
+ end.
+
+-spec exclusive_or(boolean(), boolean()) -> boolean().
+exclusive_or(A, B) ->
+ case {A, B} of
+ {false, false} ->
+ false;
+
+ {false, true} ->
+ true;
+
+ {true, false} ->
+ true;
+
+ {true, true} ->
+ false
+ end.
+
+-spec exclusive_nor(boolean(), boolean()) -> boolean().
+exclusive_nor(A, B) ->
+ case {A, B} of
+ {false, false} ->
+ true;
+
+ {false, true} ->
+ false;
+
+ {true, false} ->
+ false;
+
+ {true, true} ->
+ true
+ end.
+
+-spec compare(boolean(), boolean()) -> gleam@order:order().
+compare(A, B) ->
+ case {A, B} of
+ {true, true} ->
+ eq;
+
+ {true, false} ->
+ gt;
+
+ {false, false} ->
+ eq;
+
+ {false, true} ->
+ lt
+ end.
+
+-spec max(boolean(), boolean()) -> boolean().
+max(A, B) ->
+ case A of
+ true ->
+ true;
+
+ false ->
+ B
+ end.
+
+-spec min(boolean(), boolean()) -> boolean().
+min(A, B) ->
+ case A of
+ false ->
+ false;
+
+ true ->
+ B
+ end.
+
+-spec to_int(boolean()) -> integer().
+to_int(Bool) ->
+ case Bool of
+ false ->
+ 0;
+
+ true ->
+ 1
+ end.
+
+-spec to_string(boolean()) -> binary().
+to_string(Bool) ->
+ case Bool of
+ false ->
+ <<"False"/utf8>>;
+
+ true ->
+ <<"True"/utf8>>
+ end.
+
+-spec guard(boolean(), DDZ, fun(() -> DDZ)) -> DDZ.
+guard(Requirement, Consequence, Alternative) ->
+ case Requirement of
+ true ->
+ Consequence;
+
+ false ->
+ Alternative()
+ end.
+
+-spec lazy_guard(boolean(), fun(() -> DEA), fun(() -> DEA)) -> DEA.
+lazy_guard(Requirement, Consequence, Alternative) ->
+ case Requirement of
+ true ->
+ Consequence();
+
+ false ->
+ Alternative()
+ end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@bytes_builder.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@bytes_builder.erl
new file mode 100644
index 0000000..2f6dd93
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@bytes_builder.erl
@@ -0,0 +1,87 @@
+-module(gleam@bytes_builder).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([append_builder/2, prepend_builder/2, concat/1, new/0, from_string/1, prepend_string/2, append_string/2, from_string_builder/1, from_bit_array/1, prepend/2, append/2, concat_bit_arrays/1, to_bit_array/1, byte_size/1]).
+-export_type([bytes_builder/0]).
+
+-opaque bytes_builder() :: {bytes, bitstring()} |
+ {text, gleam@string_builder:string_builder()} |
+ {many, list(bytes_builder())}.
+
+-spec append_builder(bytes_builder(), bytes_builder()) -> bytes_builder().
+append_builder(First, Second) ->
+ gleam_stdlib:iodata_append(First, Second).
+
+-spec prepend_builder(bytes_builder(), bytes_builder()) -> bytes_builder().
+prepend_builder(Second, First) ->
+ gleam_stdlib:iodata_append(First, Second).
+
+-spec concat(list(bytes_builder())) -> bytes_builder().
+concat(Builders) ->
+ gleam_stdlib:identity(Builders).
+
+-spec new() -> bytes_builder().
+new() ->
+ gleam_stdlib:identity([]).
+
+-spec from_string(binary()) -> bytes_builder().
+from_string(String) ->
+ gleam_stdlib:wrap_list(String).
+
+-spec prepend_string(bytes_builder(), binary()) -> bytes_builder().
+prepend_string(Second, First) ->
+ gleam_stdlib:iodata_append(gleam_stdlib:wrap_list(First), Second).
+
+-spec append_string(bytes_builder(), binary()) -> bytes_builder().
+append_string(First, Second) ->
+ gleam_stdlib:iodata_append(First, gleam_stdlib:wrap_list(Second)).
+
+-spec from_string_builder(gleam@string_builder:string_builder()) -> bytes_builder().
+from_string_builder(Builder) ->
+ gleam_stdlib:wrap_list(Builder).
+
+-spec from_bit_array(bitstring()) -> bytes_builder().
+from_bit_array(Bits) ->
+ gleam_stdlib:wrap_list(Bits).
+
+-spec prepend(bytes_builder(), bitstring()) -> bytes_builder().
+prepend(Second, First) ->
+ gleam_stdlib:iodata_append(gleam_stdlib:wrap_list(First), Second).
+
+-spec append(bytes_builder(), bitstring()) -> bytes_builder().
+append(First, Second) ->
+ gleam_stdlib:iodata_append(First, gleam_stdlib:wrap_list(Second)).
+
+-spec concat_bit_arrays(list(bitstring())) -> bytes_builder().
+concat_bit_arrays(Bits) ->
+ gleam_stdlib:identity(Bits).
+
+-spec to_list(list(list(bytes_builder())), list(bitstring())) -> list(bitstring()).
+to_list(Stack, Acc) ->
+ case Stack of
+ [] ->
+ Acc;
+
+ [[] | Remaining_stack] ->
+ to_list(Remaining_stack, Acc);
+
+ [[{bytes, Bits} | Rest] | Remaining_stack@1] ->
+ to_list([Rest | Remaining_stack@1], [Bits | Acc]);
+
+ [[{text, Builder} | Rest@1] | Remaining_stack@2] ->
+ Bits@1 = gleam_stdlib:identity(
+ gleam@string_builder:to_string(Builder)
+ ),
+ to_list([Rest@1 | Remaining_stack@2], [Bits@1 | Acc]);
+
+ [[{many, Builders} | Rest@2] | Remaining_stack@3] ->
+ to_list([Builders, Rest@2 | Remaining_stack@3], Acc)
+ end.
+
+-spec to_bit_array(bytes_builder()) -> bitstring().
+to_bit_array(Builder) ->
+ erlang:list_to_bitstring(Builder).
+
+-spec byte_size(bytes_builder()) -> integer().
+byte_size(Builder) ->
+ erlang:iolist_size(Builder).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@dict.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@dict.erl
new file mode 100644
index 0000000..44b89ea
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@dict.erl
@@ -0,0 +1,97 @@
+-module(gleam@dict).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([size/1, to_list/1, from_list/1, has_key/2, new/0, get/2, insert/3, map_values/2, keys/1, values/1, filter/2, take/2, merge/2, delete/2, drop/2, update/3, fold/3]).
+-export_type([dict/2]).
+
+-type dict(KS, KT) :: any() | {gleam_phantom, KS, KT}.
+
+-spec size(dict(any(), any())) -> integer().
+size(Dict) ->
+ maps:size(Dict).
+
+-spec to_list(dict(LC, LD)) -> list({LC, LD}).
+to_list(Dict) ->
+ maps:to_list(Dict).
+
+-spec from_list(list({LM, LN})) -> dict(LM, LN).
+from_list(List) ->
+ maps:from_list(List).
+
+-spec has_key(dict(LW, any()), LW) -> boolean().
+has_key(Dict, Key) ->
+ maps:is_key(Key, Dict).
+
+-spec new() -> dict(any(), any()).
+new() ->
+ maps:new().
+
+-spec get(dict(MM, MN), MM) -> {ok, MN} | {error, nil}.
+get(From, Get) ->
+ gleam_stdlib:map_get(From, Get).
+
+-spec insert(dict(MY, MZ), MY, MZ) -> dict(MY, MZ).
+insert(Dict, Key, Value) ->
+ maps:put(Key, Value, Dict).
+
+-spec map_values(dict(NK, NL), fun((NK, NL) -> NO)) -> dict(NK, NO).
+map_values(Dict, Fun) ->
+ maps:map(Fun, Dict).
+
+-spec keys(dict(NY, any())) -> list(NY).
+keys(Dict) ->
+ maps:keys(Dict).
+
+-spec values(dict(any(), OJ)) -> list(OJ).
+values(Dict) ->
+ maps:values(Dict).
+
+-spec filter(dict(OS, OT), fun((OS, OT) -> boolean())) -> dict(OS, OT).
+filter(Dict, Predicate) ->
+ maps:filter(Predicate, Dict).
+
+-spec take(dict(PE, PF), list(PE)) -> dict(PE, PF).
+take(Dict, Desired_keys) ->
+ maps:with(Desired_keys, Dict).
+
+-spec merge(dict(PS, PT), dict(PS, PT)) -> dict(PS, PT).
+merge(Dict, New_entries) ->
+ maps:merge(Dict, New_entries).
+
+-spec delete(dict(QI, QJ), QI) -> dict(QI, QJ).
+delete(Dict, Key) ->
+ maps:remove(Key, Dict).
+
+-spec drop(dict(QU, QV), list(QU)) -> dict(QU, QV).
+drop(Dict, Disallowed_keys) ->
+ case Disallowed_keys of
+ [] ->
+ Dict;
+
+ [X | Xs] ->
+ drop(delete(Dict, X), Xs)
+ end.
+
+-spec update(dict(RB, RC), RB, fun((gleam@option:option(RC)) -> RC)) -> dict(RB, RC).
+update(Dict, Key, Fun) ->
+ _pipe = Dict,
+ _pipe@1 = get(_pipe, Key),
+ _pipe@2 = gleam@option:from_result(_pipe@1),
+ _pipe@3 = Fun(_pipe@2),
+ insert(Dict, Key, _pipe@3).
+
+-spec do_fold(list({RI, RJ}), RL, fun((RL, RI, RJ) -> RL)) -> RL.
+do_fold(List, Initial, Fun) ->
+ case List of
+ [] ->
+ Initial;
+
+ [{K, V} | Rest] ->
+ do_fold(Rest, Fun(Initial, K, V), Fun)
+ end.
+
+-spec fold(dict(RM, RN), RQ, fun((RQ, RM, RN) -> RQ)) -> RQ.
+fold(Dict, Initial, Fun) ->
+ _pipe = Dict,
+ _pipe@1 = to_list(_pipe),
+ do_fold(_pipe@1, Initial, Fun).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@dynamic.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@dynamic.erl
new file mode 100644
index 0000000..38f4b4e
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@dynamic.erl
@@ -0,0 +1,808 @@
+-module(gleam@dynamic).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([from/1, unsafe_coerce/1, dynamic/1, bit_array/1, bit_string/1, classify/1, int/1, float/1, bool/1, shallow_list/1, optional/1, any/1, decode1/2, result/2, list/1, string/1, field/2, optional_field/2, element/2, tuple2/2, tuple3/3, tuple4/4, tuple5/5, tuple6/6, dict/2, map/2, decode2/3, decode3/4, decode4/5, decode5/6, decode6/7, decode7/8, decode8/9, decode9/10]).
+-export_type([dynamic_/0, decode_error/0, unknown_tuple/0]).
+
+-type dynamic_() :: any().
+
+-type decode_error() :: {decode_error, binary(), binary(), list(binary())}.
+
+-type unknown_tuple() :: any().
+
+-spec from(any()) -> dynamic_().
+from(A) ->
+ gleam_stdlib:identity(A).
+
+-spec unsafe_coerce(dynamic_()) -> any().
+unsafe_coerce(A) ->
+ gleam_stdlib:identity(A).
+
+-spec dynamic(dynamic_()) -> {ok, dynamic_()} | {error, list(decode_error())}.
+dynamic(Value) ->
+ {ok, Value}.
+
+-spec bit_array(dynamic_()) -> {ok, bitstring()} | {error, list(decode_error())}.
+bit_array(Data) ->
+ gleam_stdlib:decode_bit_array(Data).
+
+-spec bit_string(dynamic_()) -> {ok, bitstring()} |
+ {error, list(decode_error())}.
+bit_string(Data) ->
+ bit_array(Data).
+
+-spec put_expected(decode_error(), binary()) -> decode_error().
+put_expected(Error, Expected) ->
+ erlang:setelement(2, Error, Expected).
+
+-spec classify(dynamic_()) -> binary().
+classify(Data) ->
+ gleam_stdlib:classify_dynamic(Data).
+
+-spec int(dynamic_()) -> {ok, integer()} | {error, list(decode_error())}.
+int(Data) ->
+ gleam_stdlib:decode_int(Data).
+
+-spec float(dynamic_()) -> {ok, float()} | {error, list(decode_error())}.
+float(Data) ->
+ gleam_stdlib:decode_float(Data).
+
+-spec bool(dynamic_()) -> {ok, boolean()} | {error, list(decode_error())}.
+bool(Data) ->
+ gleam_stdlib:decode_bool(Data).
+
+-spec shallow_list(dynamic_()) -> {ok, list(dynamic_())} |
+ {error, list(decode_error())}.
+shallow_list(Value) ->
+ gleam_stdlib:decode_list(Value).
+
+-spec optional(fun((dynamic_()) -> {ok, DZX} | {error, list(decode_error())})) -> fun((dynamic_()) -> {ok,
+ gleam@option:option(DZX)} |
+ {error, list(decode_error())}).
+optional(Decode) ->
+ fun(Value) -> gleam_stdlib:decode_option(Value, Decode) end.
+
+-spec at_least_decode_tuple_error(integer(), dynamic_()) -> {ok, any()} |
+ {error, list(decode_error())}.
+at_least_decode_tuple_error(Size, Data) ->
+ S = case Size of
+ 1 ->
+ <<""/utf8>>;
+
+ _ ->
+ <<"s"/utf8>>
+ end,
+ Error = begin
+ _pipe = [<<"Tuple of at least "/utf8>>,
+ gleam@int:to_string(Size),
+ <<" element"/utf8>>,
+ S],
+ _pipe@1 = gleam@string_builder:from_strings(_pipe),
+ _pipe@2 = gleam@string_builder:to_string(_pipe@1),
+ {decode_error, _pipe@2, classify(Data), []}
+ end,
+ {error, [Error]}.
+
+-spec any(list(fun((dynamic_()) -> {ok, EEE} | {error, list(decode_error())}))) -> fun((dynamic_()) -> {ok,
+ EEE} |
+ {error, list(decode_error())}).
+any(Decoders) ->
+ fun(Data) -> case Decoders of
+ [] ->
+ {error,
+ [{decode_error, <<"another type"/utf8>>, classify(Data), []}]};
+
+ [Decoder | Decoders@1] ->
+ case Decoder(Data) of
+ {ok, Decoded} ->
+ {ok, Decoded};
+
+ {error, _} ->
+ (any(Decoders@1))(Data)
+ end
+ end end.
+
+-spec all_errors({ok, any()} | {error, list(decode_error())}) -> list(decode_error()).
+all_errors(Result) ->
+ case Result of
+ {ok, _} ->
+ [];
+
+ {error, Errors} ->
+ Errors
+ end.
+
+-spec decode1(
+ fun((EEI) -> EEJ),
+ fun((dynamic_()) -> {ok, EEI} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EEJ} | {error, list(decode_error())}).
+decode1(Constructor, T1) ->
+ fun(Value) -> case T1(Value) of
+ {ok, A} ->
+ {ok, Constructor(A)};
+
+ A@1 ->
+ {error, all_errors(A@1)}
+ end end.
+
+-spec push_path(decode_error(), any()) -> decode_error().
+push_path(Error, Name) ->
+ Name@1 = from(Name),
+ Decoder = any(
+ [fun string/1,
+ fun(X) -> gleam@result:map(int(X), fun gleam@int:to_string/1) end]
+ ),
+ Name@3 = case Decoder(Name@1) of
+ {ok, Name@2} ->
+ Name@2;
+
+ {error, _} ->
+ _pipe = [<<"<"/utf8>>, classify(Name@1), <<">"/utf8>>],
+ _pipe@1 = gleam@string_builder:from_strings(_pipe),
+ gleam@string_builder:to_string(_pipe@1)
+ end,
+ erlang:setelement(4, Error, [Name@3 | erlang:element(4, Error)]).
+
+-spec result(
+ fun((dynamic_()) -> {ok, DZL} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, DZN} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {ok, DZL} | {error, DZN}} |
+ {error, list(decode_error())}).
+result(Decode_ok, Decode_error) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_result(Value),
+ fun(Inner_result) -> case Inner_result of
+ {ok, Raw} ->
+ gleam@result:'try'(
+ begin
+ _pipe = Decode_ok(Raw),
+ map_errors(
+ _pipe,
+ fun(_capture) ->
+ push_path(_capture, <<"ok"/utf8>>)
+ end
+ )
+ end,
+ fun(Value@1) -> {ok, {ok, Value@1}} end
+ );
+
+ {error, Raw@1} ->
+ gleam@result:'try'(
+ begin
+ _pipe@1 = Decode_error(Raw@1),
+ map_errors(
+ _pipe@1,
+ fun(_capture@1) ->
+ push_path(_capture@1, <<"error"/utf8>>)
+ end
+ )
+ end,
+ fun(Value@2) -> {ok, {error, Value@2}} end
+ )
+ end end
+ )
+ end.
+
+-spec list(fun((dynamic_()) -> {ok, DZS} | {error, list(decode_error())})) -> fun((dynamic_()) -> {ok,
+ list(DZS)} |
+ {error, list(decode_error())}).
+list(Decoder_type) ->
+ fun(Dynamic) ->
+ gleam@result:'try'(shallow_list(Dynamic), fun(List) -> _pipe = List,
+ _pipe@1 = gleam@list:try_map(_pipe, Decoder_type),
+ map_errors(
+ _pipe@1,
+ fun(_capture) -> push_path(_capture, <<"*"/utf8>>) end
+ ) end)
+ end.
+
+-spec map_errors(
+ {ok, DYG} | {error, list(decode_error())},
+ fun((decode_error()) -> decode_error())
+) -> {ok, DYG} | {error, list(decode_error())}.
+map_errors(Result, F) ->
+ gleam@result:map_error(
+ Result,
+ fun(_capture) -> gleam@list:map(_capture, F) end
+ ).
+
+-spec decode_string(dynamic_()) -> {ok, binary()} |
+ {error, list(decode_error())}.
+decode_string(Data) ->
+ _pipe = bit_array(Data),
+ _pipe@1 = map_errors(
+ _pipe,
+ fun(_capture) -> put_expected(_capture, <<"String"/utf8>>) end
+ ),
+ gleam@result:'try'(
+ _pipe@1,
+ fun(Raw) -> case gleam@bit_array:to_string(Raw) of
+ {ok, String} ->
+ {ok, String};
+
+ {error, nil} ->
+ {error,
+ [{decode_error,
+ <<"String"/utf8>>,
+ <<"BitArray"/utf8>>,
+ []}]}
+ end end
+ ).
+
+-spec string(dynamic_()) -> {ok, binary()} | {error, list(decode_error())}.
+string(Data) ->
+ decode_string(Data).
+
+-spec field(
+ any(),
+ fun((dynamic_()) -> {ok, EAH} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EAH} | {error, list(decode_error())}).
+field(Name, Inner_type) ->
+ fun(Value) ->
+ Missing_field_error = {decode_error,
+ <<"field"/utf8>>,
+ <<"nothing"/utf8>>,
+ []},
+ gleam@result:'try'(
+ gleam_stdlib:decode_field(Value, Name),
+ fun(Maybe_inner) -> _pipe = Maybe_inner,
+ _pipe@1 = gleam@option:to_result(_pipe, [Missing_field_error]),
+ _pipe@2 = gleam@result:'try'(_pipe@1, Inner_type),
+ map_errors(
+ _pipe@2,
+ fun(_capture) -> push_path(_capture, Name) end
+ ) end
+ )
+ end.
+
+-spec optional_field(
+ any(),
+ fun((dynamic_()) -> {ok, EAL} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, gleam@option:option(EAL)} |
+ {error, list(decode_error())}).
+optional_field(Name, Inner_type) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_field(Value, Name),
+ fun(Maybe_inner) -> case Maybe_inner of
+ none ->
+ {ok, none};
+
+ {some, Dynamic_inner} ->
+ _pipe = Dynamic_inner,
+ _pipe@1 = gleam_stdlib:decode_option(_pipe, Inner_type),
+ map_errors(
+ _pipe@1,
+ fun(_capture) -> push_path(_capture, Name) end
+ )
+ end end
+ )
+ end.
+
+-spec element(
+ integer(),
+ fun((dynamic_()) -> {ok, EAT} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EAT} | {error, list(decode_error())}).
+element(Index, Inner_type) ->
+ fun(Data) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple(Data),
+ fun(Tuple) ->
+ Size = gleam_stdlib:size_of_tuple(Tuple),
+ gleam@result:'try'(case Index >= 0 of
+ true ->
+ case Index < Size of
+ true ->
+ gleam_stdlib:tuple_get(Tuple, Index);
+
+ false ->
+ at_least_decode_tuple_error(Index + 1, Data)
+ end;
+
+ false ->
+ case gleam@int:absolute_value(Index) =< Size of
+ true ->
+ gleam_stdlib:tuple_get(Tuple, Size + Index);
+
+ false ->
+ at_least_decode_tuple_error(
+ gleam@int:absolute_value(Index),
+ Data
+ )
+ end
+ end, fun(Data@1) -> _pipe = Inner_type(Data@1),
+ map_errors(
+ _pipe,
+ fun(_capture) -> push_path(_capture, Index) end
+ ) end)
+ end
+ )
+ end.
+
+-spec tuple_errors({ok, any()} | {error, list(decode_error())}, binary()) -> list(decode_error()).
+tuple_errors(Result, Name) ->
+ case Result of
+ {ok, _} ->
+ [];
+
+ {error, Errors} ->
+ gleam@list:map(
+ Errors,
+ fun(_capture) -> push_path(_capture, Name) end
+ )
+ end.
+
+-spec tuple2(
+ fun((dynamic_()) -> {ok, EBT} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EBV} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {EBT, EBV}} | {error, list(decode_error())}).
+tuple2(Decode1, Decode2) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple2(Value),
+ fun(_use0) ->
+ {A, B} = _use0,
+ case {Decode1(A), Decode2(B)} of
+ {{ok, A@1}, {ok, B@1}} ->
+ {ok, {A@1, B@1}};
+
+ {A@2, B@2} ->
+ _pipe = tuple_errors(A@2, <<"0"/utf8>>),
+ _pipe@1 = gleam@list:append(
+ _pipe,
+ tuple_errors(B@2, <<"1"/utf8>>)
+ ),
+ {error, _pipe@1}
+ end
+ end
+ )
+ end.
+
+-spec tuple3(
+ fun((dynamic_()) -> {ok, EBY} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECA} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECC} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {EBY, ECA, ECC}} | {error, list(decode_error())}).
+tuple3(Decode1, Decode2, Decode3) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple3(Value),
+ fun(_use0) ->
+ {A, B, C} = _use0,
+ case {Decode1(A), Decode2(B), Decode3(C)} of
+ {{ok, A@1}, {ok, B@1}, {ok, C@1}} ->
+ {ok, {A@1, B@1, C@1}};
+
+ {A@2, B@2, C@2} ->
+ _pipe = tuple_errors(A@2, <<"0"/utf8>>),
+ _pipe@1 = gleam@list:append(
+ _pipe,
+ tuple_errors(B@2, <<"1"/utf8>>)
+ ),
+ _pipe@2 = gleam@list:append(
+ _pipe@1,
+ tuple_errors(C@2, <<"2"/utf8>>)
+ ),
+ {error, _pipe@2}
+ end
+ end
+ )
+ end.
+
+-spec tuple4(
+ fun((dynamic_()) -> {ok, ECF} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECH} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECJ} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECL} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {ECF, ECH, ECJ, ECL}} |
+ {error, list(decode_error())}).
+tuple4(Decode1, Decode2, Decode3, Decode4) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple4(Value),
+ fun(_use0) ->
+ {A, B, C, D} = _use0,
+ case {Decode1(A), Decode2(B), Decode3(C), Decode4(D)} of
+ {{ok, A@1}, {ok, B@1}, {ok, C@1}, {ok, D@1}} ->
+ {ok, {A@1, B@1, C@1, D@1}};
+
+ {A@2, B@2, C@2, D@2} ->
+ _pipe = tuple_errors(A@2, <<"0"/utf8>>),
+ _pipe@1 = gleam@list:append(
+ _pipe,
+ tuple_errors(B@2, <<"1"/utf8>>)
+ ),
+ _pipe@2 = gleam@list:append(
+ _pipe@1,
+ tuple_errors(C@2, <<"2"/utf8>>)
+ ),
+ _pipe@3 = gleam@list:append(
+ _pipe@2,
+ tuple_errors(D@2, <<"3"/utf8>>)
+ ),
+ {error, _pipe@3}
+ end
+ end
+ )
+ end.
+
+-spec tuple5(
+ fun((dynamic_()) -> {ok, ECO} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECQ} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECS} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECU} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, ECW} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {ECO, ECQ, ECS, ECU, ECW}} |
+ {error, list(decode_error())}).
+tuple5(Decode1, Decode2, Decode3, Decode4, Decode5) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple5(Value),
+ fun(_use0) ->
+ {A, B, C, D, E} = _use0,
+ case {Decode1(A),
+ Decode2(B),
+ Decode3(C),
+ Decode4(D),
+ Decode5(E)} of
+ {{ok, A@1}, {ok, B@1}, {ok, C@1}, {ok, D@1}, {ok, E@1}} ->
+ {ok, {A@1, B@1, C@1, D@1, E@1}};
+
+ {A@2, B@2, C@2, D@2, E@2} ->
+ _pipe = tuple_errors(A@2, <<"0"/utf8>>),
+ _pipe@1 = gleam@list:append(
+ _pipe,
+ tuple_errors(B@2, <<"1"/utf8>>)
+ ),
+ _pipe@2 = gleam@list:append(
+ _pipe@1,
+ tuple_errors(C@2, <<"2"/utf8>>)
+ ),
+ _pipe@3 = gleam@list:append(
+ _pipe@2,
+ tuple_errors(D@2, <<"3"/utf8>>)
+ ),
+ _pipe@4 = gleam@list:append(
+ _pipe@3,
+ tuple_errors(E@2, <<"4"/utf8>>)
+ ),
+ {error, _pipe@4}
+ end
+ end
+ )
+ end.
+
+-spec tuple6(
+ fun((dynamic_()) -> {ok, ECZ} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDB} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDD} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDF} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDH} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDJ} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, {ECZ, EDB, EDD, EDF, EDH, EDJ}} |
+ {error, list(decode_error())}).
+tuple6(Decode1, Decode2, Decode3, Decode4, Decode5, Decode6) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_tuple6(Value),
+ fun(_use0) ->
+ {A, B, C, D, E, F} = _use0,
+ case {Decode1(A),
+ Decode2(B),
+ Decode3(C),
+ Decode4(D),
+ Decode5(E),
+ Decode6(F)} of
+ {{ok, A@1},
+ {ok, B@1},
+ {ok, C@1},
+ {ok, D@1},
+ {ok, E@1},
+ {ok, F@1}} ->
+ {ok, {A@1, B@1, C@1, D@1, E@1, F@1}};
+
+ {A@2, B@2, C@2, D@2, E@2, F@2} ->
+ _pipe = tuple_errors(A@2, <<"0"/utf8>>),
+ _pipe@1 = gleam@list:append(
+ _pipe,
+ tuple_errors(B@2, <<"1"/utf8>>)
+ ),
+ _pipe@2 = gleam@list:append(
+ _pipe@1,
+ tuple_errors(C@2, <<"2"/utf8>>)
+ ),
+ _pipe@3 = gleam@list:append(
+ _pipe@2,
+ tuple_errors(D@2, <<"3"/utf8>>)
+ ),
+ _pipe@4 = gleam@list:append(
+ _pipe@3,
+ tuple_errors(E@2, <<"4"/utf8>>)
+ ),
+ _pipe@5 = gleam@list:append(
+ _pipe@4,
+ tuple_errors(F@2, <<"5"/utf8>>)
+ ),
+ {error, _pipe@5}
+ end
+ end
+ )
+ end.
+
+-spec dict(
+ fun((dynamic_()) -> {ok, EDM} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDO} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, gleam@dict:dict(EDM, EDO)} |
+ {error, list(decode_error())}).
+dict(Key_type, Value_type) ->
+ fun(Value) ->
+ gleam@result:'try'(
+ gleam_stdlib:decode_map(Value),
+ fun(Map) ->
+ gleam@result:'try'(
+ begin
+ _pipe = Map,
+ _pipe@1 = gleam@dict:to_list(_pipe),
+ gleam@list:try_map(
+ _pipe@1,
+ fun(Pair) ->
+ {K, V} = Pair,
+ gleam@result:'try'(
+ begin
+ _pipe@2 = Key_type(K),
+ map_errors(
+ _pipe@2,
+ fun(_capture) ->
+ push_path(
+ _capture,
+ <<"keys"/utf8>>
+ )
+ end
+ )
+ end,
+ fun(K@1) ->
+ gleam@result:'try'(
+ begin
+ _pipe@3 = Value_type(V),
+ map_errors(
+ _pipe@3,
+ fun(_capture@1) ->
+ push_path(
+ _capture@1,
+ <<"values"/utf8>>
+ )
+ end
+ )
+ end,
+ fun(V@1) -> {ok, {K@1, V@1}} end
+ )
+ end
+ )
+ end
+ )
+ end,
+ fun(Pairs) -> {ok, gleam@dict:from_list(Pairs)} end
+ )
+ end
+ )
+ end.
+
+-spec map(
+ fun((dynamic_()) -> {ok, EDT} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EDV} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, gleam@dict:dict(EDT, EDV)} |
+ {error, list(decode_error())}).
+map(Key_type, Value_type) ->
+ dict(Key_type, Value_type).
+
+-spec decode2(
+ fun((EEM, EEN) -> EEO),
+ fun((dynamic_()) -> {ok, EEM} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EEN} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EEO} | {error, list(decode_error())}).
+decode2(Constructor, T1, T2) ->
+ fun(Value) -> case {T1(Value), T2(Value)} of
+ {{ok, A}, {ok, B}} ->
+ {ok, Constructor(A, B)};
+
+ {A@1, B@1} ->
+ {error, gleam@list:concat([all_errors(A@1), all_errors(B@1)])}
+ end end.
+
+-spec decode3(
+ fun((EES, EET, EEU) -> EEV),
+ fun((dynamic_()) -> {ok, EES} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EET} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EEU} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EEV} | {error, list(decode_error())}).
+decode3(Constructor, T1, T2, T3) ->
+ fun(Value) -> case {T1(Value), T2(Value), T3(Value)} of
+ {{ok, A}, {ok, B}, {ok, C}} ->
+ {ok, Constructor(A, B, C)};
+
+ {A@1, B@1, C@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1), all_errors(B@1), all_errors(C@1)]
+ )}
+ end end.
+
+-spec decode4(
+ fun((EFA, EFB, EFC, EFD) -> EFE),
+ fun((dynamic_()) -> {ok, EFA} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFB} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFC} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFD} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EFE} | {error, list(decode_error())}).
+decode4(Constructor, T1, T2, T3, T4) ->
+ fun(X) -> case {T1(X), T2(X), T3(X), T4(X)} of
+ {{ok, A}, {ok, B}, {ok, C}, {ok, D}} ->
+ {ok, Constructor(A, B, C, D)};
+
+ {A@1, B@1, C@1, D@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1)]
+ )}
+ end end.
+
+-spec decode5(
+ fun((EFK, EFL, EFM, EFN, EFO) -> EFP),
+ fun((dynamic_()) -> {ok, EFK} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFL} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFM} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFN} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFO} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EFP} | {error, list(decode_error())}).
+decode5(Constructor, T1, T2, T3, T4, T5) ->
+ fun(X) -> case {T1(X), T2(X), T3(X), T4(X), T5(X)} of
+ {{ok, A}, {ok, B}, {ok, C}, {ok, D}, {ok, E}} ->
+ {ok, Constructor(A, B, C, D, E)};
+
+ {A@1, B@1, C@1, D@1, E@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1),
+ all_errors(E@1)]
+ )}
+ end end.
+
+-spec decode6(
+ fun((EFW, EFX, EFY, EFZ, EGA, EGB) -> EGC),
+ fun((dynamic_()) -> {ok, EFW} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFX} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFY} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EFZ} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGA} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGB} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EGC} | {error, list(decode_error())}).
+decode6(Constructor, T1, T2, T3, T4, T5, T6) ->
+ fun(X) -> case {T1(X), T2(X), T3(X), T4(X), T5(X), T6(X)} of
+ {{ok, A}, {ok, B}, {ok, C}, {ok, D}, {ok, E}, {ok, F}} ->
+ {ok, Constructor(A, B, C, D, E, F)};
+
+ {A@1, B@1, C@1, D@1, E@1, F@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1),
+ all_errors(E@1),
+ all_errors(F@1)]
+ )}
+ end end.
+
+-spec decode7(
+ fun((EGK, EGL, EGM, EGN, EGO, EGP, EGQ) -> EGR),
+ fun((dynamic_()) -> {ok, EGK} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGL} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGM} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGN} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGO} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGP} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EGQ} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EGR} | {error, list(decode_error())}).
+decode7(Constructor, T1, T2, T3, T4, T5, T6, T7) ->
+ fun(X) -> case {T1(X), T2(X), T3(X), T4(X), T5(X), T6(X), T7(X)} of
+ {{ok, A}, {ok, B}, {ok, C}, {ok, D}, {ok, E}, {ok, F}, {ok, G}} ->
+ {ok, Constructor(A, B, C, D, E, F, G)};
+
+ {A@1, B@1, C@1, D@1, E@1, F@1, G@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1),
+ all_errors(E@1),
+ all_errors(F@1),
+ all_errors(G@1)]
+ )}
+ end end.
+
+-spec decode8(
+ fun((EHA, EHB, EHC, EHD, EHE, EHF, EHG, EHH) -> EHI),
+ fun((dynamic_()) -> {ok, EHA} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHB} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHC} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHD} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHE} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHF} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHG} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHH} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EHI} | {error, list(decode_error())}).
+decode8(Constructor, T1, T2, T3, T4, T5, T6, T7, T8) ->
+ fun(X) -> case {T1(X), T2(X), T3(X), T4(X), T5(X), T6(X), T7(X), T8(X)} of
+ {{ok, A},
+ {ok, B},
+ {ok, C},
+ {ok, D},
+ {ok, E},
+ {ok, F},
+ {ok, G},
+ {ok, H}} ->
+ {ok, Constructor(A, B, C, D, E, F, G, H)};
+
+ {A@1, B@1, C@1, D@1, E@1, F@1, G@1, H@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1),
+ all_errors(E@1),
+ all_errors(F@1),
+ all_errors(G@1),
+ all_errors(H@1)]
+ )}
+ end end.
+
+-spec decode9(
+ fun((EHS, EHT, EHU, EHV, EHW, EHX, EHY, EHZ, EIA) -> EIB),
+ fun((dynamic_()) -> {ok, EHS} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHT} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHU} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHV} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHW} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHX} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHY} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EHZ} | {error, list(decode_error())}),
+ fun((dynamic_()) -> {ok, EIA} | {error, list(decode_error())})
+) -> fun((dynamic_()) -> {ok, EIB} | {error, list(decode_error())}).
+decode9(Constructor, T1, T2, T3, T4, T5, T6, T7, T8, T9) ->
+ fun(X) ->
+ case {T1(X), T2(X), T3(X), T4(X), T5(X), T6(X), T7(X), T8(X), T9(X)} of
+ {{ok, A},
+ {ok, B},
+ {ok, C},
+ {ok, D},
+ {ok, E},
+ {ok, F},
+ {ok, G},
+ {ok, H},
+ {ok, I}} ->
+ {ok, Constructor(A, B, C, D, E, F, G, H, I)};
+
+ {A@1, B@1, C@1, D@1, E@1, F@1, G@1, H@1, I@1} ->
+ {error,
+ gleam@list:concat(
+ [all_errors(A@1),
+ all_errors(B@1),
+ all_errors(C@1),
+ all_errors(D@1),
+ all_errors(E@1),
+ all_errors(F@1),
+ all_errors(G@1),
+ all_errors(H@1),
+ all_errors(I@1)]
+ )}
+ end
+ end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@float.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@float.erl
new file mode 100644
index 0000000..33b3d4a
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@float.erl
@@ -0,0 +1,181 @@
+-module(gleam@float).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([parse/1, to_string/1, compare/2, min/2, max/2, clamp/3, ceiling/1, floor/1, round/1, truncate/1, absolute_value/1, loosely_compare/3, loosely_equals/3, power/2, square_root/1, negate/1, sum/1, product/1, random/2, divide/2, add/2, multiply/2, subtract/2]).
+
+-spec parse(binary()) -> {ok, float()} | {error, nil}.
+parse(String) ->
+ gleam_stdlib:parse_float(String).
+
+-spec to_string(float()) -> binary().
+to_string(X) ->
+ gleam_stdlib:float_to_string(X).
+
+-spec compare(float(), float()) -> gleam@order:order().
+compare(A, B) ->
+ case A =:= B of
+ true ->
+ eq;
+
+ false ->
+ case A < B of
+ true ->
+ lt;
+
+ false ->
+ gt
+ end
+ end.
+
+-spec min(float(), float()) -> float().
+min(A, B) ->
+ case A < B of
+ true ->
+ A;
+
+ false ->
+ B
+ end.
+
+-spec max(float(), float()) -> float().
+max(A, B) ->
+ case A > B of
+ true ->
+ A;
+
+ false ->
+ B
+ end.
+
+-spec clamp(float(), float(), float()) -> float().
+clamp(X, Min_bound, Max_bound) ->
+ _pipe = X,
+ _pipe@1 = min(_pipe, Max_bound),
+ max(_pipe@1, Min_bound).
+
+-spec ceiling(float()) -> float().
+ceiling(X) ->
+ math:ceil(X).
+
+-spec floor(float()) -> float().
+floor(X) ->
+ math:floor(X).
+
+-spec round(float()) -> integer().
+round(X) ->
+ erlang:round(X).
+
+-spec truncate(float()) -> integer().
+truncate(X) ->
+ erlang:trunc(X).
+
+-spec absolute_value(float()) -> float().
+absolute_value(X) ->
+ case X >= +0.0 of
+ true ->
+ X;
+
+ _ ->
+ +0.0 - X
+ end.
+
+-spec loosely_compare(float(), float(), float()) -> gleam@order:order().
+loosely_compare(A, B, Tolerance) ->
+ Difference = absolute_value(A - B),
+ case Difference =< Tolerance of
+ true ->
+ eq;
+
+ false ->
+ compare(A, B)
+ end.
+
+-spec loosely_equals(float(), float(), float()) -> boolean().
+loosely_equals(A, B, Tolerance) ->
+ Difference = absolute_value(A - B),
+ Difference =< Tolerance.
+
+-spec power(float(), float()) -> {ok, float()} | {error, nil}.
+power(Base, Exponent) ->
+ Fractional = (ceiling(Exponent) - Exponent) > +0.0,
+ case ((Base < +0.0) andalso Fractional) orelse ((Base =:= +0.0) andalso (Exponent
+ < +0.0)) of
+ true ->
+ {error, nil};
+
+ false ->
+ {ok, math:pow(Base, Exponent)}
+ end.
+
+-spec square_root(float()) -> {ok, float()} | {error, nil}.
+square_root(X) ->
+ power(X, 0.5).
+
+-spec negate(float()) -> float().
+negate(X) ->
+ -1.0 * X.
+
+-spec do_sum(list(float()), float()) -> float().
+do_sum(Numbers, Initial) ->
+ case Numbers of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ do_sum(Rest, X + Initial)
+ end.
+
+-spec sum(list(float())) -> float().
+sum(Numbers) ->
+ _pipe = Numbers,
+ do_sum(_pipe, +0.0).
+
+-spec do_product(list(float()), float()) -> float().
+do_product(Numbers, Initial) ->
+ case Numbers of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ do_product(Rest, X * Initial)
+ end.
+
+-spec product(list(float())) -> float().
+product(Numbers) ->
+ case Numbers of
+ [] ->
+ 1.0;
+
+ _ ->
+ do_product(Numbers, 1.0)
+ end.
+
+-spec random(float(), float()) -> float().
+random(Min, Max) ->
+ (rand:uniform() * (Max - Min)) + Min.
+
+-spec divide(float(), float()) -> {ok, float()} | {error, nil}.
+divide(A, B) ->
+ case B of
+ +0.0 ->
+ {error, nil};
+
+ B@1 ->
+ {ok, case B@1 of
+ +0.0 -> +0.0;
+ -0.0 -> -0.0;
+ Gleam@denominator -> A / Gleam@denominator
+ end}
+ end.
+
+-spec add(float(), float()) -> float().
+add(A, B) ->
+ A + B.
+
+-spec multiply(float(), float()) -> float().
+multiply(A, B) ->
+ A * B.
+
+-spec subtract(float(), float()) -> float().
+subtract(A, B) ->
+ A - B.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@function.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@function.erl
new file mode 100644
index 0000000..3496318
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@function.erl
@@ -0,0 +1,67 @@
+-module(gleam@function).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([compose/2, curry2/1, curry3/1, curry4/1, curry5/1, curry6/1, flip/1, identity/1, constant/1, tap/2, apply1/2, apply2/3, apply3/4]).
+
+-spec compose(fun((DOO) -> DOP), fun((DOP) -> DOQ)) -> fun((DOO) -> DOQ).
+compose(Fun1, Fun2) ->
+ fun(A) -> Fun2(Fun1(A)) end.
+
+-spec curry2(fun((DOR, DOS) -> DOT)) -> fun((DOR) -> fun((DOS) -> DOT)).
+curry2(Fun) ->
+ fun(A) -> fun(B) -> Fun(A, B) end end.
+
+-spec curry3(fun((DOV, DOW, DOX) -> DOY)) -> fun((DOV) -> fun((DOW) -> fun((DOX) -> DOY))).
+curry3(Fun) ->
+ fun(A) -> fun(B) -> fun(C) -> Fun(A, B, C) end end end.
+
+-spec curry4(fun((DPA, DPB, DPC, DPD) -> DPE)) -> fun((DPA) -> fun((DPB) -> fun((DPC) -> fun((DPD) -> DPE)))).
+curry4(Fun) ->
+ fun(A) -> fun(B) -> fun(C) -> fun(D) -> Fun(A, B, C, D) end end end end.
+
+-spec curry5(fun((DPG, DPH, DPI, DPJ, DPK) -> DPL)) -> fun((DPG) -> fun((DPH) -> fun((DPI) -> fun((DPJ) -> fun((DPK) -> DPL))))).
+curry5(Fun) ->
+ fun(A) ->
+ fun(B) ->
+ fun(C) -> fun(D) -> fun(E) -> Fun(A, B, C, D, E) end end end
+ end
+ end.
+
+-spec curry6(fun((DPN, DPO, DPP, DPQ, DPR, DPS) -> DPT)) -> fun((DPN) -> fun((DPO) -> fun((DPP) -> fun((DPQ) -> fun((DPR) -> fun((DPS) -> DPT)))))).
+curry6(Fun) ->
+ fun(A) ->
+ fun(B) ->
+ fun(C) ->
+ fun(D) -> fun(E) -> fun(F) -> Fun(A, B, C, D, E, F) end end end
+ end
+ end
+ end.
+
+-spec flip(fun((DPV, DPW) -> DPX)) -> fun((DPW, DPV) -> DPX).
+flip(Fun) ->
+ fun(B, A) -> Fun(A, B) end.
+
+-spec identity(DPY) -> DPY.
+identity(X) ->
+ X.
+
+-spec constant(DPZ) -> fun((any()) -> DPZ).
+constant(Value) ->
+ fun(_) -> Value end.
+
+-spec tap(DQB, fun((DQB) -> any())) -> DQB.
+tap(Arg, Effect) ->
+ Effect(Arg),
+ Arg.
+
+-spec apply1(fun((DQD) -> DQE), DQD) -> DQE.
+apply1(Fun, Arg1) ->
+ Fun(Arg1).
+
+-spec apply2(fun((DQF, DQG) -> DQH), DQF, DQG) -> DQH.
+apply2(Fun, Arg1, Arg2) ->
+ Fun(Arg1, Arg2).
+
+-spec apply3(fun((DQI, DQJ, DQK) -> DQL), DQI, DQJ, DQK) -> DQL.
+apply3(Fun, Arg1, Arg2, Arg3) ->
+ Fun(Arg1, Arg2, Arg3).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@int.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@int.erl
new file mode 100644
index 0000000..2a5dd2c
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@int.erl
@@ -0,0 +1,332 @@
+-module(gleam@int).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([absolute_value/1, parse/1, base_parse/2, to_string/1, to_base_string/2, to_base2/1, to_base8/1, to_base16/1, to_base36/1, to_float/1, power/2, square_root/1, compare/2, min/2, max/2, clamp/3, is_even/1, is_odd/1, negate/1, sum/1, product/1, digits/2, undigits/2, random/2, divide/2, remainder/2, modulo/2, floor_divide/2, add/2, multiply/2, subtract/2, bitwise_and/2, bitwise_not/1, bitwise_or/2, bitwise_exclusive_or/2, bitwise_shift_left/2, bitwise_shift_right/2]).
+-export_type([invalid_base/0]).
+
+-type invalid_base() :: invalid_base.
+
+-spec absolute_value(integer()) -> integer().
+absolute_value(X) ->
+ case X >= 0 of
+ true ->
+ X;
+
+ false ->
+ X * -1
+ end.
+
+-spec parse(binary()) -> {ok, integer()} | {error, nil}.
+parse(String) ->
+ gleam_stdlib:parse_int(String).
+
+-spec base_parse(binary(), integer()) -> {ok, integer()} | {error, nil}.
+base_parse(String, Base) ->
+ case (Base >= 2) andalso (Base =< 36) of
+ true ->
+ gleam_stdlib:int_from_base_string(String, Base);
+
+ false ->
+ {error, nil}
+ end.
+
+-spec to_string(integer()) -> binary().
+to_string(X) ->
+ erlang:integer_to_binary(X).
+
+-spec to_base_string(integer(), integer()) -> {ok, binary()} |
+ {error, invalid_base()}.
+to_base_string(X, Base) ->
+ case (Base >= 2) andalso (Base =< 36) of
+ true ->
+ {ok, erlang:integer_to_binary(X, Base)};
+
+ false ->
+ {error, invalid_base}
+ end.
+
+-spec to_base2(integer()) -> binary().
+to_base2(X) ->
+ erlang:integer_to_binary(X, 2).
+
+-spec to_base8(integer()) -> binary().
+to_base8(X) ->
+ erlang:integer_to_binary(X, 8).
+
+-spec to_base16(integer()) -> binary().
+to_base16(X) ->
+ erlang:integer_to_binary(X, 16).
+
+-spec to_base36(integer()) -> binary().
+to_base36(X) ->
+ erlang:integer_to_binary(X, 36).
+
+-spec to_float(integer()) -> float().
+to_float(X) ->
+ erlang:float(X).
+
+-spec power(integer(), float()) -> {ok, float()} | {error, nil}.
+power(Base, Exponent) ->
+ _pipe = Base,
+ _pipe@1 = to_float(_pipe),
+ gleam@float:power(_pipe@1, Exponent).
+
+-spec square_root(integer()) -> {ok, float()} | {error, nil}.
+square_root(X) ->
+ _pipe = X,
+ _pipe@1 = to_float(_pipe),
+ gleam@float:square_root(_pipe@1).
+
+-spec compare(integer(), integer()) -> gleam@order:order().
+compare(A, B) ->
+ case A =:= B of
+ true ->
+ eq;
+
+ false ->
+ case A < B of
+ true ->
+ lt;
+
+ false ->
+ gt
+ end
+ end.
+
+-spec min(integer(), integer()) -> integer().
+min(A, B) ->
+ case A < B of
+ true ->
+ A;
+
+ false ->
+ B
+ end.
+
+-spec max(integer(), integer()) -> integer().
+max(A, B) ->
+ case A > B of
+ true ->
+ A;
+
+ false ->
+ B
+ end.
+
+-spec clamp(integer(), integer(), integer()) -> integer().
+clamp(X, Min_bound, Max_bound) ->
+ _pipe = X,
+ _pipe@1 = min(_pipe, Max_bound),
+ max(_pipe@1, Min_bound).
+
+-spec is_even(integer()) -> boolean().
+is_even(X) ->
+ (X rem 2) =:= 0.
+
+-spec is_odd(integer()) -> boolean().
+is_odd(X) ->
+ (X rem 2) /= 0.
+
+-spec negate(integer()) -> integer().
+negate(X) ->
+ -1 * X.
+
+-spec do_sum(list(integer()), integer()) -> integer().
+do_sum(Numbers, Initial) ->
+ case Numbers of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ do_sum(Rest, X + Initial)
+ end.
+
+-spec sum(list(integer())) -> integer().
+sum(Numbers) ->
+ _pipe = Numbers,
+ do_sum(_pipe, 0).
+
+-spec do_product(list(integer()), integer()) -> integer().
+do_product(Numbers, Initial) ->
+ case Numbers of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ do_product(Rest, X * Initial)
+ end.
+
+-spec product(list(integer())) -> integer().
+product(Numbers) ->
+ case Numbers of
+ [] ->
+ 1;
+
+ _ ->
+ do_product(Numbers, 1)
+ end.
+
+-spec do_digits(integer(), integer(), list(integer())) -> list(integer()).
+do_digits(X, Base, Acc) ->
+ case absolute_value(X) < Base of
+ true ->
+ [X | Acc];
+
+ false ->
+ do_digits(case Base of
+ 0 -> 0;
+ Gleam@denominator -> X div Gleam@denominator
+ end, Base, [case Base of
+ 0 -> 0;
+ Gleam@denominator@1 -> X rem Gleam@denominator@1
+ end | Acc])
+ end.
+
+-spec digits(integer(), integer()) -> {ok, list(integer())} |
+ {error, invalid_base()}.
+digits(X, Base) ->
+ case Base < 2 of
+ true ->
+ {error, invalid_base};
+
+ false ->
+ {ok, do_digits(X, Base, [])}
+ end.
+
+-spec do_undigits(list(integer()), integer(), integer()) -> {ok, integer()} |
+ {error, invalid_base()}.
+do_undigits(Numbers, Base, Acc) ->
+ case Numbers of
+ [] ->
+ {ok, Acc};
+
+ [Digit | _] when Digit >= Base ->
+ {error, invalid_base};
+
+ [Digit@1 | Rest] ->
+ do_undigits(Rest, Base, (Acc * Base) + Digit@1)
+ end.
+
+-spec undigits(list(integer()), integer()) -> {ok, integer()} |
+ {error, invalid_base()}.
+undigits(Numbers, Base) ->
+ case Base < 2 of
+ true ->
+ {error, invalid_base};
+
+ false ->
+ do_undigits(Numbers, Base, 0)
+ end.
+
+-spec random(integer(), integer()) -> integer().
+random(Min, Max) ->
+ _pipe = gleam@float:random(to_float(Min), to_float(Max)),
+ _pipe@1 = gleam@float:floor(_pipe),
+ gleam@float:round(_pipe@1).
+
+-spec divide(integer(), integer()) -> {ok, integer()} | {error, nil}.
+divide(Dividend, Divisor) ->
+ case Divisor of
+ 0 ->
+ {error, nil};
+
+ Divisor@1 ->
+ {ok, case Divisor@1 of
+ 0 -> 0;
+ Gleam@denominator -> Dividend div Gleam@denominator
+ end}
+ end.
+
+-spec remainder(integer(), integer()) -> {ok, integer()} | {error, nil}.
+remainder(Dividend, Divisor) ->
+ case Divisor of
+ 0 ->
+ {error, nil};
+
+ Divisor@1 ->
+ {ok, case Divisor@1 of
+ 0 -> 0;
+ Gleam@denominator -> Dividend rem Gleam@denominator
+ end}
+ end.
+
+-spec modulo(integer(), integer()) -> {ok, integer()} | {error, nil}.
+modulo(Dividend, Divisor) ->
+ case Divisor of
+ 0 ->
+ {error, nil};
+
+ _ ->
+ Remainder = case Divisor of
+ 0 -> 0;
+ Gleam@denominator -> Dividend rem Gleam@denominator
+ end,
+ case (Remainder * Divisor) < 0 of
+ true ->
+ {ok, Remainder + Divisor};
+
+ false ->
+ {ok, Remainder}
+ end
+ end.
+
+-spec floor_divide(integer(), integer()) -> {ok, integer()} | {error, nil}.
+floor_divide(Dividend, Divisor) ->
+ case Divisor of
+ 0 ->
+ {error, nil};
+
+ Divisor@1 ->
+ case ((Dividend * Divisor@1) < 0) andalso ((case Divisor@1 of
+ 0 -> 0;
+ Gleam@denominator -> Dividend rem Gleam@denominator
+ end) /= 0) of
+ true ->
+ {ok, (case Divisor@1 of
+ 0 -> 0;
+ Gleam@denominator@1 -> Dividend div Gleam@denominator@1
+ end) - 1};
+
+ false ->
+ {ok, case Divisor@1 of
+ 0 -> 0;
+ Gleam@denominator@2 -> Dividend div Gleam@denominator@2
+ end}
+ end
+ end.
+
+-spec add(integer(), integer()) -> integer().
+add(A, B) ->
+ A + B.
+
+-spec multiply(integer(), integer()) -> integer().
+multiply(A, B) ->
+ A * B.
+
+-spec subtract(integer(), integer()) -> integer().
+subtract(A, B) ->
+ A - B.
+
+-spec bitwise_and(integer(), integer()) -> integer().
+bitwise_and(X, Y) ->
+ erlang:'band'(X, Y).
+
+-spec bitwise_not(integer()) -> integer().
+bitwise_not(X) ->
+ erlang:'bnot'(X).
+
+-spec bitwise_or(integer(), integer()) -> integer().
+bitwise_or(X, Y) ->
+ erlang:'bor'(X, Y).
+
+-spec bitwise_exclusive_or(integer(), integer()) -> integer().
+bitwise_exclusive_or(X, Y) ->
+ erlang:'bxor'(X, Y).
+
+-spec bitwise_shift_left(integer(), integer()) -> integer().
+bitwise_shift_left(X, Y) ->
+ erlang:'bsl'(X, Y).
+
+-spec bitwise_shift_right(integer(), integer()) -> integer().
+bitwise_shift_right(X, Y) ->
+ erlang:'bsr'(X, Y).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@io.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@io.erl
new file mode 100644
index 0000000..a46eae3
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@io.erl
@@ -0,0 +1,27 @@
+-module(gleam@io).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([print/1, print_error/1, println/1, println_error/1, debug/1]).
+
+-spec print(binary()) -> nil.
+print(String) ->
+ gleam_stdlib:print(String).
+
+-spec print_error(binary()) -> nil.
+print_error(String) ->
+ gleam_stdlib:print_error(String).
+
+-spec println(binary()) -> nil.
+println(String) ->
+ gleam_stdlib:println(String).
+
+-spec println_error(binary()) -> nil.
+println_error(String) ->
+ gleam_stdlib:println_error(String).
+
+-spec debug(CZT) -> CZT.
+debug(Term) ->
+ _pipe = Term,
+ _pipe@1 = gleam@string:inspect(_pipe),
+ gleam_stdlib:println_error(_pipe@1),
+ Term.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@iterator.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@iterator.erl
new file mode 100644
index 0000000..aa84139
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@iterator.erl
@@ -0,0 +1,744 @@
+-module(gleam@iterator).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([unfold/2, repeatedly/1, repeat/1, from_list/1, transform/3, fold/3, run/1, to_list/1, step/1, take/2, drop/2, map/2, map2/3, append/2, flatten/1, concat/1, flat_map/2, filter/2, cycle/1, find/2, index/1, iterate/2, take_while/2, drop_while/2, scan/3, zip/2, chunk/2, sized_chunk/2, intersperse/2, any/2, all/2, group/2, reduce/2, last/1, empty/0, once/1, range/2, single/1, interleave/2, fold_until/3, try_fold/3, first/1, at/2, length/1, each/2, yield/2]).
+-export_type([action/1, iterator/1, step/2, chunk/2, sized_chunk/1]).
+
+-type action(BPF) :: stop | {continue, BPF, fun(() -> action(BPF))}.
+
+-opaque iterator(BPG) :: {iterator, fun(() -> action(BPG))}.
+
+-type step(BPH, BPI) :: {next, BPH, BPI} | done.
+
+-type chunk(BPJ, BPK) :: {another_by,
+ list(BPJ),
+ BPK,
+ BPJ,
+ fun(() -> action(BPJ))} |
+ {last_by, list(BPJ)}.
+
+-type sized_chunk(BPL) :: {another, list(BPL), fun(() -> action(BPL))} |
+ {last, list(BPL)} |
+ no_more.
+
+-spec stop() -> action(any()).
+stop() ->
+ stop.
+
+-spec do_unfold(BPO, fun((BPO) -> step(BPP, BPO))) -> fun(() -> action(BPP)).
+do_unfold(Initial, F) ->
+ fun() -> case F(Initial) of
+ {next, X, Acc} ->
+ {continue, X, do_unfold(Acc, F)};
+
+ done ->
+ stop
+ end end.
+
+-spec unfold(BPT, fun((BPT) -> step(BPU, BPT))) -> iterator(BPU).
+unfold(Initial, F) ->
+ _pipe = Initial,
+ _pipe@1 = do_unfold(_pipe, F),
+ {iterator, _pipe@1}.
+
+-spec repeatedly(fun(() -> BPY)) -> iterator(BPY).
+repeatedly(F) ->
+ unfold(nil, fun(_) -> {next, F(), nil} end).
+
+-spec repeat(BQA) -> iterator(BQA).
+repeat(X) ->
+ repeatedly(fun() -> X end).
+
+-spec from_list(list(BQC)) -> iterator(BQC).
+from_list(List) ->
+ Yield = fun(Acc) -> case Acc of
+ [] ->
+ done;
+
+ [Head | Tail] ->
+ {next, Head, Tail}
+ end end,
+ unfold(List, Yield).
+
+-spec do_transform(
+ fun(() -> action(BQF)),
+ BQH,
+ fun((BQH, BQF) -> step(BQI, BQH))
+) -> fun(() -> action(BQI)).
+do_transform(Continuation, State, F) ->
+ fun() -> case Continuation() of
+ stop ->
+ stop;
+
+ {continue, El, Next} ->
+ case F(State, El) of
+ done ->
+ stop;
+
+ {next, Yield, Next_state} ->
+ {continue, Yield, do_transform(Next, Next_state, F)}
+ end
+ end end.
+
+-spec transform(iterator(BQM), BQO, fun((BQO, BQM) -> step(BQP, BQO))) -> iterator(BQP).
+transform(Iterator, Initial, F) ->
+ _pipe = do_transform(erlang:element(2, Iterator), Initial, F),
+ {iterator, _pipe}.
+
+-spec do_fold(fun(() -> action(BQT)), fun((BQV, BQT) -> BQV), BQV) -> BQV.
+do_fold(Continuation, F, Accumulator) ->
+ case Continuation() of
+ {continue, Elem, Next} ->
+ do_fold(Next, F, F(Accumulator, Elem));
+
+ stop ->
+ Accumulator
+ end.
+
+-spec fold(iterator(BQW), BQY, fun((BQY, BQW) -> BQY)) -> BQY.
+fold(Iterator, Initial, F) ->
+ _pipe = erlang:element(2, Iterator),
+ do_fold(_pipe, F, Initial).
+
+-spec run(iterator(any())) -> nil.
+run(Iterator) ->
+ fold(Iterator, nil, fun(_, _) -> nil end).
+
+-spec to_list(iterator(BRB)) -> list(BRB).
+to_list(Iterator) ->
+ _pipe = Iterator,
+ _pipe@1 = fold(_pipe, [], fun(Acc, E) -> [E | Acc] end),
+ gleam@list:reverse(_pipe@1).
+
+-spec step(iterator(BRE)) -> step(BRE, iterator(BRE)).
+step(Iterator) ->
+ case (erlang:element(2, Iterator))() of
+ stop ->
+ done;
+
+ {continue, E, A} ->
+ {next, E, {iterator, A}}
+ end.
+
+-spec do_take(fun(() -> action(BRJ)), integer()) -> fun(() -> action(BRJ)).
+do_take(Continuation, Desired) ->
+ fun() -> case Desired > 0 of
+ false ->
+ stop;
+
+ true ->
+ case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ {continue, E, do_take(Next, Desired - 1)}
+ end
+ end end.
+
+-spec take(iterator(BRM), integer()) -> iterator(BRM).
+take(Iterator, Desired) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_take(_pipe, Desired),
+ {iterator, _pipe@1}.
+
+-spec do_drop(fun(() -> action(BRP)), integer()) -> action(BRP).
+do_drop(Continuation, Desired) ->
+ case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ case Desired > 0 of
+ true ->
+ do_drop(Next, Desired - 1);
+
+ false ->
+ {continue, E, Next}
+ end
+ end.
+
+-spec drop(iterator(BRS), integer()) -> iterator(BRS).
+drop(Iterator, Desired) ->
+ _pipe = fun() -> do_drop(erlang:element(2, Iterator), Desired) end,
+ {iterator, _pipe}.
+
+-spec do_map(fun(() -> action(BRV)), fun((BRV) -> BRX)) -> fun(() -> action(BRX)).
+do_map(Continuation, F) ->
+ fun() -> case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Continuation@1} ->
+ {continue, F(E), do_map(Continuation@1, F)}
+ end end.
+
+-spec map(iterator(BRZ), fun((BRZ) -> BSB)) -> iterator(BSB).
+map(Iterator, F) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_map(_pipe, F),
+ {iterator, _pipe@1}.
+
+-spec do_map2(
+ fun(() -> action(BSD)),
+ fun(() -> action(BSF)),
+ fun((BSD, BSF) -> BSH)
+) -> fun(() -> action(BSH)).
+do_map2(Continuation1, Continuation2, Fun) ->
+ fun() -> case Continuation1() of
+ stop ->
+ stop;
+
+ {continue, A, Next_a} ->
+ case Continuation2() of
+ stop ->
+ stop;
+
+ {continue, B, Next_b} ->
+ {continue, Fun(A, B), do_map2(Next_a, Next_b, Fun)}
+ end
+ end end.
+
+-spec map2(iterator(BSJ), iterator(BSL), fun((BSJ, BSL) -> BSN)) -> iterator(BSN).
+map2(Iterator1, Iterator2, Fun) ->
+ _pipe = do_map2(
+ erlang:element(2, Iterator1),
+ erlang:element(2, Iterator2),
+ Fun
+ ),
+ {iterator, _pipe}.
+
+-spec do_append(fun(() -> action(BSP)), fun(() -> action(BSP))) -> action(BSP).
+do_append(First, Second) ->
+ case First() of
+ {continue, E, First@1} ->
+ {continue, E, fun() -> do_append(First@1, Second) end};
+
+ stop ->
+ Second()
+ end.
+
+-spec append(iterator(BST), iterator(BST)) -> iterator(BST).
+append(First, Second) ->
+ _pipe = fun() ->
+ do_append(erlang:element(2, First), erlang:element(2, Second))
+ end,
+ {iterator, _pipe}.
+
+-spec do_flatten(fun(() -> action(iterator(BSX)))) -> action(BSX).
+do_flatten(Flattened) ->
+ case Flattened() of
+ stop ->
+ stop;
+
+ {continue, It, Next_iterator} ->
+ do_append(
+ erlang:element(2, It),
+ fun() -> do_flatten(Next_iterator) end
+ )
+ end.
+
+-spec flatten(iterator(iterator(BTB))) -> iterator(BTB).
+flatten(Iterator) ->
+ _pipe = fun() -> do_flatten(erlang:element(2, Iterator)) end,
+ {iterator, _pipe}.
+
+-spec concat(list(iterator(BTF))) -> iterator(BTF).
+concat(Iterators) ->
+ flatten(from_list(Iterators)).
+
+-spec flat_map(iterator(BTJ), fun((BTJ) -> iterator(BTL))) -> iterator(BTL).
+flat_map(Iterator, F) ->
+ _pipe = Iterator,
+ _pipe@1 = map(_pipe, F),
+ flatten(_pipe@1).
+
+-spec do_filter(fun(() -> action(BTO)), fun((BTO) -> boolean())) -> action(BTO).
+do_filter(Continuation, Predicate) ->
+ case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Iterator} ->
+ case Predicate(E) of
+ true ->
+ {continue, E, fun() -> do_filter(Iterator, Predicate) end};
+
+ false ->
+ do_filter(Iterator, Predicate)
+ end
+ end.
+
+-spec filter(iterator(BTR), fun((BTR) -> boolean())) -> iterator(BTR).
+filter(Iterator, Predicate) ->
+ _pipe = fun() -> do_filter(erlang:element(2, Iterator), Predicate) end,
+ {iterator, _pipe}.
+
+-spec cycle(iterator(BTU)) -> iterator(BTU).
+cycle(Iterator) ->
+ _pipe = repeat(Iterator),
+ flatten(_pipe).
+
+-spec do_find(fun(() -> action(BTY)), fun((BTY) -> boolean())) -> {ok, BTY} |
+ {error, nil}.
+do_find(Continuation, F) ->
+ case Continuation() of
+ stop ->
+ {error, nil};
+
+ {continue, E, Next} ->
+ case F(E) of
+ true ->
+ {ok, E};
+
+ false ->
+ do_find(Next, F)
+ end
+ end.
+
+-spec find(iterator(BUC), fun((BUC) -> boolean())) -> {ok, BUC} | {error, nil}.
+find(Haystack, Is_desired) ->
+ _pipe = erlang:element(2, Haystack),
+ do_find(_pipe, Is_desired).
+
+-spec do_index(fun(() -> action(BUG)), integer()) -> fun(() -> action({integer(),
+ BUG})).
+do_index(Continuation, Next) ->
+ fun() -> case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Continuation@1} ->
+ {continue, {Next, E}, do_index(Continuation@1, Next + 1)}
+ end end.
+
+-spec index(iterator(BUJ)) -> iterator({integer(), BUJ}).
+index(Iterator) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_index(_pipe, 0),
+ {iterator, _pipe@1}.
+
+-spec iterate(BUM, fun((BUM) -> BUM)) -> iterator(BUM).
+iterate(Initial, F) ->
+ unfold(Initial, fun(Element) -> {next, Element, F(Element)} end).
+
+-spec do_take_while(fun(() -> action(BUO)), fun((BUO) -> boolean())) -> fun(() -> action(BUO)).
+do_take_while(Continuation, Predicate) ->
+ fun() -> case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ case Predicate(E) of
+ false ->
+ stop;
+
+ true ->
+ {continue, E, do_take_while(Next, Predicate)}
+ end
+ end end.
+
+-spec take_while(iterator(BUR), fun((BUR) -> boolean())) -> iterator(BUR).
+take_while(Iterator, Predicate) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_take_while(_pipe, Predicate),
+ {iterator, _pipe@1}.
+
+-spec do_drop_while(fun(() -> action(BUU)), fun((BUU) -> boolean())) -> action(BUU).
+do_drop_while(Continuation, Predicate) ->
+ case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ case Predicate(E) of
+ false ->
+ {continue, E, Next};
+
+ true ->
+ do_drop_while(Next, Predicate)
+ end
+ end.
+
+-spec drop_while(iterator(BUX), fun((BUX) -> boolean())) -> iterator(BUX).
+drop_while(Iterator, Predicate) ->
+ _pipe = fun() -> do_drop_while(erlang:element(2, Iterator), Predicate) end,
+ {iterator, _pipe}.
+
+-spec do_scan(fun(() -> action(BVA)), fun((BVC, BVA) -> BVC), BVC) -> fun(() -> action(BVC)).
+do_scan(Continuation, F, Accumulator) ->
+ fun() -> case Continuation() of
+ stop ->
+ stop;
+
+ {continue, El, Next} ->
+ Accumulated = F(Accumulator, El),
+ {continue, Accumulated, do_scan(Next, F, Accumulated)}
+ end end.
+
+-spec scan(iterator(BVE), BVG, fun((BVG, BVE) -> BVG)) -> iterator(BVG).
+scan(Iterator, Initial, F) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_scan(_pipe, F, Initial),
+ {iterator, _pipe@1}.
+
+-spec do_zip(fun(() -> action(BVI)), fun(() -> action(BVK))) -> fun(() -> action({BVI,
+ BVK})).
+do_zip(Left, Right) ->
+ fun() -> case Left() of
+ stop ->
+ stop;
+
+ {continue, El_left, Next_left} ->
+ case Right() of
+ stop ->
+ stop;
+
+ {continue, El_right, Next_right} ->
+ {continue,
+ {El_left, El_right},
+ do_zip(Next_left, Next_right)}
+ end
+ end end.
+
+-spec zip(iterator(BVN), iterator(BVP)) -> iterator({BVN, BVP}).
+zip(Left, Right) ->
+ _pipe = do_zip(erlang:element(2, Left), erlang:element(2, Right)),
+ {iterator, _pipe}.
+
+-spec next_chunk(fun(() -> action(BVS)), fun((BVS) -> BVU), BVU, list(BVS)) -> chunk(BVS, BVU).
+next_chunk(Continuation, F, Previous_key, Current_chunk) ->
+ case Continuation() of
+ stop ->
+ {last_by, gleam@list:reverse(Current_chunk)};
+
+ {continue, E, Next} ->
+ Key = F(E),
+ case Key =:= Previous_key of
+ true ->
+ next_chunk(Next, F, Key, [E | Current_chunk]);
+
+ false ->
+ {another_by,
+ gleam@list:reverse(Current_chunk),
+ Key,
+ E,
+ Next}
+ end
+ end.
+
+-spec do_chunk(fun(() -> action(BVY)), fun((BVY) -> BWA), BWA, BVY) -> action(list(BVY)).
+do_chunk(Continuation, F, Previous_key, Previous_element) ->
+ case next_chunk(Continuation, F, Previous_key, [Previous_element]) of
+ {last_by, Chunk} ->
+ {continue, Chunk, fun stop/0};
+
+ {another_by, Chunk@1, Key, El, Next} ->
+ {continue, Chunk@1, fun() -> do_chunk(Next, F, Key, El) end}
+ end.
+
+-spec chunk(iterator(BWD), fun((BWD) -> any())) -> iterator(list(BWD)).
+chunk(Iterator, F) ->
+ _pipe = fun() -> case (erlang:element(2, Iterator))() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ do_chunk(Next, F, F(E), E)
+ end end,
+ {iterator, _pipe}.
+
+-spec next_sized_chunk(fun(() -> action(BWI)), integer(), list(BWI)) -> sized_chunk(BWI).
+next_sized_chunk(Continuation, Left, Current_chunk) ->
+ case Continuation() of
+ stop ->
+ case Current_chunk of
+ [] ->
+ no_more;
+
+ Remaining ->
+ {last, gleam@list:reverse(Remaining)}
+ end;
+
+ {continue, E, Next} ->
+ Chunk = [E | Current_chunk],
+ case Left > 1 of
+ false ->
+ {another, gleam@list:reverse(Chunk), Next};
+
+ true ->
+ next_sized_chunk(Next, Left - 1, Chunk)
+ end
+ end.
+
+-spec do_sized_chunk(fun(() -> action(BWM)), integer()) -> fun(() -> action(list(BWM))).
+do_sized_chunk(Continuation, Count) ->
+ fun() -> case next_sized_chunk(Continuation, Count, []) of
+ no_more ->
+ stop;
+
+ {last, Chunk} ->
+ {continue, Chunk, fun stop/0};
+
+ {another, Chunk@1, Next_element} ->
+ {continue, Chunk@1, do_sized_chunk(Next_element, Count)}
+ end end.
+
+-spec sized_chunk(iterator(BWQ), integer()) -> iterator(list(BWQ)).
+sized_chunk(Iterator, Count) ->
+ _pipe = erlang:element(2, Iterator),
+ _pipe@1 = do_sized_chunk(_pipe, Count),
+ {iterator, _pipe@1}.
+
+-spec do_intersperse(fun(() -> action(BWU)), BWU) -> action(BWU).
+do_intersperse(Continuation, Separator) ->
+ case Continuation() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ Next_interspersed = fun() -> do_intersperse(Next, Separator) end,
+ {continue, Separator, fun() -> {continue, E, Next_interspersed} end}
+ end.
+
+-spec intersperse(iterator(BWX), BWX) -> iterator(BWX).
+intersperse(Iterator, Elem) ->
+ _pipe = fun() -> case (erlang:element(2, Iterator))() of
+ stop ->
+ stop;
+
+ {continue, E, Next} ->
+ {continue, E, fun() -> do_intersperse(Next, Elem) end}
+ end end,
+ {iterator, _pipe}.
+
+-spec do_any(fun(() -> action(BXA)), fun((BXA) -> boolean())) -> boolean().
+do_any(Continuation, Predicate) ->
+ case Continuation() of
+ stop ->
+ false;
+
+ {continue, E, Next} ->
+ case Predicate(E) of
+ true ->
+ true;
+
+ false ->
+ do_any(Next, Predicate)
+ end
+ end.
+
+-spec any(iterator(BXC), fun((BXC) -> boolean())) -> boolean().
+any(Iterator, Predicate) ->
+ _pipe = erlang:element(2, Iterator),
+ do_any(_pipe, Predicate).
+
+-spec do_all(fun(() -> action(BXE)), fun((BXE) -> boolean())) -> boolean().
+do_all(Continuation, Predicate) ->
+ case Continuation() of
+ stop ->
+ true;
+
+ {continue, E, Next} ->
+ case Predicate(E) of
+ true ->
+ do_all(Next, Predicate);
+
+ false ->
+ false
+ end
+ end.
+
+-spec all(iterator(BXG), fun((BXG) -> boolean())) -> boolean().
+all(Iterator, Predicate) ->
+ _pipe = erlang:element(2, Iterator),
+ do_all(_pipe, Predicate).
+
+-spec update_group_with(BXI) -> fun((gleam@option:option(list(BXI))) -> list(BXI)).
+update_group_with(El) ->
+ fun(Maybe_group) -> case Maybe_group of
+ {some, Group} ->
+ [El | Group];
+
+ none ->
+ [El]
+ end end.
+
+-spec group_updater(fun((BXM) -> BXN)) -> fun((gleam@dict:dict(BXN, list(BXM)), BXM) -> gleam@dict:dict(BXN, list(BXM))).
+group_updater(F) ->
+ fun(Groups, Elem) -> _pipe = Groups,
+ gleam@dict:update(_pipe, F(Elem), update_group_with(Elem)) end.
+
+-spec group(iterator(BXU), fun((BXU) -> BXW)) -> gleam@dict:dict(BXW, list(BXU)).
+group(Iterator, Key) ->
+ _pipe = Iterator,
+ _pipe@1 = fold(_pipe, gleam@dict:new(), group_updater(Key)),
+ gleam@dict:map_values(
+ _pipe@1,
+ fun(_, Group) -> gleam@list:reverse(Group) end
+ ).
+
+-spec reduce(iterator(BYA), fun((BYA, BYA) -> BYA)) -> {ok, BYA} | {error, nil}.
+reduce(Iterator, F) ->
+ case (erlang:element(2, Iterator))() of
+ stop ->
+ {error, nil};
+
+ {continue, E, Next} ->
+ _pipe = do_fold(Next, F, E),
+ {ok, _pipe}
+ end.
+
+-spec last(iterator(BYE)) -> {ok, BYE} | {error, nil}.
+last(Iterator) ->
+ _pipe = Iterator,
+ reduce(_pipe, fun(_, Elem) -> Elem end).
+
+-spec empty() -> iterator(any()).
+empty() ->
+ {iterator, fun stop/0}.
+
+-spec once(fun(() -> BYK)) -> iterator(BYK).
+once(F) ->
+ _pipe = fun() -> {continue, F(), fun stop/0} end,
+ {iterator, _pipe}.
+
+-spec range(integer(), integer()) -> iterator(integer()).
+range(Start, Stop) ->
+ case gleam@int:compare(Start, Stop) of
+ eq ->
+ once(fun() -> Start end);
+
+ gt ->
+ unfold(Start, fun(Current) -> case Current < Stop of
+ false ->
+ {next, Current, Current - 1};
+
+ true ->
+ done
+ end end);
+
+ lt ->
+ unfold(Start, fun(Current@1) -> case Current@1 > Stop of
+ false ->
+ {next, Current@1, Current@1 + 1};
+
+ true ->
+ done
+ end end)
+ end.
+
+-spec single(BYM) -> iterator(BYM).
+single(Elem) ->
+ once(fun() -> Elem end).
+
+-spec do_interleave(fun(() -> action(BYO)), fun(() -> action(BYO))) -> action(BYO).
+do_interleave(Current, Next) ->
+ case Current() of
+ stop ->
+ Next();
+
+ {continue, E, Next_other} ->
+ {continue, E, fun() -> do_interleave(Next, Next_other) end}
+ end.
+
+-spec interleave(iterator(BYS), iterator(BYS)) -> iterator(BYS).
+interleave(Left, Right) ->
+ _pipe = fun() ->
+ do_interleave(erlang:element(2, Left), erlang:element(2, Right))
+ end,
+ {iterator, _pipe}.
+
+-spec do_fold_until(
+ fun(() -> action(BYW)),
+ fun((BYY, BYW) -> gleam@list:continue_or_stop(BYY)),
+ BYY
+) -> BYY.
+do_fold_until(Continuation, F, Accumulator) ->
+ case Continuation() of
+ stop ->
+ Accumulator;
+
+ {continue, Elem, Next} ->
+ case F(Accumulator, Elem) of
+ {continue, Accumulator@1} ->
+ do_fold_until(Next, F, Accumulator@1);
+
+ {stop, Accumulator@2} ->
+ Accumulator@2
+ end
+ end.
+
+-spec fold_until(
+ iterator(BZA),
+ BZC,
+ fun((BZC, BZA) -> gleam@list:continue_or_stop(BZC))
+) -> BZC.
+fold_until(Iterator, Initial, F) ->
+ _pipe = erlang:element(2, Iterator),
+ do_fold_until(_pipe, F, Initial).
+
+-spec do_try_fold(
+ fun(() -> action(BZE)),
+ fun((BZG, BZE) -> {ok, BZG} | {error, BZH}),
+ BZG
+) -> {ok, BZG} | {error, BZH}.
+do_try_fold(Continuation, F, Accumulator) ->
+ case Continuation() of
+ stop ->
+ {ok, Accumulator};
+
+ {continue, Elem, Next} ->
+ gleam@result:'try'(
+ F(Accumulator, Elem),
+ fun(Accumulator@1) -> do_try_fold(Next, F, Accumulator@1) end
+ )
+ end.
+
+-spec try_fold(iterator(BZM), BZO, fun((BZO, BZM) -> {ok, BZO} | {error, BZP})) -> {ok,
+ BZO} |
+ {error, BZP}.
+try_fold(Iterator, Initial, F) ->
+ _pipe = erlang:element(2, Iterator),
+ do_try_fold(_pipe, F, Initial).
+
+-spec first(iterator(BZU)) -> {ok, BZU} | {error, nil}.
+first(Iterator) ->
+ case (erlang:element(2, Iterator))() of
+ stop ->
+ {error, nil};
+
+ {continue, E, _} ->
+ {ok, E}
+ end.
+
+-spec at(iterator(BZY), integer()) -> {ok, BZY} | {error, nil}.
+at(Iterator, Index) ->
+ _pipe = Iterator,
+ _pipe@1 = drop(_pipe, Index),
+ first(_pipe@1).
+
+-spec do_length(fun(() -> action(any())), integer()) -> integer().
+do_length(Continuation, Length) ->
+ case Continuation() of
+ stop ->
+ Length;
+
+ {continue, _, Next} ->
+ do_length(Next, Length + 1)
+ end.
+
+-spec length(iterator(any())) -> integer().
+length(Iterator) ->
+ _pipe = erlang:element(2, Iterator),
+ do_length(_pipe, 0).
+
+-spec each(iterator(CAG), fun((CAG) -> any())) -> nil.
+each(Iterator, F) ->
+ _pipe = Iterator,
+ _pipe@1 = map(_pipe, F),
+ run(_pipe@1).
+
+-spec yield(CAJ, fun(() -> iterator(CAJ))) -> iterator(CAJ).
+yield(Element, Next) ->
+ {iterator, fun() -> {continue, Element, erlang:element(2, Next())} end}.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@list.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@list.erl
new file mode 100644
index 0000000..6c2e684
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@list.erl
@@ -0,0 +1,1129 @@
+-module(gleam@list).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([length/1, reverse/1, is_empty/1, contains/2, first/1, rest/1, filter/2, filter_map/2, map/2, map2/3, index_map/2, try_map/2, drop/2, take/2, new/0, append/2, prepend/2, concat/1, flatten/1, flat_map/2, fold/3, group/2, map_fold/3, fold_right/3, index_fold/3, try_fold/3, fold_until/3, find/2, find_map/2, all/2, any/2, zip/2, strict_zip/2, unzip/1, intersperse/2, at/2, unique/1, sort/2, range/2, repeat/2, split/2, split_while/2, key_find/2, key_filter/2, pop/2, pop_map/2, key_pop/2, key_set/3, each/2, try_each/2, partition/2, permutations/1, window/2, window_by_2/1, drop_while/2, take_while/2, chunk/2, sized_chunk/2, reduce/2, scan/3, last/1, combinations/2, combination_pairs/1, transpose/1, interleave/1, shuffle/1]).
+-export_type([length_mismatch/0, continue_or_stop/1]).
+
+-type length_mismatch() :: length_mismatch.
+
+-type continue_or_stop(UD) :: {continue, UD} | {stop, UD}.
+
+-spec length(list(any())) -> integer().
+length(List) ->
+ erlang:length(List).
+
+-spec reverse(list(UI)) -> list(UI).
+reverse(Xs) ->
+ lists:reverse(Xs).
+
+-spec is_empty(list(any())) -> boolean().
+is_empty(List) ->
+ List =:= [].
+
+-spec contains(list(UQ), UQ) -> boolean().
+contains(List, Elem) ->
+ case List of
+ [] ->
+ false;
+
+ [First | _] when First =:= Elem ->
+ true;
+
+ [_ | Rest] ->
+ contains(Rest, Elem)
+ end.
+
+-spec first(list(US)) -> {ok, US} | {error, nil}.
+first(List) ->
+ case List of
+ [] ->
+ {error, nil};
+
+ [X | _] ->
+ {ok, X}
+ end.
+
+-spec rest(list(UW)) -> {ok, list(UW)} | {error, nil}.
+rest(List) ->
+ case List of
+ [] ->
+ {error, nil};
+
+ [_ | Xs] ->
+ {ok, Xs}
+ end.
+
+-spec update_group(fun((VB) -> VC)) -> fun((gleam@dict:dict(VC, list(VB)), VB) -> gleam@dict:dict(VC, list(VB))).
+update_group(F) ->
+ fun(Groups, Elem) -> case gleam@dict:get(Groups, F(Elem)) of
+ {ok, Existing} ->
+ gleam@dict:insert(Groups, F(Elem), [Elem | Existing]);
+
+ {error, _} ->
+ gleam@dict:insert(Groups, F(Elem), [Elem])
+ end end.
+
+-spec do_filter(list(VP), fun((VP) -> boolean()), list(VP)) -> list(VP).
+do_filter(List, Fun, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Xs] ->
+ New_acc = case Fun(X) of
+ true ->
+ [X | Acc];
+
+ false ->
+ Acc
+ end,
+ do_filter(Xs, Fun, New_acc)
+ end.
+
+-spec filter(list(VT), fun((VT) -> boolean())) -> list(VT).
+filter(List, Predicate) ->
+ do_filter(List, Predicate, []).
+
+-spec do_filter_map(list(VW), fun((VW) -> {ok, VY} | {error, any()}), list(VY)) -> list(VY).
+do_filter_map(List, Fun, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Xs] ->
+ New_acc = case Fun(X) of
+ {ok, X@1} ->
+ [X@1 | Acc];
+
+ {error, _} ->
+ Acc
+ end,
+ do_filter_map(Xs, Fun, New_acc)
+ end.
+
+-spec filter_map(list(WE), fun((WE) -> {ok, WG} | {error, any()})) -> list(WG).
+filter_map(List, Fun) ->
+ do_filter_map(List, Fun, []).
+
+-spec do_map(list(WL), fun((WL) -> WN), list(WN)) -> list(WN).
+do_map(List, Fun, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Xs] ->
+ do_map(Xs, Fun, [Fun(X) | Acc])
+ end.
+
+-spec map(list(WQ), fun((WQ) -> WS)) -> list(WS).
+map(List, Fun) ->
+ do_map(List, Fun, []).
+
+-spec do_map2(list(XA), list(XC), fun((XA, XC) -> XE), list(XE)) -> list(XE).
+do_map2(List1, List2, Fun, Acc) ->
+ case {List1, List2} of
+ {[], _} ->
+ reverse(Acc);
+
+ {_, []} ->
+ reverse(Acc);
+
+ {[A | As_], [B | Bs]} ->
+ do_map2(As_, Bs, Fun, [Fun(A, B) | Acc])
+ end.
+
+-spec map2(list(WU), list(WW), fun((WU, WW) -> WY)) -> list(WY).
+map2(List1, List2, Fun) ->
+ do_map2(List1, List2, Fun, []).
+
+-spec do_index_map(list(XM), fun((integer(), XM) -> XO), integer(), list(XO)) -> list(XO).
+do_index_map(List, Fun, Index, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Xs] ->
+ Acc@1 = [Fun(Index, X) | Acc],
+ do_index_map(Xs, Fun, Index + 1, Acc@1)
+ end.
+
+-spec index_map(list(XR), fun((integer(), XR) -> XT)) -> list(XT).
+index_map(List, Fun) ->
+ do_index_map(List, Fun, 0, []).
+
+-spec do_try_map(list(XV), fun((XV) -> {ok, XX} | {error, XY}), list(XX)) -> {ok,
+ list(XX)} |
+ {error, XY}.
+do_try_map(List, Fun, Acc) ->
+ case List of
+ [] ->
+ {ok, reverse(Acc)};
+
+ [X | Xs] ->
+ case Fun(X) of
+ {ok, Y} ->
+ do_try_map(Xs, Fun, [Y | Acc]);
+
+ {error, Error} ->
+ {error, Error}
+ end
+ end.
+
+-spec try_map(list(YF), fun((YF) -> {ok, YH} | {error, YI})) -> {ok, list(YH)} |
+ {error, YI}.
+try_map(List, Fun) ->
+ do_try_map(List, Fun, []).
+
+-spec drop(list(YO), integer()) -> list(YO).
+drop(List, N) ->
+ case N =< 0 of
+ true ->
+ List;
+
+ false ->
+ case List of
+ [] ->
+ [];
+
+ [_ | Xs] ->
+ drop(Xs, N - 1)
+ end
+ end.
+
+-spec do_take(list(YR), integer(), list(YR)) -> list(YR).
+do_take(List, N, Acc) ->
+ case N =< 0 of
+ true ->
+ reverse(Acc);
+
+ false ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Xs] ->
+ do_take(Xs, N - 1, [X | Acc])
+ end
+ end.
+
+-spec take(list(YV), integer()) -> list(YV).
+take(List, N) ->
+ do_take(List, N, []).
+
+-spec new() -> list(any()).
+new() ->
+ [].
+
+-spec append(list(AAA), list(AAA)) -> list(AAA).
+append(First, Second) ->
+ lists:append(First, Second).
+
+-spec prepend(list(AAI), AAI) -> list(AAI).
+prepend(List, Item) ->
+ [Item | List].
+
+-spec reverse_and_prepend(list(AAL), list(AAL)) -> list(AAL).
+reverse_and_prepend(Prefix, Suffix) ->
+ case Prefix of
+ [] ->
+ Suffix;
+
+ [First | Rest] ->
+ reverse_and_prepend(Rest, [First | Suffix])
+ end.
+
+-spec do_concat(list(list(AAP)), list(AAP)) -> list(AAP).
+do_concat(Lists, Acc) ->
+ case Lists of
+ [] ->
+ reverse(Acc);
+
+ [List | Further_lists] ->
+ do_concat(Further_lists, reverse_and_prepend(List, Acc))
+ end.
+
+-spec concat(list(list(AAU))) -> list(AAU).
+concat(Lists) ->
+ do_concat(Lists, []).
+
+-spec flatten(list(list(AAY))) -> list(AAY).
+flatten(Lists) ->
+ do_concat(Lists, []).
+
+-spec flat_map(list(ABC), fun((ABC) -> list(ABE))) -> list(ABE).
+flat_map(List, Fun) ->
+ _pipe = map(List, Fun),
+ concat(_pipe).
+
+-spec fold(list(ABH), ABJ, fun((ABJ, ABH) -> ABJ)) -> ABJ.
+fold(List, Initial, Fun) ->
+ case List of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ fold(Rest, Fun(Initial, X), Fun)
+ end.
+
+-spec group(list(VJ), fun((VJ) -> VL)) -> gleam@dict:dict(VL, list(VJ)).
+group(List, Key) ->
+ fold(List, gleam@dict:new(), update_group(Key)).
+
+-spec map_fold(list(XH), XJ, fun((XJ, XH) -> {XJ, XK})) -> {XJ, list(XK)}.
+map_fold(List, Acc, Fun) ->
+ _pipe = fold(
+ List,
+ {Acc, []},
+ fun(Acc@1, Item) ->
+ {Current_acc, Items} = Acc@1,
+ {Next_acc, Next_item} = Fun(Current_acc, Item),
+ {Next_acc, [Next_item | Items]}
+ end
+ ),
+ gleam@pair:map_second(_pipe, fun reverse/1).
+
+-spec fold_right(list(ABK), ABM, fun((ABM, ABK) -> ABM)) -> ABM.
+fold_right(List, Initial, Fun) ->
+ case List of
+ [] ->
+ Initial;
+
+ [X | Rest] ->
+ Fun(fold_right(Rest, Initial, Fun), X)
+ end.
+
+-spec do_index_fold(
+ list(ABN),
+ ABP,
+ fun((ABP, ABN, integer()) -> ABP),
+ integer()
+) -> ABP.
+do_index_fold(Over, Acc, With, Index) ->
+ case Over of
+ [] ->
+ Acc;
+
+ [First | Rest] ->
+ do_index_fold(Rest, With(Acc, First, Index), With, Index + 1)
+ end.
+
+-spec index_fold(list(ABQ), ABS, fun((ABS, ABQ, integer()) -> ABS)) -> ABS.
+index_fold(Over, Initial, Fun) ->
+ do_index_fold(Over, Initial, Fun, 0).
+
+-spec try_fold(list(ABT), ABV, fun((ABV, ABT) -> {ok, ABV} | {error, ABW})) -> {ok,
+ ABV} |
+ {error, ABW}.
+try_fold(Collection, Accumulator, Fun) ->
+ case Collection of
+ [] ->
+ {ok, Accumulator};
+
+ [First | Rest] ->
+ case Fun(Accumulator, First) of
+ {ok, Result} ->
+ try_fold(Rest, Result, Fun);
+
+ {error, _} = Error ->
+ Error
+ end
+ end.
+
+-spec fold_until(list(ACB), ACD, fun((ACD, ACB) -> continue_or_stop(ACD))) -> ACD.
+fold_until(Collection, Accumulator, Fun) ->
+ case Collection of
+ [] ->
+ Accumulator;
+
+ [First | Rest] ->
+ case Fun(Accumulator, First) of
+ {continue, Next_accumulator} ->
+ fold_until(Rest, Next_accumulator, Fun);
+
+ {stop, B} ->
+ B
+ end
+ end.
+
+-spec find(list(ACF), fun((ACF) -> boolean())) -> {ok, ACF} | {error, nil}.
+find(Haystack, Is_desired) ->
+ case Haystack of
+ [] ->
+ {error, nil};
+
+ [X | Rest] ->
+ case Is_desired(X) of
+ true ->
+ {ok, X};
+
+ _ ->
+ find(Rest, Is_desired)
+ end
+ end.
+
+-spec find_map(list(ACJ), fun((ACJ) -> {ok, ACL} | {error, any()})) -> {ok, ACL} |
+ {error, nil}.
+find_map(Haystack, Fun) ->
+ case Haystack of
+ [] ->
+ {error, nil};
+
+ [X | Rest] ->
+ case Fun(X) of
+ {ok, X@1} ->
+ {ok, X@1};
+
+ _ ->
+ find_map(Rest, Fun)
+ end
+ end.
+
+-spec all(list(ACR), fun((ACR) -> boolean())) -> boolean().
+all(List, Predicate) ->
+ case List of
+ [] ->
+ true;
+
+ [First | Rest] ->
+ case Predicate(First) of
+ true ->
+ all(Rest, Predicate);
+
+ false ->
+ false
+ end
+ end.
+
+-spec any(list(ACT), fun((ACT) -> boolean())) -> boolean().
+any(List, Predicate) ->
+ case List of
+ [] ->
+ false;
+
+ [First | Rest] ->
+ case Predicate(First) of
+ true ->
+ true;
+
+ false ->
+ any(Rest, Predicate)
+ end
+ end.
+
+-spec do_zip(list(ACV), list(ACX), list({ACV, ACX})) -> list({ACV, ACX}).
+do_zip(Xs, Ys, Acc) ->
+ case {Xs, Ys} of
+ {[X | Xs@1], [Y | Ys@1]} ->
+ do_zip(Xs@1, Ys@1, [{X, Y} | Acc]);
+
+ {_, _} ->
+ reverse(Acc)
+ end.
+
+-spec zip(list(ADB), list(ADD)) -> list({ADB, ADD}).
+zip(List, Other) ->
+ do_zip(List, Other, []).
+
+-spec strict_zip(list(ADG), list(ADI)) -> {ok, list({ADG, ADI})} |
+ {error, length_mismatch()}.
+strict_zip(List, Other) ->
+ case length(List) =:= length(Other) of
+ true ->
+ {ok, zip(List, Other)};
+
+ false ->
+ {error, length_mismatch}
+ end.
+
+-spec do_unzip(list({ATA, ATB}), list(ATA), list(ATB)) -> {list(ATA), list(ATB)}.
+do_unzip(Input, Xs, Ys) ->
+ case Input of
+ [] ->
+ {reverse(Xs), reverse(Ys)};
+
+ [{X, Y} | Rest] ->
+ do_unzip(Rest, [X | Xs], [Y | Ys])
+ end.
+
+-spec unzip(list({ADR, ADS})) -> {list(ADR), list(ADS)}.
+unzip(Input) ->
+ do_unzip(Input, [], []).
+
+-spec do_intersperse(list(ADW), ADW, list(ADW)) -> list(ADW).
+do_intersperse(List, Separator, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [X | Rest] ->
+ do_intersperse(Rest, Separator, [X, Separator | Acc])
+ end.
+
+-spec intersperse(list(AEA), AEA) -> list(AEA).
+intersperse(List, Elem) ->
+ case List of
+ [] ->
+ List;
+
+ [_] ->
+ List;
+
+ [X | Rest] ->
+ do_intersperse(Rest, Elem, [X])
+ end.
+
+-spec at(list(AED), integer()) -> {ok, AED} | {error, nil}.
+at(List, Index) ->
+ case Index >= 0 of
+ true ->
+ _pipe = List,
+ _pipe@1 = drop(_pipe, Index),
+ first(_pipe@1);
+
+ false ->
+ {error, nil}
+ end.
+
+-spec unique(list(AEH)) -> list(AEH).
+unique(List) ->
+ case List of
+ [] ->
+ [];
+
+ [X | Rest] ->
+ [X | unique(filter(Rest, fun(Y) -> Y /= X end))]
+ end.
+
+-spec merge_up(
+ integer(),
+ integer(),
+ list(AEK),
+ list(AEK),
+ list(AEK),
+ fun((AEK, AEK) -> gleam@order:order())
+) -> list(AEK).
+merge_up(Na, Nb, A, B, Acc, Compare) ->
+ case {Na, Nb, A, B} of
+ {0, 0, _, _} ->
+ Acc;
+
+ {_, 0, [Ax | Ar], _} ->
+ merge_up(Na - 1, Nb, Ar, B, [Ax | Acc], Compare);
+
+ {0, _, _, [Bx | Br]} ->
+ merge_up(Na, Nb - 1, A, Br, [Bx | Acc], Compare);
+
+ {_, _, [Ax@1 | Ar@1], [Bx@1 | Br@1]} ->
+ case Compare(Ax@1, Bx@1) of
+ gt ->
+ merge_up(Na, Nb - 1, A, Br@1, [Bx@1 | Acc], Compare);
+
+ _ ->
+ merge_up(Na - 1, Nb, Ar@1, B, [Ax@1 | Acc], Compare)
+ end;
+
+ {_, _, _, _} ->
+ Acc
+ end.
+
+-spec merge_down(
+ integer(),
+ integer(),
+ list(AEP),
+ list(AEP),
+ list(AEP),
+ fun((AEP, AEP) -> gleam@order:order())
+) -> list(AEP).
+merge_down(Na, Nb, A, B, Acc, Compare) ->
+ case {Na, Nb, A, B} of
+ {0, 0, _, _} ->
+ Acc;
+
+ {_, 0, [Ax | Ar], _} ->
+ merge_down(Na - 1, Nb, Ar, B, [Ax | Acc], Compare);
+
+ {0, _, _, [Bx | Br]} ->
+ merge_down(Na, Nb - 1, A, Br, [Bx | Acc], Compare);
+
+ {_, _, [Ax@1 | Ar@1], [Bx@1 | Br@1]} ->
+ case Compare(Bx@1, Ax@1) of
+ lt ->
+ merge_down(Na - 1, Nb, Ar@1, B, [Ax@1 | Acc], Compare);
+
+ _ ->
+ merge_down(Na, Nb - 1, A, Br@1, [Bx@1 | Acc], Compare)
+ end;
+
+ {_, _, _, _} ->
+ Acc
+ end.
+
+-spec merge_sort(
+ list(AEU),
+ integer(),
+ fun((AEU, AEU) -> gleam@order:order()),
+ boolean()
+) -> list(AEU).
+merge_sort(L, Ln, Compare, Down) ->
+ N = Ln div 2,
+ A = L,
+ B = drop(L, N),
+ case Ln < 3 of
+ true ->
+ case Down of
+ true ->
+ merge_down(N, Ln - N, A, B, [], Compare);
+
+ false ->
+ merge_up(N, Ln - N, A, B, [], Compare)
+ end;
+
+ false ->
+ case Down of
+ true ->
+ merge_down(
+ N,
+ Ln - N,
+ merge_sort(A, N, Compare, false),
+ merge_sort(B, Ln - N, Compare, false),
+ [],
+ Compare
+ );
+
+ false ->
+ merge_up(
+ N,
+ Ln - N,
+ merge_sort(A, N, Compare, true),
+ merge_sort(B, Ln - N, Compare, true),
+ [],
+ Compare
+ )
+ end
+ end.
+
+-spec sort(list(AEX), fun((AEX, AEX) -> gleam@order:order())) -> list(AEX).
+sort(List, Compare) ->
+ merge_sort(List, length(List), Compare, true).
+
+-spec tail_recursive_range(integer(), integer(), list(integer())) -> list(integer()).
+tail_recursive_range(Start, Stop, Acc) ->
+ case gleam@int:compare(Start, Stop) of
+ eq ->
+ [Stop | Acc];
+
+ gt ->
+ tail_recursive_range(Start, Stop + 1, [Stop | Acc]);
+
+ lt ->
+ tail_recursive_range(Start, Stop - 1, [Stop | Acc])
+ end.
+
+-spec range(integer(), integer()) -> list(integer()).
+range(Start, Stop) ->
+ tail_recursive_range(Start, Stop, []).
+
+-spec do_repeat(AFD, integer(), list(AFD)) -> list(AFD).
+do_repeat(A, Times, Acc) ->
+ case Times =< 0 of
+ true ->
+ Acc;
+
+ false ->
+ do_repeat(A, Times - 1, [A | Acc])
+ end.
+
+-spec repeat(AFG, integer()) -> list(AFG).
+repeat(A, Times) ->
+ do_repeat(A, Times, []).
+
+-spec do_split(list(AFI), integer(), list(AFI)) -> {list(AFI), list(AFI)}.
+do_split(List, N, Taken) ->
+ case N =< 0 of
+ true ->
+ {reverse(Taken), List};
+
+ false ->
+ case List of
+ [] ->
+ {reverse(Taken), []};
+
+ [X | Xs] ->
+ do_split(Xs, N - 1, [X | Taken])
+ end
+ end.
+
+-spec split(list(AFN), integer()) -> {list(AFN), list(AFN)}.
+split(List, Index) ->
+ do_split(List, Index, []).
+
+-spec do_split_while(list(AFR), fun((AFR) -> boolean()), list(AFR)) -> {list(AFR),
+ list(AFR)}.
+do_split_while(List, F, Acc) ->
+ case List of
+ [] ->
+ {reverse(Acc), []};
+
+ [X | Xs] ->
+ case F(X) of
+ false ->
+ {reverse(Acc), List};
+
+ _ ->
+ do_split_while(Xs, F, [X | Acc])
+ end
+ end.
+
+-spec split_while(list(AFW), fun((AFW) -> boolean())) -> {list(AFW), list(AFW)}.
+split_while(List, Predicate) ->
+ do_split_while(List, Predicate, []).
+
+-spec key_find(list({AGA, AGB}), AGA) -> {ok, AGB} | {error, nil}.
+key_find(Keyword_list, Desired_key) ->
+ find_map(
+ Keyword_list,
+ fun(Keyword) ->
+ {Key, Value} = Keyword,
+ case Key =:= Desired_key of
+ true ->
+ {ok, Value};
+
+ false ->
+ {error, nil}
+ end
+ end
+ ).
+
+-spec key_filter(list({AGF, AGG}), AGF) -> list(AGG).
+key_filter(Keyword_list, Desired_key) ->
+ filter_map(
+ Keyword_list,
+ fun(Keyword) ->
+ {Key, Value} = Keyword,
+ case Key =:= Desired_key of
+ true ->
+ {ok, Value};
+
+ false ->
+ {error, nil}
+ end
+ end
+ ).
+
+-spec do_pop(list(AWT), fun((AWT) -> boolean()), list(AWT)) -> {ok,
+ {AWT, list(AWT)}} |
+ {error, nil}.
+do_pop(Haystack, Predicate, Checked) ->
+ case Haystack of
+ [] ->
+ {error, nil};
+
+ [X | Rest] ->
+ case Predicate(X) of
+ true ->
+ {ok, {X, append(reverse(Checked), Rest)}};
+
+ false ->
+ do_pop(Rest, Predicate, [X | Checked])
+ end
+ end.
+
+-spec pop(list(AGN), fun((AGN) -> boolean())) -> {ok, {AGN, list(AGN)}} |
+ {error, nil}.
+pop(Haystack, Is_desired) ->
+ do_pop(Haystack, Is_desired, []).
+
+-spec do_pop_map(list(AXH), fun((AXH) -> {ok, AXU} | {error, any()}), list(AXH)) -> {ok,
+ {AXU, list(AXH)}} |
+ {error, nil}.
+do_pop_map(Haystack, Mapper, Checked) ->
+ case Haystack of
+ [] ->
+ {error, nil};
+
+ [X | Rest] ->
+ case Mapper(X) of
+ {ok, Y} ->
+ {ok, {Y, append(reverse(Checked), Rest)}};
+
+ {error, _} ->
+ do_pop_map(Rest, Mapper, [X | Checked])
+ end
+ end.
+
+-spec pop_map(list(AGW), fun((AGW) -> {ok, AGY} | {error, any()})) -> {ok,
+ {AGY, list(AGW)}} |
+ {error, nil}.
+pop_map(Haystack, Is_desired) ->
+ do_pop_map(Haystack, Is_desired, []).
+
+-spec key_pop(list({AHF, AHG}), AHF) -> {ok, {AHG, list({AHF, AHG})}} |
+ {error, nil}.
+key_pop(Haystack, Key) ->
+ pop_map(
+ Haystack,
+ fun(Entry) ->
+ {K, V} = Entry,
+ case K of
+ K@1 when K@1 =:= Key ->
+ {ok, V};
+
+ _ ->
+ {error, nil}
+ end
+ end
+ ).
+
+-spec key_set(list({AHL, AHM}), AHL, AHM) -> list({AHL, AHM}).
+key_set(List, Key, Value) ->
+ case List of
+ [] ->
+ [{Key, Value}];
+
+ [{K, _} | Rest] when K =:= Key ->
+ [{Key, Value} | Rest];
+
+ [First | Rest@1] ->
+ [First | key_set(Rest@1, Key, Value)]
+ end.
+
+-spec each(list(AHP), fun((AHP) -> any())) -> nil.
+each(List, F) ->
+ case List of
+ [] ->
+ nil;
+
+ [X | Xs] ->
+ F(X),
+ each(Xs, F)
+ end.
+
+-spec try_each(list(AHS), fun((AHS) -> {ok, any()} | {error, AHV})) -> {ok, nil} |
+ {error, AHV}.
+try_each(List, Fun) ->
+ case List of
+ [] ->
+ {ok, nil};
+
+ [X | Xs] ->
+ case Fun(X) of
+ {ok, _} ->
+ try_each(Xs, Fun);
+
+ {error, E} ->
+ {error, E}
+ end
+ end.
+
+-spec do_partition(list(AZB), fun((AZB) -> boolean()), list(AZB), list(AZB)) -> {list(AZB),
+ list(AZB)}.
+do_partition(List, Categorise, Trues, Falses) ->
+ case List of
+ [] ->
+ {reverse(Trues), reverse(Falses)};
+
+ [X | Xs] ->
+ case Categorise(X) of
+ true ->
+ do_partition(Xs, Categorise, [X | Trues], Falses);
+
+ false ->
+ do_partition(Xs, Categorise, Trues, [X | Falses])
+ end
+ end.
+
+-spec partition(list(AIF), fun((AIF) -> boolean())) -> {list(AIF), list(AIF)}.
+partition(List, Categorise) ->
+ do_partition(List, Categorise, [], []).
+
+-spec permutations(list(AIJ)) -> list(list(AIJ)).
+permutations(L) ->
+ case L of
+ [] ->
+ [[]];
+
+ _ ->
+ _pipe = L,
+ _pipe@5 = index_map(_pipe, fun(I_idx, I) -> _pipe@1 = L,
+ _pipe@2 = index_fold(
+ _pipe@1,
+ [],
+ fun(Acc, J, J_idx) -> case I_idx =:= J_idx of
+ true ->
+ Acc;
+
+ false ->
+ [J | Acc]
+ end end
+ ),
+ _pipe@3 = reverse(_pipe@2),
+ _pipe@4 = permutations(_pipe@3),
+ map(_pipe@4, fun(Permutation) -> [I | Permutation] end) end),
+ concat(_pipe@5)
+ end.
+
+-spec do_window(list(list(AIN)), list(AIN), integer()) -> list(list(AIN)).
+do_window(Acc, L, N) ->
+ Window = take(L, N),
+ case length(Window) =:= N of
+ true ->
+ do_window([Window | Acc], drop(L, 1), N);
+
+ false ->
+ Acc
+ end.
+
+-spec window(list(AIT), integer()) -> list(list(AIT)).
+window(L, N) ->
+ _pipe = do_window([], L, N),
+ reverse(_pipe).
+
+-spec window_by_2(list(AIX)) -> list({AIX, AIX}).
+window_by_2(L) ->
+ zip(L, drop(L, 1)).
+
+-spec drop_while(list(AJA), fun((AJA) -> boolean())) -> list(AJA).
+drop_while(List, Predicate) ->
+ case List of
+ [] ->
+ [];
+
+ [X | Xs] ->
+ case Predicate(X) of
+ true ->
+ drop_while(Xs, Predicate);
+
+ false ->
+ [X | Xs]
+ end
+ end.
+
+-spec do_take_while(list(AJD), fun((AJD) -> boolean()), list(AJD)) -> list(AJD).
+do_take_while(List, Predicate, Acc) ->
+ case List of
+ [] ->
+ reverse(Acc);
+
+ [First | Rest] ->
+ case Predicate(First) of
+ true ->
+ do_take_while(Rest, Predicate, [First | Acc]);
+
+ false ->
+ reverse(Acc)
+ end
+ end.
+
+-spec take_while(list(AJH), fun((AJH) -> boolean())) -> list(AJH).
+take_while(List, Predicate) ->
+ do_take_while(List, Predicate, []).
+
+-spec do_chunk(list(AJK), fun((AJK) -> AJM), AJM, list(AJK), list(list(AJK))) -> list(list(AJK)).
+do_chunk(List, F, Previous_key, Current_chunk, Acc) ->
+ case List of
+ [First | Rest] ->
+ Key = F(First),
+ case Key =:= Previous_key of
+ false ->
+ New_acc = [reverse(Current_chunk) | Acc],
+ do_chunk(Rest, F, Key, [First], New_acc);
+
+ _ ->
+ do_chunk(Rest, F, Key, [First | Current_chunk], Acc)
+ end;
+
+ _ ->
+ reverse([reverse(Current_chunk) | Acc])
+ end.
+
+-spec chunk(list(AJS), fun((AJS) -> any())) -> list(list(AJS)).
+chunk(List, F) ->
+ case List of
+ [] ->
+ [];
+
+ [First | Rest] ->
+ do_chunk(Rest, F, F(First), [First], [])
+ end.
+
+-spec do_sized_chunk(
+ list(AJX),
+ integer(),
+ integer(),
+ list(AJX),
+ list(list(AJX))
+) -> list(list(AJX)).
+do_sized_chunk(List, Count, Left, Current_chunk, Acc) ->
+ case List of
+ [] ->
+ case Current_chunk of
+ [] ->
+ reverse(Acc);
+
+ Remaining ->
+ reverse([reverse(Remaining) | Acc])
+ end;
+
+ [First | Rest] ->
+ Chunk = [First | Current_chunk],
+ case Left > 1 of
+ false ->
+ do_sized_chunk(
+ Rest,
+ Count,
+ Count,
+ [],
+ [reverse(Chunk) | Acc]
+ );
+
+ true ->
+ do_sized_chunk(Rest, Count, Left - 1, Chunk, Acc)
+ end
+ end.
+
+-spec sized_chunk(list(AKE), integer()) -> list(list(AKE)).
+sized_chunk(List, Count) ->
+ do_sized_chunk(List, Count, Count, [], []).
+
+-spec reduce(list(AKI), fun((AKI, AKI) -> AKI)) -> {ok, AKI} | {error, nil}.
+reduce(List, Fun) ->
+ case List of
+ [] ->
+ {error, nil};
+
+ [First | Rest] ->
+ {ok, fold(Rest, First, Fun)}
+ end.
+
+-spec do_scan(list(AKM), AKO, list(AKO), fun((AKO, AKM) -> AKO)) -> list(AKO).
+do_scan(List, Accumulator, Accumulated, Fun) ->
+ case List of
+ [] ->
+ reverse(Accumulated);
+
+ [X | Xs] ->
+ Next = Fun(Accumulator, X),
+ do_scan(Xs, Next, [Next | Accumulated], Fun)
+ end.
+
+-spec scan(list(AKR), AKT, fun((AKT, AKR) -> AKT)) -> list(AKT).
+scan(List, Initial, Fun) ->
+ do_scan(List, Initial, [], Fun).
+
+-spec last(list(AKV)) -> {ok, AKV} | {error, nil}.
+last(List) ->
+ _pipe = List,
+ reduce(_pipe, fun(_, Elem) -> Elem end).
+
+-spec combinations(list(AKZ), integer()) -> list(list(AKZ)).
+combinations(Items, N) ->
+ case N of
+ 0 ->
+ [[]];
+
+ _ ->
+ case Items of
+ [] ->
+ [];
+
+ [X | Xs] ->
+ First_combinations = begin
+ _pipe = map(
+ combinations(Xs, N - 1),
+ fun(Com) -> [X | Com] end
+ ),
+ reverse(_pipe)
+ end,
+ fold(
+ First_combinations,
+ combinations(Xs, N),
+ fun(Acc, C) -> [C | Acc] end
+ )
+ end
+ end.
+
+-spec do_combination_pairs(list(ALD)) -> list(list({ALD, ALD})).
+do_combination_pairs(Items) ->
+ case Items of
+ [] ->
+ [];
+
+ [X | Xs] ->
+ First_combinations = map(Xs, fun(Other) -> {X, Other} end),
+ [First_combinations | do_combination_pairs(Xs)]
+ end.
+
+-spec combination_pairs(list(ALH)) -> list({ALH, ALH}).
+combination_pairs(Items) ->
+ _pipe = do_combination_pairs(Items),
+ concat(_pipe).
+
+-spec transpose(list(list(ALO))) -> list(list(ALO)).
+transpose(List_of_list) ->
+ Take_first = fun(List) -> case List of
+ [] ->
+ [];
+
+ [F] ->
+ [F];
+
+ [F@1 | _] ->
+ [F@1]
+ end end,
+ case List_of_list of
+ [] ->
+ [];
+
+ [[] | Xss] ->
+ transpose(Xss);
+
+ Rows ->
+ Firsts = begin
+ _pipe = Rows,
+ _pipe@1 = map(_pipe, Take_first),
+ concat(_pipe@1)
+ end,
+ Rest = transpose(map(Rows, fun(_capture) -> drop(_capture, 1) end)),
+ [Firsts | Rest]
+ end.
+
+-spec interleave(list(list(ALK))) -> list(ALK).
+interleave(List) ->
+ _pipe = transpose(List),
+ concat(_pipe).
+
+-spec do_shuffle_pair_unwrap(list({float(), ALT}), list(ALT)) -> list(ALT).
+do_shuffle_pair_unwrap(List, Acc) ->
+ case List of
+ [] ->
+ Acc;
+
+ [Elem_pair | Enumerable] ->
+ do_shuffle_pair_unwrap(
+ Enumerable,
+ [erlang:element(2, Elem_pair) | Acc]
+ )
+ end.
+
+-spec do_shuffle_by_pair_indexes(list({float(), ALX})) -> list({float(), ALX}).
+do_shuffle_by_pair_indexes(List_of_pairs) ->
+ sort(
+ List_of_pairs,
+ fun(A_pair, B_pair) ->
+ gleam@float:compare(
+ erlang:element(1, A_pair),
+ erlang:element(1, B_pair)
+ )
+ end
+ ).
+
+-spec shuffle(list(AMA)) -> list(AMA).
+shuffle(List) ->
+ _pipe = List,
+ _pipe@1 = fold(
+ _pipe,
+ [],
+ fun(Acc, A) -> [{gleam@float:random(+0.0, 1.0), A} | Acc] end
+ ),
+ _pipe@2 = do_shuffle_by_pair_indexes(_pipe@1),
+ do_shuffle_pair_unwrap(_pipe@2, []).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@map.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@map.erl
new file mode 100644
index 0000000..33e89a9
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@map.erl
@@ -0,0 +1,76 @@
+-module(gleam@map).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([size/1, to_list/1, from_list/1, has_key/2, new/0, get/2, insert/3, map_values/2, keys/1, values/1, filter/2, take/2, merge/2, delete/2, drop/2, update/3, fold/3]).
+
+-spec size(gleam@dict:dict(any(), any())) -> integer().
+size(Map) ->
+ gleam@dict:size(Map).
+
+-spec to_list(gleam@dict:dict(DAJ, DAK)) -> list({DAJ, DAK}).
+to_list(Map) ->
+ gleam@dict:to_list(Map).
+
+-spec from_list(list({DAM, DAN})) -> gleam@dict:dict(DAM, DAN).
+from_list(List) ->
+ gleam@dict:from_list(List).
+
+-spec has_key(gleam@dict:dict(DAR, any()), DAR) -> boolean().
+has_key(Map, Key) ->
+ gleam@dict:has_key(Map, Key).
+
+-spec new() -> gleam@dict:dict(any(), any()).
+new() ->
+ gleam@dict:new().
+
+-spec get(gleam@dict:dict(DAU, DAV), DAU) -> {ok, DAV} | {error, nil}.
+get(From, Get) ->
+ gleam@dict:get(From, Get).
+
+-spec insert(gleam@dict:dict(DAZ, DBA), DAZ, DBA) -> gleam@dict:dict(DAZ, DBA).
+insert(Map, Key, Value) ->
+ gleam@dict:insert(Map, Key, Value).
+
+-spec map_values(gleam@dict:dict(DBD, DBE), fun((DBD, DBE) -> DBF)) -> gleam@dict:dict(DBD, DBF).
+map_values(Map, Fun) ->
+ gleam@dict:map_values(Map, Fun).
+
+-spec keys(gleam@dict:dict(DBI, any())) -> list(DBI).
+keys(Map) ->
+ gleam@dict:keys(Map).
+
+-spec values(gleam@dict:dict(any(), DBL)) -> list(DBL).
+values(Map) ->
+ gleam@dict:values(Map).
+
+-spec filter(gleam@dict:dict(DBO, DBP), fun((DBO, DBP) -> boolean())) -> gleam@dict:dict(DBO, DBP).
+filter(Map, Predicate) ->
+ gleam@dict:filter(Map, Predicate).
+
+-spec take(gleam@dict:dict(DBS, DDM), list(DBS)) -> gleam@dict:dict(DBS, DDM).
+take(Map, Desired_keys) ->
+ gleam@dict:take(Map, Desired_keys).
+
+-spec merge(gleam@dict:dict(DDN, DDO), gleam@dict:dict(DDN, DDO)) -> gleam@dict:dict(DDN, DDO).
+merge(Map, New_entries) ->
+ gleam@dict:merge(Map, New_entries).
+
+-spec delete(gleam@dict:dict(DBZ, DDQ), DBZ) -> gleam@dict:dict(DBZ, DDQ).
+delete(Map, Key) ->
+ gleam@dict:delete(Map, Key).
+
+-spec drop(gleam@dict:dict(DCC, DDS), list(DCC)) -> gleam@dict:dict(DCC, DDS).
+drop(Map, Disallowed_keys) ->
+ gleam@dict:drop(Map, Disallowed_keys).
+
+-spec update(
+ gleam@dict:dict(DCG, DCH),
+ DCG,
+ fun((gleam@option:option(DCH)) -> DCH)
+) -> gleam@dict:dict(DCG, DCH).
+update(Map, Key, Fun) ->
+ gleam@dict:update(Map, Key, Fun).
+
+-spec fold(gleam@dict:dict(DCM, DCN), DCL, fun((DCL, DCM, DCN) -> DCL)) -> DCL.
+fold(Map, Initial, Fun) ->
+ gleam@dict:fold(Map, Initial, Fun).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@option.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@option.erl
new file mode 100644
index 0000000..5c20713
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@option.erl
@@ -0,0 +1,147 @@
+-module(gleam@option).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([all/1, is_some/1, is_none/1, to_result/2, from_result/1, unwrap/2, lazy_unwrap/2, map/2, flatten/1, then/2, 'or'/2, lazy_or/2, values/1]).
+-export_type([option/1]).
+
+-type option(GB) :: {some, GB} | none.
+
+-spec do_all(list(option(GC)), list(GC)) -> option(list(GC)).
+do_all(List, Acc) ->
+ case List of
+ [] ->
+ {some, Acc};
+
+ [X | Rest] ->
+ Accumulate = fun(Acc@1, Item) -> case {Acc@1, Item} of
+ {{some, Values}, {some, Value}} ->
+ {some, [Value | Values]};
+
+ {_, _} ->
+ none
+ end end,
+ Accumulate(do_all(Rest, Acc), X)
+ end.
+
+-spec all(list(option(GI))) -> option(list(GI)).
+all(List) ->
+ do_all(List, []).
+
+-spec is_some(option(any())) -> boolean().
+is_some(Option) ->
+ Option /= none.
+
+-spec is_none(option(any())) -> boolean().
+is_none(Option) ->
+ Option =:= none.
+
+-spec to_result(option(GR), GU) -> {ok, GR} | {error, GU}.
+to_result(Option, E) ->
+ case Option of
+ {some, A} ->
+ {ok, A};
+
+ _ ->
+ {error, E}
+ end.
+
+-spec from_result({ok, GX} | {error, any()}) -> option(GX).
+from_result(Result) ->
+ case Result of
+ {ok, A} ->
+ {some, A};
+
+ _ ->
+ none
+ end.
+
+-spec unwrap(option(HC), HC) -> HC.
+unwrap(Option, Default) ->
+ case Option of
+ {some, X} ->
+ X;
+
+ none ->
+ Default
+ end.
+
+-spec lazy_unwrap(option(HE), fun(() -> HE)) -> HE.
+lazy_unwrap(Option, Default) ->
+ case Option of
+ {some, X} ->
+ X;
+
+ none ->
+ Default()
+ end.
+
+-spec map(option(HG), fun((HG) -> HI)) -> option(HI).
+map(Option, Fun) ->
+ case Option of
+ {some, X} ->
+ {some, Fun(X)};
+
+ none ->
+ none
+ end.
+
+-spec flatten(option(option(HK))) -> option(HK).
+flatten(Option) ->
+ case Option of
+ {some, X} ->
+ X;
+
+ none ->
+ none
+ end.
+
+-spec then(option(HO), fun((HO) -> option(HQ))) -> option(HQ).
+then(Option, Fun) ->
+ case Option of
+ {some, X} ->
+ Fun(X);
+
+ none ->
+ none
+ end.
+
+-spec 'or'(option(HT), option(HT)) -> option(HT).
+'or'(First, Second) ->
+ case First of
+ {some, _} ->
+ First;
+
+ none ->
+ Second
+ end.
+
+-spec lazy_or(option(HX), fun(() -> option(HX))) -> option(HX).
+lazy_or(First, Second) ->
+ case First of
+ {some, _} ->
+ First;
+
+ none ->
+ Second()
+ end.
+
+-spec do_values(list(option(IB)), list(IB)) -> list(IB).
+do_values(List, Acc) ->
+ case List of
+ [] ->
+ Acc;
+
+ [X | Xs] ->
+ Accumulate = fun(Acc@1, Item) -> case Item of
+ {some, Value} ->
+ [Value | Acc@1];
+
+ none ->
+ Acc@1
+ end end,
+ Accumulate(do_values(Xs, Acc), X)
+ end.
+
+-spec values(list(option(IG))) -> list(IG).
+values(Options) ->
+ do_values(Options, []).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@order.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@order.erl
new file mode 100644
index 0000000..61649b9
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@order.erl
@@ -0,0 +1,79 @@
+-module(gleam@order).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([negate/1, to_int/1, compare/2, max/2, min/2, reverse/1]).
+-export_type([order/0]).
+
+-type order() :: lt | eq | gt.
+
+-spec negate(order()) -> order().
+negate(Order) ->
+ case Order of
+ lt ->
+ gt;
+
+ eq ->
+ eq;
+
+ gt ->
+ lt
+ end.
+
+-spec to_int(order()) -> integer().
+to_int(Order) ->
+ case Order of
+ lt ->
+ -1;
+
+ eq ->
+ 0;
+
+ gt ->
+ 1
+ end.
+
+-spec compare(order(), order()) -> order().
+compare(A, B) ->
+ case {A, B} of
+ {X, Y} when X =:= Y ->
+ eq;
+
+ {lt, _} ->
+ lt;
+
+ {eq, gt} ->
+ lt;
+
+ {_, _} ->
+ gt
+ end.
+
+-spec max(order(), order()) -> order().
+max(A, B) ->
+ case {A, B} of
+ {gt, _} ->
+ gt;
+
+ {eq, lt} ->
+ eq;
+
+ {_, _} ->
+ B
+ end.
+
+-spec min(order(), order()) -> order().
+min(A, B) ->
+ case {A, B} of
+ {lt, _} ->
+ lt;
+
+ {eq, gt} ->
+ eq;
+
+ {_, _} ->
+ B
+ end.
+
+-spec reverse(fun((I, I) -> order())) -> fun((I, I) -> order()).
+reverse(Orderer) ->
+ fun(A, B) -> Orderer(B, A) end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@pair.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@pair.erl
new file mode 100644
index 0000000..f4eff52
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@pair.erl
@@ -0,0 +1,33 @@
+-module(gleam@pair).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([first/1, second/1, swap/1, map_first/2, map_second/2, new/2]).
+
+-spec first({FM, any()}) -> FM.
+first(Pair) ->
+ {A, _} = Pair,
+ A.
+
+-spec second({any(), FP}) -> FP.
+second(Pair) ->
+ {_, A} = Pair,
+ A.
+
+-spec swap({FQ, FR}) -> {FR, FQ}.
+swap(Pair) ->
+ {A, B} = Pair,
+ {B, A}.
+
+-spec map_first({FS, FT}, fun((FS) -> FU)) -> {FU, FT}.
+map_first(Pair, Fun) ->
+ {A, B} = Pair,
+ {Fun(A), B}.
+
+-spec map_second({FV, FW}, fun((FW) -> FX)) -> {FV, FX}.
+map_second(Pair, Fun) ->
+ {A, B} = Pair,
+ {A, Fun(B)}.
+
+-spec new(FY, FZ) -> {FY, FZ}.
+new(First, Second) ->
+ {First, Second}.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@queue.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@queue.erl
new file mode 100644
index 0000000..6b587e7
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@queue.erl
@@ -0,0 +1,121 @@
+-module(gleam@queue).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([new/0, from_list/1, to_list/1, is_empty/1, length/1, push_back/2, push_front/2, pop_back/1, pop_front/1, reverse/1, is_logically_equal/3, is_equal/2]).
+-export_type([queue/1]).
+
+-opaque queue(DRL) :: {queue, list(DRL), list(DRL)}.
+
+-spec new() -> queue(any()).
+new() ->
+ {queue, [], []}.
+
+-spec from_list(list(DRO)) -> queue(DRO).
+from_list(List) ->
+ {queue, [], List}.
+
+-spec to_list(queue(DRR)) -> list(DRR).
+to_list(Queue) ->
+ _pipe = erlang:element(3, Queue),
+ gleam@list:append(_pipe, gleam@list:reverse(erlang:element(2, Queue))).
+
+-spec is_empty(queue(any())) -> boolean().
+is_empty(Queue) ->
+ (erlang:element(2, Queue) =:= []) andalso (erlang:element(3, Queue) =:= []).
+
+-spec length(queue(any())) -> integer().
+length(Queue) ->
+ gleam@list:length(erlang:element(2, Queue)) + gleam@list:length(
+ erlang:element(3, Queue)
+ ).
+
+-spec push_back(queue(DRY), DRY) -> queue(DRY).
+push_back(Queue, Item) ->
+ {queue, [Item | erlang:element(2, Queue)], erlang:element(3, Queue)}.
+
+-spec push_front(queue(DSB), DSB) -> queue(DSB).
+push_front(Queue, Item) ->
+ {queue, erlang:element(2, Queue), [Item | erlang:element(3, Queue)]}.
+
+-spec pop_back(queue(DSE)) -> {ok, {DSE, queue(DSE)}} | {error, nil}.
+pop_back(Queue) ->
+ case Queue of
+ {queue, [], []} ->
+ {error, nil};
+
+ {queue, [], Out} ->
+ pop_back({queue, gleam@list:reverse(Out), []});
+
+ {queue, [First | Rest], Out@1} ->
+ Queue@1 = {queue, Rest, Out@1},
+ {ok, {First, Queue@1}}
+ end.
+
+-spec pop_front(queue(DSJ)) -> {ok, {DSJ, queue(DSJ)}} | {error, nil}.
+pop_front(Queue) ->
+ case Queue of
+ {queue, [], []} ->
+ {error, nil};
+
+ {queue, In, []} ->
+ pop_front({queue, [], gleam@list:reverse(In)});
+
+ {queue, In@1, [First | Rest]} ->
+ Queue@1 = {queue, In@1, Rest},
+ {ok, {First, Queue@1}}
+ end.
+
+-spec reverse(queue(DSO)) -> queue(DSO).
+reverse(Queue) ->
+ {queue, erlang:element(3, Queue), erlang:element(2, Queue)}.
+
+-spec check_equal(
+ list(DSR),
+ list(DSR),
+ list(DSR),
+ list(DSR),
+ fun((DSR, DSR) -> boolean())
+) -> boolean().
+check_equal(Xs, X_tail, Ys, Y_tail, Eq) ->
+ case {Xs, X_tail, Ys, Y_tail} of
+ {[], [], [], []} ->
+ true;
+
+ {[X | Xs@1], _, [Y | Ys@1], _} ->
+ case Eq(X, Y) of
+ false ->
+ false;
+
+ true ->
+ check_equal(Xs@1, X_tail, Ys@1, Y_tail, Eq)
+ end;
+
+ {[], [_ | _], _, _} ->
+ check_equal(gleam@list:reverse(X_tail), [], Ys, Y_tail, Eq);
+
+ {_, _, [], [_ | _]} ->
+ check_equal(Xs, X_tail, gleam@list:reverse(Y_tail), [], Eq);
+
+ {_, _, _, _} ->
+ false
+ end.
+
+-spec is_logically_equal(queue(DSW), queue(DSW), fun((DSW, DSW) -> boolean())) -> boolean().
+is_logically_equal(A, B, Element_is_equal) ->
+ check_equal(
+ erlang:element(3, A),
+ erlang:element(2, A),
+ erlang:element(3, B),
+ erlang:element(2, B),
+ Element_is_equal
+ ).
+
+-spec is_equal(queue(DSZ), queue(DSZ)) -> boolean().
+is_equal(A, B) ->
+ check_equal(
+ erlang:element(3, A),
+ erlang:element(2, A),
+ erlang:element(3, B),
+ erlang:element(2, B),
+ fun(A@1, B@1) -> A@1 =:= B@1 end
+ ).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@regex.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@regex.erl
new file mode 100644
index 0000000..2d1c5fc
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@regex.erl
@@ -0,0 +1,33 @@
+-module(gleam@regex).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([compile/2, from_string/1, check/2, split/2, scan/2]).
+-export_type([regex/0, match/0, compile_error/0, options/0]).
+
+-type regex() :: any().
+
+-type match() :: {match, binary(), list(gleam@option:option(binary()))}.
+
+-type compile_error() :: {compile_error, binary(), integer()}.
+
+-type options() :: {options, boolean(), boolean()}.
+
+-spec compile(binary(), options()) -> {ok, regex()} | {error, compile_error()}.
+compile(Pattern, Options) ->
+ gleam_stdlib:compile_regex(Pattern, Options).
+
+-spec from_string(binary()) -> {ok, regex()} | {error, compile_error()}.
+from_string(Pattern) ->
+ compile(Pattern, {options, false, false}).
+
+-spec check(regex(), binary()) -> boolean().
+check(Regex, Content) ->
+ gleam_stdlib:regex_check(Regex, Content).
+
+-spec split(regex(), binary()) -> list(binary()).
+split(Regex, String) ->
+ gleam_stdlib:regex_split(Regex, String).
+
+-spec scan(regex(), binary()) -> list(match()).
+scan(Regex, String) ->
+ gleam_stdlib:regex_scan(Regex, String).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@result.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@result.erl
new file mode 100644
index 0000000..7324e45
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@result.erl
@@ -0,0 +1,201 @@
+-module(gleam@result).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([is_ok/1, is_error/1, map/2, map_error/2, flatten/1, 'try'/2, then/2, unwrap/2, lazy_unwrap/2, unwrap_error/2, unwrap_both/1, nil_error/1, 'or'/2, lazy_or/2, all/1, partition/1, replace/2, replace_error/2, values/1, try_recover/2]).
+
+-spec is_ok({ok, any()} | {error, any()}) -> boolean().
+is_ok(Result) ->
+ case Result of
+ {error, _} ->
+ false;
+
+ {ok, _} ->
+ true
+ end.
+
+-spec is_error({ok, any()} | {error, any()}) -> boolean().
+is_error(Result) ->
+ case Result of
+ {ok, _} ->
+ false;
+
+ {error, _} ->
+ true
+ end.
+
+-spec map({ok, BFM} | {error, BFN}, fun((BFM) -> BFQ)) -> {ok, BFQ} |
+ {error, BFN}.
+map(Result, Fun) ->
+ case Result of
+ {ok, X} ->
+ {ok, Fun(X)};
+
+ {error, E} ->
+ {error, E}
+ end.
+
+-spec map_error({ok, BFT} | {error, BFU}, fun((BFU) -> BFX)) -> {ok, BFT} |
+ {error, BFX}.
+map_error(Result, Fun) ->
+ case Result of
+ {ok, X} ->
+ {ok, X};
+
+ {error, Error} ->
+ {error, Fun(Error)}
+ end.
+
+-spec flatten({ok, {ok, BGA} | {error, BGB}} | {error, BGB}) -> {ok, BGA} |
+ {error, BGB}.
+flatten(Result) ->
+ case Result of
+ {ok, X} ->
+ X;
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+-spec 'try'({ok, BGI} | {error, BGJ}, fun((BGI) -> {ok, BGM} | {error, BGJ})) -> {ok,
+ BGM} |
+ {error, BGJ}.
+'try'(Result, Fun) ->
+ case Result of
+ {ok, X} ->
+ Fun(X);
+
+ {error, E} ->
+ {error, E}
+ end.
+
+-spec then({ok, BGR} | {error, BGS}, fun((BGR) -> {ok, BGV} | {error, BGS})) -> {ok,
+ BGV} |
+ {error, BGS}.
+then(Result, Fun) ->
+ 'try'(Result, Fun).
+
+-spec unwrap({ok, BHA} | {error, any()}, BHA) -> BHA.
+unwrap(Result, Default) ->
+ case Result of
+ {ok, V} ->
+ V;
+
+ {error, _} ->
+ Default
+ end.
+
+-spec lazy_unwrap({ok, BHE} | {error, any()}, fun(() -> BHE)) -> BHE.
+lazy_unwrap(Result, Default) ->
+ case Result of
+ {ok, V} ->
+ V;
+
+ {error, _} ->
+ Default()
+ end.
+
+-spec unwrap_error({ok, any()} | {error, BHJ}, BHJ) -> BHJ.
+unwrap_error(Result, Default) ->
+ case Result of
+ {ok, _} ->
+ Default;
+
+ {error, E} ->
+ E
+ end.
+
+-spec unwrap_both({ok, BHM} | {error, BHM}) -> BHM.
+unwrap_both(Result) ->
+ case Result of
+ {ok, A} ->
+ A;
+
+ {error, A@1} ->
+ A@1
+ end.
+
+-spec nil_error({ok, BHP} | {error, any()}) -> {ok, BHP} | {error, nil}.
+nil_error(Result) ->
+ map_error(Result, fun(_) -> nil end).
+
+-spec 'or'({ok, BHV} | {error, BHW}, {ok, BHV} | {error, BHW}) -> {ok, BHV} |
+ {error, BHW}.
+'or'(First, Second) ->
+ case First of
+ {ok, _} ->
+ First;
+
+ {error, _} ->
+ Second
+ end.
+
+-spec lazy_or({ok, BID} | {error, BIE}, fun(() -> {ok, BID} | {error, BIE})) -> {ok,
+ BID} |
+ {error, BIE}.
+lazy_or(First, Second) ->
+ case First of
+ {ok, _} ->
+ First;
+
+ {error, _} ->
+ Second()
+ end.
+
+-spec all(list({ok, BIL} | {error, BIM})) -> {ok, list(BIL)} | {error, BIM}.
+all(Results) ->
+ gleam@list:try_map(Results, fun(X) -> X end).
+
+-spec do_partition(list({ok, BJA} | {error, BJB}), list(BJA), list(BJB)) -> {list(BJA),
+ list(BJB)}.
+do_partition(Results, Oks, Errors) ->
+ case Results of
+ [] ->
+ {Oks, Errors};
+
+ [{ok, A} | Rest] ->
+ do_partition(Rest, [A | Oks], Errors);
+
+ [{error, E} | Rest@1] ->
+ do_partition(Rest@1, Oks, [E | Errors])
+ end.
+
+-spec partition(list({ok, BIT} | {error, BIU})) -> {list(BIT), list(BIU)}.
+partition(Results) ->
+ do_partition(Results, [], []).
+
+-spec replace({ok, any()} | {error, BJJ}, BJM) -> {ok, BJM} | {error, BJJ}.
+replace(Result, Value) ->
+ case Result of
+ {ok, _} ->
+ {ok, Value};
+
+ {error, Error} ->
+ {error, Error}
+ end.
+
+-spec replace_error({ok, BJP} | {error, any()}, BJT) -> {ok, BJP} | {error, BJT}.
+replace_error(Result, Error) ->
+ case Result of
+ {ok, X} ->
+ {ok, X};
+
+ {error, _} ->
+ {error, Error}
+ end.
+
+-spec values(list({ok, BJW} | {error, any()})) -> list(BJW).
+values(Results) ->
+ gleam@list:filter_map(Results, fun(R) -> R end).
+
+-spec try_recover(
+ {ok, BKC} | {error, BKD},
+ fun((BKD) -> {ok, BKC} | {error, BKG})
+) -> {ok, BKC} | {error, BKG}.
+try_recover(Result, Fun) ->
+ case Result of
+ {ok, Value} ->
+ {ok, Value};
+
+ {error, Error} ->
+ Fun(Error)
+ end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@set.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@set.erl
new file mode 100644
index 0000000..df87b13
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@set.erl
@@ -0,0 +1,85 @@
+-module(gleam@set).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([new/0, size/1, insert/2, contains/2, delete/2, to_list/1, from_list/1, fold/3, filter/2, drop/2, take/2, union/2, intersection/2]).
+-export_type([set/1]).
+
+-opaque set(DJZ) :: {set, gleam@dict:dict(DJZ, list(nil))}.
+
+-spec new() -> set(any()).
+new() ->
+ {set, gleam@dict:new()}.
+
+-spec size(set(any())) -> integer().
+size(Set) ->
+ gleam@dict:size(erlang:element(2, Set)).
+
+-spec insert(set(DKF), DKF) -> set(DKF).
+insert(Set, Member) ->
+ {set, gleam@dict:insert(erlang:element(2, Set), Member, [])}.
+
+-spec contains(set(DKI), DKI) -> boolean().
+contains(Set, Member) ->
+ _pipe = erlang:element(2, Set),
+ _pipe@1 = gleam@dict:get(_pipe, Member),
+ gleam@result:is_ok(_pipe@1).
+
+-spec delete(set(DKK), DKK) -> set(DKK).
+delete(Set, Member) ->
+ {set, gleam@dict:delete(erlang:element(2, Set), Member)}.
+
+-spec to_list(set(DKN)) -> list(DKN).
+to_list(Set) ->
+ gleam@dict:keys(erlang:element(2, Set)).
+
+-spec from_list(list(DKQ)) -> set(DKQ).
+from_list(Members) ->
+ Map = gleam@list:fold(
+ Members,
+ gleam@dict:new(),
+ fun(M, K) -> gleam@dict:insert(M, K, []) end
+ ),
+ {set, Map}.
+
+-spec fold(set(DKT), DKV, fun((DKV, DKT) -> DKV)) -> DKV.
+fold(Set, Initial, Reducer) ->
+ gleam@dict:fold(
+ erlang:element(2, Set),
+ Initial,
+ fun(A, K, _) -> Reducer(A, K) end
+ ).
+
+-spec filter(set(DKW), fun((DKW) -> boolean())) -> set(DKW).
+filter(Set, Predicate) ->
+ {set,
+ gleam@dict:filter(erlang:element(2, Set), fun(M, _) -> Predicate(M) end)}.
+
+-spec drop(set(DKZ), list(DKZ)) -> set(DKZ).
+drop(Set, Disallowed) ->
+ gleam@list:fold(Disallowed, Set, fun delete/2).
+
+-spec take(set(DLD), list(DLD)) -> set(DLD).
+take(Set, Desired) ->
+ {set, gleam@dict:take(erlang:element(2, Set), Desired)}.
+
+-spec order(set(DLH), set(DLH)) -> {set(DLH), set(DLH)}.
+order(First, Second) ->
+ case gleam@dict:size(erlang:element(2, First)) > gleam@dict:size(
+ erlang:element(2, Second)
+ ) of
+ true ->
+ {First, Second};
+
+ false ->
+ {Second, First}
+ end.
+
+-spec union(set(DLM), set(DLM)) -> set(DLM).
+union(First, Second) ->
+ {Larger, Smaller} = order(First, Second),
+ fold(Smaller, Larger, fun insert/2).
+
+-spec intersection(set(DLQ), set(DLQ)) -> set(DLQ).
+intersection(First, Second) ->
+ {Larger, Smaller} = order(First, Second),
+ take(Larger, to_list(Smaller)).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@string.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@string.erl
new file mode 100644
index 0000000..6cba31d
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@string.erl
@@ -0,0 +1,352 @@
+-module(gleam@string).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([is_empty/1, length/1, reverse/1, replace/3, lowercase/1, uppercase/1, compare/2, slice/3, crop/2, drop_left/2, drop_right/2, contains/2, starts_with/2, ends_with/2, split_once/2, append/2, concat/1, repeat/2, join/2, pad_left/3, pad_right/3, trim/1, trim_left/1, trim_right/1, pop_grapheme/1, to_graphemes/1, split/2, to_utf_codepoints/1, from_utf_codepoints/1, utf_codepoint/1, utf_codepoint_to_int/1, to_option/1, first/1, last/1, capitalise/1, inspect/1, byte_size/1]).
+-export_type([direction/0]).
+
+-type direction() :: leading | trailing | both.
+
+-spec is_empty(binary()) -> boolean().
+is_empty(Str) ->
+ Str =:= <<""/utf8>>.
+
+-spec length(binary()) -> integer().
+length(String) ->
+ string:length(String).
+
+-spec do_reverse(binary()) -> binary().
+do_reverse(String) ->
+ _pipe = String,
+ _pipe@1 = gleam@string_builder:from_string(_pipe),
+ _pipe@2 = gleam@string_builder:reverse(_pipe@1),
+ gleam@string_builder:to_string(_pipe@2).
+
+-spec reverse(binary()) -> binary().
+reverse(String) ->
+ do_reverse(String).
+
+-spec replace(binary(), binary(), binary()) -> binary().
+replace(String, Pattern, Substitute) ->
+ _pipe = String,
+ _pipe@1 = gleam@string_builder:from_string(_pipe),
+ _pipe@2 = gleam@string_builder:replace(_pipe@1, Pattern, Substitute),
+ gleam@string_builder:to_string(_pipe@2).
+
+-spec lowercase(binary()) -> binary().
+lowercase(String) ->
+ string:lowercase(String).
+
+-spec uppercase(binary()) -> binary().
+uppercase(String) ->
+ string:uppercase(String).
+
+-spec compare(binary(), binary()) -> gleam@order:order().
+compare(A, B) ->
+ case A =:= B of
+ true ->
+ eq;
+
+ _ ->
+ case gleam_stdlib:less_than(A, B) of
+ true ->
+ lt;
+
+ _ ->
+ gt
+ end
+ end.
+
+-spec slice(binary(), integer(), integer()) -> binary().
+slice(String, Idx, Len) ->
+ case Len < 0 of
+ true ->
+ <<""/utf8>>;
+
+ false ->
+ case Idx < 0 of
+ true ->
+ Translated_idx = length(String) + Idx,
+ case Translated_idx < 0 of
+ true ->
+ <<""/utf8>>;
+
+ false ->
+ string:slice(String, Translated_idx, Len)
+ end;
+
+ false ->
+ string:slice(String, Idx, Len)
+ end
+ end.
+
+-spec crop(binary(), binary()) -> binary().
+crop(String, Substring) ->
+ gleam_stdlib:crop_string(String, Substring).
+
+-spec drop_left(binary(), integer()) -> binary().
+drop_left(String, Num_graphemes) ->
+ case Num_graphemes < 0 of
+ true ->
+ String;
+
+ false ->
+ slice(String, Num_graphemes, length(String) - Num_graphemes)
+ end.
+
+-spec drop_right(binary(), integer()) -> binary().
+drop_right(String, Num_graphemes) ->
+ case Num_graphemes < 0 of
+ true ->
+ String;
+
+ false ->
+ slice(String, 0, length(String) - Num_graphemes)
+ end.
+
+-spec contains(binary(), binary()) -> boolean().
+contains(Haystack, Needle) ->
+ gleam_stdlib:contains_string(Haystack, Needle).
+
+-spec starts_with(binary(), binary()) -> boolean().
+starts_with(String, Prefix) ->
+ gleam_stdlib:string_starts_with(String, Prefix).
+
+-spec ends_with(binary(), binary()) -> boolean().
+ends_with(String, Suffix) ->
+ gleam_stdlib:string_ends_with(String, Suffix).
+
+-spec do_split_once(binary(), binary()) -> {ok, {binary(), binary()}} |
+ {error, nil}.
+do_split_once(X, Substring) ->
+ case string:split(X, Substring) of
+ [First, Rest] ->
+ {ok, {First, Rest}};
+
+ _ ->
+ {error, nil}
+ end.
+
+-spec split_once(binary(), binary()) -> {ok, {binary(), binary()}} |
+ {error, nil}.
+split_once(X, Substring) ->
+ do_split_once(X, Substring).
+
+-spec append(binary(), binary()) -> binary().
+append(First, Second) ->
+ _pipe = First,
+ _pipe@1 = gleam@string_builder:from_string(_pipe),
+ _pipe@2 = gleam@string_builder:append(_pipe@1, Second),
+ gleam@string_builder:to_string(_pipe@2).
+
+-spec concat(list(binary())) -> binary().
+concat(Strings) ->
+ _pipe = Strings,
+ _pipe@1 = gleam@string_builder:from_strings(_pipe),
+ gleam@string_builder:to_string(_pipe@1).
+
+-spec repeat(binary(), integer()) -> binary().
+repeat(String, Times) ->
+ _pipe = gleam@iterator:repeat(String),
+ _pipe@1 = gleam@iterator:take(_pipe, Times),
+ _pipe@2 = gleam@iterator:to_list(_pipe@1),
+ concat(_pipe@2).
+
+-spec do_join(list(binary()), binary()) -> binary().
+do_join(Strings, Separator) ->
+ _pipe = Strings,
+ _pipe@1 = gleam@list:intersperse(_pipe, Separator),
+ concat(_pipe@1).
+
+-spec join(list(binary()), binary()) -> binary().
+join(Strings, Separator) ->
+ do_join(Strings, Separator).
+
+-spec padding(integer(), binary()) -> gleam@iterator:iterator(binary()).
+padding(Size, Pad_string) ->
+ Pad_length = length(Pad_string),
+ Num_pads = case Pad_length of
+ 0 -> 0;
+ Gleam@denominator -> Size div Gleam@denominator
+ end,
+ Extra = case Pad_length of
+ 0 -> 0;
+ Gleam@denominator@1 -> Size rem Gleam@denominator@1
+ end,
+ _pipe = gleam@iterator:repeat(Pad_string),
+ _pipe@1 = gleam@iterator:take(_pipe, Num_pads),
+ gleam@iterator:append(
+ _pipe@1,
+ gleam@iterator:single(slice(Pad_string, 0, Extra))
+ ).
+
+-spec pad_left(binary(), integer(), binary()) -> binary().
+pad_left(String, Desired_length, Pad_string) ->
+ Current_length = length(String),
+ To_pad_length = Desired_length - Current_length,
+ _pipe = padding(To_pad_length, Pad_string),
+ _pipe@1 = gleam@iterator:append(_pipe, gleam@iterator:single(String)),
+ _pipe@2 = gleam@iterator:to_list(_pipe@1),
+ concat(_pipe@2).
+
+-spec pad_right(binary(), integer(), binary()) -> binary().
+pad_right(String, Desired_length, Pad_string) ->
+ Current_length = length(String),
+ To_pad_length = Desired_length - Current_length,
+ _pipe = gleam@iterator:single(String),
+ _pipe@1 = gleam@iterator:append(_pipe, padding(To_pad_length, Pad_string)),
+ _pipe@2 = gleam@iterator:to_list(_pipe@1),
+ concat(_pipe@2).
+
+-spec do_trim(binary()) -> binary().
+do_trim(String) ->
+ string:trim(String, both).
+
+-spec trim(binary()) -> binary().
+trim(String) ->
+ do_trim(String).
+
+-spec do_trim_left(binary()) -> binary().
+do_trim_left(String) ->
+ string:trim(String, leading).
+
+-spec trim_left(binary()) -> binary().
+trim_left(String) ->
+ do_trim_left(String).
+
+-spec do_trim_right(binary()) -> binary().
+do_trim_right(String) ->
+ string:trim(String, trailing).
+
+-spec trim_right(binary()) -> binary().
+trim_right(String) ->
+ do_trim_right(String).
+
+-spec pop_grapheme(binary()) -> {ok, {binary(), binary()}} | {error, nil}.
+pop_grapheme(String) ->
+ gleam_stdlib:string_pop_grapheme(String).
+
+-spec do_to_graphemes(binary(), list(binary())) -> list(binary()).
+do_to_graphemes(String, Acc) ->
+ case pop_grapheme(String) of
+ {ok, {Grapheme, Rest}} ->
+ do_to_graphemes(Rest, [Grapheme | Acc]);
+
+ _ ->
+ Acc
+ end.
+
+-spec to_graphemes(binary()) -> list(binary()).
+to_graphemes(String) ->
+ _pipe = do_to_graphemes(String, []),
+ gleam@list:reverse(_pipe).
+
+-spec split(binary(), binary()) -> list(binary()).
+split(X, Substring) ->
+ case Substring of
+ <<""/utf8>> ->
+ to_graphemes(X);
+
+ _ ->
+ _pipe = X,
+ _pipe@1 = gleam@string_builder:from_string(_pipe),
+ _pipe@2 = gleam@string_builder:split(_pipe@1, Substring),
+ gleam@list:map(_pipe@2, fun gleam@string_builder:to_string/1)
+ end.
+
+-spec do_to_utf_codepoints_impl(bitstring(), list(integer())) -> list(integer()).
+do_to_utf_codepoints_impl(Bit_array, Acc) ->
+ case Bit_array of
+ <<First/utf8, Rest/binary>> ->
+ do_to_utf_codepoints_impl(Rest, [First | Acc]);
+
+ _ ->
+ Acc
+ end.
+
+-spec do_to_utf_codepoints(binary()) -> list(integer()).
+do_to_utf_codepoints(String) ->
+ _pipe = do_to_utf_codepoints_impl(<<String/binary>>, []),
+ gleam@list:reverse(_pipe).
+
+-spec to_utf_codepoints(binary()) -> list(integer()).
+to_utf_codepoints(String) ->
+ do_to_utf_codepoints(String).
+
+-spec from_utf_codepoints(list(integer())) -> binary().
+from_utf_codepoints(Utf_codepoints) ->
+ gleam_stdlib:utf_codepoint_list_to_string(Utf_codepoints).
+
+-spec utf_codepoint(integer()) -> {ok, integer()} | {error, nil}.
+utf_codepoint(Value) ->
+ case Value of
+ I when I > 1114111 ->
+ {error, nil};
+
+ 65534 ->
+ {error, nil};
+
+ 65535 ->
+ {error, nil};
+
+ I@1 when (I@1 >= 55296) andalso (I@1 =< 57343) ->
+ {error, nil};
+
+ I@2 ->
+ {ok, gleam_stdlib:identity(I@2)}
+ end.
+
+-spec utf_codepoint_to_int(integer()) -> integer().
+utf_codepoint_to_int(Cp) ->
+ gleam_stdlib:identity(Cp).
+
+-spec to_option(binary()) -> gleam@option:option(binary()).
+to_option(S) ->
+ case S of
+ <<""/utf8>> ->
+ none;
+
+ _ ->
+ {some, S}
+ end.
+
+-spec first(binary()) -> {ok, binary()} | {error, nil}.
+first(S) ->
+ case pop_grapheme(S) of
+ {ok, {First, _}} ->
+ {ok, First};
+
+ {error, E} ->
+ {error, E}
+ end.
+
+-spec last(binary()) -> {ok, binary()} | {error, nil}.
+last(S) ->
+ case pop_grapheme(S) of
+ {ok, {First, <<""/utf8>>}} ->
+ {ok, First};
+
+ {ok, {_, Rest}} ->
+ {ok, slice(Rest, -1, 1)};
+
+ {error, E} ->
+ {error, E}
+ end.
+
+-spec capitalise(binary()) -> binary().
+capitalise(S) ->
+ case pop_grapheme(S) of
+ {ok, {First, Rest}} ->
+ append(uppercase(First), lowercase(Rest));
+
+ _ ->
+ <<""/utf8>>
+ end.
+
+-spec inspect(any()) -> binary().
+inspect(Term) ->
+ _pipe = gleam_stdlib:inspect(Term),
+ gleam@string_builder:to_string(_pipe).
+
+-spec byte_size(binary()) -> integer().
+byte_size(String) ->
+ erlang:byte_size(String).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@string_builder.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@string_builder.erl
new file mode 100644
index 0000000..693e840
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@string_builder.erl
@@ -0,0 +1,91 @@
+-module(gleam@string_builder).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([prepend_builder/2, append_builder/2, new/0, from_strings/1, concat/1, from_string/1, prepend/2, append/2, to_string/1, byte_size/1, join/2, lowercase/1, uppercase/1, reverse/1, split/2, replace/3, is_equal/2, is_empty/1]).
+-export_type([string_builder/0, direction/0]).
+
+-type string_builder() :: any().
+
+-type direction() :: all.
+
+-spec prepend_builder(string_builder(), string_builder()) -> string_builder().
+prepend_builder(Builder, Prefix) ->
+ gleam_stdlib:iodata_append(Prefix, Builder).
+
+-spec append_builder(string_builder(), string_builder()) -> string_builder().
+append_builder(Builder, Suffix) ->
+ gleam_stdlib:iodata_append(Builder, Suffix).
+
+-spec new() -> string_builder().
+new() ->
+ gleam_stdlib:identity([]).
+
+-spec from_strings(list(binary())) -> string_builder().
+from_strings(Strings) ->
+ gleam_stdlib:identity(Strings).
+
+-spec concat(list(string_builder())) -> string_builder().
+concat(Builders) ->
+ gleam_stdlib:identity(Builders).
+
+-spec from_string(binary()) -> string_builder().
+from_string(String) ->
+ gleam_stdlib:identity(String).
+
+-spec prepend(string_builder(), binary()) -> string_builder().
+prepend(Builder, Prefix) ->
+ append_builder(from_string(Prefix), Builder).
+
+-spec append(string_builder(), binary()) -> string_builder().
+append(Builder, Second) ->
+ append_builder(Builder, from_string(Second)).
+
+-spec to_string(string_builder()) -> binary().
+to_string(Builder) ->
+ unicode:characters_to_binary(Builder).
+
+-spec byte_size(string_builder()) -> integer().
+byte_size(Builder) ->
+ erlang:iolist_size(Builder).
+
+-spec join(list(string_builder()), binary()) -> string_builder().
+join(Builders, Sep) ->
+ _pipe = Builders,
+ _pipe@1 = gleam@list:intersperse(_pipe, from_string(Sep)),
+ concat(_pipe@1).
+
+-spec lowercase(string_builder()) -> string_builder().
+lowercase(Builder) ->
+ string:lowercase(Builder).
+
+-spec uppercase(string_builder()) -> string_builder().
+uppercase(Builder) ->
+ string:uppercase(Builder).
+
+-spec reverse(string_builder()) -> string_builder().
+reverse(Builder) ->
+ string:reverse(Builder).
+
+-spec do_split(string_builder(), binary()) -> list(string_builder()).
+do_split(Iodata, Pattern) ->
+ string:split(Iodata, Pattern, all).
+
+-spec split(string_builder(), binary()) -> list(string_builder()).
+split(Iodata, Pattern) ->
+ do_split(Iodata, Pattern).
+
+-spec do_replace(string_builder(), binary(), binary()) -> string_builder().
+do_replace(Iodata, Pattern, Substitute) ->
+ string:replace(Iodata, Pattern, Substitute, all).
+
+-spec replace(string_builder(), binary(), binary()) -> string_builder().
+replace(Builder, Pattern, Substitute) ->
+ do_replace(Builder, Pattern, Substitute).
+
+-spec is_equal(string_builder(), string_builder()) -> boolean().
+is_equal(A, B) ->
+ string:equal(A, B).
+
+-spec is_empty(string_builder()) -> boolean().
+is_empty(Builder) ->
+ string:is_empty(Builder).
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam@uri.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam@uri.erl
new file mode 100644
index 0000000..7ec4fe7
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam@uri.erl
@@ -0,0 +1,252 @@
+-module(gleam@uri).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([parse/1, parse_query/1, percent_encode/1, query_to_string/1, percent_decode/1, path_segments/1, to_string/1, origin/1, merge/2]).
+-export_type([uri/0]).
+
+-type uri() :: {uri,
+ gleam@option:option(binary()),
+ gleam@option:option(binary()),
+ gleam@option:option(binary()),
+ gleam@option:option(integer()),
+ binary(),
+ gleam@option:option(binary()),
+ gleam@option:option(binary())}.
+
+-spec parse(binary()) -> {ok, uri()} | {error, nil}.
+parse(Uri_string) ->
+ gleam_stdlib:uri_parse(Uri_string).
+
+-spec parse_query(binary()) -> {ok, list({binary(), binary()})} | {error, nil}.
+parse_query(Query) ->
+ gleam_stdlib:parse_query(Query).
+
+-spec percent_encode(binary()) -> binary().
+percent_encode(Value) ->
+ gleam_stdlib:percent_encode(Value).
+
+-spec query_pair({binary(), binary()}) -> gleam@string_builder:string_builder().
+query_pair(Pair) ->
+ gleam@string_builder:from_strings(
+ [percent_encode(erlang:element(1, Pair)),
+ <<"="/utf8>>,
+ percent_encode(erlang:element(2, Pair))]
+ ).
+
+-spec query_to_string(list({binary(), binary()})) -> binary().
+query_to_string(Query) ->
+ _pipe = Query,
+ _pipe@1 = gleam@list:map(_pipe, fun query_pair/1),
+ _pipe@2 = gleam@list:intersperse(
+ _pipe@1,
+ gleam@string_builder:from_string(<<"&"/utf8>>)
+ ),
+ _pipe@3 = gleam@string_builder:concat(_pipe@2),
+ gleam@string_builder:to_string(_pipe@3).
+
+-spec percent_decode(binary()) -> {ok, binary()} | {error, nil}.
+percent_decode(Value) ->
+ gleam_stdlib:percent_decode(Value).
+
+-spec do_remove_dot_segments(list(binary()), list(binary())) -> list(binary()).
+do_remove_dot_segments(Input, Accumulator) ->
+ case Input of
+ [] ->
+ gleam@list:reverse(Accumulator);
+
+ [Segment | Rest] ->
+ Accumulator@5 = case {Segment, Accumulator} of
+ {<<""/utf8>>, Accumulator@1} ->
+ Accumulator@1;
+
+ {<<"."/utf8>>, Accumulator@2} ->
+ Accumulator@2;
+
+ {<<".."/utf8>>, []} ->
+ [];
+
+ {<<".."/utf8>>, [_ | Accumulator@3]} ->
+ Accumulator@3;
+
+ {Segment@1, Accumulator@4} ->
+ [Segment@1 | Accumulator@4]
+ end,
+ do_remove_dot_segments(Rest, Accumulator@5)
+ end.
+
+-spec remove_dot_segments(list(binary())) -> list(binary()).
+remove_dot_segments(Input) ->
+ do_remove_dot_segments(Input, []).
+
+-spec path_segments(binary()) -> list(binary()).
+path_segments(Path) ->
+ remove_dot_segments(gleam@string:split(Path, <<"/"/utf8>>)).
+
+-spec to_string(uri()) -> binary().
+to_string(Uri) ->
+ Parts = case erlang:element(8, Uri) of
+ {some, Fragment} ->
+ [<<"#"/utf8>>, Fragment];
+
+ _ ->
+ []
+ end,
+ Parts@1 = case erlang:element(7, Uri) of
+ {some, Query} ->
+ [<<"?"/utf8>>, Query | Parts];
+
+ _ ->
+ Parts
+ end,
+ Parts@2 = [erlang:element(6, Uri) | Parts@1],
+ Parts@3 = case {erlang:element(4, Uri),
+ gleam@string:starts_with(erlang:element(6, Uri), <<"/"/utf8>>)} of
+ {{some, Host}, false} when Host =/= <<""/utf8>> ->
+ [<<"/"/utf8>> | Parts@2];
+
+ {_, _} ->
+ Parts@2
+ end,
+ Parts@4 = case {erlang:element(4, Uri), erlang:element(5, Uri)} of
+ {{some, _}, {some, Port}} ->
+ [<<":"/utf8>>, gleam@int:to_string(Port) | Parts@3];
+
+ {_, _} ->
+ Parts@3
+ end,
+ Parts@5 = case {erlang:element(2, Uri),
+ erlang:element(3, Uri),
+ erlang:element(4, Uri)} of
+ {{some, S}, {some, U}, {some, H}} ->
+ [S, <<"://"/utf8>>, U, <<"@"/utf8>>, H | Parts@4];
+
+ {{some, S@1}, none, {some, H@1}} ->
+ [S@1, <<"://"/utf8>>, H@1 | Parts@4];
+
+ {{some, S@2}, {some, _}, none} ->
+ [S@2, <<":"/utf8>> | Parts@4];
+
+ {{some, S@2}, none, none} ->
+ [S@2, <<":"/utf8>> | Parts@4];
+
+ {none, none, {some, H@2}} ->
+ [<<"//"/utf8>>, H@2 | Parts@4];
+
+ {_, _, _} ->
+ Parts@4
+ end,
+ gleam@string:concat(Parts@5).
+
+-spec origin(uri()) -> {ok, binary()} | {error, nil}.
+origin(Uri) ->
+ {uri, Scheme, _, Host, Port, _, _, _} = Uri,
+ case Scheme of
+ {some, <<"https"/utf8>>} when Port =:= {some, 443} ->
+ Origin = {uri, Scheme, none, Host, none, <<""/utf8>>, none, none},
+ {ok, to_string(Origin)};
+
+ {some, <<"http"/utf8>>} when Port =:= {some, 80} ->
+ Origin@1 = {uri, Scheme, none, Host, none, <<""/utf8>>, none, none},
+ {ok, to_string(Origin@1)};
+
+ {some, S} when (S =:= <<"http"/utf8>>) orelse (S =:= <<"https"/utf8>>) ->
+ Origin@2 = {uri, Scheme, none, Host, Port, <<""/utf8>>, none, none},
+ {ok, to_string(Origin@2)};
+
+ _ ->
+ {error, nil}
+ end.
+
+-spec drop_last(list(DFL)) -> list(DFL).
+drop_last(Elements) ->
+ gleam@list:take(Elements, gleam@list:length(Elements) - 1).
+
+-spec join_segments(list(binary())) -> binary().
+join_segments(Segments) ->
+ gleam@string:join([<<""/utf8>> | Segments], <<"/"/utf8>>).
+
+-spec merge(uri(), uri()) -> {ok, uri()} | {error, nil}.
+merge(Base, Relative) ->
+ case Base of
+ {uri, {some, _}, _, {some, _}, _, _, _, _} ->
+ case Relative of
+ {uri, _, _, {some, _}, _, _, _, _} ->
+ Path = begin
+ _pipe = gleam@string:split(
+ erlang:element(6, Relative),
+ <<"/"/utf8>>
+ ),
+ _pipe@1 = remove_dot_segments(_pipe),
+ join_segments(_pipe@1)
+ end,
+ Resolved = {uri,
+ gleam@option:'or'(
+ erlang:element(2, Relative),
+ erlang:element(2, Base)
+ ),
+ none,
+ erlang:element(4, Relative),
+ gleam@option:'or'(
+ erlang:element(5, Relative),
+ erlang:element(5, Base)
+ ),
+ Path,
+ erlang:element(7, Relative),
+ erlang:element(8, Relative)},
+ {ok, Resolved};
+
+ _ ->
+ {New_path, New_query} = case erlang:element(6, Relative) of
+ <<""/utf8>> ->
+ {erlang:element(6, Base),
+ gleam@option:'or'(
+ erlang:element(7, Relative),
+ erlang:element(7, Base)
+ )};
+
+ _ ->
+ Path_segments = case gleam@string:starts_with(
+ erlang:element(6, Relative),
+ <<"/"/utf8>>
+ ) of
+ true ->
+ gleam@string:split(
+ erlang:element(6, Relative),
+ <<"/"/utf8>>
+ );
+
+ false ->
+ _pipe@2 = gleam@string:split(
+ erlang:element(6, Base),
+ <<"/"/utf8>>
+ ),
+ _pipe@3 = drop_last(_pipe@2),
+ gleam@list:append(
+ _pipe@3,
+ gleam@string:split(
+ erlang:element(6, Relative),
+ <<"/"/utf8>>
+ )
+ )
+ end,
+ Path@1 = begin
+ _pipe@4 = Path_segments,
+ _pipe@5 = remove_dot_segments(_pipe@4),
+ join_segments(_pipe@5)
+ end,
+ {Path@1, erlang:element(7, Relative)}
+ end,
+ Resolved@1 = {uri,
+ erlang:element(2, Base),
+ none,
+ erlang:element(4, Base),
+ erlang:element(5, Base),
+ New_path,
+ New_query,
+ erlang:element(8, Relative)},
+ {ok, Resolved@1}
+ end;
+
+ _ ->
+ {error, nil}
+ end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.app.src b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.app.src
new file mode 100644
index 0000000..bcf08e2
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.app.src
@@ -0,0 +1,31 @@
+{application, gleam_stdlib, [
+ {vsn, "0.33.0"},
+ {applications, []},
+ {description, "A standard library for the Gleam programming language"},
+ {modules, [gleam@base,
+ gleam@bit_array,
+ gleam@bit_builder,
+ gleam@bit_string,
+ gleam@bool,
+ gleam@bytes_builder,
+ gleam@dict,
+ gleam@dynamic,
+ gleam@float,
+ gleam@function,
+ gleam@int,
+ gleam@io,
+ gleam@iterator,
+ gleam@list,
+ gleam@map,
+ gleam@option,
+ gleam@order,
+ gleam@pair,
+ gleam@queue,
+ gleam@regex,
+ gleam@result,
+ gleam@set,
+ gleam@string,
+ gleam@string_builder,
+ gleam@uri]},
+ {registered, []}
+]}.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.erl b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.erl
new file mode 100644
index 0000000..c6ea125
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.erl
@@ -0,0 +1,529 @@
+-module(gleam_stdlib).
+
+-export([
+ map_get/2, iodata_append/2, identity/1, decode_int/1, decode_bool/1,
+ decode_float/1, decode_list/1, decode_option/2, decode_field/2, parse_int/1,
+ parse_float/1, less_than/2, string_pop_grapheme/1, string_starts_with/2,
+ wrap_list/1, string_ends_with/2, string_pad/4, decode_map/1, uri_parse/1,
+ bit_array_int_to_u32/1, bit_array_int_from_u32/1, decode_result/1,
+ bit_array_slice/3, decode_bit_array/1, compile_regex/2, regex_scan/2,
+ percent_encode/1, percent_decode/1, regex_check/2, regex_split/2,
+ base_decode64/1, parse_query/1, bit_array_concat/1, size_of_tuple/1,
+ decode_tuple/1, decode_tuple2/1, decode_tuple3/1, decode_tuple4/1,
+ decode_tuple5/1, decode_tuple6/1, tuple_get/2, classify_dynamic/1, print/1,
+ println/1, print_error/1, println_error/1, inspect/1, float_to_string/1,
+ int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2,
+ crop_string/2, base16_decode/1
+]).
+
+%% Taken from OTP's uri_string module
+-define(DEC2HEX(X),
+ if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0;
+ ((X) >= 10) andalso ((X) =< 15) -> (X) + $A - 10
+ end).
+
+%% Taken from OTP's uri_string module
+-define(HEX2DEC(X),
+ if ((X) >= $0) andalso ((X) =< $9) -> (X) - $0;
+ ((X) >= $A) andalso ((X) =< $F) -> (X) - $A + 10;
+ ((X) >= $a) andalso ((X) =< $f) -> (X) - $a + 10
+ end).
+
+-define(is_lowercase_char(X), (X > 96 andalso X < 123)).
+-define(is_underscore_char(X), (X == 95)).
+-define(is_digit_char(X), (X > 47 andalso X < 58)).
+
+uppercase(X) -> X - 32.
+
+map_get(Map, Key) ->
+ case maps:find(Key, Map) of
+ error -> {error, nil};
+ OkFound -> OkFound
+ end.
+
+iodata_append(Iodata, String) -> [Iodata, String].
+
+identity(X) -> X.
+
+decode_error_msg(Expected, Data) when is_binary(Expected) ->
+ decode_error(Expected, classify_dynamic(Data)).
+decode_error(Expected, Got) when is_binary(Expected) andalso is_binary(Got) ->
+ {error, [{decode_error, Expected, Got, []}]}.
+
+classify_dynamic(nil) -> <<"Nil">>;
+classify_dynamic(X) when is_atom(X) -> <<"Atom">>;
+classify_dynamic(X) when is_binary(X) -> <<"String">>;
+classify_dynamic(X) when is_bitstring(X) -> <<"BitArray">>;
+classify_dynamic(X) when is_integer(X) -> <<"Int">>;
+classify_dynamic(X) when is_float(X) -> <<"Float">>;
+classify_dynamic(X) when is_list(X) -> <<"List">>;
+classify_dynamic(X) when is_boolean(X) -> <<"Bool">>;
+classify_dynamic(X) when is_map(X) -> <<"Map">>;
+classify_dynamic(X) when is_tuple(X) ->
+ iolist_to_binary(["Tuple of ", integer_to_list(tuple_size(X)), " elements"]);
+classify_dynamic(X) when
+ is_function(X, 0) orelse is_function(X, 1) orelse is_function(X, 2) orelse
+ is_function(X, 3) orelse is_function(X, 4) orelse is_function(X, 5) orelse
+ is_function(X, 6) orelse is_function(X, 7) orelse is_function(X, 8) orelse
+ is_function(X, 9) orelse is_function(X, 10) orelse is_function(X, 11) orelse
+ is_function(X, 12) -> <<"Function">>;
+classify_dynamic(_) -> <<"Some other type">>.
+
+decode_map(Data) when is_map(Data) -> {ok, Data};
+decode_map(Data) -> decode_error_msg(<<"Map">>, Data).
+
+decode_bit_array(Data) when is_bitstring(Data) -> {ok, Data};
+decode_bit_array(Data) -> decode_error_msg(<<"BitArray">>, Data).
+
+decode_int(Data) when is_integer(Data) -> {ok, Data};
+decode_int(Data) -> decode_error_msg(<<"Int">>, Data).
+
+decode_float(Data) when is_float(Data) -> {ok, Data};
+decode_float(Data) -> decode_error_msg(<<"Float">>, Data).
+
+decode_bool(Data) when is_boolean(Data) -> {ok, Data};
+decode_bool(Data) -> decode_error_msg(<<"Bool">>, Data).
+
+decode_list(Data) when is_list(Data) -> {ok, Data};
+decode_list(Data) -> decode_error_msg(<<"List">>, Data).
+
+decode_field(Data, Key) when is_map(Data) ->
+ case Data of
+ #{Key := Value} -> {ok, {some, Value}};
+ _ ->
+ {ok, none}
+ end;
+decode_field(Data, _) ->
+ decode_error_msg(<<"Map">>, Data).
+
+size_of_tuple(Data) -> tuple_size(Data).
+
+tuple_get(_tup, Index) when Index < 0 -> {error, nil};
+tuple_get(Data, Index) when Index >= tuple_size(Data) -> {error, nil};
+tuple_get(Data, Index) -> {ok, element(Index + 1, Data)}.
+
+decode_tuple(Data) when is_tuple(Data) -> {ok, Data};
+decode_tuple(Data) -> decode_error_msg(<<"Tuple">>, Data).
+
+decode_tuple2({_,_} = A) -> {ok, A};
+decode_tuple2([A,B]) -> {ok, {A,B}};
+decode_tuple2(Data) -> decode_error_msg(<<"Tuple of 2 elements">>, Data).
+
+decode_tuple3({_,_,_} = A) -> {ok, A};
+decode_tuple3([A,B,C]) -> {ok, {A,B,C}};
+decode_tuple3(Data) -> decode_error_msg(<<"Tuple of 3 elements">>, Data).
+
+decode_tuple4({_,_,_,_} = A) -> {ok, A};
+decode_tuple4([A,B,C,D]) -> {ok, {A,B,C,D}};
+decode_tuple4(Data) -> decode_error_msg(<<"Tuple of 4 elements">>, Data).
+
+decode_tuple5({_,_,_,_,_} = A) -> {ok, A};
+decode_tuple5([A,B,C,D,E]) -> {ok, {A,B,C,D,E}};
+decode_tuple5(Data) -> decode_error_msg(<<"Tuple of 5 elements">>, Data).
+
+decode_tuple6({_,_,_,_,_,_} = A) -> {ok, A};
+decode_tuple6([A,B,C,D,E,F]) -> {ok, {A,B,C,D,E,F}};
+decode_tuple6(Data) -> decode_error_msg(<<"Tuple of 6 elements">>, Data).
+
+decode_option(Term, F) ->
+ Decode = fun(Inner) ->
+ case F(Inner) of
+ {ok, Decoded} -> {ok, {some, Decoded}};
+ Error -> Error
+ end
+ end,
+ case Term of
+ undefined -> {ok, none};
+ error -> {ok, none};
+ null -> {ok, none};
+ none -> {ok, none};
+ nil -> {ok, none};
+ {some, Inner} -> Decode(Inner);
+ _ -> Decode(Term)
+ end.
+
+decode_result(Term) ->
+ case Term of
+ {ok, Inner} -> {ok, {ok, Inner}};
+ ok -> {ok, {ok, nil}};
+ {error, Inner} -> {ok, {error, Inner}};
+ error -> {ok, {error, nil}};
+ _ -> decode_error_msg(<<"Result">>, Term)
+ end.
+
+int_from_base_string(String, Base) ->
+ case catch binary_to_integer(String, Base) of
+ Int when is_integer(Int) -> {ok, Int};
+ _ -> {error, nil}
+ end.
+
+parse_int(String) ->
+ case catch binary_to_integer(String) of
+ Int when is_integer(Int) -> {ok, Int};
+ _ -> {error, nil}
+ end.
+
+parse_float(String) ->
+ case catch binary_to_float(String) of
+ Float when is_float(Float) -> {ok, Float};
+ _ -> {error, nil}
+ end.
+
+less_than(Lhs, Rhs) ->
+ Lhs < Rhs.
+
+string_starts_with(_, <<>>) -> true;
+string_starts_with(String, Prefix) when byte_size(Prefix) > byte_size(String) -> false;
+string_starts_with(String, Prefix) ->
+ PrefixSize = byte_size(Prefix),
+ Prefix == binary_part(String, 0, PrefixSize).
+
+string_ends_with(_, <<>>) -> true;
+string_ends_with(String, Suffix) when byte_size(Suffix) > byte_size(String) -> false;
+string_ends_with(String, Suffix) ->
+ SuffixSize = byte_size(Suffix),
+ Suffix == binary_part(String, byte_size(String) - SuffixSize, SuffixSize).
+
+string_pad(String, Length, Dir, PadString) ->
+ Chars = string:pad(String, Length, Dir, binary_to_list(PadString)),
+ case unicode:characters_to_binary(Chars) of
+ Bin when is_binary(Bin) -> Bin;
+ Error -> erlang:error({gleam_error, {string_invalid_utf8, Error}})
+ end.
+
+string_pop_grapheme(String) ->
+ case string:next_grapheme(String) of
+ [ Next | Rest ] ->
+ {ok, {unicode:characters_to_binary([Next]), unicode:characters_to_binary(Rest)}};
+ _ -> {error, nil}
+ end.
+
+bit_array_concat(BitArrays) ->
+ list_to_bitstring(BitArrays).
+
+bit_array_slice(Bin, Pos, Len) ->
+ try {ok, binary:part(Bin, Pos, Len)}
+ catch error:badarg -> {error, nil}
+ end.
+
+bit_array_int_to_u32(I) when 0 =< I, I < 4294967296 ->
+ {ok, <<I:32>>};
+bit_array_int_to_u32(_) ->
+ {error, nil}.
+
+bit_array_int_from_u32(<<I:32>>) ->
+ {ok, I};
+bit_array_int_from_u32(_) ->
+ {error, nil}.
+
+compile_regex(String, Options) ->
+ {options, Caseless, Multiline} = Options,
+ OptionsList = [
+ unicode,
+ ucp,
+ Caseless andalso caseless,
+ Multiline andalso multiline
+ ],
+ FilteredOptions = [Option || Option <- OptionsList, Option /= false],
+ case re:compile(String, FilteredOptions) of
+ {ok, MP} -> {ok, MP};
+ {error, {Str, Pos}} ->
+ {error, {compile_error, unicode:characters_to_binary(Str), Pos}}
+ end.
+
+regex_check(Regex, String) ->
+ re:run(String, Regex) /= nomatch.
+
+regex_split(Regex, String) ->
+ re:split(String, Regex).
+
+regex_submatches(_, {-1, 0}) -> none;
+regex_submatches(String, {Start, Length}) ->
+ BinarySlice = binary:part(String, {Start, Length}),
+ case string:is_empty(binary_to_list(BinarySlice)) of
+ true -> none;
+ false -> {some, BinarySlice}
+ end.
+
+regex_matches(String, [{Start, Length} | Submatches]) ->
+ Submatches1 = lists:map(fun(X) -> regex_submatches(String, X) end, Submatches),
+ {match, binary:part(String, Start, Length), Submatches1}.
+
+regex_scan(Regex, String) ->
+ case re:run(String, Regex, [global]) of
+ {match, Captured} -> lists:map(fun(X) -> regex_matches(String, X) end, Captured);
+ nomatch -> []
+ end.
+
+base_decode64(S) ->
+ try {ok, base64:decode(S)}
+ catch error:_ -> {error, nil}
+ end.
+
+wrap_list(X) when is_list(X) -> X;
+wrap_list(X) -> [X].
+
+parse_query(Query) ->
+ case uri_string:dissect_query(Query) of
+ {error, _, _} -> {error, nil};
+ Pairs ->
+ Pairs1 = lists:map(fun
+ ({K, true}) -> {K, <<"">>};
+ (Pair) -> Pair
+ end, Pairs),
+ {ok, Pairs1}
+ end.
+
+percent_encode(B) -> percent_encode(B, <<>>).
+percent_encode(<<>>, Acc) ->
+ Acc;
+percent_encode(<<H,T/binary>>, Acc) ->
+ case percent_ok(H) of
+ true ->
+ percent_encode(T, <<Acc/binary,H>>);
+ false ->
+ <<A:4,B:4>> = <<H>>,
+ percent_encode(T, <<Acc/binary,$%,(?DEC2HEX(A)),(?DEC2HEX(B))>>)
+ end.
+
+percent_decode(Cs) -> percent_decode(Cs, <<>>).
+percent_decode(<<$%, C0, C1, Cs/binary>>, Acc) ->
+ case is_hex_digit(C0) andalso is_hex_digit(C1) of
+ true ->
+ B = ?HEX2DEC(C0)*16+?HEX2DEC(C1),
+ percent_decode(Cs, <<Acc/binary, B>>);
+ false ->
+ {error, nil}
+ end;
+percent_decode(<<C,Cs/binary>>, Acc) ->
+ percent_decode(Cs, <<Acc/binary, C>>);
+percent_decode(<<>>, Acc) ->
+ check_utf8(Acc).
+
+percent_ok($!) -> true;
+percent_ok($$) -> true;
+percent_ok($') -> true;
+percent_ok($() -> true;
+percent_ok($)) -> true;
+percent_ok($*) -> true;
+percent_ok($+) -> true;
+percent_ok($-) -> true;
+percent_ok($.) -> true;
+percent_ok($_) -> true;
+percent_ok($~) -> true;
+percent_ok(C) when $0 =< C, C =< $9 -> true;
+percent_ok(C) when $A =< C, C =< $Z -> true;
+percent_ok(C) when $a =< C, C =< $z -> true;
+percent_ok(_) -> false.
+
+is_hex_digit(C) ->
+ ($0 =< C andalso C =< $9) orelse ($a =< C andalso C =< $f) orelse ($A =< C andalso C =< $F).
+
+check_utf8(Cs) ->
+ case unicode:characters_to_list(Cs) of
+ {incomplete, _, _} -> {error, nil};
+ {error, _, _} -> {error, nil};
+ _ -> {ok, Cs}
+ end.
+
+uri_parse(String) ->
+ case uri_string:parse(String) of
+ {error, _, _} -> {error, nil};
+ Uri ->
+ {ok, {uri,
+ maps_get_optional(Uri, scheme),
+ maps_get_optional(Uri, userinfo),
+ maps_get_optional(Uri, host),
+ maps_get_optional(Uri, port),
+ maps_get_or(Uri, path, <<>>),
+ maps_get_optional(Uri, query),
+ maps_get_optional(Uri, fragment)
+ }}
+ end.
+
+maps_get_optional(Map, Key) ->
+ try {some, maps:get(Key, Map)}
+ catch _:_ -> none
+ end.
+
+maps_get_or(Map, Key, Default) ->
+ try maps:get(Key, Map)
+ catch _:_ -> Default
+ end.
+
+print(String) ->
+ io:put_chars(String),
+ nil.
+
+println(String) ->
+ io:put_chars([String, $\n]),
+ nil.
+
+print_error(String) ->
+ io:put_chars(standard_error, String),
+ nil.
+
+println_error(String) ->
+ io:put_chars(standard_error, [String, $\n]),
+ nil.
+
+inspect(true) ->
+ "True";
+inspect(false) ->
+ "False";
+inspect(nil) ->
+ "Nil";
+inspect(Data) when is_map(Data) ->
+ Fields = [
+ [<<"#(">>, inspect(Key), <<", ">>, inspect(Value), <<")">>]
+ || {Key, Value} <- maps:to_list(Data)
+ ],
+ ["dict.from_list([", lists:join(", ", Fields), "])"];
+inspect(Atom) when is_atom(Atom) ->
+ Binary = erlang:atom_to_binary(Atom),
+ case inspect_maybe_gleam_atom(Binary, none, <<>>) of
+ {ok, Inspected} -> Inspected;
+ {error, _} -> ["atom.create_from_string(\"", Binary, "\")"]
+ end;
+inspect(Any) when is_integer(Any) ->
+ erlang:integer_to_list(Any);
+inspect(Any) when is_float(Any) ->
+ io_lib_format:fwrite_g(Any);
+inspect(Binary) when is_binary(Binary) ->
+ case inspect_maybe_utf8_string(Binary, <<>>) of
+ {ok, InspectedUtf8String} -> InspectedUtf8String;
+ {error, not_a_utf8_string} ->
+ Segments = [erlang:integer_to_list(X) || <<X>> <= Binary],
+ ["<<", lists:join(", ", Segments), ">>"]
+ end;
+inspect(Bits) when is_bitstring(Bits) ->
+ inspect_bit_array(Bits);
+inspect(List) when is_list(List) ->
+ case inspect_list(List) of
+ {proper, Elements} -> ["[", Elements, "]"];
+ {improper, Elements} -> ["//erl([", Elements, "])"]
+ end;
+inspect(Any) when is_tuple(Any) % Record constructors
+ andalso is_atom(element(1, Any))
+ andalso element(1, Any) =/= false
+ andalso element(1, Any) =/= true
+ andalso element(1, Any) =/= nil
+->
+ [Atom | ArgsList] = erlang:tuple_to_list(Any),
+ Args = lists:join(<<", ">>,
+ lists:map(fun inspect/1, ArgsList)
+ ),
+ [inspect(Atom), "(", Args, ")"];
+inspect(Tuple) when is_tuple(Tuple) ->
+ Elements = lists:map(fun inspect/1, erlang:tuple_to_list(Tuple)),
+ ["#(", lists:join(", ", Elements), ")"];
+inspect(Any) when is_function(Any) ->
+ {arity, Arity} = erlang:fun_info(Any, arity),
+ ArgsAsciiCodes = lists:seq($a, $a + Arity - 1),
+ Args = lists:join(<<", ">>,
+ lists:map(fun(Arg) -> <<Arg>> end, ArgsAsciiCodes)
+ ),
+ ["//fn(", Args, ") { ... }"];
+inspect(Any) ->
+ ["//erl(", io_lib:format("~p", [Any]), ")"].
+
+
+inspect_maybe_gleam_atom(<<>>, none, _) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<First, _Rest/binary>>, none, _) when ?is_digit_char(First) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<First, _Rest/binary>>, _PrevChar, _Acc)
+ when not (?is_lowercase_char(First) orelse ?is_underscore_char(First) orelse ?is_digit_char(First)) ->
+ {error, nil};
+inspect_maybe_gleam_atom(<<First, Rest/binary>>, none, Acc) ->
+ inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, (uppercase(First))>>);
+inspect_maybe_gleam_atom(<<"_", Rest/binary>>, _PrevChar, Acc) ->
+ inspect_maybe_gleam_atom(Rest, $_, Acc);
+inspect_maybe_gleam_atom(<<First, Rest/binary>>, $_, Acc) ->
+ inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, (uppercase(First))>>);
+inspect_maybe_gleam_atom(<<First, Rest/binary>>, _PrevChar, Acc) ->
+ inspect_maybe_gleam_atom(Rest, First, <<Acc/binary, First>>);
+inspect_maybe_gleam_atom(<<>>, _PrevChar, Acc) ->
+ {ok, Acc};
+inspect_maybe_gleam_atom(A, B, C) ->
+ erlang:display({A, B, C}),
+ throw({gleam_error, A, B, C}).
+
+inspect_list([]) ->
+ {proper, []};
+inspect_list([First]) ->
+ {proper, [inspect(First)]};
+inspect_list([First | Rest]) when is_list(Rest) ->
+ {Kind, Inspected} = inspect_list(Rest),
+ {Kind, [inspect(First), <<", ">> | Inspected]};
+inspect_list([First | ImproperTail]) ->
+ {improper, [inspect(First), <<" | ">>, inspect(ImproperTail)]}.
+
+inspect_bit_array(Bits) ->
+ Text = inspect_bit_array(Bits, <<"<<">>),
+ <<Text/binary, ">>">>.
+
+inspect_bit_array(<<>>, Acc) ->
+ Acc;
+inspect_bit_array(<<X, Rest/bitstring>>, Acc) ->
+ inspect_bit_array(Rest, append_segment(Acc, erlang:integer_to_binary(X)));
+inspect_bit_array(Rest, Acc) ->
+ Size = bit_size(Rest),
+ <<X:Size>> = Rest,
+ X1 = erlang:integer_to_binary(X),
+ Size1 = erlang:integer_to_binary(Size),
+ Segment = <<X1/binary, ":size(", Size1/binary, ")">>,
+ inspect_bit_array(<<>>, append_segment(Acc, Segment)).
+
+append_segment(<<"<<">>, Segment) ->
+ <<"<<", Segment/binary>>;
+append_segment(Acc, Segment) ->
+ <<Acc/binary, ", ", Segment/binary>>.
+
+
+inspect_maybe_utf8_string(Binary, Acc) ->
+ case Binary of
+ <<>> -> {ok, <<$", Acc/binary, $">>};
+ <<First/utf8, Rest/binary>> ->
+ Escaped = case First of
+ $" -> <<$\\, $">>;
+ $\\ -> <<$\\, $\\>>;
+ $\r -> <<$\\, $r>>;
+ $\n -> <<$\\, $n>>;
+ $\t -> <<$\\, $t>>;
+ Other -> <<Other/utf8>>
+ end,
+ inspect_maybe_utf8_string(Rest, <<Acc/binary, Escaped/binary>>);
+ _ -> {error, not_a_utf8_string}
+ end.
+
+float_to_string(Float) when is_float(Float) ->
+ erlang:iolist_to_binary(io_lib_format:fwrite_g(Float)).
+
+utf_codepoint_list_to_string(List) ->
+ case unicode:characters_to_binary(List) of
+ {error, _} -> erlang:error({gleam_error, {string_invalid_utf8, List}});
+ Binary -> Binary
+ end.
+
+crop_string(String, Prefix) ->
+ case string:find(String, Prefix) of
+ nomatch -> String;
+ New -> New
+ end.
+
+contains_string(String, Substring) ->
+ is_bitstring(string:find(String, Substring)).
+
+base16_decode(String) ->
+ try
+ {ok, binary:decode_hex(String)}
+ catch
+ _:_ -> {error, nil}
+ end.
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.mjs b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.mjs
new file mode 100644
index 0000000..a908b23
--- /dev/null
+++ b/aoc2023/build/packages/gleam_stdlib/src/gleam_stdlib.mjs
@@ -0,0 +1,875 @@
+import {
+ BitArray,
+ Error,
+ List,
+ Ok,
+ Result,
+ UtfCodepoint,
+ stringBits,
+ toBitArray,
+ NonEmpty,
+ CustomType,
+} from "./gleam.mjs";
+import {
+ CompileError as RegexCompileError,
+ Match as RegexMatch,
+} from "./gleam/regex.mjs";
+import { DecodeError } from "./gleam/dynamic.mjs";
+import { Some, None } from "./gleam/option.mjs";
+import Dict from "./dict.mjs";
+
+const Nil = undefined;
+const NOT_FOUND = {};
+
+export function identity(x) {
+ return x;
+}
+
+export function parse_int(value) {
+ if (/^[-+]?(\d+)$/.test(value)) {
+ return new Ok(parseInt(value));
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function parse_float(value) {
+ if (/^[-+]?(\d+)\.(\d+)$/.test(value)) {
+ return new Ok(parseFloat(value));
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function to_string(term) {
+ return term.toString();
+}
+
+export function float_to_string(float) {
+ const string = float.toString();
+ if (string.indexOf(".") >= 0) {
+ return string;
+ } else {
+ return string + ".0";
+ }
+}
+
+export function int_to_base_string(int, base) {
+ return int.toString(base).toUpperCase();
+}
+
+const int_base_patterns = {
+ 2: /[^0-1]/,
+ 3: /[^0-2]/,
+ 4: /[^0-3]/,
+ 5: /[^0-4]/,
+ 6: /[^0-5]/,
+ 7: /[^0-6]/,
+ 8: /[^0-7]/,
+ 9: /[^0-8]/,
+ 10: /[^0-9]/,
+ 11: /[^0-9a]/,
+ 12: /[^0-9a-b]/,
+ 13: /[^0-9a-c]/,
+ 14: /[^0-9a-d]/,
+ 15: /[^0-9a-e]/,
+ 16: /[^0-9a-f]/,
+ 17: /[^0-9a-g]/,
+ 18: /[^0-9a-h]/,
+ 19: /[^0-9a-i]/,
+ 20: /[^0-9a-j]/,
+ 21: /[^0-9a-k]/,
+ 22: /[^0-9a-l]/,
+ 23: /[^0-9a-m]/,
+ 24: /[^0-9a-n]/,
+ 25: /[^0-9a-o]/,
+ 26: /[^0-9a-p]/,
+ 27: /[^0-9a-q]/,
+ 28: /[^0-9a-r]/,
+ 29: /[^0-9a-s]/,
+ 30: /[^0-9a-t]/,
+ 31: /[^0-9a-u]/,
+ 32: /[^0-9a-v]/,
+ 33: /[^0-9a-w]/,
+ 34: /[^0-9a-x]/,
+ 35: /[^0-9a-y]/,
+ 36: /[^0-9a-z]/,
+};
+
+export function int_from_base_string(string, base) {
+ if (int_base_patterns[base].test(string.replace(/^-/, "").toLowerCase())) {
+ return new Error(Nil);
+ }
+
+ const result = parseInt(string, base);
+
+ if (isNaN(result)) {
+ return new Error(Nil);
+ }
+
+ return new Ok(result);
+}
+
+export function string_replace(string, target, substitute) {
+ if (typeof string.replaceAll !== "undefined") {
+ return string.replaceAll(target, substitute);
+ }
+ // Fallback for older Node.js versions:
+ // 1. <https://stackoverflow.com/a/1144788>
+ // 2. <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping>
+ // TODO: This fallback could be remove once Node.js 14 is EOL
+ // aka <https://nodejs.org/en/about/releases/> on or after 2024-04-30
+ return string.replace(
+ // $& means the whole matched string
+ new RegExp(target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
+ substitute
+ );
+}
+
+export function string_reverse(string) {
+ return [...string].reverse().join("");
+}
+
+export function string_length(string) {
+ if (string === "") {
+ return 0;
+ }
+ const iterator = graphemes_iterator(string);
+ if (iterator) {
+ let i = 0;
+ for (const _ of iterator) {
+ i++;
+ }
+ return i;
+ } else {
+ return string.match(/./gsu).length;
+ }
+}
+
+export function graphemes(string) {
+ return List.fromArray(
+ Array.from(graphemes_iterator(string)).map((item) => item.segment)
+ );
+}
+
+function graphemes_iterator(string) {
+ if (Intl && Intl.Segmenter) {
+ return new Intl.Segmenter().segment(string)[Symbol.iterator]();
+ }
+}
+
+export function pop_grapheme(string) {
+ let first;
+ const iterator = graphemes_iterator(string);
+ if (iterator) {
+ first = iterator.next().value?.segment;
+ } else {
+ first = string.match(/./su)?.[0];
+ }
+ if (first) {
+ return new Ok([first, string.slice(first.length)]);
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function lowercase(string) {
+ return string.toLowerCase();
+}
+
+export function uppercase(string) {
+ return string.toUpperCase();
+}
+
+export function less_than(a, b) {
+ return a < b;
+}
+
+export function add(a, b) {
+ return a + b;
+}
+
+export function equal(a, b) {
+ return a === b;
+}
+
+export function split(xs, pattern) {
+ return List.fromArray(xs.split(pattern));
+}
+
+export function join(xs, separator) {
+ const iterator = xs[Symbol.iterator]();
+ let result = iterator.next().value || "";
+ let current = iterator.next();
+ while (!current.done) {
+ result = result + separator + current.value;
+ current = iterator.next();
+ }
+ return result;
+}
+
+export function concat(xs) {
+ let result = "";
+ for (const x of xs) {
+ result = result + x;
+ }
+ return result;
+}
+
+export function length(data) {
+ return data.length;
+}
+
+export function crop_string(string, substring) {
+ return string.substring(string.indexOf(substring));
+}
+
+export function contains_string(haystack, needle) {
+ return haystack.indexOf(needle) >= 0;
+}
+
+export function starts_with(haystack, needle) {
+ return haystack.startsWith(needle);
+}
+
+export function ends_with(haystack, needle) {
+ return haystack.endsWith(needle);
+}
+
+export function split_once(haystack, needle) {
+ const index = haystack.indexOf(needle);
+ if (index >= 0) {
+ const before = haystack.slice(0, index);
+ const after = haystack.slice(index + needle.length);
+ return new Ok([before, after]);
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function trim(string) {
+ return string.trim();
+}
+
+export function trim_left(string) {
+ return string.trimLeft();
+}
+
+export function trim_right(string) {
+ return string.trimRight();
+}
+
+export function bit_array_from_string(string) {
+ return toBitArray([stringBits(string)]);
+}
+
+export function bit_array_concat(bit_arrays) {
+ return toBitArray(bit_arrays.toArray().map((b) => b.buffer));
+}
+
+export function console_log(term) {
+ console.log(term);
+}
+
+export function console_error(term) {
+ console.error(term);
+}
+
+export function crash(message) {
+ throw new globalThis.Error(message);
+}
+
+export function bit_array_to_string(bit_array) {
+ try {
+ const decoder = new TextDecoder("utf-8", { fatal: true });
+ return new Ok(decoder.decode(bit_array.buffer));
+ } catch (_error) {
+ return new Error(Nil);
+ }
+}
+
+export function print(string) {
+ if (typeof process === "object") {
+ process.stdout.write(string); // We can write without a trailing newline
+ } else if (typeof Deno === "object") {
+ Deno.stdout.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline
+ } else {
+ console.log(string); // We're in a browser. Newlines are mandated
+ }
+}
+
+export function print_error(string) {
+ if (typeof process === "object" && process.stderr?.write) {
+ process.stderr.write(string); // We can write without a trailing newline
+ } else if (typeof Deno === "object") {
+ Deno.stderr.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline
+ } else {
+ console.error(string); // We're in a browser. Newlines are mandated
+ }
+}
+
+export function print_debug(string) {
+ if (typeof process === "object" && process.stderr?.write) {
+ process.stderr.write(string + "\n"); // If we're in Node.js, use `stderr`
+ } else if (typeof Deno === "object") {
+ Deno.stderr.writeSync(new TextEncoder().encode(string + "\n")); // If we're in Deno, use `stderr`
+ } else {
+ console.log(string); // Otherwise, use `console.log` (so that it doesn't look like an error)
+ }
+}
+
+export function ceiling(float) {
+ return Math.ceil(float);
+}
+
+export function floor(float) {
+ return Math.floor(float);
+}
+
+export function round(float) {
+ return Math.round(float);
+}
+
+export function truncate(float) {
+ return Math.trunc(float);
+}
+
+export function power(base, exponent) {
+ // It is checked in Gleam that:
+ // - The base is non-negative and that the exponent is not fractional.
+ // - The base is non-zero and the exponent is non-negative (otherwise
+ // the result will essentially be division by zero).
+ // It can thus be assumed that valid input is passed to the Math.pow
+ // function and a NaN or Infinity value will not be produced.
+ return Math.pow(base, exponent);
+}
+
+export function random_uniform() {
+ const random_uniform_result = Math.random();
+ // With round-to-nearest-even behavior, the ranges claimed for the functions below
+ // (excluding the one for Math.random() itself) aren't exact.
+ // If extremely large bounds are chosen (2^53 or higher),
+ // it's possible in extremely rare cases to calculate the usually-excluded upper bound.
+ // Note that as numbers in JavaScript are IEEE 754 floating point numbers
+ // See: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random>
+ // Because of this, we just loop 'until' we get a valid result where 0.0 <= x < 1.0:
+ if (random_uniform_result === 1.0) {
+ return random_uniform();
+ }
+ return random_uniform_result;
+}
+
+export function bit_array_slice(bits, position, length) {
+ const start = Math.min(position, position + length);
+ const end = Math.max(position, position + length);
+ if (start < 0 || end > bits.length) return new Error(Nil);
+ const buffer = new Uint8Array(bits.buffer.buffer, start, Math.abs(length));
+ return new Ok(new BitArray(buffer));
+}
+
+export function codepoint(int) {
+ return new UtfCodepoint(int);
+}
+
+export function string_to_codepoint_integer_list(string) {
+ return List.fromArray(Array.from(string).map((item) => item.codePointAt(0)));
+}
+
+export function utf_codepoint_list_to_string(utf_codepoint_integer_list) {
+ return utf_codepoint_integer_list
+ .toArray()
+ .map((x) => String.fromCodePoint(x.value))
+ .join("");
+}
+
+export function utf_codepoint_to_int(utf_codepoint) {
+ return utf_codepoint.value;
+}
+
+export function regex_check(regex, string) {
+ regex.lastIndex = 0;
+ return regex.test(string);
+}
+
+export function compile_regex(pattern, options) {
+ try {
+ let flags = "gu";
+ if (options.case_insensitive) flags += "i";
+ if (options.multi_line) flags += "m";
+ return new Ok(new RegExp(pattern, flags));
+ } catch (error) {
+ const number = (error.columnNumber || 0) | 0;
+ return new Error(new RegexCompileError(error.message, number));
+ }
+}
+
+export function regex_scan(regex, string) {
+ const matches = Array.from(string.matchAll(regex)).map((match) => {
+ const content = match[0];
+ const submatches = [];
+ for (let n = match.length - 1; n > 0; n--) {
+ if (match[n]) {
+ submatches[n - 1] = new Some(match[n]);
+ continue;
+ }
+ if (submatches.length > 0) {
+ submatches[n - 1] = new None();
+ }
+ }
+ return new RegexMatch(content, List.fromArray(submatches));
+ });
+ return List.fromArray(matches);
+}
+
+export function new_map() {
+ return Dict.new();
+}
+
+export function map_size(map) {
+ return map.size;
+}
+
+export function map_to_list(map) {
+ return List.fromArray(map.entries());
+}
+
+export function map_remove(key, map) {
+ return map.delete(key);
+}
+
+export function map_get(map, key) {
+ const value = map.get(key, NOT_FOUND);
+ if (value === NOT_FOUND) {
+ return new Error(Nil);
+ }
+ return new Ok(value);
+}
+
+export function map_insert(key, value, map) {
+ return map.set(key, value);
+}
+
+function unsafe_percent_decode(string) {
+ return decodeURIComponent((string || "").replace("+", " "));
+}
+
+export function percent_decode(string) {
+ try {
+ return new Ok(unsafe_percent_decode(string));
+ } catch (_error) {
+ return new Error(Nil);
+ }
+}
+
+export function percent_encode(string) {
+ return encodeURIComponent(string);
+}
+
+export function parse_query(query) {
+ try {
+ const pairs = [];
+ for (const section of query.split("&")) {
+ const [key, value] = section.split("=");
+ if (!key) continue;
+ pairs.push([unsafe_percent_decode(key), unsafe_percent_decode(value)]);
+ }
+ return new Ok(List.fromArray(pairs));
+ } catch (_error) {
+ return new Error(Nil);
+ }
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+export function encode64(bit_array) {
+ const aBytes = bit_array.buffer;
+ let nMod3 = 2;
+ let sB64Enc = "";
+
+ for (let nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
+ nMod3 = nIdx % 3;
+ if (nIdx > 0 && ((nIdx * 4) / 3) % 76 === 0) {
+ sB64Enc += "\r\n";
+ }
+ nUint24 |= aBytes[nIdx] << ((16 >>> nMod3) & 24);
+ if (nMod3 === 2 || aBytes.length - nIdx === 1) {
+ sB64Enc += String.fromCharCode(
+ uint6ToB64((nUint24 >>> 18) & 63),
+ uint6ToB64((nUint24 >>> 12) & 63),
+ uint6ToB64((nUint24 >>> 6) & 63),
+ uint6ToB64(nUint24 & 63)
+ );
+ nUint24 = 0;
+ }
+ }
+
+ return (
+ sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) +
+ (nMod3 === 2 ? "" : nMod3 === 1 ? "=" : "==")
+ );
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+function uint6ToB64(nUint6) {
+ return nUint6 < 26
+ ? nUint6 + 65
+ : nUint6 < 52
+ ? nUint6 + 71
+ : nUint6 < 62
+ ? nUint6 - 4
+ : nUint6 === 62
+ ? 43
+ : nUint6 === 63
+ ? 47
+ : 65;
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+function b64ToUint6(nChr) {
+ return nChr > 64 && nChr < 91
+ ? nChr - 65
+ : nChr > 96 && nChr < 123
+ ? nChr - 71
+ : nChr > 47 && nChr < 58
+ ? nChr + 4
+ : nChr === 43
+ ? 62
+ : nChr === 47
+ ? 63
+ : 0;
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+export function decode64(sBase64) {
+ if (sBase64.match(/[^A-Za-z0-9\+\/=]/g)) return new Error(Nil);
+ const sB64Enc = sBase64.replace(/=/g, "");
+ const nInLen = sB64Enc.length;
+ const nOutLen = (nInLen * 3 + 1) >> 2;
+ const taBytes = new Uint8Array(nOutLen);
+
+ for (
+ let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0;
+ nInIdx < nInLen;
+ nInIdx++
+ ) {
+ nMod4 = nInIdx & 3;
+ nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (6 * (3 - nMod4));
+ if (nMod4 === 3 || nInLen - nInIdx === 1) {
+ for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
+ taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
+ }
+ nUint24 = 0;
+ }
+ }
+
+ return new Ok(new BitArray(taBytes));
+}
+
+export function classify_dynamic(data) {
+ if (typeof data === "string") {
+ return "String";
+ } else if (data instanceof Result) {
+ return "Result";
+ } else if (data instanceof List) {
+ return "List";
+ } else if (data instanceof BitArray) {
+ return "BitArray";
+ } else if (data instanceof Dict) {
+ return "Map";
+ } else if (Number.isInteger(data)) {
+ return "Int";
+ } else if (Array.isArray(data)) {
+ return `Tuple of ${data.length} elements`;
+ } else if (typeof data === "number") {
+ return "Float";
+ } else if (data === null) {
+ return "Null";
+ } else if (data === undefined) {
+ return "Nil";
+ } else {
+ const type = typeof data;
+ return type.charAt(0).toUpperCase() + type.slice(1);
+ }
+}
+
+function decoder_error(expected, got) {
+ return decoder_error_no_classify(expected, classify_dynamic(got));
+}
+
+function decoder_error_no_classify(expected, got) {
+ return new Error(
+ List.fromArray([new DecodeError(expected, got, List.fromArray([]))])
+ );
+}
+
+export function decode_string(data) {
+ return typeof data === "string"
+ ? new Ok(data)
+ : decoder_error("String", data);
+}
+
+export function decode_int(data) {
+ return Number.isInteger(data) ? new Ok(data) : decoder_error("Int", data);
+}
+
+export function decode_float(data) {
+ return typeof data === "number" ? new Ok(data) : decoder_error("Float", data);
+}
+
+export function decode_bool(data) {
+ return typeof data === "boolean" ? new Ok(data) : decoder_error("Bool", data);
+}
+
+export function decode_bit_array(data) {
+ if (data instanceof BitArray) {
+ return new Ok(data);
+ }
+ if (data instanceof Uint8Array) {
+ return new Ok(new BitArray(data));
+ }
+ return decoder_error("BitArray", data);
+}
+
+export function decode_tuple(data) {
+ return Array.isArray(data) ? new Ok(data) : decoder_error("Tuple", data);
+}
+
+export function decode_tuple2(data) {
+ return decode_tupleN(data, 2);
+}
+
+export function decode_tuple3(data) {
+ return decode_tupleN(data, 3);
+}
+
+export function decode_tuple4(data) {
+ return decode_tupleN(data, 4);
+}
+
+export function decode_tuple5(data) {
+ return decode_tupleN(data, 5);
+}
+
+export function decode_tuple6(data) {
+ return decode_tupleN(data, 6);
+}
+
+function decode_tupleN(data, n) {
+ if (Array.isArray(data) && data.length == n) {
+ return new Ok(data);
+ }
+
+ const list = decode_exact_length_list(data, n);
+ if (list) return new Ok(list);
+
+ return decoder_error(`Tuple of ${n} elements`, data);
+}
+
+function decode_exact_length_list(data, n) {
+ if (!(data instanceof List)) return;
+
+ const elements = [];
+ let current = data;
+
+ for (let i = 0; i < n; i++) {
+ if (!(current instanceof NonEmpty)) break;
+ elements.push(current.head);
+ current = current.tail;
+ }
+
+ if (elements.length === n && !(current instanceof NonEmpty)) return elements;
+}
+
+export function tuple_get(data, index) {
+ return index >= 0 && data.length > index
+ ? new Ok(data[index])
+ : new Error(Nil);
+}
+
+export function decode_list(data) {
+ if (Array.isArray(data)) {
+ return new Ok(List.fromArray(data));
+ }
+ return data instanceof List ? new Ok(data) : decoder_error("List", data);
+}
+
+export function decode_result(data) {
+ return data instanceof Result ? new Ok(data) : decoder_error("Result", data);
+}
+
+export function decode_map(data) {
+ if (data instanceof Dict) {
+ return new Ok(Dict.fromMap(data));
+ }
+ if (data == null) {
+ return decoder_error("Map", data);
+ }
+ if (typeof data !== "object") {
+ return decoder_error("Map", data);
+ }
+ const proto = Object.getPrototypeOf(data);
+ if (proto === Object.prototype || proto === null) {
+ return new Ok(Dict.fromObject(data));
+ }
+ return decoder_error("Map", data);
+}
+
+export function decode_option(data, decoder) {
+ if (data === null || data === undefined || data instanceof None)
+ return new Ok(new None());
+ if (data instanceof Some) data = data[0];
+ const result = decoder(data);
+ if (result.isOk()) {
+ return new Ok(new Some(result[0]));
+ } else {
+ return result;
+ }
+}
+
+export function decode_field(value, name) {
+ const not_a_map_error = () => decoder_error("Map", value);
+
+ if (
+ value instanceof Dict ||
+ value instanceof WeakMap ||
+ value instanceof Map
+ ) {
+ const entry = map_get(value, name);
+ return new Ok(entry.isOk() ? new Some(entry[0]) : new None());
+ } else if (Object.getPrototypeOf(value) == Object.prototype) {
+ return try_get_field(value, name, () => new Ok(new None()));
+ } else {
+ return try_get_field(value, name, not_a_map_error);
+ }
+}
+
+function try_get_field(value, field, or_else) {
+ try {
+ return field in value ? new Ok(new Some(value[field])) : or_else();
+ } catch {
+ return or_else();
+ }
+}
+
+export function byte_size(string) {
+ return new TextEncoder().encode(string).length;
+}
+
+// In Javascript bitwise operations convert numbers to a sequence of 32 bits
+// while Erlang uses arbitrary precision.
+// To get around this problem and get consistent results use BigInt and then
+// downcast the value back to a Number value.
+
+export function bitwise_and(x, y) {
+ return Number(BigInt(x) & BigInt(y));
+}
+
+export function bitwise_not(x) {
+ return Number(~BigInt(x));
+}
+
+export function bitwise_or(x, y) {
+ return Number(BigInt(x) | BigInt(y));
+}
+
+export function bitwise_exclusive_or(x, y) {
+ return Number(BigInt(x) ^ BigInt(y));
+}
+
+export function bitwise_shift_left(x, y) {
+ return Number(BigInt(x) << BigInt(y));
+}
+
+export function bitwise_shift_right(x, y) {
+ return Number(BigInt(x) >> BigInt(y));
+}
+
+export function inspect(v) {
+ const t = typeof v;
+ if (v === true) return "True";
+ if (v === false) return "False";
+ if (v === null) return "//js(null)";
+ if (v === undefined) return "Nil";
+ if (t === "string") return JSON.stringify(v);
+ if (t === "bigint" || t === "number") return v.toString();
+ if (Array.isArray(v)) return `#(${v.map(inspect).join(", ")})`;
+ if (v instanceof List) return inspectList(v);
+ if (v instanceof UtfCodepoint) return inspectUtfCodepoint(v);
+ if (v instanceof BitArray) return inspectBitArray(v);
+ if (v instanceof CustomType) return inspectCustomType(v);
+ if (v instanceof Dict) return inspectDict(v);
+ if (v instanceof Set) return `//js(Set(${[...v].map(inspect).join(", ")}))`;
+ if (v instanceof RegExp) return `//js(${v})`;
+ if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`;
+ if (v instanceof Function) {
+ const args = [];
+ for (const i of Array(v.length).keys())
+ args.push(String.fromCharCode(i + 97));
+ return `//fn(${args.join(", ")}) { ... }`;
+ }
+ return inspectObject(v);
+}
+
+function inspectDict(map) {
+ let body = "dict.from_list([";
+ let first = true;
+ map.forEach((value, key) => {
+ if (!first) body = body + ", ";
+ body = body + "#(" + inspect(key) + ", " + inspect(value) + ")";
+ first = false;
+ });
+ return body + "])";
+}
+
+function inspectObject(v) {
+ const name = Object.getPrototypeOf(v)?.constructor?.name || "Object";
+ const props = [];
+ for (const k of Object.keys(v)) {
+ props.push(`${inspect(k)}: ${inspect(v[k])}`);
+ }
+ const body = props.length ? " " + props.join(", ") + " " : "";
+ const head = name === "Object" ? "" : name + " ";
+ return `//js(${head}{${body}})`;
+}
+
+function inspectCustomType(record) {
+ const props = Object.keys(record)
+ .map((label) => {
+ const value = inspect(record[label]);
+ return isNaN(parseInt(label)) ? `${label}: ${value}` : value;
+ })
+ .join(", ");
+ return props
+ ? `${record.constructor.name}(${props})`
+ : record.constructor.name;
+}
+
+export function inspectList(list) {
+ return `[${list.toArray().map(inspect).join(", ")}]`;
+}
+
+export function inspectBitArray(bits) {
+ return `<<${Array.from(bits.buffer).join(", ")}>>`;
+}
+
+export function inspectUtfCodepoint(codepoint) {
+ return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`;
+}
+
+export function base16_encode(bit_array) {
+ let result = "";
+ for (const byte of bit_array.buffer) {
+ result += byte.toString(16).padStart(2, "0").toUpperCase();
+ }
+ return result;
+}
+
+export function base16_decode(string) {
+ const bytes = new Uint8Array(string.length / 2);
+ for (let i = 0; i < string.length; i += 2) {
+ const a = parseInt(string[i], 16);
+ const b = parseInt(string[i + 1], 16);
+ if (isNaN(a) || isNaN(b)) return new Error(Nil);
+ bytes[i / 2] = a * 16 + b;
+ }
+ return new Ok(new BitArray(bytes));
+}