A "high-level" language for the Gameboy
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.6 KiB

  1. import { intersect } from "./set"
  2. export type Graph<V> = {
  3. numVertices: number,
  4. numEdges: number,
  5. vertices: {
  6. [v: string]: V
  7. },
  8. edges: {
  9. [v: string]: Set<string>
  10. }
  11. }
  12. export type AddVertex<V> = (name: string, v: V) => void
  13. export type AddEdge = (source: string, dest: string) => void
  14. export const createGraph = <V>(cons: (v: AddVertex<V>, e: AddEdge) => void): Graph<V> => {
  15. const g: Graph<V> = { numVertices: 0, numEdges: 0, vertices: {}, edges: {} }
  16. const addVertex = (name: string, v: V) => {
  17. if (typeof g.vertices[name] !== "undefined") {
  18. return
  19. }
  20. g.vertices[name] = v
  21. g.edges[name] = new Set()
  22. g.numVertices++
  23. }
  24. const addEdge = (source: string, dest: string) => {
  25. if (typeof g.vertices[source] === "undefined") {
  26. throw new Error(`vertex \`${source}' does not exist in graph`)
  27. } else if (typeof g.vertices[dest] === "undefined") {
  28. throw new Error(`vertex \`${dest}' does not exist in graph`)
  29. }
  30. g.edges[source].add(dest)
  31. g.edges[dest].add(source)
  32. g.numEdges++
  33. }
  34. cons(addVertex, addEdge)
  35. return g
  36. }
  37. export const neighbors = <V>(g: Graph<V>, name: string): Set<string> => {
  38. if (typeof g.vertices[name] === "undefined") {
  39. throw new Error(`vertex \`${name}' does not exist in graph`)
  40. }
  41. return g.edges[name]
  42. }
  43. export const vertices = <V>(g: Graph<V>): Set<string> => new Set(Object.keys(g.vertices))
  44. export const edgeConnects = <V>(g: Graph<V>, v1: string, v2: string): boolean => g.edges[v1].has(v2)
  45. export const maxCardinalitySearch = <V>(g: Graph<V>): Array<string> => {
  46. const weights: { [s: string]: number } = {}
  47. const ordering: Array<string> = []
  48. const W = vertices(g)
  49. const numVertices = W.size
  50. for (let i = 0; i < numVertices; ++i) {
  51. const v = findMaxWeight(weights, W)
  52. ordering.push(v)
  53. intersect(W, neighbors(g, v)).forEach(x =>
  54. weights[x] = (weights[x] || 0) + 1
  55. )
  56. W.delete(v)
  57. }
  58. return ordering
  59. }
  60. const findMaxWeight = (weights: { [s: string]: number }, W: Set<string>): string => {
  61. let maxV = null
  62. let maxWeight = -Infinity
  63. W.forEach(v => {
  64. const vWeight = weights[v] || 0
  65. if (vWeight > maxWeight) {
  66. maxV = v
  67. maxWeight = vWeight
  68. }
  69. })
  70. if (maxV === null) {
  71. throw new Error(`remaining vertex set is empty`)
  72. }
  73. return maxV
  74. }
  75. export type GraphColoring = {
  76. numColors: number,
  77. colors: VertexColoring,
  78. }
  79. export type VertexColoring = {
  80. [s: string]: number,
  81. }
  82. export const colorGreedy = <V>(g: Graph<V>, vertexOrdering: Array<string>): GraphColoring => {
  83. if (vertexOrdering.length !== g.numVertices) {
  84. throw new Error("ordering does not cover all vertices")
  85. }
  86. const coloring: GraphColoring = { numColors: 0, colors: {} }
  87. const usedColors: { [c: number]: boolean } = {}
  88. vertexOrdering.forEach(v => {
  89. const ns = neighbors(g, v)
  90. let color = 0
  91. while (!colorAvailable(color, ns, coloring.colors)) {
  92. color++
  93. }
  94. coloring.colors[v] = color
  95. usedColors[color] = true
  96. })
  97. coloring.numColors = Object.keys(usedColors).length
  98. return coloring
  99. }
  100. const colorAvailable = (color: number, neighbors: Set<string>, colors: VertexColoring): boolean => {
  101. // TODO: Why can't I just iterate over neighbors directly...?
  102. for (const nbor of Array.from(neighbors)) {
  103. if (colors[nbor] === color) {
  104. return false
  105. }
  106. }
  107. return true
  108. }