@no-k/hermes 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +88 -0
- package/bin/hermes.mjs +159 -0
- package/catalog.json +1084 -0
- package/lib/catalog.mjs +51 -0
- package/package.json +28 -0
- package/templates/algorithm/geometry/ccw/ccw.ts +25 -0
- package/templates/algorithm/geometry/ccw/index.ts +1 -0
- package/templates/algorithm/geometry/convex-hull/convex-hull.ts +43 -0
- package/templates/algorithm/geometry/convex-hull/index.ts +1 -0
- package/templates/algorithm/geometry/polygon-area/index.ts +1 -0
- package/templates/algorithm/geometry/polygon-area/polygon-area.ts +13 -0
- package/templates/algorithm/graph/bellman-ford/bellman-ford.ts +46 -0
- package/templates/algorithm/graph/bellman-ford/index.ts +1 -0
- package/templates/algorithm/graph/bfs/bfs.ts +21 -0
- package/templates/algorithm/graph/bfs/index.ts +1 -0
- package/templates/algorithm/graph/dfs/dfs.ts +22 -0
- package/templates/algorithm/graph/dfs/index.ts +1 -0
- package/templates/algorithm/graph/dijkstra/dijkstra.ts +37 -0
- package/templates/algorithm/graph/dijkstra/index.ts +1 -0
- package/templates/algorithm/graph/floyd-warshall/floyd-warshall.ts +17 -0
- package/templates/algorithm/graph/floyd-warshall/index.ts +1 -0
- package/templates/algorithm/graph/kruskal/index.ts +1 -0
- package/templates/algorithm/graph/kruskal/kruskal.ts +29 -0
- package/templates/algorithm/graph/prim/index.ts +1 -0
- package/templates/algorithm/graph/prim/prim.ts +44 -0
- package/templates/algorithm/graph/topology-sort/index.ts +1 -0
- package/templates/algorithm/graph/topology-sort/topology-sort.ts +31 -0
- package/templates/algorithm/search/binary-search/binary-search.ts +74 -0
- package/templates/algorithm/search/binary-search/index.ts +1 -0
- package/templates/algorithm/string/kmp/index.ts +1 -0
- package/templates/algorithm/string/kmp/kmp.ts +46 -0
- package/templates/algorithm/string/z-algorithm/index.ts +1 -0
- package/templates/algorithm/string/z-algorithm/z-algorithm.ts +41 -0
- package/templates/algorithm/tree/avl-tree/avl-tree.ts +197 -0
- package/templates/algorithm/tree/avl-tree/index.ts +1 -0
- package/templates/algorithm/tree/binary-search-tree/binary-search-tree.ts +144 -0
- package/templates/algorithm/tree/binary-search-tree/index.ts +1 -0
- package/templates/algorithm/tree/lca/index.ts +1 -0
- package/templates/algorithm/tree/lca/lca.ts +119 -0
- package/templates/array/chunk/chunk.ts +13 -0
- package/templates/array/chunk/index.ts +1 -0
- package/templates/array/compact/compact.ts +8 -0
- package/templates/array/compact/index.ts +1 -0
- package/templates/array/count-by/count-by.ts +12 -0
- package/templates/array/count-by/index.ts +1 -0
- package/templates/array/difference/difference.ts +6 -0
- package/templates/array/difference/index.ts +1 -0
- package/templates/array/drop/drop.ts +7 -0
- package/templates/array/drop/index.ts +1 -0
- package/templates/array/group-by/group-by.ts +18 -0
- package/templates/array/group-by/index.ts +1 -0
- package/templates/array/intersection/index.ts +1 -0
- package/templates/array/intersection/intersection.ts +15 -0
- package/templates/array/key-by/index.ts +1 -0
- package/templates/array/key-by/key-by.ts +11 -0
- package/templates/array/max-by/index.ts +1 -0
- package/templates/array/max-by/max-by.ts +20 -0
- package/templates/array/min-by/index.ts +1 -0
- package/templates/array/min-by/min-by.ts +20 -0
- package/templates/array/partition/index.ts +1 -0
- package/templates/array/partition/partition.ts +16 -0
- package/templates/array/range/index.ts +1 -0
- package/templates/array/range/range.ts +22 -0
- package/templates/array/sample/index.ts +1 -0
- package/templates/array/sample/sample.ts +10 -0
- package/templates/array/shuffle/index.ts +1 -0
- package/templates/array/shuffle/shuffle.ts +15 -0
- package/templates/array/sort-by/index.ts +1 -0
- package/templates/array/sort-by/sort-by.ts +32 -0
- package/templates/array/sum-by/index.ts +1 -0
- package/templates/array/sum-by/sum-by.ts +9 -0
- package/templates/array/take/index.ts +1 -0
- package/templates/array/take/take.ts +7 -0
- package/templates/array/union/index.ts +1 -0
- package/templates/array/union/union.ts +5 -0
- package/templates/array/unique/index.ts +1 -0
- package/templates/array/unique/unique.ts +5 -0
- package/templates/array/unique-by/index.ts +1 -0
- package/templates/array/unique-by/unique-by.ts +19 -0
- package/templates/array/windowed/index.ts +1 -0
- package/templates/array/windowed/windowed.ts +15 -0
- package/templates/array/zip/index.ts +1 -0
- package/templates/array/zip/zip.ts +11 -0
- package/templates/boolean/parse-boolean/index.ts +1 -0
- package/templates/boolean/parse-boolean/parse-boolean.ts +17 -0
- package/templates/boolean/toggle/index.ts +1 -0
- package/templates/boolean/toggle/toggle.ts +2 -0
- package/templates/boolean/xor/index.ts +1 -0
- package/templates/boolean/xor/xor.ts +2 -0
- package/templates/data-structure/deque/deque.ts +79 -0
- package/templates/data-structure/deque/index.ts +1 -0
- package/templates/data-structure/fenwick-tree/fenwick-tree.ts +76 -0
- package/templates/data-structure/fenwick-tree/index.ts +1 -0
- package/templates/data-structure/heap/heap.ts +88 -0
- package/templates/data-structure/heap/index.ts +3 -0
- package/templates/data-structure/heap/max-heap.ts +7 -0
- package/templates/data-structure/heap/min-heap.ts +7 -0
- package/templates/data-structure/priority-queue/index.ts +1 -0
- package/templates/data-structure/priority-queue/priority-queue.ts +44 -0
- package/templates/data-structure/queue/index.ts +1 -0
- package/templates/data-structure/queue/queue.ts +66 -0
- package/templates/data-structure/segment-tree/index.ts +1 -0
- package/templates/data-structure/segment-tree/segment-tree.ts +79 -0
- package/templates/data-structure/trie/index.ts +1 -0
- package/templates/data-structure/trie/trie.ts +104 -0
- package/templates/data-structure/union-find/index.ts +1 -0
- package/templates/data-structure/union-find/union-find.ts +79 -0
- package/templates/dom/attribute/attribute.ts +24 -0
- package/templates/dom/attribute/index.ts +1 -0
- package/templates/dom/class-name/class-name.ts +48 -0
- package/templates/dom/class-name/index.ts +1 -0
- package/templates/dom/is-element/index.ts +1 -0
- package/templates/dom/is-element/is-element.ts +20 -0
- package/templates/dom/query/index.ts +1 -0
- package/templates/dom/query/query.ts +7 -0
- package/templates/http/content-type/content-type.ts +130 -0
- package/templates/http/content-type/index.ts +1 -0
- package/templates/http/header/header.ts +56 -0
- package/templates/http/header/index.ts +1 -0
- package/templates/http/method/index.ts +1 -0
- package/templates/http/method/method.ts +25 -0
- package/templates/http/status/index.ts +1 -0
- package/templates/http/status/status.ts +191 -0
- package/templates/number/clamp/clamp.ts +11 -0
- package/templates/number/clamp/index.ts +1 -0
- package/templates/number/factorial/factorial.ts +15 -0
- package/templates/number/factorial/index.ts +1 -0
- package/templates/number/format-thousands/format-thousands.ts +24 -0
- package/templates/number/format-thousands/index.ts +1 -0
- package/templates/number/gcd/gcd.ts +13 -0
- package/templates/number/gcd/index.ts +1 -0
- package/templates/number/in-range/in-range.ts +3 -0
- package/templates/number/in-range/index.ts +1 -0
- package/templates/number/is-even/index.ts +1 -0
- package/templates/number/is-even/is-even.ts +5 -0
- package/templates/number/is-odd/index.ts +1 -0
- package/templates/number/is-odd/is-odd.ts +7 -0
- package/templates/number/lcm/index.ts +1 -0
- package/templates/number/lcm/lcm.ts +12 -0
- package/templates/number/lerp/index.ts +1 -0
- package/templates/number/lerp/lerp.ts +3 -0
- package/templates/number/percentage/index.ts +1 -0
- package/templates/number/percentage/percentage.ts +7 -0
- package/templates/number/random/index.ts +1 -0
- package/templates/number/random/random.ts +9 -0
- package/templates/number/random-int/index.ts +1 -0
- package/templates/number/random-int/random-int.ts +9 -0
- package/templates/number/round/index.ts +1 -0
- package/templates/number/round/round.ts +22 -0
- package/templates/number/safe-parse-number/index.ts +1 -0
- package/templates/number/safe-parse-number/safe-parse-number.ts +29 -0
- package/templates/number/to-ordinal/index.ts +1 -0
- package/templates/number/to-ordinal/to-ordinal.ts +28 -0
- package/templates/object/deep-clone/deep-clone.ts +47 -0
- package/templates/object/deep-clone/index.ts +1 -0
- package/templates/object/deep-equal/deep-equal.ts +66 -0
- package/templates/object/deep-equal/index.ts +1 -0
- package/templates/object/deep-merge/deep-merge.ts +49 -0
- package/templates/object/deep-merge/index.ts +1 -0
- package/templates/object/defaults/defaults.ts +14 -0
- package/templates/object/defaults/index.ts +1 -0
- package/templates/object/filter-object/filter-object.ts +11 -0
- package/templates/object/filter-object/index.ts +1 -0
- package/templates/object/get/get.ts +75 -0
- package/templates/object/get/index.ts +1 -0
- package/templates/object/has/has.ts +60 -0
- package/templates/object/has/index.ts +1 -0
- package/templates/object/invert/index.ts +1 -0
- package/templates/object/invert/invert.ts +13 -0
- package/templates/object/map-keys/index.ts +1 -0
- package/templates/object/map-keys/map-keys.ts +16 -0
- package/templates/object/map-values/index.ts +1 -0
- package/templates/object/map-values/map-values.ts +14 -0
- package/templates/object/omit/index.ts +1 -0
- package/templates/object/omit/omit.ts +15 -0
- package/templates/object/omit-by/index.ts +1 -0
- package/templates/object/omit-by/omit-by.ts +19 -0
- package/templates/object/pick/index.ts +1 -0
- package/templates/object/pick/pick.ts +15 -0
- package/templates/object/pick-by/index.ts +1 -0
- package/templates/object/pick-by/pick-by.ts +19 -0
- package/templates/object/set/index.ts +1 -0
- package/templates/object/set/set.ts +118 -0
- package/templates/string/capitalize/capitalize.ts +4 -0
- package/templates/string/capitalize/index.ts +1 -0
- package/templates/string/change-case/change-case.ts +98 -0
- package/templates/string/change-case/index.ts +1 -0
- package/templates/string/char/char.ts +14 -0
- package/templates/string/char/index.ts +1 -0
- package/templates/string/count-occurrences/count-occurrences.ts +20 -0
- package/templates/string/count-occurrences/index.ts +1 -0
- package/templates/string/escape-html/escape-html.ts +16 -0
- package/templates/string/escape-html/index.ts +1 -0
- package/templates/string/get-decorated-string/get-decorated-string.ts +7 -0
- package/templates/string/get-decorated-string/index.ts +1 -0
- package/templates/string/is-blank/index.ts +1 -0
- package/templates/string/is-blank/is-blank.ts +6 -0
- package/templates/string/mask/index.ts +1 -0
- package/templates/string/mask/mask.ts +30 -0
- package/templates/string/pad/index.ts +1 -0
- package/templates/string/pad/pad.ts +17 -0
- package/templates/string/random-string/index.ts +1 -0
- package/templates/string/random-string/random-string.ts +20 -0
- package/templates/string/reverse/index.ts +1 -0
- package/templates/string/reverse/reverse.ts +6 -0
- package/templates/string/slugify/index.ts +1 -0
- package/templates/string/slugify/slugify.ts +11 -0
- package/templates/string/strip-tags/index.ts +1 -0
- package/templates/string/strip-tags/strip-tags.ts +8 -0
- package/templates/string/template/index.ts +1 -0
- package/templates/string/template/template.ts +14 -0
- package/templates/string/truncate/index.ts +1 -0
- package/templates/string/truncate/truncate.ts +17 -0
- package/templates/string/truncate-middle/index.ts +1 -0
- package/templates/string/truncate-middle/truncate-middle.ts +25 -0
- package/templates/string/uuid/index.ts +1 -0
- package/templates/string/uuid/uuid.ts +9 -0
- package/templates/tailwindcss/class-variant/class-variant.ts +35 -0
- package/templates/tailwindcss/class-variant/index.ts +1 -0
- package/templates/tailwindcss/cn/cn.ts +29 -0
- package/templates/tailwindcss/cn/index.ts +1 -0
- package/templates/tailwindcss/create-tailwind-css/create-tailwind-css.ts +16 -0
- package/templates/tailwindcss/create-tailwind-css/index.ts +1 -0
- package/templates/tailwindcss/tw-merge-config/index.ts +1 -0
- package/templates/tailwindcss/tw-merge-config/tw-merge-config.ts +14 -0
- package/templates/type/get-type/get-type.ts +27 -0
- package/templates/type/get-type/index.ts +1 -0
- package/templates/type/is-type-of/index.ts +1 -0
- package/templates/type/is-type-of/is-type-of.ts +26 -0
- package/templates/util/mutex/index.ts +1 -0
- package/templates/util/mutex/mutex.ts +109 -0
- package/templates/util/url-parser/index.ts +1 -0
- package/templates/util/url-parser/url-parser.ts +44 -0
package/lib/catalog.mjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const itemNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2
|
+
|
|
3
|
+
export function isSourcePath(value) {
|
|
4
|
+
return (
|
|
5
|
+
typeof value === "string" &&
|
|
6
|
+
value.endsWith(".ts") &&
|
|
7
|
+
!value.includes("\\") &&
|
|
8
|
+
!value.includes("\0") &&
|
|
9
|
+
!value.includes(":") &&
|
|
10
|
+
value.split("/").every((part) => part && part !== "." && part !== "..") &&
|
|
11
|
+
!/\.(test|spec|stories|config|d)\.ts$/.test(value)
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function validateCatalog(catalog) {
|
|
16
|
+
if (
|
|
17
|
+
catalog?.schemaVersion !== 1 ||
|
|
18
|
+
typeof catalog.version !== "string" ||
|
|
19
|
+
!Array.isArray(catalog.items) ||
|
|
20
|
+
catalog.items.length === 0
|
|
21
|
+
) {
|
|
22
|
+
throw new Error("Invalid Hermes catalog.");
|
|
23
|
+
}
|
|
24
|
+
const names = new Set();
|
|
25
|
+
for (const item of catalog.items) {
|
|
26
|
+
if (
|
|
27
|
+
!item ||
|
|
28
|
+
typeof item.name !== "string" ||
|
|
29
|
+
!itemNamePattern.test(item.name) ||
|
|
30
|
+
names.has(item.name) ||
|
|
31
|
+
typeof item.description !== "string" ||
|
|
32
|
+
!Array.isArray(item.files) ||
|
|
33
|
+
item.files.length === 0 ||
|
|
34
|
+
!item.files.every(isSourcePath) ||
|
|
35
|
+
new Set(item.files).size !== item.files.length ||
|
|
36
|
+
(item.runtime !== undefined && !["node", "dom"].includes(item.runtime))
|
|
37
|
+
) {
|
|
38
|
+
throw new Error("Invalid or duplicate Hermes item: " + item?.name);
|
|
39
|
+
}
|
|
40
|
+
for (const values of [item.dependencies, item.devDependencies ?? []]) {
|
|
41
|
+
if (
|
|
42
|
+
!Array.isArray(values) ||
|
|
43
|
+
!values.every((value) => typeof value === "string" && /^[^\s]+@[^\s]+$/.test(value))
|
|
44
|
+
) {
|
|
45
|
+
throw new Error("Invalid dependencies for " + item.name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
names.add(item.name);
|
|
49
|
+
}
|
|
50
|
+
return catalog;
|
|
51
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@no-k/hermes",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Add individual TypeScript utilities to your project with the Hermes CLI.",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"files": ["bin", "lib", "templates", "catalog.json", "README.md", "LICENSE"],
|
|
7
|
+
"keywords": ["cli", "typescript", "utilities"],
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/no-k/hermes.git",
|
|
11
|
+
"directory": "packages/cli"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/",
|
|
16
|
+
"provenance": false
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"prepack": "pnpm --workspace-root run build:cli"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"hermes": "./bin/hermes.mjs"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type Point = {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const ccw = (a: Point, b: Point, c: Point) => {
|
|
7
|
+
const cross = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
8
|
+
|
|
9
|
+
if (cross > 0) {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (cross < 0) {
|
|
14
|
+
return -1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return 0;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const distanceSquare = (a: Point, b: Point) => {
|
|
21
|
+
const dx = a.x - b.x;
|
|
22
|
+
const dy = a.y - b.y;
|
|
23
|
+
|
|
24
|
+
return dx * dx + dy * dy;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ccw";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Point, ccw } from "../ccw/index";
|
|
2
|
+
|
|
3
|
+
const comparePoint = (a: Point, b: Point) => {
|
|
4
|
+
if (a.x !== b.x) {
|
|
5
|
+
return a.x - b.x;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return a.y - b.y;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const samePoint = (a: Point, b: Point) => a.x === b.x && a.y === b.y;
|
|
12
|
+
|
|
13
|
+
export const convexHull = (points: Point[]) => {
|
|
14
|
+
const sorted = [...points].sort(comparePoint).filter((point, index, values) => {
|
|
15
|
+
return index === 0 || !samePoint(point, values[index - 1]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (sorted.length <= 1) {
|
|
19
|
+
return sorted;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const lower: Point[] = [];
|
|
23
|
+
for (const point of sorted) {
|
|
24
|
+
while (lower.length >= 2 && ccw(lower[lower.length - 2], lower[lower.length - 1], point) <= 0) {
|
|
25
|
+
lower.pop();
|
|
26
|
+
}
|
|
27
|
+
lower.push(point);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const upper: Point[] = [];
|
|
31
|
+
for (let index = sorted.length - 1; index >= 0; index -= 1) {
|
|
32
|
+
const point = sorted[index];
|
|
33
|
+
while (upper.length >= 2 && ccw(upper[upper.length - 2], upper[upper.length - 1], point) <= 0) {
|
|
34
|
+
upper.pop();
|
|
35
|
+
}
|
|
36
|
+
upper.push(point);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
lower.pop();
|
|
40
|
+
upper.pop();
|
|
41
|
+
|
|
42
|
+
return [...lower, ...upper];
|
|
43
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./convex-hull";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./polygon-area";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Point } from "../ccw/index";
|
|
2
|
+
|
|
3
|
+
export const polygonArea = (points: Point[]) => {
|
|
4
|
+
let doubledArea = 0;
|
|
5
|
+
|
|
6
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
7
|
+
const current = points[index];
|
|
8
|
+
const next = points[(index + 1) % points.length];
|
|
9
|
+
doubledArea += current.x * next.y - current.y * next.x;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return Math.abs(doubledArea) / 2;
|
|
13
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type DirectedWeightedEdge<T> = {
|
|
2
|
+
from: T;
|
|
3
|
+
to: T;
|
|
4
|
+
weight: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const bellmanFord = <T>(nodes: Iterable<T>, edges: DirectedWeightedEdge<T>[], start: T) => {
|
|
8
|
+
const distances = new Map<T, number>();
|
|
9
|
+
|
|
10
|
+
for (const node of nodes) {
|
|
11
|
+
distances.set(node, Number.POSITIVE_INFINITY);
|
|
12
|
+
}
|
|
13
|
+
distances.set(start, 0);
|
|
14
|
+
|
|
15
|
+
const nodeList = [...distances.keys()];
|
|
16
|
+
|
|
17
|
+
for (let count = 0; count < nodeList.length - 1; count += 1) {
|
|
18
|
+
let updated = false;
|
|
19
|
+
|
|
20
|
+
for (const edge of edges) {
|
|
21
|
+
const fromDistance = distances.get(edge.from) ?? Number.POSITIVE_INFINITY;
|
|
22
|
+
const toDistance = distances.get(edge.to) ?? Number.POSITIVE_INFINITY;
|
|
23
|
+
|
|
24
|
+
if (fromDistance !== Number.POSITIVE_INFINITY && fromDistance + edge.weight < toDistance) {
|
|
25
|
+
distances.set(edge.to, fromDistance + edge.weight);
|
|
26
|
+
updated = true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!updated) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hasNegativeCycle = edges.some((edge) => {
|
|
36
|
+
const fromDistance = distances.get(edge.from) ?? Number.POSITIVE_INFINITY;
|
|
37
|
+
const toDistance = distances.get(edge.to) ?? Number.POSITIVE_INFINITY;
|
|
38
|
+
|
|
39
|
+
return fromDistance !== Number.POSITIVE_INFINITY && fromDistance + edge.weight < toDistance;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
distances,
|
|
44
|
+
hasNegativeCycle,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./bellman-ford";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const bfs = <T>(start: T, getNeighbors: (node: T) => Iterable<T>) => {
|
|
2
|
+
const visited = new Set<T>([start]);
|
|
3
|
+
const queue: T[] = [start];
|
|
4
|
+
const order: T[] = [];
|
|
5
|
+
|
|
6
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
7
|
+
const node = queue[index];
|
|
8
|
+
order.push(node);
|
|
9
|
+
|
|
10
|
+
for (const next of getNeighbors(node)) {
|
|
11
|
+
if (visited.has(next)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
visited.add(next);
|
|
16
|
+
queue.push(next);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return order;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./bfs";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const dfs = <T>(start: T, getNeighbors: (node: T) => Iterable<T>) => {
|
|
2
|
+
const visited = new Set<T>();
|
|
3
|
+
const stack: T[] = [start];
|
|
4
|
+
const order: T[] = [];
|
|
5
|
+
|
|
6
|
+
while (stack.length) {
|
|
7
|
+
const node = stack.pop() as T;
|
|
8
|
+
if (visited.has(node)) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
visited.add(node);
|
|
13
|
+
order.push(node);
|
|
14
|
+
|
|
15
|
+
const neighbors = [...getNeighbors(node)];
|
|
16
|
+
for (let index = neighbors.length - 1; index >= 0; index -= 1) {
|
|
17
|
+
stack.push(neighbors[index]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return order;
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dfs";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PriorityQueue } from "../../../data-structure/priority-queue/index";
|
|
2
|
+
|
|
3
|
+
export type WeightedEdge<T> = {
|
|
4
|
+
to: T;
|
|
5
|
+
weight: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const dijkstra = <T>(start: T, getEdges: (node: T) => Iterable<WeightedEdge<T>>) => {
|
|
9
|
+
const distances = new Map<T, number>([[start, 0]]);
|
|
10
|
+
const queue = new PriorityQueue<{ node: T; distance: number }>({
|
|
11
|
+
compare: (a, b) => a.distance - b.distance,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
queue.enqueue({ node: start, distance: 0 });
|
|
15
|
+
|
|
16
|
+
while (!queue.isEmpty()) {
|
|
17
|
+
const current = queue.dequeue() as { node: T; distance: number };
|
|
18
|
+
|
|
19
|
+
if (current.distance > (distances.get(current.node) as number)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const edge of getEdges(current.node)) {
|
|
24
|
+
const nextDistance = current.distance + edge.weight;
|
|
25
|
+
const previousDistance = distances.get(edge.to);
|
|
26
|
+
|
|
27
|
+
if (previousDistance !== undefined && previousDistance <= nextDistance) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
distances.set(edge.to, nextDistance);
|
|
32
|
+
queue.enqueue({ node: edge.to, distance: nextDistance });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return distances;
|
|
37
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dijkstra";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const floydWarshall = (matrix: number[][]) => {
|
|
2
|
+
const distance = matrix.map((row) => [...row]);
|
|
3
|
+
const size = distance.length;
|
|
4
|
+
|
|
5
|
+
for (let mid = 0; mid < size; mid += 1) {
|
|
6
|
+
for (let from = 0; from < size; from += 1) {
|
|
7
|
+
for (let to = 0; to < size; to += 1) {
|
|
8
|
+
const next = distance[from][mid] + distance[mid][to];
|
|
9
|
+
if (next < distance[from][to]) {
|
|
10
|
+
distance[from][to] = next;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return distance;
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./floyd-warshall";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./kruskal";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { UnionFind } from "../../../data-structure/union-find/index";
|
|
2
|
+
|
|
3
|
+
export type WeightedUndirectedEdge<T> = {
|
|
4
|
+
from: T;
|
|
5
|
+
to: T;
|
|
6
|
+
weight: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const kruskal = <T>(nodes: Iterable<T>, edges: WeightedUndirectedEdge<T>[]) => {
|
|
10
|
+
const unionFind = new UnionFind(nodes);
|
|
11
|
+
const selected: WeightedUndirectedEdge<T>[] = [];
|
|
12
|
+
let totalWeight = 0;
|
|
13
|
+
|
|
14
|
+
const sortedEdges = [...edges].sort((a, b) => a.weight - b.weight);
|
|
15
|
+
|
|
16
|
+
for (const edge of sortedEdges) {
|
|
17
|
+
if (!unionFind.union(edge.from, edge.to)) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
selected.push(edge);
|
|
22
|
+
totalWeight += edge.weight;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
edges: selected,
|
|
27
|
+
totalWeight,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./prim";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PriorityQueue } from "../../../data-structure/priority-queue/index";
|
|
2
|
+
import type { WeightedEdge } from "../dijkstra/index";
|
|
3
|
+
|
|
4
|
+
type QueueItem<T> = {
|
|
5
|
+
from: T | null;
|
|
6
|
+
to: T;
|
|
7
|
+
weight: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const prim = <T>(start: T, getEdges: (node: T) => Iterable<WeightedEdge<T>>) => {
|
|
11
|
+
const visited = new Set<T>();
|
|
12
|
+
const selected: Array<{ from: T; to: T; weight: number }> = [];
|
|
13
|
+
const queue = new PriorityQueue<QueueItem<T>>({
|
|
14
|
+
compare: (a, b) => a.weight - b.weight,
|
|
15
|
+
});
|
|
16
|
+
let totalWeight = 0;
|
|
17
|
+
|
|
18
|
+
queue.enqueue({ from: null, to: start, weight: 0 });
|
|
19
|
+
|
|
20
|
+
while (!queue.isEmpty()) {
|
|
21
|
+
const current = queue.dequeue() as QueueItem<T>;
|
|
22
|
+
if (visited.has(current.to)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
visited.add(current.to);
|
|
27
|
+
|
|
28
|
+
if (current.from !== null) {
|
|
29
|
+
selected.push({ from: current.from, to: current.to, weight: current.weight });
|
|
30
|
+
totalWeight += current.weight;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const edge of getEdges(current.to)) {
|
|
34
|
+
if (!visited.has(edge.to)) {
|
|
35
|
+
queue.enqueue({ from: current.to, to: edge.to, weight: edge.weight });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
edges: selected,
|
|
42
|
+
totalWeight,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./topology-sort";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const topologicalSort = <T>(nodes: Iterable<T>, getNeighbors: (node: T) => Iterable<T>) => {
|
|
2
|
+
const visited = new Set<T>();
|
|
3
|
+
const visiting = new Set<T>();
|
|
4
|
+
const result: T[] = [];
|
|
5
|
+
|
|
6
|
+
const visit = (node: T) => {
|
|
7
|
+
if (visited.has(node)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (visiting.has(node)) {
|
|
12
|
+
throw new Error("cycle detected");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
visiting.add(node);
|
|
16
|
+
|
|
17
|
+
for (const next of getNeighbors(node)) {
|
|
18
|
+
visit(next);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
visiting.delete(node);
|
|
22
|
+
visited.add(node);
|
|
23
|
+
result.push(node);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
for (const node of nodes) {
|
|
27
|
+
visit(node);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result.reverse();
|
|
31
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const defaultCompare = <T>(value: T, target: T) => {
|
|
2
|
+
if (value < target) {
|
|
3
|
+
return -1;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (value > target) {
|
|
7
|
+
return 1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return 0;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const lowerBound = <T>(values: T[], target: T, compare = defaultCompare<T>) => {
|
|
14
|
+
let left = 0;
|
|
15
|
+
let right = values.length;
|
|
16
|
+
|
|
17
|
+
while (left < right) {
|
|
18
|
+
const mid = Math.floor((left + right) / 2);
|
|
19
|
+
|
|
20
|
+
if (compare(values[mid], target) < 0) {
|
|
21
|
+
left = mid + 1;
|
|
22
|
+
} else {
|
|
23
|
+
right = mid;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return left;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const upperBound = <T>(values: T[], target: T, compare = defaultCompare<T>) => {
|
|
31
|
+
let left = 0;
|
|
32
|
+
let right = values.length;
|
|
33
|
+
|
|
34
|
+
while (left < right) {
|
|
35
|
+
const mid = Math.floor((left + right) / 2);
|
|
36
|
+
|
|
37
|
+
if (compare(values[mid], target) <= 0) {
|
|
38
|
+
left = mid + 1;
|
|
39
|
+
} else {
|
|
40
|
+
right = mid;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return left;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const binarySearch = <T>(values: T[], target: T, compare = defaultCompare<T>) => {
|
|
48
|
+
const index = lowerBound(values, target, compare);
|
|
49
|
+
|
|
50
|
+
if (index >= values.length || compare(values[index], target) !== 0) {
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return index;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const binarySearchAnswer = (left: number, right: number, canUse: (value: number) => boolean) => {
|
|
58
|
+
let low = left;
|
|
59
|
+
let high = right;
|
|
60
|
+
let answer = right + 1;
|
|
61
|
+
|
|
62
|
+
while (low <= high) {
|
|
63
|
+
const mid = Math.floor((low + high) / 2);
|
|
64
|
+
|
|
65
|
+
if (canUse(mid)) {
|
|
66
|
+
answer = mid;
|
|
67
|
+
high = mid - 1;
|
|
68
|
+
} else {
|
|
69
|
+
low = mid + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return answer;
|
|
74
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./binary-search";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./kmp";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const buildPrefixTable = (pattern: string) => {
|
|
2
|
+
const table = Array(pattern.length).fill(0);
|
|
3
|
+
let matched = 0;
|
|
4
|
+
|
|
5
|
+
for (let index = 1; index < pattern.length; index += 1) {
|
|
6
|
+
while (matched > 0 && pattern[index] !== pattern[matched]) {
|
|
7
|
+
matched = table[matched - 1];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (pattern[index] === pattern[matched]) {
|
|
11
|
+
matched += 1;
|
|
12
|
+
table[index] = matched;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return table;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const kmpSearch = (text: string, pattern: string) => {
|
|
20
|
+
if (!pattern.length) {
|
|
21
|
+
return Array.from({ length: text.length + 1 }, (_, index) => index);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const table = buildPrefixTable(pattern);
|
|
25
|
+
const result: number[] = [];
|
|
26
|
+
let matched = 0;
|
|
27
|
+
|
|
28
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
29
|
+
while (matched > 0 && text[index] !== pattern[matched]) {
|
|
30
|
+
matched = table[matched - 1];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (text[index] !== pattern[matched]) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
matched += 1;
|
|
38
|
+
|
|
39
|
+
if (matched === pattern.length) {
|
|
40
|
+
result.push(index - pattern.length + 1);
|
|
41
|
+
matched = table[matched - 1];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./z-algorithm";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const buildZArray = (value: string) => {
|
|
2
|
+
const z = Array(value.length).fill(0);
|
|
3
|
+
let left = 0;
|
|
4
|
+
let right = 0;
|
|
5
|
+
|
|
6
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
7
|
+
if (index <= right) {
|
|
8
|
+
z[index] = Math.min(right - index + 1, z[index - left]);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
while (index + z[index] < value.length && value[z[index]] === value[index + z[index]]) {
|
|
12
|
+
z[index] += 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (index + z[index] - 1 > right) {
|
|
16
|
+
left = index;
|
|
17
|
+
right = index + z[index] - 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return z;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const zSearch = (text: string, pattern: string) => {
|
|
25
|
+
if (!pattern.length) {
|
|
26
|
+
return Array.from({ length: text.length + 1 }, (_, index) => index);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const separator = "\0";
|
|
30
|
+
const z = buildZArray(`${pattern}${separator}${text}`);
|
|
31
|
+
const result: number[] = [];
|
|
32
|
+
const offset = pattern.length + 1;
|
|
33
|
+
|
|
34
|
+
for (let index = offset; index < z.length; index += 1) {
|
|
35
|
+
if (z[index] >= pattern.length) {
|
|
36
|
+
result.push(index - offset);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return result;
|
|
41
|
+
};
|