import { boolean } from "ts-matches" export type Vertex = { metadata: VMetadata edges: Array> } export type Edge = { metadata: EMetadata from: Vertex to: Vertex } export class Graph { private readonly vertices: Array> = [] constructor() {} addVertex( metadata: VMetadata, fromEdges: Array, "to">>, toEdges: Array, "from">>, ): Vertex { const vertex: Vertex = { metadata, edges: [], } for (let edge of fromEdges) { const vEdge = { metadata: edge.metadata, from: edge.from, to: vertex, } edge.from.edges.push(vEdge) vertex.edges.push(vEdge) } for (let edge of toEdges) { const vEdge = { metadata: edge.metadata, from: vertex, to: edge.to, } edge.to.edges.push(vEdge) vertex.edges.push(vEdge) } this.vertices.push(vertex) return vertex } findVertex( predicate: (vertex: Vertex) => boolean, ): Generator, void> { const veritces = this.vertices function* gen() { for (let vertex of veritces) { if (predicate(vertex)) { yield vertex } } } return gen() } addEdge( metadata: EMetadata, from: Vertex, to: Vertex, ): Edge { const edge = { metadata, from, to, } edge.from.edges.push(edge) edge.to.edges.push(edge) return edge } breadthFirstSearch( from: | Vertex | ((vertex: Vertex) => boolean), ): Generator, void> { const visited: Array> = [] function* rec( vertex: Vertex, ): Generator, void> { if (visited.includes(vertex)) { return } visited.push(vertex) yield vertex let generators = vertex.edges .filter((e) => e.from === vertex) .map((e) => rec(e.to)) while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (!next.done) { generators.push(gen) yield next.value } } } } if (from instanceof Function) { let generators = this.vertices.filter(from).map(rec) return (function* () { while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (!next.done) { generators.push(gen) yield next.value } } } })() } else { return rec(from) } } reverseBreadthFirstSearch( to: | Vertex | ((vertex: Vertex) => boolean), ): Generator, void> { const visited: Array> = [] function* rec( vertex: Vertex, ): Generator, void> { if (visited.includes(vertex)) { return } visited.push(vertex) yield vertex let generators = vertex.edges .filter((e) => e.to === vertex) .map((e) => rec(e.from)) while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (!next.done) { generators.push(gen) yield next.value } } } } if (to instanceof Function) { let generators = this.vertices.filter(to).map(rec) return (function* () { while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (!next.done) { generators.push(gen) yield next.value } } } })() } else { return rec(to) } } shortestPath( from: | Vertex | ((vertex: Vertex) => boolean), to: | Vertex | ((vertex: Vertex) => boolean), ): Array> | void { const isDone = to instanceof Function ? to : (v: Vertex) => v === to const path: Array> = [] const visited: Array> = [] function* check( vertex: Vertex, path: Array>, ): Generator> | undefined> { if (isDone(vertex)) { return path } if (visited.includes(vertex)) { return } visited.push(vertex) yield let generators = vertex.edges .filter((e) => e.from === vertex) .map((e) => check(e.to, [...path, e])) while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (next.done === true) { if (next.value) { return next.value } } else { generators.push(gen) yield } } } } if (from instanceof Function) { let generators = this.vertices.filter(from).map((v) => check(v, [])) while (generators.length) { let prev = generators generators = [] for (let gen of prev) { const next = gen.next() if (next.done === true) { if (next.value) { return next.value } } else { generators.push(gen) } } } } else { const gen = check(from, []) while (true) { const next = gen.next() if (next.done) { return next.value } } } } }