import { intersect } from "./set" export type Graph = { numVertices: number, numEdges: number, vertices: { [v: string]: V }, edges: { [v: string]: Set } } export type AddVertex = (name: string, v: V) => void export type AddEdge = (source: string, dest: string) => void export const createGraph = (cons: (v: AddVertex, e: AddEdge) => void): Graph => { const g: Graph = { numVertices: 0, numEdges: 0, vertices: {}, edges: {} } const addVertex = (name: string, v: V) => { if (typeof g.vertices[name] !== "undefined") { return } g.vertices[name] = v g.edges[name] = new Set() g.numVertices++ } const addEdge = (source: string, dest: string) => { if (typeof g.vertices[source] === "undefined") { throw new Error(`vertex \`${source}' does not exist in graph`) } else if (typeof g.vertices[dest] === "undefined") { throw new Error(`vertex \`${dest}' does not exist in graph`) } g.edges[source].add(dest) g.edges[dest].add(source) g.numEdges++ } cons(addVertex, addEdge) return g } export const neighbors = (g: Graph, name: string): Set => { if (typeof g.vertices[name] === "undefined") { throw new Error(`vertex \`${name}' does not exist in graph`) } return g.edges[name] } export const vertices = (g: Graph): Set => new Set(Object.keys(g.vertices)) export const edgeConnects = (g: Graph, v1: string, v2: string): boolean => g.edges[v1].has(v2) export const maxCardinalitySearch = (g: Graph): Array => { const weights: { [s: string]: number } = {} const ordering: Array = [] const W = vertices(g) const numVertices = W.size for (let i = 0; i < numVertices; ++i) { const v = findMaxWeight(weights, W) ordering.push(v) intersect(W, neighbors(g, v)).forEach(x => weights[x] = (weights[x] || 0) + 1 ) W.delete(v) } return ordering } const findMaxWeight = (weights: { [s: string]: number }, W: Set): string => { let maxV = null let maxWeight = -Infinity W.forEach(v => { const vWeight = weights[v] || 0 if (vWeight > maxWeight) { maxV = v maxWeight = vWeight } }) if (maxV === null) { throw new Error(`remaining vertex set is empty`) } return maxV } export type GraphColoring = { numColors: number, colors: VertexColoring, } export type VertexColoring = { [s: string]: number, } export const colorGreedy = (g: Graph, vertexOrdering: Array): GraphColoring => { if (vertexOrdering.length !== g.numVertices) { throw new Error("ordering does not cover all vertices") } const coloring: GraphColoring = { numColors: 0, colors: {} } const usedColors: { [c: number]: boolean } = {} vertexOrdering.forEach(v => { const ns = neighbors(g, v) let color = 0 while (!colorAvailable(color, ns, coloring.colors)) { color++ } coloring.colors[v] = color usedColors[color] = true }) coloring.numColors = Object.keys(usedColors).length return coloring } const colorAvailable = (color: number, neighbors: Set, colors: VertexColoring): boolean => { // TODO: Why can't I just iterate over neighbors directly...? for (const nbor of Array.from(neighbors)) { if (colors[nbor] === color) { return false } } return true }