diff --git a/lib/data/graph.ts b/lib/data/graph.ts index c26ac53..804d1a8 100644 --- a/lib/data/graph.ts +++ b/lib/data/graph.ts @@ -1,6 +1,8 @@ import { intersect } from "./set" export type Graph = { + numVertices: number, + numEdges: number, vertices: { [v: string]: V }, @@ -13,11 +15,12 @@ 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 = { vertices: {}, edges: {} } + const g: Graph = { numVertices: 0, numEdges: 0, vertices: {}, edges: {} } const addVertex = (name: string, v: V) => { g.vertices[name] = v g.edges[name] = new Set() + g.numVertices++ } const addEdge = (source: string, dest: string) => { @@ -29,6 +32,7 @@ export const createGraph = (cons: (v: AddVertex, e: AddEdge) => void): Gra g.edges[source].add(dest) g.edges[dest].add(source) + g.numEdges++ } cons(addVertex, addEdge) @@ -85,3 +89,47 @@ const findMaxWeight = (weights: { [s: string]: number }, W: Set): string 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 = {} + + 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 +} diff --git a/test/data/graph.spec.ts b/test/data/graph.spec.ts index 4850bd1..7c43b58 100644 --- a/test/data/graph.spec.ts +++ b/test/data/graph.spec.ts @@ -1,6 +1,6 @@ import { expect } from "chai" -import { createGraph, neighbors, vertices } from "../../lib/data/graph" +import { colorGreedy, createGraph, neighbors, vertices } from "../../lib/data/graph" const g = createGraph((v, e) => { v("a", true) @@ -37,4 +37,22 @@ describe("graph", () => { expect(vs).to.deep.equal(new Set(["a", "b", "c", "d"])) }) }) + + describe("colorGreedy", () => { + it("colors graph", () => { + const g = createGraph((v, e) => { + [0, 1, 2, 3, 4].forEach(i => v("x" + i, true)) + + e("x0", "x1") + e("x1", "x2") + e("x2", "x3") + }) + + const coloring1 = colorGreedy(g, ["x0", "x1", "x2", "x3", "x4"]) + expect(coloring1.numColors).to.equal(2) + + const coloring2 = colorGreedy(g, ["x0", "x3", "x1", "x2", "x4"]) + expect(coloring2.numColors).to.equal(3) + }) + }) })