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.

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