1
0

Initial commit

This commit is contained in:
2025-12-16 20:39:11 +00:00
commit baa87fb2fb
70 changed files with 3895 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

11
README.md Normal file
View File

@@ -0,0 +1,11 @@
# test-kata-machine
Kata machine practice.
https://frontendmasters.com/courses/algorithms/
https://github.com/ThePrimeagen/kata-machine
1. `pnpm install`
2. `pnpm test`

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "test-kata-machine",
"version": "0.0.1",
"scripts": {
"test": "vitest",
"format": "prettier --write ./src"
},
"devDependencies": {
"vite": "^7.0.5",
"vitest": "^3.2.4"
},
"dependencies": {
"prettier": "^3.6.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsconfig-paths-jest": "^0.0.1",
"typescript": "^5.8.3"
}
}

1666
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

8
prettier.config.ts Normal file
View File

@@ -0,0 +1,8 @@
export default {
tabWidth: 4,
printWidth: 80,
proseWrap: "never",
trailingComma: "all",
singleQuote: false,
semi: true,
};

101
src/array-test.ts Normal file
View File

@@ -0,0 +1,101 @@
const a: number[] = [];
function time(fn: () => void): number {
const now = Date.now();
fn();
return Date.now() - now;
}
function unshift(number: number) {
for (let i = 0; i < number; ++i) {
a.unshift(Math.random());
}
}
function shift(number: number) {
for (let i = 0; i < number; ++i) {
a.shift();
}
}
function push(number: number) {
for (let i = 0; i < number; ++i) {
a.push(Math.random());
}
}
function pop(number: number) {
for (let i = 0; i < number; ++i) {
a.pop();
}
}
function get(idx: number) {
return function () {
return a[idx];
};
}
function push_arr(count: number) {
return function () {
push(count);
};
}
function pop_arr(count: number) {
return function () {
pop(count);
};
}
function unshift_arr(count: number) {
return function () {
unshift(count);
};
}
function shift_arr(count: number) {
return function () {
shift(count);
};
}
const tests = [10, 100, 1000, 10000, 100000, 1_000_000, 10_000_000];
console.log("Testing get");
tests.forEach((t) => {
a.length = 0;
push(t);
console.log(t, time(get(t - 1)));
});
console.log("push");
tests.forEach((t) => {
a.length = 0;
push(t);
console.log(t, time(push_arr(1000)));
});
console.log("pop");
tests.forEach((t) => {
a.length = 0;
push(t);
console.log(t, time(pop_arr(1000)));
});
console.log("unshift");
tests.forEach((t) => {
a.length = 0;
push(t);
console.log(t, time(unshift_arr(1000)));
});
console.log("shift");
tests.forEach((t) => {
a.length = 0;
push(t);
console.log(t, time(shift_arr(1000)));
});

View File

@@ -0,0 +1,7 @@
import ArrayList from "@code/ArrayList";
import { test_list } from "./ListTest";
test("array-list", function () {
const list = new ArrayList<number>(3);
test_list(list);
});

93
src/day1/ArrayList.ts Normal file
View File

@@ -0,0 +1,93 @@
export default class ArrayList<T> {
public length: number;
private array: Array<T>;
private capacity: number;
constructor(capacity: number = 5) {
this.capacity = capacity;
this.array = [];
this.array.length = this.capacity;
this.length = 0;
}
private grow() {
const old = this.array;
this.capacity *= 2;
this.array = [];
this.array.length = this.capacity;
let i = 0;
for (; i < this.length; ++i) {
this.array[i] = old[i];
}
}
// ShiftRight manages the size of the array and shifts everything
// from array[idx] one to the right. Creating free space at array[idx].
private shiftRight(idx: number) {
if (this.length == this.capacity) {
this.grow();
}
let i = this.length;
for (; i > idx; --i) {
this.array[i] = this.array[i - 1];
}
this.length++;
}
// ShiftLeft manages the size of the array and shifts everything to the
// right of array[idx] one to the left. Overwriting array[idx].
private ShiftLeft(idx: number) {
this.length--;
let i = idx;
for (; i < this.length; ++i) {
this.array[i] = this.array[i + 1];
}
}
prepend(item: T): void {
this.shiftRight(0);
this.array[0] = item;
}
insertAt(item: T, idx: number): void {
this.shiftRight(idx);
this.array[idx] = item;
}
append(item: T): void {
if (this.length == this.capacity) {
this.grow();
}
this.array[this.length] = item;
this.length++;
}
remove(item: T): T | undefined {
let i = 0;
for (; i < this.length; ++i) {
if (this.array[i] == item) {
break;
}
}
// item was not found
if (i == this.length) {
return undefined;
}
return this.removeAt(i);
}
get(idx: number): T | undefined {
return idx >= this.length ? undefined : this.array[idx];
}
removeAt(idx: number): T | undefined {
const item = this.get(idx);
this.ShiftLeft(idx);
return item;
}
}

View File

@@ -0,0 +1,8 @@
import bfs from "@code/BFSGraphList";
import { list2 } from "./graph";
test("bfs - graph", function () {
expect(bfs(list2, 0, 6)).toEqual([0, 1, 4, 5, 6]);
expect(bfs(list2, 6, 0)).toEqual(null);
});

45
src/day1/BFSGraphList.ts Normal file
View File

@@ -0,0 +1,45 @@
export default function bfs(
graph: WeightedAdjacencyList,
source: number,
needle: number,
): number[] | null {
const seen = new Array(graph.length).fill(false);
const prev = new Array(graph.length).fill(-1);
const queue: number[] = [source];
seen[source] = true;
while (queue.length > 0) {
const curr = queue.shift() as number;
if (curr === needle) {
break;
}
for (const edge of graph[curr]) {
const neighbor = edge.to;
if (seen[neighbor]) {
continue;
}
seen[neighbor] = true;
prev[neighbor] = curr;
queue.push(neighbor);
}
}
if (!seen[needle]) {
return null;
}
const path: number[] = [];
let curr = needle;
while (curr !== -1) {
path.push(curr);
curr = prev[curr];
}
return path.reverse();
}

View File

@@ -0,0 +1,8 @@
import bfs from "@code/BFSGraphMatrix";
import { matrix2 } from "./graph";
test("bfs - graph matrix", function () {
expect(bfs(matrix2, 0, 6)).toEqual([0, 1, 4, 5, 6]);
expect(bfs(matrix2, 6, 0)).toEqual(null);
});

View File

@@ -0,0 +1,43 @@
export default function bfs(
graph: number[][],
source: number,
needle: number,
): number[] | null {
const seen = new Array(graph.length).fill(false);
const prev = new Array(graph.length).fill(-1);
const q: number[] = [source];
seen[source] = true;
do {
const curr = q.shift() as number;
if (curr === needle) {
break;
}
const adjs = graph[curr];
for (let i = 0; i < graph.length; i++) {
if (adjs[i] === 0 || seen[i]) {
continue;
}
seen[i] = true;
prev[i] = curr;
q.push(i);
}
} while (q.length);
if (prev[needle] === -1) {
return null;
}
let curr = needle;
const out: number[] = [];
while (prev[curr] !== -1) {
out.push(curr);
curr = prev[curr];
}
return [source].concat(out.reverse());
}

8
src/day1/BTBFS.test.ts Normal file
View File

@@ -0,0 +1,8 @@
import bfs from "@code/BTBFS";
import { tree } from "./tree";
test("bt bfs", function () {
expect(bfs(tree, 45)).toEqual(true);
expect(bfs(tree, 7)).toEqual(true);
expect(bfs(tree, 69)).toEqual(false);
});

20
src/day1/BTBFS.ts Normal file
View File

@@ -0,0 +1,20 @@
export default function bfs(head: BinaryNode<number>, needle: number): boolean {
const q: (BinaryNode<number> | null)[] = [head];
while (q.length) {
const curr = q.shift() as BinaryNode<number> | undefined | null;
if (!curr) {
continue;
}
// search
if (curr.value === needle) {
return true;
}
q.push(curr.left);
q.push(curr.right);
}
return false;
}

View File

@@ -0,0 +1,6 @@
import bt_in_order from "@code/BTInOrder";
import { tree } from "./tree";
test("In order", function () {
expect(bt_in_order(tree)).toEqual([5, 7, 10, 15, 20, 29, 30, 45, 50, 100]);
});

17
src/day1/BTInOrder.ts Normal file
View File

@@ -0,0 +1,17 @@
function walk(curr: BinaryNode<number> | null, path: number[]): number[] {
if (!curr) {
return path;
}
// recurse
walk(curr.left, path);
path.push(curr.value);
walk(curr.right, path);
// post
return path;
}
export default function in_order_search(head: BinaryNode<number>): number[] {
return walk(head, []);
}

View File

@@ -0,0 +1,8 @@
import bt_post_order from "@code/BTPostOrder";
import { tree } from "./tree";
test("post order", function () {
expect(bt_post_order(tree)).toEqual([
7, 5, 15, 10, 29, 45, 30, 100, 50, 20,
]);
});

17
src/day1/BTPostOrder.ts Normal file
View File

@@ -0,0 +1,17 @@
function walk(curr: BinaryNode<number> | null, path: number[]): number[] {
if (!curr) {
return path;
}
// recurse
walk(curr.left, path);
walk(curr.right, path);
// post
path.push(curr.value);
return path;
}
export default function post_order_search(head: BinaryNode<number>): number[] {
return walk(head, []);
}

View File

@@ -0,0 +1,6 @@
import bt_pre_order from "@code/BTPreOrder";
import { tree } from "./tree";
test("Pre order", function () {
expect(bt_pre_order(tree)).toEqual([20, 10, 5, 7, 15, 50, 30, 29, 45, 100]);
});

19
src/day1/BTPreOrder.ts Normal file
View File

@@ -0,0 +1,19 @@
function walk(curr: BinaryNode<number> | null, path: number[]): number[] {
if (!curr) {
return path;
}
// pre
path.push(curr.value);
// recurse
walk(curr.left, path);
walk(curr.right, path);
// post
return path;
}
export default function pre_order_search(head: BinaryNode<number>): number[] {
return walk(head, []);
}

View File

@@ -0,0 +1,11 @@
import binary_fn from "@code/BinarySearchList";
test("binary search array", function () {
const foo = [1, 3, 4, 69, 71, 81, 90, 99, 420, 1337, 69420];
expect(binary_fn(foo, 69)).toEqual(true);
expect(binary_fn(foo, 1336)).toEqual(false);
expect(binary_fn(foo, 69420)).toEqual(true);
expect(binary_fn(foo, 69421)).toEqual(false);
expect(binary_fn(foo, 1)).toEqual(true);
expect(binary_fn(foo, 0)).toEqual(false);
});

View File

@@ -0,0 +1,19 @@
export default function bs_list(haystack: number[], needle: number): boolean {
let lo = 0;
let hi = haystack.length;
while (lo < hi) {
const m = Math.floor(lo + (hi - lo) / 2);
const v = haystack[m];
if (v === needle) {
return true;
} else if (v > needle) {
hi = m;
} else {
lo = m + 1;
}
}
return false;
}

View File

@@ -0,0 +1,9 @@
import bubble_sort from "@code/BubbleSort";
test("bubble-sort", function () {
const arr = [9, 3, 7, 4, 69, 420, 42];
debugger;
bubble_sort(arr);
expect(arr).toEqual([3, 4, 7, 9, 42, 69, 420]);
});

11
src/day1/BubbleSort.ts Normal file
View File

@@ -0,0 +1,11 @@
export default function bubble_sort(arr: number[]): void {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
const tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}

View File

@@ -0,0 +1,7 @@
import compare from "@code/CompareBinaryTrees";
import { tree, tree2 } from "./tree";
test("Compare Binary Trees", function () {
expect(compare(tree, tree)).toEqual(true);
expect(compare(tree, tree2)).toEqual(false);
});

View File

@@ -0,0 +1,21 @@
export default function compare(
a: BinaryNode<number> | null,
b: BinaryNode<number> | null,
): boolean {
// structual check
if (a === null && b === null) {
return true;
}
// structual check
if (a === null || b === null) {
return false;
}
// value check
if (a?.value !== b?.value) {
return false;
}
return compare(a.left, b.left) && compare(a.right, b.right);
}

View File

@@ -0,0 +1,8 @@
import dfs from "@code/DFSGraphList";
import { list2 } from "./graph";
test("dfs - graph", function () {
expect(dfs(list2, 0, 6)).toEqual([0, 1, 4, 5, 6]);
expect(dfs(list2, 6, 0)).toEqual(null);
});

50
src/day1/DFSGraphList.ts Normal file
View File

@@ -0,0 +1,50 @@
function walk(
graph: WeightedAdjacencyList,
curr: number,
needle: number,
seen: boolean[],
path: number[],
): boolean {
if (seen[curr]) {
return false;
}
seen[curr] = true;
// pre
path.push(curr);
if (curr === needle) {
return true;
}
// recurse
const list = graph[curr];
for (let i = 0; i < list.length; i++) {
const edge = list[i];
if (walk(graph, edge.to, needle, seen, path)) {
return true;
}
}
// post
path.pop();
return false;
}
export default function dfs(
graph: WeightedAdjacencyList,
source: number,
needle: number,
): number[] | null {
const seen: boolean[] = new Array(graph.length).fill(false);
const path: number[] = [];
walk(graph, source, needle, seen, path);
if (path.length === 0) {
return null;
}
return path;
}

View File

@@ -0,0 +1,8 @@
import dfs from "@code/DFSOnBST";
import { tree } from "./tree";
test("DFS on BST", function () {
expect(dfs(tree, 45)).toEqual(true);
expect(dfs(tree, 7)).toEqual(true);
expect(dfs(tree, 69)).toEqual(false);
});

19
src/day1/DFSOnBST.ts Normal file
View File

@@ -0,0 +1,19 @@
function search(curr: BinaryNode<number> | null, needle: number): boolean {
if (!curr) {
return false;
}
if (curr.value === needle) {
return true;
}
if (curr.value < needle) {
return search(curr.right, needle);
}
return search(curr.left, needle);
}
export default function dfs(head: BinaryNode<number>, needle: number): boolean {
return search(head, needle);
}

View File

@@ -0,0 +1,9 @@
import dijkstra_list from "@code/DijkstraList";
import { list1 } from "./graph";
test("dijkstra via adj list", function () {
/// waht?
// what..
// what...
expect(dijkstra_list(0, 6, list1)).toEqual([0, 1, 4, 5, 6]);
});

61
src/day1/DijkstraList.ts Normal file
View File

@@ -0,0 +1,61 @@
function hasUnvisited(seen: boolean[], dists: number[]): boolean {
return seen.some((s, i) => !s && dists[i] < Infinity);
}
function getLowestUnvisited(seen: boolean[], dists: number[]): number {
let idx = -1;
let lowestDistance = Infinity;
for (let i = 0; i < seen.length; i++) {
if (seen[i]) {
continue;
}
if (lowestDistance > dists[i]) {
lowestDistance = dists[i];
idx = i;
}
}
return idx;
}
export default function dijkstra_list(
source: number,
sink: number,
arr: WeightedAdjacencyList,
): number[] {
const seen = new Array(arr.length).fill(false);
const prev = new Array(arr.length).fill(-1);
const dists = new Array(arr.length).fill(Infinity);
dists[source] = 0;
while (hasUnvisited(seen, dists)) {
const curr = getLowestUnvisited(seen, dists);
seen[curr] = true;
const adjs = arr[curr];
for (let i = 0; i < adjs.length; i++) {
const edge = adjs[i];
if (seen[edge.to]) {
continue;
}
const dist = dists[curr] + edge.weight;
if (dist < dists[edge.to]) {
dists[edge.to] = dist;
prev[edge.to] = curr;
}
}
}
let curr = sink;
const out: number[] = [];
while (prev[curr] !== -1) {
out.push(curr);
curr = prev[curr];
}
return [source].concat(out.reverse());
}

View File

@@ -0,0 +1,7 @@
import LinkedList from "@code/DoublyLinkedList";
import { test_list } from "./ListTest";
test("DoublyLinkedList", function () {
const list = new LinkedList<number>();
test_list(list);
});

View File

@@ -0,0 +1,136 @@
class Node<T> {
public value: T;
public prev?: Node<T>;
public next?: Node<T>;
}
export default class DoublyLinkedList<T> {
public length: number;
public head?: Node<T>;
public tail?: Node<T>;
constructor() {
this.length = 0;
this.head = undefined;
}
prepend(item: T): void {
const node = { value: item } as Node<T>;
this.length++;
if (!this.head) {
this.head = node;
this.tail = node;
return;
}
node.next = this.head;
this.head.prev = node;
this.head = node;
}
insertAt(item: T, idx: number): void {
if (idx > this.length) {
throw new Error("oh no");
} else if (idx === this.length) {
this.append(item);
return;
} else if (idx === 0) {
this.prepend(item);
}
this.length++;
const curr = this.getAt(idx) as Node<T>;
const node = { value: item } as Node<T>;
node.next = curr;
node.prev = curr.prev;
curr.prev = node;
if (node.prev) {
node.prev.next = node;
}
}
append(item: T): void {
const node = { value: item } as Node<T>;
this.length++;
if (!this.tail) {
this.head = node;
this.tail = node;
return;
}
node.prev = this.tail;
this.tail.next = node;
this.tail = node;
}
remove(item: T): T | undefined {
let curr = this.head;
for (let i = 0; curr && i < this.length; i++) {
if (curr.value === item) {
break;
}
curr = curr.next;
}
if (!curr) {
return undefined;
}
return this.removeNode(curr);
}
get(idx: number): T | undefined {
return this.getAt(idx)?.value;
}
removeAt(idx: number): T | undefined {
const node = this.getAt(idx);
if (!node) {
return undefined;
}
return this.removeNode(node);
}
private removeNode(node: Node<T>): T | undefined {
this.length--;
if (this.length === 0) {
const out = this.head?.value;
this.head = undefined;
this.tail = undefined;
return out;
}
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (node === this.head) {
this.head = node.next;
}
if (node === this.tail) {
this.tail = node.prev;
}
node.prev = undefined;
node.next = undefined;
return node.value;
}
private getAt(idx: number): Node<T> | undefined {
let curr = this.head;
for (let i = 0; curr && i < idx; i++) {
curr = curr.next;
}
return curr;
}
}

View File

@@ -0,0 +1,9 @@
import insertion_sort from "@code/InsertionSort";
test("insertion-sort", function () {
const arr = [9, 3, 7, 4, 69, 420, 42];
debugger;
// where is my debugger
insertion_sort(arr);
expect(arr).toEqual([3, 4, 7, 9, 42, 69, 420]);
});

13
src/day1/InsertionSort.ts Normal file
View File

@@ -0,0 +1,13 @@
export default function insertion_sort(arr: number[]): void {
for (let i = 1; i < arr.length; i++) {
const key = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}

27
src/day1/LRU.test.ts Normal file
View File

@@ -0,0 +1,27 @@
import LRU from "@code/LRU";
test("LRU", function () {
const lru = new LRU<string, number>(3) as ILRU<string, number>;
expect(lru.get("foo")).toEqual(undefined);
lru.update("foo", 69);
expect(lru.get("foo")).toEqual(69);
lru.update("bar", 420);
expect(lru.get("bar")).toEqual(420);
lru.update("baz", 1337);
expect(lru.get("baz")).toEqual(1337);
lru.update("ball", 69420);
expect(lru.get("ball")).toEqual(69420);
expect(lru.get("foo")).toEqual(undefined);
expect(lru.get("bar")).toEqual(420);
lru.update("foo", 69);
expect(lru.get("bar")).toEqual(420);
expect(lru.get("foo")).toEqual(69);
// shouldn't of been deleted, but since bar was get'd, bar was added to the
// front of the list, so baz became the end
expect(lru.get("baz")).toEqual(undefined);
});

99
src/day1/LRU.ts Normal file
View File

@@ -0,0 +1,99 @@
function createNode<V>(value: V): Node<V> {
return { value };
}
export default class LRU<K, V> {
private length: number;
private capacity: number;
private head?: Node<V>;
private tail?: Node<V>;
private lookup: Map<K, Node<V>>;
private reverseLookup: Map<Node<V>, K>;
constructor(capacity: number) {
this.length = 0;
this.capacity = capacity;
this.head = this.tail = undefined;
this.lookup = new Map<K, Node<V>>();
this.reverseLookup = new Map<Node<V>, K>();
}
update(key: K, value: V): void {
// check the cache for existence
let node = this.lookup.get(key);
if (!node) {
node = createNode(value);
this.length++;
this.prepend(node);
this.trimCache();
this.lookup.set(key, node);
this.reverseLookup.set(node, key);
} else {
this.detach(node);
this.prepend(node);
node.value = value;
}
}
get(key: K): V | undefined {
// check the cache for existence
const node = this.lookup.get(key);
if (!node) {
return undefined;
}
// update the value we found and move it to the front
this.detach(node);
this.prepend(node);
// return out the value found
return node.value;
}
private detach(node: Node<V>): void {
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (this.head === node) {
this.head = this.head.next;
}
if (this.tail === node) {
this.tail = this.tail.prev;
}
node.next = undefined;
node.prev = undefined;
}
private prepend(node: Node<V>): void {
if (!this.head) {
this.head = this.tail = node;
return;
}
node.next = this.head;
this.head.prev = node;
this.head = node;
}
private trimCache(): void {
if (this.length <= this.capacity) {
return;
}
const tail = this.tail as Node<V>;
this.detach(this.tail as Node<V>);
const key = this.reverseLookup.get(tail) as K;
this.lookup.delete(key);
this.reverseLookup.delete(tail);
this.length--;
}
}

View File

@@ -0,0 +1,11 @@
import linear_fn from "@code/LinearSearchList";
test("linear search array", function () {
const foo = [1, 3, 4, 69, 71, 81, 90, 99, 420, 1337, 69420];
expect(linear_fn(foo, 69)).toEqual(true);
expect(linear_fn(foo, 1336)).toEqual(false);
expect(linear_fn(foo, 69420)).toEqual(true);
expect(linear_fn(foo, 69421)).toEqual(false);
expect(linear_fn(foo, 1)).toEqual(true);
expect(linear_fn(foo, 0)).toEqual(false);
});

View File

@@ -0,0 +1,12 @@
export default function linear_search(
haystack: number[],
needle: number,
): boolean {
for (let i = 0; i < haystack.length; i++) {
if (haystack[i] === needle) {
return true;
}
}
return false;
}

26
src/day1/ListTest.ts Normal file
View File

@@ -0,0 +1,26 @@
export function test_list(list: List<number>): void {
list.append(5);
list.append(7);
list.append(9);
expect(list.get(2)).toEqual(9);
expect(list.removeAt(1)).toEqual(7);
expect(list.length).toEqual(2);
list.append(11);
expect(list.removeAt(1)).toEqual(9);
expect(list.remove(9)).toEqual(undefined);
expect(list.removeAt(0)).toEqual(5);
expect(list.removeAt(0)).toEqual(11);
expect(list.length).toEqual(0);
list.prepend(5);
list.prepend(7);
list.prepend(9);
expect(list.get(2)).toEqual(5);
expect(list.get(0)).toEqual(9);
expect(list.remove(9)).toEqual(9);
expect(list.length).toEqual(2);
expect(list.get(0)).toEqual(7);
}

23
src/day1/Map.test.ts Normal file
View File

@@ -0,0 +1,23 @@
import MyMap from "@code/Map";
test("Map", function () {
const map = new MyMap<string, number>();
map.set("foo", 55);
expect(map.size()).toEqual(1);
map.set("fool", 75);
expect(map.size()).toEqual(2);
map.set("foolish", 105);
expect(map.size()).toEqual(3);
map.set("bar", 69);
expect(map.size()).toEqual(4);
expect(map.get("bar")).toEqual(69);
expect(map.get("blaz")).toEqual(undefined);
map.delete("barblabr");
expect(map.size()).toEqual(4);
map.delete("bar");
expect(map.size()).toEqual(3);
expect(map.get("bar")).toEqual(undefined);
});

34
src/day1/Map.ts Normal file
View File

@@ -0,0 +1,34 @@
export default class Map<T extends string | number, V> {
private store: { [key: string]: V };
private count: number;
constructor() {
this.store = {};
this.count = 0;
}
get(key: T): V | undefined {
return this.store.hasOwnProperty(key) ? this.store[key] : undefined;
}
set(key: T, value: V): void {
if (!this.store.hasOwnProperty(key)) {
this.count++;
}
this.store[key] = value;
}
delete(key: T): V | undefined {
if (this.store.hasOwnProperty(key)) {
const value = this.store[key];
delete this.store[key];
this.count--;
return value;
}
return undefined;
}
size(): number {
return this.count;
}
}

View File

@@ -0,0 +1,44 @@
import maze_solver from "@code/MazeSolver";
test("maze solver", function () {
const maze = [
"xxxxxxxxxx x",
"x x x",
"x x x",
"x xxxxxxxx x",
"x x",
"x xxxxxxxxxx",
];
const mazeResult = [
{ x: 10, y: 0 },
{ x: 10, y: 1 },
{ x: 10, y: 2 },
{ x: 10, y: 3 },
{ x: 10, y: 4 },
{ x: 9, y: 4 },
{ x: 8, y: 4 },
{ x: 7, y: 4 },
{ x: 6, y: 4 },
{ x: 5, y: 4 },
{ x: 4, y: 4 },
{ x: 3, y: 4 },
{ x: 2, y: 4 },
{ x: 1, y: 4 },
{ x: 1, y: 5 },
];
// there is only one path through
const result = maze_solver(maze, "x", { x: 10, y: 0 }, { x: 1, y: 5 });
expect(drawPath(maze, result)).toEqual(drawPath(maze, mazeResult));
});
function drawPath(data: string[], path: Point[]) {
const data2 = data.map((row) => row.split(""));
path.forEach((p) => {
if (data2[p.y] && data2[p.y][p.x]) {
data2[p.y][p.x] = "*";
}
});
return data2.map((d) => d.join(""));
}

57
src/day1/MazeSolver.ts Normal file
View File

@@ -0,0 +1,57 @@
export default function solve(
maze: string[],
wall: string,
start: Point,
end: Point,
): Point[] {
const numRows = maze.length;
const numCols = maze[0].length;
const visited = new Set<string>();
const queue: { point: Point; path: Point[] }[] = [];
function isValid(x: number, y: number): boolean {
return (
x >= 0 &&
x < numCols &&
y >= 0 &&
y < numRows &&
maze[y][x] !== wall
);
}
const key = (p: Point) => `${p.x},${p.y}`;
queue.push({ point: start, path: [start] });
visited.add(key(start));
const directions = [
// up
{ x: 0, y: -1 },
// right
{ x: 1, y: 0 },
// down
{ x: 0, y: 1 },
// left
{ x: -1, y: 0 },
];
while (queue.length > 0) {
const { point, path } = queue.shift()!;
if (point.x === end.x && point.y === end.y) {
return path;
}
for (const dir of directions) {
const next = { x: point.x + dir.x, y: point.y + dir.y };
const nextKey = key(next);
if (isValid(next.x, next.y) && !visited.has(nextKey)) {
visited.add(nextKey);
queue.push({ point: next, path: [...path, next] });
}
}
}
// No path found
return [];
}

View File

@@ -0,0 +1,7 @@
import merge_sort from "@code/MergeSort";
test("merge-sort", function () {
const arr = [9, 3, 7, 4, 69, 420, 42];
merge_sort(arr);
expect(arr).toEqual([3, 4, 7, 9, 42, 69, 420]);
});

36
src/day1/MergeSort.ts Normal file
View File

@@ -0,0 +1,36 @@
function mergeSortHelper(arr: number[], start: number, end: number): void {
if (start >= end) {
return;
}
const mid = Math.floor((start + end) / 2);
mergeSortHelper(arr, start, mid);
mergeSortHelper(arr, mid + 1, end);
merge(arr, start, mid, end);
}
function merge(arr: number[], start: number, mid: number, end: number): void {
const left = arr.slice(start, mid + 1);
const right = arr.slice(mid + 1, end + 1);
let i = 0;
let j = 0;
let k = start;
while (i < left.length && j < right.length) {
arr[k++] = left[i] <= right[j] ? left[i++] : right[j++];
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
export default function merge_sort(arr: number[]): void {
mergeSortHelper(arr, 0, arr.length - 1);
}

28
src/day1/MinHeap.test.ts Normal file
View File

@@ -0,0 +1,28 @@
import MinHeap from "@code/MinHeap";
test("min heap", function () {
const heap = new MinHeap();
expect(heap.length).toEqual(0);
heap.insert(5);
heap.insert(3);
heap.insert(69);
heap.insert(420);
heap.insert(4);
heap.insert(1);
heap.insert(8);
heap.insert(7);
expect(heap.length).toEqual(8);
expect(heap.delete()).toEqual(1);
expect(heap.delete()).toEqual(3);
expect(heap.delete()).toEqual(4);
expect(heap.delete()).toEqual(5);
expect(heap.length).toEqual(4);
expect(heap.delete()).toEqual(7);
expect(heap.delete()).toEqual(8);
expect(heap.delete()).toEqual(69);
expect(heap.delete()).toEqual(420);
expect(heap.length).toEqual(0);
});

89
src/day1/MinHeap.ts Normal file
View File

@@ -0,0 +1,89 @@
export default class MinHeap {
public length: number;
private data: number[];
constructor() {
this.data = [];
this.length = 0;
}
insert(value: number): void {
this.data[this.length] = value;
this.heapifyUp(this.length);
this.length++;
}
delete(): number {
if (this.length === 0) {
return -1;
}
const out = this.data[0];
this.length--;
if (this.length === 0) {
this.data = [];
return out;
}
this.data[0] = this.data[this.length];
this.heapifyDown(0);
return out;
}
private heapifyDown(idx: number): void {
const lIdx = this.leftChild(idx);
const rIdx = this.rightChild(idx);
if (idx >= this.length || lIdx >= this.length) {
return;
}
const lV = this.data[lIdx];
const rV = this.data[rIdx];
const v = this.data[idx];
if (lV > rV && v > rV) {
this.data[idx] = rV;
this.data[rIdx] = v;
this.heapifyDown(rIdx);
} else if (rV > lV && v > lV) {
this.data[idx] = lV;
this.data[lIdx] = v;
this.heapifyDown(lIdx);
} else if (lV == rV && v > rV) {
this.data[idx] = rV;
this.data[rIdx] = v;
this.heapifyDown(rIdx);
}
}
private heapifyUp(idx: number): void {
if (idx === 0) {
return;
}
const p = this.parent(idx);
const parentV = this.data[p];
const v = this.data[idx];
if (parentV > v) {
this.data[idx] = parentV;
this.data[p] = v;
this.heapifyUp(p);
}
}
private parent(idx: number): number {
return Math.floor((idx - 1) / 2);
}
private leftChild(idx: number): number {
return idx * 2 + 1;
}
private rightChild(idx: number): number {
return idx * 2 + 2;
}
}

View File

@@ -0,0 +1,58 @@
export default function prims(
list: WeightedAdjacencyList,
): WeightedAdjacencyList | null {
const n = list.length;
if (n === 0) {
return null;
}
const inMST = new Array(n).fill(false);
const mst: WeightedAdjacencyList = Array.from({ length: n }, () => []);
const minEdge: { to: number; from: number; weight: number }[] = new Array(
n,
).fill(null as any);
minEdge[0] = { to: 0, from: -1, weight: 0 };
for (let i = 0; i < n; i++) {
let u = -1;
for (let v = 0; v < n; v++) {
if (
!inMST[v] &&
(u === -1 ||
(minEdge[v] && minEdge[v].weight < minEdge[u].weight))
) {
u = v;
}
}
if (u === -1 || minEdge[u] === null) {
return null;
}
inMST[u] = true;
const edge = minEdge[u];
if (edge.from !== -1) {
mst[edge.from].push({ to: u, weight: edge.weight });
mst[u].push({ to: edge.from, weight: edge.weight });
}
for (const neighbor of list[u]) {
if (
!inMST[neighbor.to] &&
(!minEdge[neighbor.to] ||
neighbor.weight < minEdge[neighbor.to].weight)
) {
minEdge[neighbor.to] = {
to: neighbor.to,
from: u,
weight: neighbor.weight,
};
}
}
}
return mst;
}

View File

@@ -0,0 +1,30 @@
import prims from "@code/PrimsAlgorithm";
import { list1 } from "./graph";
test("PrimsAlgorithm", function () {
// there is only one right answer for this graph
expect(prims(list1)).toEqual([
[
{ to: 2, weight: 1 },
{ to: 1, weight: 3 },
],
[
{ to: 0, weight: 3 },
{ to: 4, weight: 1 },
],
[{ to: 0, weight: 1 }],
[{ to: 6, weight: 1 }],
[
{ to: 1, weight: 1 },
{ to: 5, weight: 2 },
],
[
{ to: 4, weight: 2 },
{ to: 6, weight: 1 },
],
[
{ to: 5, weight: 1 },
{ to: 3, weight: 1 },
],
]);
});

31
src/day1/Queue.test.ts Normal file
View File

@@ -0,0 +1,31 @@
import Queue from "@code/Queue";
test("queue", function () {
const list = new Queue<number>();
list.enqueue(5);
list.enqueue(7);
list.enqueue(9);
expect(list.deque()).toEqual(5);
expect(list.length).toEqual(2);
// this must be wrong..?
debugger;
// i hate using debuggers
list.enqueue(11);
debugger;
expect(list.deque()).toEqual(7);
expect(list.deque()).toEqual(9);
expect(list.peek()).toEqual(11);
expect(list.deque()).toEqual(11);
expect(list.deque()).toEqual(undefined);
expect(list.length).toEqual(0);
// just wanted to make sure that I could not blow up myself when i remove
// everything
list.enqueue(69);
expect(list.peek()).toEqual(69);
expect(list.length).toEqual(1);
});

50
src/day1/Queue.ts Normal file
View File

@@ -0,0 +1,50 @@
type Node<T> = {
value: T;
next?: Node<T>;
};
export default class Queue<T> {
public length: number;
private head?: Node<T>;
private tail?: Node<T>;
constructor() {
this.head = undefined;
this.tail = undefined;
this.length = 0;
}
enqueue(item: T): void {
const node = { value: item } as Node<T>;
this.length++;
if (!this.tail) {
this.head = node;
this.tail = node;
return;
}
this.tail.next = node;
this.tail = node;
}
deque(): T | undefined {
if (!this.head) {
return undefined;
}
this.length--;
const head = this.head;
this.head = this.head.next;
head.next = undefined;
if (this.length === 0) {
this.tail = undefined;
}
return head.value;
}
peek(): T | undefined {
return this.head?.value;
}
}

View File

@@ -0,0 +1,9 @@
import quick_sort from "@code/QuickSort";
test("quick-sort", function () {
const arr = [9, 3, 7, 4, 69, 420, 42];
debugger;
quick_sort(arr);
expect(arr).toEqual([3, 4, 7, 9, 42, 69, 420]);
});

34
src/day1/QuickSort.ts Normal file
View File

@@ -0,0 +1,34 @@
function qs(arr: number[], lo: number, hi: number): void {
if (lo >= hi) {
return;
}
const pivotIdx = partition(arr, lo, hi);
qs(arr, lo, pivotIdx - 1);
qs(arr, pivotIdx + 1, hi);
}
function partition(arr: number[], lo: number, hi: number): number {
const pivot = arr[hi];
let idx = lo - 1;
for (let i = lo; i < hi; i++) {
if (arr[i] <= pivot) {
idx++;
const tmp = arr[i];
arr[i] = arr[idx];
arr[idx] = tmp;
}
}
idx++;
arr[hi] = arr[idx];
arr[idx] = pivot;
return idx;
}
export default function quick_sort(arr: number[]): void {
qs(arr, 0, arr.length - 1);
}

View File

@@ -0,0 +1,22 @@
import RingBuffer from "@code/RingBuffer";
test("RingBuffer", function () {
const buffer = new RingBuffer<number>();
buffer.push(5);
expect(buffer.pop()).toEqual(5);
expect(buffer.pop()).toEqual(undefined);
buffer.push(42);
buffer.push(9);
expect(buffer.pop()).toEqual(42);
expect(buffer.pop()).toEqual(9);
expect(buffer.pop()).toEqual(undefined);
buffer.push(42);
buffer.push(9);
buffer.push(12);
expect(buffer.get(2)).toEqual(12);
expect(buffer.get(1)).toEqual(9);
expect(buffer.get(0)).toEqual(42);
});

59
src/day1/RingBuffer.ts Normal file
View File

@@ -0,0 +1,59 @@
export default class RingBuffer<T> {
private items: T[] = [];
private start: number = 0;
private end: number = 0;
private count: number = 0;
constructor(initialCapacity: number = 4) {
this.items = new Array<T>(initialCapacity);
}
public get length(): number {
return this.count;
}
push(item: T): void {
if (this.count === this.items.length) {
this.resize();
}
this.items[this.end] = item;
this.end = (this.end + 1) % this.items.length;
this.count++;
}
pop(): T | undefined {
if (this.count === 0) {
return undefined;
}
const item = this.items[this.start];
this.items[this.start] = undefined as any;
this.start = (this.start + 1) % this.items.length;
this.count--;
return item;
}
get(idx: number): T | undefined {
if (idx < 0 || idx >= this.count) {
return undefined;
}
const realIndex = (this.start + idx) % this.items.length;
return this.items[realIndex];
}
private resize(): void {
const newCapacity = this.items.length * 2 || 4;
const newItems = new Array<T>(newCapacity);
for (let i = 0; i < this.count; i++) {
newItems[i] = this.get(i)!;
}
this.items = newItems;
this.start = 0;
this.end = this.count;
}
}

View File

@@ -0,0 +1,7 @@
import SinglyLinkedList from "@code/SinglyLinkedList";
import { test_list } from "./ListTest";
test("linked-list", function () {
const list = new SinglyLinkedList<number>();
test_list(list);
});

View File

@@ -0,0 +1,138 @@
class Node<T> {
public value: T;
public next?: Node<T>;
constructor(t: T) {
this.value = t;
}
}
export default class SinglyLinkedList<T> {
public length: number;
public head?: Node<T>;
public tail?: Node<T>;
constructor() {
this.length = 0;
this.head = undefined;
}
prepend(item: T): void {
let node = new Node<T>(item);
if (this.head != null) {
node.next = this.head;
}
this.head = node;
this.length++;
}
insertAt(item: T, idx: number): void {
let current_node = this.head;
let i = 0;
while (current_node?.next != null && i < idx) {
current_node = current_node.next;
i += 1;
}
if (current_node?.next != null) {
let node = new Node<T>(item);
node.next = current_node.next;
current_node.next = node;
this.length++;
}
}
append(item: T): void {
if (this.length == 0) {
let node = new Node<T>(item);
this.head = node;
this.length++;
return;
}
let current_node = this.head;
while (current_node?.next != null) {
current_node = current_node.next;
}
if (current_node != null) {
let node = new Node<T>(item);
current_node.next = node;
this.length++;
}
}
remove(item: T): T | undefined {
let current_node = this.head;
let prev_node = this.head;
let node_to_remove = null;
while (current_node != null) {
if (current_node.value === item) {
node_to_remove = current_node;
break;
}
prev_node = current_node;
current_node = current_node.next;
}
if (node_to_remove == this.head) {
this.head = node_to_remove.next;
this.length--;
} else if (prev_node?.next != null) {
prev_node.next = node_to_remove?.next;
this.length--;
}
return current_node?.value;
}
get(idx: number): T | undefined {
let current_node = this.head;
let i = 0;
while (current_node != null && i < idx) {
current_node = current_node.next;
i += 1;
}
return current_node?.value;
}
removeAt(idx: number): T | undefined {
let current_node = this.head;
if (idx == 0 && current_node != null) {
this.head = current_node.next;
this.length--;
return current_node.value;
}
let i = 0;
while (current_node?.next != null && i < idx - 1) {
current_node = current_node.next;
i += 1;
}
if (current_node?.next != null) {
let node_to_remove = current_node.next;
current_node.next = node_to_remove.next;
this.length--;
return node_to_remove.value;
}
return undefined;
}
debug() {
let current_node = this.head;
let result = "";
while (current_node != null) {
result += ` ${current_node.value},`;
current_node = current_node.next;
}
console.log(result);
}
}

25
src/day1/Stack.test.ts Normal file
View File

@@ -0,0 +1,25 @@
import Stack from "@code/Stack";
test("stack", function () {
const list = new Stack<number>();
list.push(5);
list.push(7);
list.push(9);
expect(list.pop()).toEqual(9);
expect(list.length).toEqual(2);
list.push(11);
expect(list.pop()).toEqual(11);
expect(list.pop()).toEqual(7);
expect(list.peek()).toEqual(5);
expect(list.pop()).toEqual(5);
expect(list.pop()).toEqual(undefined);
// just wanted to make sure that I could not blow up myself when i remove
// everything
list.push(69);
expect(list.peek()).toEqual(69);
expect(list.length).toEqual(1);
});

43
src/day1/Stack.ts Normal file
View File

@@ -0,0 +1,43 @@
type Node<T> = {
value: T;
prev?: Node<T>;
};
export default class Stack<T> {
public length: number;
private head?: Node<T>;
constructor() {
this.head = undefined;
this.length = 0;
}
push(item: T): void {
const node = { value: item } as Node<T>;
this.length++;
if (!this.head) {
this.head = node;
return;
}
node.prev = this.head;
this.head = node;
}
pop(): T | undefined {
this.length = Math.max(0, this.length - 1);
if (this.length === 0) {
const head = this.head;
this.head = undefined;
return head?.value;
}
const head = this.head as Node<T>;
this.head = head.prev;
return head?.value;
}
peek(): T | undefined {
return this.head?.value;
}
}

15
src/day1/Trie.test.ts Normal file
View File

@@ -0,0 +1,15 @@
import Trie from "@code/Trie";
test("Trie", function () {
const trie = new Trie();
trie.insert("foo");
trie.insert("fool");
trie.insert("foolish");
trie.insert("bar");
expect(trie.find("fo").sort()).toEqual(["foo", "fool", "foolish"]);
trie.delete("fool");
expect(trie.find("fo").sort()).toEqual(["foo", "foolish"]);
});

86
src/day1/Trie.ts Normal file
View File

@@ -0,0 +1,86 @@
type TrieNode = {
children: TrieNode[];
isValue: boolean;
value?: string;
};
// Creates a new TrieNode with defaults
function createTrieNode() {
return {
children: [],
isValue: false,
};
}
// Char code for 'a'.
const start = "a".charCodeAt(0);
// Returns the distance from 'a' which can be used to index into an array.
function getIdx(item: string, idx: number): number {
return item.toLowerCase().charCodeAt(idx) - start;
}
export default class Trie {
private head: TrieNode;
constructor() {
this.head = createTrieNode();
}
insert(item: string): void {
let current: TrieNode = this.head;
for (let i = 0; i < item.length; ++i) {
const letter = getIdx(item, i);
if (!current.children[letter]) {
current.children[letter] = createTrieNode();
}
current = current.children[letter];
}
current.isValue = true;
current.value = item;
}
delete(item: string): void {
let current: TrieNode = this.head;
for (let i = 0; current && i < item.length; ++i) {
current = current.children[getIdx(item, i)];
}
if (!current) {
return;
}
current.value = undefined;
current.isValue = false;
}
find(partial: string): string[] {
let current = this.head;
for (let i = 0; i < partial.length; ++i) {
current = current.children[getIdx(partial, i)];
}
return this.findRecursively(current, []);
}
// Finds all the possible words to complete node.
private findRecursively(
node: TrieNode | undefined,
out: string[],
): string[] {
if (!node) {
return out;
}
if (node.isValue) {
out.push(node.value as string);
}
for (let i = 0; i < node.children.length; ++i) {
this.findRecursively(node.children[i], out);
}
return out;
}
}

View File

@@ -0,0 +1,13 @@
import two_crystal_balls from "@code/TwoCrystalBalls";
test("two crystal balls", function () {
let idx = Math.floor(Math.random() * 10000);
const data = new Array(10000).fill(false);
for (let i = idx; i < 10000; ++i) {
data[i] = true;
}
expect(two_crystal_balls(data)).toEqual(idx);
expect(two_crystal_balls(new Array(821).fill(false))).toEqual(-1);
});

View File

@@ -0,0 +1,20 @@
export default function two_crystal_balls(breaks: boolean[]): number {
const jmpAmount = Math.floor(Math.sqrt(breaks.length));
let i = jmpAmount;
for (; i < breaks.length; i += jmpAmount) {
if (breaks[i]) {
break;
}
}
i -= jmpAmount;
for (let j = 0; j < jmpAmount && i < breaks.length; j++, i++) {
if (breaks[i]) {
return i;
}
}
return -1;
}

80
src/day1/graph.ts Normal file
View File

@@ -0,0 +1,80 @@
export const list1: WeightedAdjacencyList = [];
// (1) --- (4) ---- (5)
// / | | /|
// (0) | ------|------- |
// \ |/ | |
// (2) --- (3) ---- (6)
list1[0] = [
{ to: 1, weight: 3 },
{ to: 2, weight: 1 },
];
list1[1] = [
{ to: 0, weight: 3 },
{ to: 2, weight: 4 },
{ to: 4, weight: 1 },
];
list1[2] = [
{ to: 1, weight: 4 },
{ to: 3, weight: 7 },
{ to: 0, weight: 1 },
];
list1[3] = [
{ to: 2, weight: 7 },
{ to: 4, weight: 5 },
{ to: 6, weight: 1 },
];
list1[4] = [
{ to: 1, weight: 1 },
{ to: 3, weight: 5 },
{ to: 5, weight: 2 },
];
list1[5] = [
{ to: 6, weight: 1 },
{ to: 4, weight: 2 },
{ to: 2, weight: 18 },
];
list1[6] = [
{ to: 3, weight: 1 },
{ to: 5, weight: 1 },
];
export const list2: WeightedAdjacencyList = [];
// >(1)<--->(4) ---->(5)
// / | /|
// (0) ------|------- |
// \ v v v
// >(2) --> (3) <----(6)
list2[0] = [
{ to: 1, weight: 3 },
{ to: 2, weight: 1 },
];
list2[1] = [{ to: 4, weight: 1 }];
list2[2] = [{ to: 3, weight: 7 }];
list2[3] = [];
list2[4] = [
{ to: 1, weight: 1 },
{ to: 3, weight: 5 },
{ to: 5, weight: 2 },
];
list2[5] = [
{ to: 2, weight: 18 },
{ to: 6, weight: 1 },
];
list2[6] = [{ to: 3, weight: 1 }];
// >(1)<--->(4) ---->(5)
// / | /|
// (0) ------|------- |
// \ v v v
// >(2) --> (3) <----(6)
export const matrix2: WeightedAdjacencyMatrix = [
[0, 3, 1, 0, 0, 0, 0], // 0
[0, 0, 0, 0, 1, 0, 0],
[0, 0, 7, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 5, 0, 2, 0],
[0, 0, 18, 0, 0, 0, 1],
[0, 0, 0, 1, 0, 0, 1],
];

87
src/day1/tree.ts Normal file
View File

@@ -0,0 +1,87 @@
export const tree: BinaryNode<number> = {
value: 20,
right: {
value: 50,
right: {
value: 100,
right: null,
left: null,
},
left: {
value: 30,
right: {
value: 45,
right: null,
left: null,
},
left: {
value: 29,
right: null,
left: null,
},
},
},
left: {
value: 10,
right: {
value: 15,
right: null,
left: null,
},
left: {
value: 5,
right: {
value: 7,
right: null,
left: null,
},
left: null,
},
},
};
export const tree2: BinaryNode<number> = {
value: 20,
right: {
value: 50,
right: null,
left: {
value: 30,
right: {
value: 45,
right: {
value: 49,
left: null,
right: null,
},
left: null,
},
left: {
value: 29,
right: null,
left: {
value: 21,
right: null,
left: null,
},
},
},
},
left: {
value: 10,
right: {
value: 15,
right: null,
left: null,
},
left: {
value: 5,
right: {
value: 7,
right: null,
left: null,
},
left: null,
},
},
};

52
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,52 @@
declare type Point = {
x: number;
y: number;
};
declare type ListNode<T> = {
value: T;
next?: ListNode<T>;
prev?: ListNode<T>;
};
declare interface List<T> {
get length(): number;
removeAt(index: number): T | undefined;
remove(item: T): T | undefined;
get(index: number): T | undefined;
prepend(item: T): void;
append(item: T): void;
insertAt(item: T, idx: number): void;
}
declare type CompleteGraphEdge = { from: number; to: number; weight: number };
declare type GraphEdge = { to: number; weight: number };
declare type WeightedAdjacencyList = GraphEdge[][];
// A number means weight
declare type WeightedAdjacencyMatrix = number[][];
declare type AdjacencyList = number[][];
// A 1 means connected
declare type AdjacencyMatrix = number[][];
declare type BinaryNode<T> = {
value: T;
left: BinaryNode<T> | null;
right: BinaryNode<T> | null;
};
declare type GeneralNode<T> = {
value: T;
children: GeneralNode<T>[];
};
declare interface ILRU<K, V> {
update(key: K, value: V): void;
get(key: K): V | undefined;
}
declare type BinaryNode<T> = {
value: T;
left: BinaryNode<T> | null;
right: BinaryNode<T> | null;
};

19
tsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"outDir": "dist",
"allowJs": true,
"noImplicitAny": true,
"strictNullChecks": true,
"resolveJsonModule": true,
"noImplicitReturns": true,
"esModuleInterop": true,
"target": "esnext",
"module": "commonjs",
"baseUrl": "src",
"paths": {
"@code/*": ["day1/*"]
}
},
"include": ["./src/**/*"],
"exclude": ["node_modules"]
}

15
vitest.config.ts Normal file
View File

@@ -0,0 +1,15 @@
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
resolve: {
alias: {
"@code": path.resolve(__dirname, "src/day1"),
},
},
test: {
globals: true,
environment: "node",
include: ["src/**/*.test.{ts,js}"],
},
});