JSR?
This commit is contained in:
25 files changed
+186
-94
No files matched your search
@@ -1 +1,4 @@
|
||||
Implement Dijkstra's, Prim and Kruskal's, Topological sort, maybe like Ford-Fulkerson, maybe Christofides
|
||||
|
||||
export * as YAML from "https://esm.sh/yaml@2.3.4"
|
||||
Use YAML format for storing graphs! Use references or something!
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Graph } from './graph.ts'
|
||||
import { Graph } from './mod.ts'
|
||||
|
||||
// Box drawing characters!
|
||||
// ┌ ┬ ┐ ─│
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Graph, Vertex, VertexType, Edge, EdgeType, Path, PathType } from "./graph.ts"
|
||||
import { Graph, Vertex, VertexType, Edge, EdgeType, Path, PathType } from "./mod.ts"
|
||||
import { Queue } from "../structures/Queue.ts"
|
||||
import { Stack } from "../structures/Stack.ts"
|
||||
import { BinaryHeap } from "../structures/Heap.ts"
|
||||
@@ -89,20 +89,24 @@ export class GraphSolver<vData, eData> {
|
||||
* @author MindfulMinun
|
||||
* @since 2022-11-14
|
||||
*/
|
||||
bidi(left: Vertex<vData, eData>, right: Vertex<vData, eData>): Path<vData, eData> | null {
|
||||
const qL = new Queue([left])
|
||||
const qR = new Queue([right])
|
||||
bidi({ source, sink, skipEdge }: {
|
||||
source: Vertex<vData, eData>
|
||||
sink: Vertex<vData, eData>
|
||||
skipEdge?: <vData, eData>(e: Edge<vData, eData>, v: Vertex<vData, eData>, g: Graph<vData, eData>) => boolean
|
||||
}): Path<vData, eData> | null {
|
||||
const qL = new Queue([source])
|
||||
const qR = new Queue([sink])
|
||||
|
||||
// Map a vertex to the path that led to it. Note that paths from the right
|
||||
// will be reversed so they can be concatenated with the paths from the left.
|
||||
const backL = new Map<Vertex<vData, eData>, Path<vData, eData>>()
|
||||
const backR = new Map<Vertex<vData, eData>, Path<vData, eData>>()
|
||||
|
||||
backL.set(left, this.G.createPath(left))
|
||||
backR.set(right, this.G.createPath(right))
|
||||
backL.set(source, this.G.createPath(source))
|
||||
backR.set(sink, this.G.createPath(sink))
|
||||
|
||||
const seenL = new Set<Vertex<vData, eData>>([left])
|
||||
const seenR = new Set<Vertex<vData, eData>>([right])
|
||||
const seenL = new Set<Vertex<vData, eData>>([source])
|
||||
const seenR = new Set<Vertex<vData, eData>>([sink])
|
||||
|
||||
while (qL.length && qR.length) {
|
||||
const l = qL.pop()!
|
||||
@@ -113,6 +117,7 @@ export class GraphSolver<vData, eData> {
|
||||
for (const E of l.adjacentEdges) {
|
||||
if (E.directed && l !== E.u) continue
|
||||
const w = E.not(l)
|
||||
if (skipEdge?.(E, l, this.G) || false) continue
|
||||
if (!seenL.has(w)) {
|
||||
seenL.add(w)
|
||||
const path = backL.get(l)!.copy()
|
||||
@@ -129,6 +134,7 @@ export class GraphSolver<vData, eData> {
|
||||
// Edge u -> v
|
||||
if (E.directed && r !== E.v) continue
|
||||
const w = E.not(r)
|
||||
if (skipEdge?.(E, w, this.G) || false) continue
|
||||
if (!seenR.has(w)) {
|
||||
seenR.add(w)
|
||||
const path = backR.get(r)!.copy()
|
||||
@@ -166,7 +172,7 @@ export class GraphSolver<vData, eData> {
|
||||
* @author MindfulMinun
|
||||
* @since 2023-03-30
|
||||
*/
|
||||
kruskalWithSort() {
|
||||
kruskalWithSort(): Set<Edge<vData, eData>> {
|
||||
this.sortEdgesByWeight()
|
||||
return this.kruskalPresorted()
|
||||
}
|
||||
@@ -228,4 +234,8 @@ export class GraphSolver<vData, eData> {
|
||||
.map(e => [e.id, e] as const)
|
||||
this.G.edges = new Map(edges)
|
||||
}
|
||||
|
||||
floydWarshall() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as Colors from "https://deno.land/[email protected]/fmt/colors.ts"
|
||||
import * as Colors from "@std/fmt/colors"
|
||||
|
||||
type UUID = string
|
||||
|
||||
@@ -60,7 +60,7 @@ export class Graph<vData, eData> {
|
||||
this.vertices.delete(V.id)
|
||||
}
|
||||
|
||||
createEdge(u: Vertex<vData, eData>, v: Vertex<vData, eData>, data: eData, opts: Partial<EdgeOpts> = {}) {
|
||||
createEdge(u: Vertex<vData, eData>, v: Vertex<vData, eData>, data: eData, opts: Partial<EdgeOpts> = {}): Edge<vData, eData> {
|
||||
const options: EdgeOpts = {
|
||||
directed: this.directed,
|
||||
id: crypto.randomUUID(),
|
||||
@@ -81,14 +81,15 @@ export class Graph<vData, eData> {
|
||||
}
|
||||
|
||||
|
||||
createPath(start: Vertex<vData, eData>) {
|
||||
createPath(start: Vertex<vData, eData>): Path<vData, eData> {
|
||||
return new Path<vData, eData>(this, start)
|
||||
}
|
||||
|
||||
// FIXME: return type
|
||||
toJSON(
|
||||
vertexReplacer: (this: Graph<vData, eData>, data: vData) => unknown = data => data,
|
||||
edgeReplacer: (this: Graph<vData, eData>, data: eData) => unknown = data => data
|
||||
) {
|
||||
): unknown {
|
||||
return {
|
||||
"@graph": Graph.signature,
|
||||
v: Array.from(this.vertices.values()).map(v => v.toJSON(vertexReplacer)),
|
||||
@@ -100,7 +101,7 @@ export class Graph<vData, eData> {
|
||||
json: unknown,
|
||||
vertexReviver: (this: Graph<vData, eData>, json: unknown) => vData = data => data as vData,
|
||||
edgeReviver: (this: Graph<vData, eData>, json: unknown) => eData = data => data as eData
|
||||
) {
|
||||
): Graph<vData, eData> {
|
||||
const G = new Graph<vData, eData>()
|
||||
|
||||
if (!json || typeof json !== 'object') throw Error("Failed to parse: Unexpected object!")
|
||||
@@ -172,15 +173,18 @@ export class Vertex<vData, eData> {
|
||||
*/
|
||||
delete() { this.graph.deleteVertex(this) }
|
||||
|
||||
toJSON(replacer: (this: Graph<vData, eData>, data: vData) => unknown = data => data) {
|
||||
// FIXME: return type as tuple
|
||||
toJSON(
|
||||
replacer: (this: Graph<vData, eData>, data: vData) => unknown = data => data
|
||||
): [unknown, unknown] {
|
||||
return [this.id, replacer.call(this.graph, this.data)]
|
||||
}
|
||||
|
||||
toString() {
|
||||
toString(): string {
|
||||
return Colors.cyan(`<${this.id.slice(0, 8)}>: ${this.data}`)
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.customInspect")]() {
|
||||
[Symbol.for("Deno.customInspect")](): string {
|
||||
return this.toString()
|
||||
}
|
||||
}
|
||||
@@ -225,14 +229,16 @@ export class Edge<vData, eData> {
|
||||
}
|
||||
|
||||
/** Given one vertex, get the opposite vertex of this edge */
|
||||
not(v: typeof this.u | typeof this.v) {
|
||||
not(v: typeof this.u | typeof this.v): Vertex<vData, eData> {
|
||||
return this.v !== v ? this.v : this.u
|
||||
}
|
||||
|
||||
delete() { this.graph.deleteEdge(this) }
|
||||
|
||||
toJSON(replacer: (this: Graph<vData, eData>, data: eData) => unknown = data => data) {
|
||||
// tuple!
|
||||
// FIXME: Type me as a tuple!
|
||||
toJSON(
|
||||
replacer: (this: Graph<vData, eData>, data: eData) => unknown = data => data
|
||||
): [unknown, unknown, unknown, unknown, unknown] {
|
||||
return [
|
||||
this.id,
|
||||
this.u.id,
|
||||
@@ -242,11 +248,11 @@ export class Edge<vData, eData> {
|
||||
]
|
||||
}
|
||||
|
||||
toString() {
|
||||
toString(): string {
|
||||
return Colors.magenta(`<${this.id.slice(0, 8)}>: ${this.u} ${!this.directed ? '<' : ''}--> ${this.v}`)
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.customInspect")]() {
|
||||
[Symbol.for("Deno.customInspect")](): string {
|
||||
return this.toString()
|
||||
}
|
||||
}
|
||||
@@ -276,7 +282,7 @@ export class Path<vData, eData> {
|
||||
}
|
||||
|
||||
/** The vertices belonging to this path */
|
||||
get vertices() {
|
||||
get vertices(): Iterable<Vertex<vData, eData>> {
|
||||
if (this.#vertices) return this.#vertices
|
||||
|
||||
const vertices: Vertex<vData, eData>[] = []
|
||||
@@ -294,7 +300,7 @@ export class Path<vData, eData> {
|
||||
return this.#vertices
|
||||
}
|
||||
|
||||
copy() {
|
||||
copy(): Path<vData, eData> {
|
||||
const path = new Path<vData, eData>(this.graph, this.start ?? undefined)
|
||||
path.edges = this.edges.slice()
|
||||
path.#vertices = this.#vertices?.slice() ?? null
|
||||
+14
-11
@@ -1,6 +1,6 @@
|
||||
import { fill } from "../../core/iterable.ts"
|
||||
import * as Colors from "https://deno.land/[email protected]/fmt/colors.ts"
|
||||
import { Memo } from "../structures.ts"
|
||||
import * as Colors from "@std/fmt/colors"
|
||||
import { Memo } from "../structures/Memo.ts"
|
||||
|
||||
|
||||
const memodDeterminant = new Memo({
|
||||
@@ -26,17 +26,17 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
this.entries = entries
|
||||
}
|
||||
|
||||
set(row: number, col: number, value: number) {
|
||||
set(row: number, col: number, value: number): void {
|
||||
const offset = this.#calculateOffset(row, col)
|
||||
this.entries[offset] = value
|
||||
}
|
||||
|
||||
get(row: number, col: number) {
|
||||
get(row: number, col: number): number {
|
||||
const offset = this.#calculateOffset(row, col)
|
||||
return this.entries[offset]
|
||||
}
|
||||
|
||||
copy() {
|
||||
copy(): Matrix<M, N> {
|
||||
return new Matrix(this.m, this.n, this.entries.slice())
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
* Performs naive matrix multiplication.
|
||||
* @thorws If the dimensions of the matrices are incompatible.
|
||||
*/
|
||||
rightMultiply<P extends number, Q extends number>(right: Matrix<P, Q>) {
|
||||
rightMultiply<P extends number, Q extends number>(
|
||||
right: Matrix<P, Q>
|
||||
): N & P extends never ? never : Matrix<M, Q> {
|
||||
if (this.n as number !== right.m as number) {
|
||||
throw new Error(`Cannot multiply ${this.m}x${this.n} matrix by ${right.m}x${right.n} matrix.`)
|
||||
}
|
||||
@@ -89,7 +91,8 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
/**
|
||||
* Returns the submatrix obtained by removing the given row and column.
|
||||
*/
|
||||
laplaceExpansion(row: number, col: number) {
|
||||
// FIXME: Type this correctly
|
||||
laplaceExpansion(row: number, col: number): Matrix {
|
||||
const matrix = Matrix.null(this.m - 1, this.n - 1)
|
||||
matrix.entries = this.entries.filter((_, i) => {
|
||||
const r = Math.floor(i / this.n)
|
||||
@@ -106,7 +109,7 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
* @author MindfulMinun
|
||||
* @since 2023-01-06
|
||||
*/
|
||||
toUpperTriangular() {
|
||||
toUpperTriangular(): Matrix<M, N> | null {
|
||||
const A = this.copy()
|
||||
const { m, n } = A
|
||||
for (let i = 0; i < m; i++) {
|
||||
@@ -171,7 +174,7 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
toString(): string {
|
||||
let out = ''
|
||||
out += 'Matrix<' + this.m + ', ' + this.n + '>['
|
||||
out += this.entries.map(e => e.toString()).join(', ')
|
||||
@@ -179,7 +182,7 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
return out
|
||||
}
|
||||
|
||||
[Symbol.for("Deno.customInspect")]() {
|
||||
[Symbol.for("Deno.customInspect")](): string {
|
||||
const strs = this.entries.map(e => e.toString())
|
||||
const maxLengths = Array.from(fill(0, this.n))
|
||||
|
||||
@@ -215,7 +218,7 @@ export class Matrix<M extends number = number, N extends number = number> {
|
||||
* Creates a new matrix with the given numbers and entries.
|
||||
* All entries are initialized to 0.
|
||||
*/
|
||||
static null<M extends number, N extends number>(m: M, n: N) {
|
||||
static null<M extends number, N extends number>(m: M, n: N): Matrix<M, N> {
|
||||
const zeroes = Array.from(fill(0, m * n))
|
||||
return new Matrix(m, n, zeroes)
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
console.error("Hey there, past me! Please don't import `structures.ts`, instead import the specific structure you need.")
|
||||
|
||||
export * from './structures/Queue.ts'
|
||||
export * from './structures/Stack.ts'
|
||||
@@ -24,7 +24,7 @@ export class BinaryHeap<T> implements Iterable<T> {
|
||||
for (const el of initials) this.push(el)
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
*[Symbol.iterator](): Generator<NonNullable<T>, void, unknown> {
|
||||
while (this.length !== 0) yield this.pop()!
|
||||
}
|
||||
|
||||
@@ -112,5 +112,5 @@ export class BinaryHeap<T> implements Iterable<T> {
|
||||
}
|
||||
|
||||
/** The number of elements in the heap */
|
||||
get length() { return this.#elements.length }
|
||||
get length(): number { return this.#elements.length }
|
||||
}
|
||||
@@ -69,11 +69,11 @@ export class Memo<A extends unknown[], R extends unknown> {
|
||||
return result
|
||||
}
|
||||
|
||||
get ratio() {
|
||||
get ratio(): number {
|
||||
return this.hits / (this.hits + this.misses)
|
||||
}
|
||||
|
||||
get fn() {
|
||||
get fn(): (...args: A) => R {
|
||||
return this.#memoHandler.bind(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,11 @@ export class Queue<T> implements Iterable<T> {
|
||||
}
|
||||
|
||||
/** The number of elements remaining in the queue */
|
||||
get length() { return this.#tail - this.#head }
|
||||
get length(): number {
|
||||
return this.#tail - this.#head
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
*[Symbol.iterator](): Generator<NonNullable<T>, void, unknown> {
|
||||
while (this.length !== 0) yield this.pop()!
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,9 @@ export class Stack<T> implements Iterable<T> {
|
||||
}
|
||||
|
||||
/** The number of elements remaining in the stack */
|
||||
get length() { return this.#head }
|
||||
get length(): number { return this.#head }
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
*[Symbol.iterator](): Generator<NonNullable<T>, void, unknown> {
|
||||
while (this.length !== 0) yield this.pop()!
|
||||
}
|
||||
}
|
||||
@@ -25,26 +25,28 @@ export class StateMachine<State extends string | number | symbol = 0, InputAlpha
|
||||
this.reset()
|
||||
}
|
||||
|
||||
runWithInput(elements: Iterable<InputAlphabet>) {
|
||||
runWithInput(elements: Iterable<InputAlphabet>): this {
|
||||
for (const element of elements) {
|
||||
this.transitionWith(element)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
transitionWith(input: InputAlphabet) {
|
||||
transitionWith(input: InputAlphabet): this {
|
||||
const fn = this.#transitionTable[this.currentState]
|
||||
if (!fn) throw new Error(`No transition function for state ${String(this.currentState)}`)
|
||||
fn.call(this, input, this)
|
||||
return this
|
||||
}
|
||||
|
||||
goto(state: State) {
|
||||
goto(state: State): this {
|
||||
this.currentState = state
|
||||
return this
|
||||
}
|
||||
|
||||
accepts(state = this.currentState) { return this.#acceptStates.has(state) }
|
||||
accepts(state = this.currentState): boolean {
|
||||
return this.#acceptStates.has(state)
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.currentState = this.#initialState
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export * as DisjointSet from './DisjointSet.ts'
|
||||
export * as Heap from './Heap.ts'
|
||||
export * as Memo from './Memo.ts'
|
||||
export * as Queue from './Queue.ts'
|
||||
export * as Stack from './Stack.ts'
|
||||
export * as StateMachine from './StateMachine.ts'
|
||||
Reference in new issue
Block a user