This commit is contained in:
mindfulminun committed 2024-07-20 15:30:48 -05:00
1 parent 9cf0bf415b
commit 71323fc7fd
25 files changed
+186 -94

No files matched your search

+3
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
import { Graph } from './graph.ts'
import { Graph } from './mod.ts'
// Box drawing characters!
// ┌ ┬ ┐ ─│
+19 -9
View File
@@ -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() {
}
}
+21 -15
View File
@@ -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
View File
@@ -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)
}
-4
View File
@@ -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'
+2 -2
View File
@@ -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 }
}
+2 -2
View File
@@ -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)
}
}
+4 -2
View File
@@ -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()!
}
}
+2 -2
View File
@@ -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()!
}
}
+6 -4
View File
@@ -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
+6
View File
@@ -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'