@graphty/algorithms 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +827 -60
  2. package/dist/algorithms.d.ts +2 -0
  3. package/dist/algorithms.d.ts.map +1 -0
  4. package/dist/algorithms.js +2 -0
  5. package/dist/algorithms.js.map +1 -0
  6. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  7. package/dist/src/algorithms/centrality/betweenness.js +22 -7
  8. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  9. package/dist/src/algorithms/centrality/closeness.js +7 -7
  10. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  11. package/dist/src/algorithms/centrality/degree.js +1 -1
  12. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  13. package/dist/src/algorithms/centrality/eigenvector.d.ts +27 -0
  14. package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -0
  15. package/dist/src/algorithms/centrality/eigenvector.js +113 -0
  16. package/dist/src/algorithms/centrality/eigenvector.js.map +1 -0
  17. package/dist/src/algorithms/centrality/hits.d.ts +35 -0
  18. package/dist/src/algorithms/centrality/hits.d.ts.map +1 -0
  19. package/dist/src/algorithms/centrality/hits.js +146 -0
  20. package/dist/src/algorithms/centrality/hits.js.map +1 -0
  21. package/dist/src/algorithms/centrality/index.d.ts +6 -0
  22. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  23. package/dist/src/algorithms/centrality/index.js +3 -0
  24. package/dist/src/algorithms/centrality/index.js.map +1 -1
  25. package/dist/src/algorithms/centrality/katz.d.ts +30 -0
  26. package/dist/src/algorithms/centrality/katz.d.ts.map +1 -0
  27. package/dist/src/algorithms/centrality/katz.js +85 -0
  28. package/dist/src/algorithms/centrality/katz.js.map +1 -0
  29. package/dist/src/algorithms/centrality/pagerank.js +3 -3
  30. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  31. package/dist/src/algorithms/community/girvan-newman.d.ts +23 -0
  32. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -0
  33. package/dist/src/algorithms/community/girvan-newman.js +297 -0
  34. package/dist/src/algorithms/community/girvan-newman.js.map +1 -0
  35. package/dist/src/algorithms/community/index.d.ts +12 -0
  36. package/dist/src/algorithms/community/index.d.ts.map +1 -0
  37. package/dist/src/algorithms/community/index.js +12 -0
  38. package/dist/src/algorithms/community/index.js.map +1 -0
  39. package/dist/src/algorithms/community/label-propagation.d.ts +42 -0
  40. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -0
  41. package/dist/src/algorithms/community/label-propagation.js +307 -0
  42. package/dist/src/algorithms/community/label-propagation.js.map +1 -0
  43. package/dist/src/algorithms/community/leiden.d.ts +33 -0
  44. package/dist/src/algorithms/community/leiden.d.ts.map +1 -0
  45. package/dist/src/algorithms/community/leiden.js +434 -0
  46. package/dist/src/algorithms/community/leiden.js.map +1 -0
  47. package/dist/src/algorithms/community/louvain.d.ts +19 -0
  48. package/dist/src/algorithms/community/louvain.d.ts.map +1 -0
  49. package/dist/src/algorithms/community/louvain.js +219 -0
  50. package/dist/src/algorithms/community/louvain.js.map +1 -0
  51. package/dist/src/algorithms/components/connected.js +7 -7
  52. package/dist/src/algorithms/components/connected.js.map +1 -1
  53. package/dist/src/algorithms/index.d.ts +7 -0
  54. package/dist/src/algorithms/index.d.ts.map +1 -1
  55. package/dist/src/algorithms/index.js +14 -0
  56. package/dist/src/algorithms/index.js.map +1 -1
  57. package/dist/src/algorithms/matching/bipartite.d.ts +37 -0
  58. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -0
  59. package/dist/src/algorithms/matching/bipartite.js +127 -0
  60. package/dist/src/algorithms/matching/bipartite.js.map +1 -0
  61. package/dist/src/algorithms/matching/index.d.ts +8 -0
  62. package/dist/src/algorithms/matching/index.d.ts.map +1 -0
  63. package/dist/src/algorithms/matching/index.js +6 -0
  64. package/dist/src/algorithms/matching/index.js.map +1 -0
  65. package/dist/src/algorithms/matching/isomorphism.d.ts +32 -0
  66. package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -0
  67. package/dist/src/algorithms/matching/isomorphism.js +291 -0
  68. package/dist/src/algorithms/matching/isomorphism.js.map +1 -0
  69. package/dist/src/algorithms/mst/index.d.ts +4 -0
  70. package/dist/src/algorithms/mst/index.d.ts.map +1 -0
  71. package/dist/src/algorithms/mst/index.js +3 -0
  72. package/dist/src/algorithms/mst/index.js.map +1 -0
  73. package/dist/src/algorithms/mst/kruskal.d.ts +9 -0
  74. package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -0
  75. package/dist/src/algorithms/mst/kruskal.js +43 -0
  76. package/dist/src/algorithms/mst/kruskal.js.map +1 -0
  77. package/dist/src/algorithms/mst/prim.d.ts +5 -0
  78. package/dist/src/algorithms/mst/prim.d.ts.map +1 -0
  79. package/dist/src/algorithms/mst/prim.js +63 -0
  80. package/dist/src/algorithms/mst/prim.js.map +1 -0
  81. package/dist/src/algorithms/shortest-path/bellman-ford.js +2 -2
  82. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  83. package/dist/src/algorithms/shortest-path/dijkstra.js +5 -5
  84. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  85. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +14 -0
  86. package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -0
  87. package/dist/src/algorithms/shortest-path/floyd-warshall.js +129 -0
  88. package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -0
  89. package/dist/src/algorithms/shortest-path/index.d.ts +2 -0
  90. package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
  91. package/dist/src/algorithms/shortest-path/index.js +1 -0
  92. package/dist/src/algorithms/shortest-path/index.js.map +1 -1
  93. package/dist/src/algorithms/traversal/bfs.js +2 -2
  94. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  95. package/dist/src/algorithms/traversal/dfs.js +11 -11
  96. package/dist/src/algorithms/traversal/dfs.js.map +1 -1
  97. package/dist/src/clustering/hierarchical.d.ts +48 -0
  98. package/dist/src/clustering/hierarchical.d.ts.map +1 -0
  99. package/dist/src/clustering/hierarchical.js +439 -0
  100. package/dist/src/clustering/hierarchical.js.map +1 -0
  101. package/dist/src/clustering/index.d.ts +7 -0
  102. package/dist/src/clustering/index.d.ts.map +1 -0
  103. package/dist/src/clustering/index.js +5 -0
  104. package/dist/src/clustering/index.js.map +1 -0
  105. package/dist/src/clustering/k-core.d.ts +63 -0
  106. package/dist/src/clustering/k-core.d.ts.map +1 -0
  107. package/dist/src/clustering/k-core.js +377 -0
  108. package/dist/src/clustering/k-core.js.map +1 -0
  109. package/dist/src/clustering/mcl.d.ts +36 -0
  110. package/dist/src/clustering/mcl.d.ts.map +1 -0
  111. package/dist/src/clustering/mcl.js +343 -0
  112. package/dist/src/clustering/mcl.js.map +1 -0
  113. package/dist/src/clustering/spectral.d.ts +28 -0
  114. package/dist/src/clustering/spectral.d.ts.map +1 -0
  115. package/dist/src/clustering/spectral.js +447 -0
  116. package/dist/src/clustering/spectral.js.map +1 -0
  117. package/dist/src/core/graph.d.ts +9 -0
  118. package/dist/src/core/graph.d.ts.map +1 -1
  119. package/dist/src/core/graph.js +31 -11
  120. package/dist/src/core/graph.js.map +1 -1
  121. package/dist/src/data-structures/priority-queue.js +0 -2
  122. package/dist/src/data-structures/priority-queue.js.map +1 -1
  123. package/dist/src/data-structures/union-find.js +2 -5
  124. package/dist/src/data-structures/union-find.js.map +1 -1
  125. package/dist/src/flow/ford-fulkerson.d.ts +53 -0
  126. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -0
  127. package/dist/src/flow/ford-fulkerson.js +355 -0
  128. package/dist/src/flow/ford-fulkerson.js.map +1 -0
  129. package/dist/src/flow/index.d.ts +3 -0
  130. package/dist/src/flow/index.d.ts.map +1 -0
  131. package/dist/src/flow/index.js +3 -0
  132. package/dist/src/flow/index.js.map +1 -0
  133. package/dist/src/flow/min-cut.d.ts +49 -0
  134. package/dist/src/flow/min-cut.d.ts.map +1 -0
  135. package/dist/src/flow/min-cut.js +383 -0
  136. package/dist/src/flow/min-cut.js.map +1 -0
  137. package/dist/src/index.d.ts +1 -1
  138. package/dist/src/index.d.ts.map +1 -1
  139. package/dist/src/index.js.map +1 -1
  140. package/dist/src/link-prediction/adamic-adar.d.ts +56 -0
  141. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -0
  142. package/dist/src/link-prediction/adamic-adar.js +256 -0
  143. package/dist/src/link-prediction/adamic-adar.js.map +1 -0
  144. package/dist/src/link-prediction/common-neighbors.d.ts +50 -0
  145. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -0
  146. package/dist/src/link-prediction/common-neighbors.js +155 -0
  147. package/dist/src/link-prediction/common-neighbors.js.map +1 -0
  148. package/dist/src/link-prediction/index.d.ts +7 -0
  149. package/dist/src/link-prediction/index.d.ts.map +1 -0
  150. package/dist/src/link-prediction/index.js +6 -0
  151. package/dist/src/link-prediction/index.js.map +1 -0
  152. package/dist/src/pathfinding/astar.d.ts +50 -0
  153. package/dist/src/pathfinding/astar.d.ts.map +1 -0
  154. package/dist/src/pathfinding/astar.js +180 -0
  155. package/dist/src/pathfinding/astar.js.map +1 -0
  156. package/dist/src/pathfinding/index.d.ts +3 -0
  157. package/dist/src/pathfinding/index.d.ts.map +1 -0
  158. package/dist/src/pathfinding/index.js +3 -0
  159. package/dist/src/pathfinding/index.js.map +1 -0
  160. package/dist/src/pathfinding/utils.d.ts +18 -0
  161. package/dist/src/pathfinding/utils.d.ts.map +1 -0
  162. package/dist/src/pathfinding/utils.js +68 -0
  163. package/dist/src/pathfinding/utils.js.map +1 -0
  164. package/dist/src/types/index.d.ts +9 -0
  165. package/dist/src/types/index.d.ts.map +1 -1
  166. package/dist/src/utils/graphNode.d.ts +8 -0
  167. package/dist/src/utils/graphNode.d.ts.map +1 -0
  168. package/dist/src/utils/graphNode.js +2 -0
  169. package/dist/src/utils/graphNode.js.map +1 -0
  170. package/dist/src/utils/priorityQueue.d.ts +21 -0
  171. package/dist/src/utils/priorityQueue.d.ts.map +1 -0
  172. package/dist/src/utils/priorityQueue.js +101 -0
  173. package/dist/src/utils/priorityQueue.js.map +1 -0
  174. package/package.json +3 -1
  175. package/src/algorithms/centrality/betweenness.ts +25 -7
  176. package/src/algorithms/centrality/closeness.ts +7 -7
  177. package/src/algorithms/centrality/degree.ts +1 -1
  178. package/src/algorithms/centrality/eigenvector.ts +162 -0
  179. package/src/algorithms/centrality/hits.ts +210 -0
  180. package/src/algorithms/centrality/index.ts +6 -0
  181. package/src/algorithms/centrality/katz.ts +139 -0
  182. package/src/algorithms/centrality/pagerank.ts +3 -3
  183. package/src/algorithms/community/girvan-newman.ts +372 -0
  184. package/src/algorithms/community/index.ts +13 -0
  185. package/src/algorithms/community/label-propagation.ts +392 -0
  186. package/src/algorithms/community/leiden.ts +572 -0
  187. package/src/algorithms/community/louvain.ts +298 -0
  188. package/src/algorithms/components/connected.ts +7 -7
  189. package/src/algorithms/index.ts +21 -0
  190. package/src/algorithms/matching/bipartite.ts +179 -0
  191. package/src/algorithms/matching/index.ts +8 -0
  192. package/src/algorithms/matching/isomorphism.ts +392 -0
  193. package/src/algorithms/mst/index.ts +4 -0
  194. package/src/algorithms/mst/kruskal.ts +61 -0
  195. package/src/algorithms/mst/prim.ts +80 -0
  196. package/src/algorithms/shortest-path/bellman-ford.ts +2 -2
  197. package/src/algorithms/shortest-path/dijkstra.ts +5 -5
  198. package/src/algorithms/shortest-path/floyd-warshall.ts +170 -0
  199. package/src/algorithms/shortest-path/index.ts +2 -0
  200. package/src/algorithms/traversal/bfs.ts +2 -2
  201. package/src/algorithms/traversal/dfs.ts +11 -11
  202. package/src/clustering/hierarchical.ts +555 -0
  203. package/src/clustering/index.ts +6 -0
  204. package/src/clustering/k-core.ts +460 -0
  205. package/src/clustering/mcl.ts +454 -0
  206. package/src/clustering/spectral.ts +545 -0
  207. package/src/core/graph.ts +34 -6
  208. package/src/data-structures/union-find.ts +2 -2
  209. package/src/flow/ford-fulkerson.ts +459 -0
  210. package/src/flow/index.ts +2 -0
  211. package/src/flow/min-cut.ts +482 -0
  212. package/src/index.ts +2 -0
  213. package/src/link-prediction/adamic-adar.ts +353 -0
  214. package/src/link-prediction/common-neighbors.ts +252 -0
  215. package/src/link-prediction/index.ts +20 -0
  216. package/src/pathfinding/astar.ts +229 -0
  217. package/src/pathfinding/index.ts +2 -0
  218. package/src/pathfinding/utils.ts +87 -0
  219. package/src/types/index.ts +11 -0
  220. package/src/utils/graphNode.ts +7 -0
  221. package/src/utils/priorityQueue.ts +120 -0
  222. package/dist/test/browser/basic.test.d.ts +0 -2
  223. package/dist/test/browser/basic.test.d.ts.map +0 -1
  224. package/dist/test/browser/basic.test.js +0 -70
  225. package/dist/test/browser/basic.test.js.map +0 -1
  226. package/dist/test/unit/bellman-ford.test.d.ts +0 -2
  227. package/dist/test/unit/bellman-ford.test.d.ts.map +0 -1
  228. package/dist/test/unit/bellman-ford.test.js +0 -231
  229. package/dist/test/unit/bellman-ford.test.js.map +0 -1
  230. package/dist/test/unit/betweenness-centrality.test.d.ts +0 -2
  231. package/dist/test/unit/betweenness-centrality.test.d.ts.map +0 -1
  232. package/dist/test/unit/betweenness-centrality.test.js +0 -311
  233. package/dist/test/unit/betweenness-centrality.test.js.map +0 -1
  234. package/dist/test/unit/bfs.test.d.ts +0 -2
  235. package/dist/test/unit/bfs.test.d.ts.map +0 -1
  236. package/dist/test/unit/bfs.test.js +0 -277
  237. package/dist/test/unit/bfs.test.js.map +0 -1
  238. package/dist/test/unit/closeness.test.d.ts +0 -2
  239. package/dist/test/unit/closeness.test.d.ts.map +0 -1
  240. package/dist/test/unit/closeness.test.js +0 -266
  241. package/dist/test/unit/closeness.test.js.map +0 -1
  242. package/dist/test/unit/connected-components.test.d.ts +0 -2
  243. package/dist/test/unit/connected-components.test.d.ts.map +0 -1
  244. package/dist/test/unit/connected-components.test.js +0 -355
  245. package/dist/test/unit/connected-components.test.js.map +0 -1
  246. package/dist/test/unit/degree-centrality.test.d.ts +0 -2
  247. package/dist/test/unit/degree-centrality.test.d.ts.map +0 -1
  248. package/dist/test/unit/degree-centrality.test.js +0 -260
  249. package/dist/test/unit/degree-centrality.test.js.map +0 -1
  250. package/dist/test/unit/dfs.test.d.ts +0 -2
  251. package/dist/test/unit/dfs.test.d.ts.map +0 -1
  252. package/dist/test/unit/dfs.test.js +0 -350
  253. package/dist/test/unit/dfs.test.js.map +0 -1
  254. package/dist/test/unit/dijkstra.test.d.ts +0 -2
  255. package/dist/test/unit/dijkstra.test.d.ts.map +0 -1
  256. package/dist/test/unit/dijkstra.test.js +0 -311
  257. package/dist/test/unit/dijkstra.test.js.map +0 -1
  258. package/dist/test/unit/graph.test.d.ts +0 -2
  259. package/dist/test/unit/graph.test.d.ts.map +0 -1
  260. package/dist/test/unit/graph.test.js +0 -175
  261. package/dist/test/unit/graph.test.js.map +0 -1
  262. package/dist/test/unit/pagerank.test.d.ts +0 -2
  263. package/dist/test/unit/pagerank.test.d.ts.map +0 -1
  264. package/dist/test/unit/pagerank.test.js +0 -281
  265. package/dist/test/unit/pagerank.test.js.map +0 -1
  266. package/dist/test/unit/priority-queue.test.d.ts +0 -2
  267. package/dist/test/unit/priority-queue.test.d.ts.map +0 -1
  268. package/dist/test/unit/priority-queue.test.js +0 -189
  269. package/dist/test/unit/priority-queue.test.js.map +0 -1
  270. package/dist/test/unit/union-find.test.d.ts +0 -2
  271. package/dist/test/unit/union-find.test.d.ts.map +0 -1
  272. package/dist/test/unit/union-find.test.js +0 -245
  273. package/dist/test/unit/union-find.test.js.map +0 -1
@@ -0,0 +1,460 @@
1
+ /**
2
+ * K-Core Decomposition Algorithm
3
+ *
4
+ * Finds the k-core subgraph where each node has at least k neighbors
5
+ * within the subgraph. Used for identifying cohesive groups and
6
+ * understanding graph structure.
7
+ */
8
+
9
+ export interface KCoreResult<T> {
10
+ cores: Map<number, Set<T>>;
11
+ coreness: Map<T, number>;
12
+ maxCore: number;
13
+ }
14
+
15
+ /**
16
+ * K-Core decomposition algorithm
17
+ * Finds all k-cores in the graph and assigns coreness values to nodes
18
+ *
19
+ * @param graph - Undirected graph as adjacency list
20
+ * @returns K-core decomposition results
21
+ *
22
+ * Time Complexity: O(V + E)
23
+ * Space Complexity: O(V)
24
+ */
25
+ export function kCoreDecomposition<T>(
26
+ graph: Map<T, Set<T>>,
27
+ ): KCoreResult<T> {
28
+ if (graph.size === 0) {
29
+ return {cores: new Map(), coreness: new Map(), maxCore: 0};
30
+ }
31
+
32
+ const n = graph.size;
33
+ const nodes = Array.from(graph.keys());
34
+ const nodeToIndex = new Map<T, number>();
35
+ nodes.forEach((node, i) => nodeToIndex.set(node, i));
36
+
37
+ // Initialize data structures
38
+ const degree = new Array<number>(n).fill(0);
39
+ const pos = new Array<number>(n).fill(0);
40
+ const vert = new Array<number>(n).fill(0);
41
+ const coreness = new Map<T, number>();
42
+
43
+ // Initialize coreness for all nodes to 0
44
+ for (let i = 0; i < n; i++) {
45
+ const node = nodes[i];
46
+ if (node !== undefined) {
47
+ coreness.set(node, 0);
48
+ }
49
+ }
50
+
51
+ // Compute initial degrees
52
+ let maxDegree = 0;
53
+ for (let i = 0; i < n; i++) {
54
+ const node = nodes[i];
55
+ if (!node) {
56
+ continue;
57
+ }
58
+
59
+ const neighbors = graph.get(node);
60
+ const nodeSize = neighbors ? neighbors.size : 0;
61
+
62
+ degree[i] = nodeSize;
63
+ maxDegree = Math.max(maxDegree, nodeSize);
64
+ }
65
+
66
+ // Count nodes of each degree
67
+ const bin = new Array<number>(maxDegree + 1).fill(0);
68
+ for (let i = 0; i < n; i++) {
69
+ const deg = degree[i];
70
+ if (deg !== undefined && deg >= 0 && deg < bin.length) {
71
+ const currentCount = bin[deg];
72
+ if (currentCount !== undefined) {
73
+ bin[deg] = currentCount + 1;
74
+ }
75
+ }
76
+ }
77
+
78
+ // Starting position of each degree in sorted array
79
+ let start = 0;
80
+ for (let d = 0; d <= maxDegree; d++) {
81
+ const temp = bin[d] ?? 0;
82
+ bin[d] = start;
83
+ start += temp;
84
+ }
85
+
86
+ // Sort nodes by degree
87
+ for (let i = 0; i < n; i++) {
88
+ const deg = degree[i];
89
+ if (deg !== undefined && deg >= 0 && deg < bin.length) {
90
+ const posIndex = bin[deg] ?? 0;
91
+ pos[i] = posIndex;
92
+ vert[posIndex] = i;
93
+ const currentBin = bin[deg];
94
+ if (currentBin !== undefined) {
95
+ bin[deg] = currentBin + 1;
96
+ }
97
+ } else {
98
+ // If we can't properly place the node in vert, set its position to -1
99
+ // This shouldn't happen with our fixes, but just in case
100
+ pos[i] = -1;
101
+ }
102
+ }
103
+
104
+ // Recover starting positions
105
+ for (let d = maxDegree; d > 0; d--) {
106
+ bin[d] = bin[d - 1] ?? 0;
107
+ }
108
+ bin[0] = 0;
109
+
110
+ // Main algorithm
111
+ for (let i = 0; i < n; i++) {
112
+ const v = vert[i];
113
+ if (v === undefined) {
114
+ continue;
115
+ }
116
+
117
+ const node = nodes[v];
118
+ if (node === undefined) {
119
+ continue;
120
+ }
121
+
122
+ const degreeV = degree[v];
123
+ if (degreeV !== undefined) {
124
+ coreness.set(node, degreeV);
125
+ }
126
+
127
+ // Process neighbors
128
+ const neighbors = graph.get(node);
129
+ if (!neighbors) {
130
+ // Node has no neighbors, skip neighbor processing but coreness is already set
131
+ continue;
132
+ }
133
+
134
+ for (const neighbor of neighbors) {
135
+ const u = nodeToIndex.get(neighbor);
136
+ if (u === undefined) {
137
+ continue;
138
+ }
139
+
140
+ const degreeU = degree[u];
141
+ const degreeV = degree[v];
142
+ if (degreeU !== undefined && degreeV !== undefined && degreeU > degreeV) {
143
+ const du = degreeU;
144
+ const pu = pos[u];
145
+ const pw = bin[du] ?? 0;
146
+ const w = vert[pw];
147
+
148
+ if (u !== w && w !== undefined && pw < n && pu !== undefined) {
149
+ // Swap u and w in the sorted order
150
+ vert[pu] = w;
151
+ vert[pw] = u;
152
+ pos[u] = pw;
153
+ pos[w] = pu;
154
+ }
155
+
156
+ const currentBin = bin[du];
157
+ if (currentBin !== undefined) {
158
+ bin[du] = currentBin + 1;
159
+ }
160
+
161
+ degree[u] = degreeU - 1;
162
+ }
163
+ }
164
+ }
165
+
166
+ // Build cores map
167
+ const cores = new Map<number, Set<T>>();
168
+ let maxCore = 0;
169
+
170
+ for (const [node, core] of coreness) {
171
+ if (!cores.has(core)) {
172
+ cores.set(core, new Set());
173
+ }
174
+
175
+ const coreSet = cores.get(core);
176
+ if (coreSet) {
177
+ coreSet.add(node);
178
+ }
179
+
180
+ maxCore = Math.max(maxCore, core);
181
+ }
182
+
183
+ return {cores, coreness, maxCore};
184
+ }
185
+
186
+ /**
187
+ * Extract the k-core subgraph
188
+ * Returns nodes that belong to k-core or higher
189
+ *
190
+ * @param graph - Undirected graph
191
+ * @param k - Core number
192
+ * @returns Set of nodes in k-core or higher
193
+ */
194
+ export function getKCore<T>(
195
+ graph: Map<T, Set<T>>,
196
+ k: number,
197
+ ): Set<T> {
198
+ const {coreness} = kCoreDecomposition(graph);
199
+ const kCore = new Set<T>();
200
+
201
+ for (const [node, core] of coreness) {
202
+ if (core >= k) {
203
+ kCore.add(node);
204
+ }
205
+ }
206
+
207
+ return kCore;
208
+ }
209
+
210
+ /**
211
+ * Get the induced subgraph for k-core
212
+ * Returns the actual subgraph containing only k-core nodes
213
+ *
214
+ * @param graph - Original graph
215
+ * @param k - Core number
216
+ * @returns K-core subgraph
217
+ */
218
+ export function getKCoreSubgraph<T>(
219
+ graph: Map<T, Set<T>>,
220
+ k: number,
221
+ ): Map<T, Set<T>> {
222
+ const kCoreNodes = getKCore(graph, k);
223
+ const subgraph = new Map<T, Set<T>>();
224
+
225
+ for (const node of kCoreNodes) {
226
+ const neighbors = graph.get(node);
227
+ if (neighbors) {
228
+ const coreNeighbors = new Set<T>();
229
+ for (const neighbor of neighbors) {
230
+ if (kCoreNodes.has(neighbor)) {
231
+ coreNeighbors.add(neighbor);
232
+ }
233
+ }
234
+ subgraph.set(node, coreNeighbors);
235
+ }
236
+ }
237
+
238
+ return subgraph;
239
+ }
240
+
241
+ /**
242
+ * Degeneracy ordering of the graph
243
+ * Orders nodes by their coreness values
244
+ *
245
+ * @param graph - Undirected graph
246
+ * @returns Array of nodes ordered by degeneracy
247
+ */
248
+ export function degeneracyOrdering<T>(
249
+ graph: Map<T, Set<T>>,
250
+ ): T[] {
251
+ const degree = new Map<T, number>();
252
+ const remaining = new Map<T, Set<T>>();
253
+ const ordering: T[] = [];
254
+
255
+ // Initialize
256
+ for (const [node, neighbors] of graph) {
257
+ degree.set(node, neighbors.size);
258
+ remaining.set(node, new Set(neighbors));
259
+ }
260
+
261
+ // Build ordering
262
+ while (ordering.length < graph.size) {
263
+ // Find minimum degree node
264
+ let minDegree = Infinity;
265
+ let minNode: T | undefined;
266
+
267
+ for (const [node, deg] of degree) {
268
+ if (!ordering.includes(node) && deg < minDegree) {
269
+ minDegree = deg;
270
+ minNode = node;
271
+ }
272
+ }
273
+
274
+ if (minNode === undefined) {
275
+ break;
276
+ }
277
+
278
+ ordering.push(minNode);
279
+
280
+ // Update neighbors
281
+ const neighbors = remaining.get(minNode);
282
+ if (neighbors) {
283
+ for (const neighbor of neighbors) {
284
+ const neighborDegree = degree.get(neighbor);
285
+ if (neighborDegree !== undefined) {
286
+ degree.set(neighbor, neighborDegree - 1);
287
+ }
288
+
289
+ remaining.get(neighbor)?.delete(minNode);
290
+ }
291
+ }
292
+ }
293
+
294
+ return ordering;
295
+ }
296
+
297
+ /**
298
+ * Find k-truss subgraph (triangular k-cores)
299
+ * Each edge must be part of at least k-2 triangles
300
+ *
301
+ * @param graph - Undirected graph
302
+ * @param k - Truss number (k >= 2)
303
+ * @returns Edges in k-truss
304
+ */
305
+ export function kTruss<T>(
306
+ graph: Map<T, Set<T>>,
307
+ k: number,
308
+ ): Set<string> {
309
+ if (k < 2) {
310
+ throw new Error("k must be at least 2 for k-truss");
311
+ }
312
+
313
+ // Count triangles for each edge
314
+ const edgeTriangles = new Map<string, number>();
315
+ const edges = new Set<string>();
316
+
317
+ // Initialize edges
318
+ for (const [u, neighbors] of graph) {
319
+ for (const v of neighbors) {
320
+ if (u < v) { // Avoid duplicates
321
+ const edge = `${String(u)},${String(v)}`;
322
+ edges.add(edge);
323
+ edgeTriangles.set(edge, 0);
324
+ }
325
+ }
326
+ }
327
+
328
+ // Count triangles
329
+ for (const [u, uNeighbors] of graph) {
330
+ for (const v of uNeighbors) {
331
+ if (u < v) {
332
+ const vNeighbors = graph.get(v);
333
+ if (vNeighbors) {
334
+ // Find common neighbors (triangles)
335
+ for (const w of uNeighbors) {
336
+ if (v < w && vNeighbors.has(w)) {
337
+ // Triangle found: u-v-w
338
+ const edge1 = `${String(u)},${String(v)}`;
339
+ const edge2 = `${String(u)},${String(w)}`;
340
+ const edge3 = `${String(v)},${String(w)}`;
341
+
342
+ const edge1Count = edgeTriangles.get(edge1);
343
+ const edge2Count = edgeTriangles.get(edge2);
344
+ const edge3Count = edgeTriangles.get(edge3);
345
+ if (edge1Count !== undefined) {
346
+ edgeTriangles.set(edge1, edge1Count + 1);
347
+ }
348
+
349
+ if (edge2Count !== undefined) {
350
+ edgeTriangles.set(edge2, edge2Count + 1);
351
+ }
352
+
353
+ if (edge3Count !== undefined) {
354
+ edgeTriangles.set(edge3, edge3Count + 1);
355
+ }
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ // Remove edges with insufficient triangles
364
+ const kTrussEdges = new Set<string>(edges);
365
+ let changed = true;
366
+
367
+ while (changed) {
368
+ changed = false;
369
+ const toRemove = new Set<string>();
370
+
371
+ for (const edge of kTrussEdges) {
372
+ const triangleCount = edgeTriangles.get(edge);
373
+ if (triangleCount !== undefined && triangleCount < k - 2) {
374
+ toRemove.add(edge);
375
+ changed = true;
376
+ }
377
+ }
378
+
379
+ // Remove edges and update triangle counts
380
+ for (const edge of toRemove) {
381
+ kTrussEdges.delete(edge);
382
+ const parts = edge.split(",");
383
+ if (parts.length < 2) {
384
+ continue;
385
+ }
386
+
387
+ const [u, v] = parts;
388
+ if (!u || !v) {
389
+ continue;
390
+ }
391
+
392
+ // Update triangle counts for affected edges
393
+ const uNode = u as T;
394
+ const vNode = v as T;
395
+ const uNeighbors = graph.get(uNode);
396
+ const vNeighbors = graph.get(vNode);
397
+
398
+ if (uNeighbors && vNeighbors) {
399
+ for (const w of uNeighbors) {
400
+ if (vNeighbors.has(w)) {
401
+ const edge1 = uNode < w ? `${String(uNode)},${String(w)}` : `${String(w)},${String(uNode)}`;
402
+ const edge2 = vNode < w ? `${String(vNode)},${String(w)}` : `${String(w)},${String(vNode)}`;
403
+
404
+ if (kTrussEdges.has(edge1)) {
405
+ const edge1Count = edgeTriangles.get(edge1);
406
+ if (edge1Count !== undefined) {
407
+ edgeTriangles.set(edge1, edge1Count - 1);
408
+ }
409
+ }
410
+
411
+ if (kTrussEdges.has(edge2)) {
412
+ const edge2Count = edgeTriangles.get(edge2);
413
+ if (edge2Count !== undefined) {
414
+ edgeTriangles.set(edge2, edge2Count - 1);
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ return kTrussEdges;
424
+ }
425
+
426
+ /**
427
+ * Convert directed graph to undirected for k-core analysis
428
+ */
429
+ export function toUndirected<T>(
430
+ directedGraph: Map<T, Map<T, number>>,
431
+ ): Map<T, Set<T>> {
432
+ const undirected = new Map<T, Set<T>>();
433
+
434
+ // Initialize all nodes
435
+ for (const node of directedGraph.keys()) {
436
+ undirected.set(node, new Set());
437
+ }
438
+
439
+ // Add edges in both directions
440
+ for (const [u, neighbors] of directedGraph) {
441
+ for (const v of neighbors.keys()) {
442
+ const uNeighbors = undirected.get(u);
443
+ if (uNeighbors) {
444
+ uNeighbors.add(v);
445
+ }
446
+
447
+ if (!undirected.has(v)) {
448
+ undirected.set(v, new Set());
449
+ }
450
+
451
+ const vNeighbors = undirected.get(v);
452
+ if (vNeighbors) {
453
+ vNeighbors.add(u);
454
+ }
455
+ }
456
+ }
457
+
458
+ return undirected;
459
+ }
460
+