@graphty/graph-format 0.0.0 → 0.2.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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -43
  3. package/dist/graph-format.d.ts +1 -0
  4. package/dist/graph-format.d.ts.map +1 -0
  5. package/dist/graph-format.js +15798 -0
  6. package/dist/graph-format.js.map +1 -0
  7. package/dist/src/builder/arena.d.ts +85 -0
  8. package/dist/src/builder/arena.d.ts.map +1 -0
  9. package/dist/src/builder/arena.js +154 -0
  10. package/dist/src/builder/arena.js.map +1 -0
  11. package/dist/src/builder/compact.d.ts +515 -0
  12. package/dist/src/builder/compact.d.ts.map +1 -0
  13. package/dist/src/builder/compact.js +1553 -0
  14. package/dist/src/builder/compact.js.map +1 -0
  15. package/dist/src/builder/counting-sort.d.ts +96 -0
  16. package/dist/src/builder/counting-sort.d.ts.map +1 -0
  17. package/dist/src/builder/counting-sort.js +430 -0
  18. package/dist/src/builder/counting-sort.js.map +1 -0
  19. package/dist/src/builder/freeze.d.ts +72 -0
  20. package/dist/src/builder/freeze.d.ts.map +1 -0
  21. package/dist/src/builder/freeze.js +548 -0
  22. package/dist/src/builder/freeze.js.map +1 -0
  23. package/dist/src/builder/graph-builder.d.ts +602 -0
  24. package/dist/src/builder/graph-builder.d.ts.map +1 -0
  25. package/dist/src/builder/graph-builder.js +1940 -0
  26. package/dist/src/builder/graph-builder.js.map +1 -0
  27. package/dist/src/builder/options.d.ts +16 -0
  28. package/dist/src/builder/options.d.ts.map +1 -0
  29. package/dist/src/builder/options.js +21 -0
  30. package/dist/src/builder/options.js.map +1 -0
  31. package/dist/src/columns/bitmap.d.ts +96 -0
  32. package/dist/src/columns/bitmap.d.ts.map +1 -0
  33. package/dist/src/columns/bitmap.js +184 -0
  34. package/dist/src/columns/bitmap.js.map +1 -0
  35. package/dist/src/columns/column.d.ts +220 -0
  36. package/dist/src/columns/column.d.ts.map +1 -0
  37. package/dist/src/columns/column.js +2109 -0
  38. package/dist/src/columns/column.js.map +1 -0
  39. package/dist/src/columns/dictionary.d.ts +60 -0
  40. package/dist/src/columns/dictionary.d.ts.map +1 -0
  41. package/dist/src/columns/dictionary.js +102 -0
  42. package/dist/src/columns/dictionary.js.map +1 -0
  43. package/dist/src/columns/growable.d.ts +198 -0
  44. package/dist/src/columns/growable.d.ts.map +1 -0
  45. package/dist/src/columns/growable.js +296 -0
  46. package/dist/src/columns/growable.js.map +1 -0
  47. package/dist/src/columns/infer.d.ts +121 -0
  48. package/dist/src/columns/infer.d.ts.map +1 -0
  49. package/dist/src/columns/infer.js +268 -0
  50. package/dist/src/columns/infer.js.map +1 -0
  51. package/dist/src/columns/remap.d.ts +143 -0
  52. package/dist/src/columns/remap.d.ts.map +1 -0
  53. package/dist/src/columns/remap.js +588 -0
  54. package/dist/src/columns/remap.js.map +1 -0
  55. package/dist/src/columns/table.d.ts +184 -0
  56. package/dist/src/columns/table.d.ts.map +1 -0
  57. package/dist/src/columns/table.js +485 -0
  58. package/dist/src/columns/table.js.map +1 -0
  59. package/dist/src/constants.d.ts +34 -0
  60. package/dist/src/constants.d.ts.map +1 -0
  61. package/dist/src/constants.js +79 -0
  62. package/dist/src/constants.js.map +1 -0
  63. package/dist/src/errors.d.ts +76 -0
  64. package/dist/src/errors.d.ts.map +1 -0
  65. package/dist/src/errors.js +32 -0
  66. package/dist/src/errors.js.map +1 -0
  67. package/dist/src/ids/edge-id-index.d.ts +77 -0
  68. package/dist/src/ids/edge-id-index.d.ts.map +1 -0
  69. package/dist/src/ids/edge-id-index.js +155 -0
  70. package/dist/src/ids/edge-id-index.js.map +1 -0
  71. package/dist/src/ids/node-id-map.d.ts +280 -0
  72. package/dist/src/ids/node-id-map.d.ts.map +1 -0
  73. package/dist/src/ids/node-id-map.js +1139 -0
  74. package/dist/src/ids/node-id-map.js.map +1 -0
  75. package/dist/src/ids/string-store.d.ts +219 -0
  76. package/dist/src/ids/string-store.d.ts.map +1 -0
  77. package/dist/src/ids/string-store.js +526 -0
  78. package/dist/src/ids/string-store.js.map +1 -0
  79. package/dist/src/index.d.ts +30 -0
  80. package/dist/src/index.d.ts.map +1 -0
  81. package/dist/src/index.js +29 -0
  82. package/dist/src/index.js.map +1 -0
  83. package/dist/src/populate/from-csr.d.ts +61 -0
  84. package/dist/src/populate/from-csr.d.ts.map +1 -0
  85. package/dist/src/populate/from-csr.js +706 -0
  86. package/dist/src/populate/from-csr.js.map +1 -0
  87. package/dist/src/populate/from-edge-arrays.d.ts +51 -0
  88. package/dist/src/populate/from-edge-arrays.d.ts.map +1 -0
  89. package/dist/src/populate/from-edge-arrays.js +217 -0
  90. package/dist/src/populate/from-edge-arrays.js.map +1 -0
  91. package/dist/src/populate/from-records.d.ts +51 -0
  92. package/dist/src/populate/from-records.d.ts.map +1 -0
  93. package/dist/src/populate/from-records.js +447 -0
  94. package/dist/src/populate/from-records.js.map +1 -0
  95. package/dist/src/snapshot/derived.d.ts +211 -0
  96. package/dist/src/snapshot/derived.d.ts.map +1 -0
  97. package/dist/src/snapshot/derived.js +1254 -0
  98. package/dist/src/snapshot/derived.js.map +1 -0
  99. package/dist/src/snapshot/graph-meta.d.ts +23 -0
  100. package/dist/src/snapshot/graph-meta.d.ts.map +1 -0
  101. package/dist/src/snapshot/graph-meta.js +159 -0
  102. package/dist/src/snapshot/graph-meta.js.map +1 -0
  103. package/dist/src/snapshot/graph-snapshot.d.ts +509 -0
  104. package/dist/src/snapshot/graph-snapshot.d.ts.map +1 -0
  105. package/dist/src/snapshot/graph-snapshot.js +1247 -0
  106. package/dist/src/snapshot/graph-snapshot.js.map +1 -0
  107. package/dist/src/snapshot/hash.d.ts +90 -0
  108. package/dist/src/snapshot/hash.d.ts.map +1 -0
  109. package/dist/src/snapshot/hash.js +228 -0
  110. package/dist/src/snapshot/hash.js.map +1 -0
  111. package/dist/src/snapshot/queries.d.ts +74 -0
  112. package/dist/src/snapshot/queries.d.ts.map +1 -0
  113. package/dist/src/snapshot/queries.js +131 -0
  114. package/dist/src/snapshot/queries.js.map +1 -0
  115. package/dist/src/snapshot/validate.d.ts +192 -0
  116. package/dist/src/snapshot/validate.d.ts.map +1 -0
  117. package/dist/src/snapshot/validate.js +990 -0
  118. package/dist/src/snapshot/validate.js.map +1 -0
  119. package/dist/src/snapshot/views.d.ts +274 -0
  120. package/dist/src/snapshot/views.d.ts.map +1 -0
  121. package/dist/src/snapshot/views.js +856 -0
  122. package/dist/src/snapshot/views.js.map +1 -0
  123. package/dist/src/types/builder.d.ts +456 -0
  124. package/dist/src/types/builder.d.ts.map +1 -0
  125. package/dist/src/types/builder.js +11 -0
  126. package/dist/src/types/builder.js.map +1 -0
  127. package/dist/src/types/columns.d.ts +615 -0
  128. package/dist/src/types/columns.d.ts.map +1 -0
  129. package/dist/src/types/columns.js +14 -0
  130. package/dist/src/types/columns.js.map +1 -0
  131. package/dist/src/types/index.d.ts +14 -0
  132. package/dist/src/types/index.d.ts.map +1 -0
  133. package/dist/src/types/index.js +11 -0
  134. package/dist/src/types/index.js.map +1 -0
  135. package/dist/src/types/internal.d.ts +172 -0
  136. package/dist/src/types/internal.d.ts.map +1 -0
  137. package/dist/src/types/internal.js +13 -0
  138. package/dist/src/types/internal.js.map +1 -0
  139. package/dist/src/types/snapshot.d.ts +801 -0
  140. package/dist/src/types/snapshot.d.ts.map +1 -0
  141. package/dist/src/types/snapshot.js +14 -0
  142. package/dist/src/types/snapshot.js.map +1 -0
  143. package/dist/src/types/wire.d.ts +196 -0
  144. package/dist/src/types/wire.d.ts.map +1 -0
  145. package/dist/src/types/wire.js +8 -0
  146. package/dist/src/types/wire.js.map +1 -0
  147. package/dist/src/util/mask.d.ts +55 -0
  148. package/dist/src/util/mask.d.ts.map +1 -0
  149. package/dist/src/util/mask.js +71 -0
  150. package/dist/src/util/mask.js.map +1 -0
  151. package/dist/src/util/options.d.ts +22 -0
  152. package/dist/src/util/options.d.ts.map +1 -0
  153. package/dist/src/util/options.js +49 -0
  154. package/dist/src/util/options.js.map +1 -0
  155. package/dist/src/util/shared-buffers.d.ts +42 -0
  156. package/dist/src/util/shared-buffers.d.ts.map +1 -0
  157. package/dist/src/util/shared-buffers.js +52 -0
  158. package/dist/src/util/shared-buffers.js.map +1 -0
  159. package/dist/src/util/typed-array.d.ts +205 -0
  160. package/dist/src/util/typed-array.d.ts.map +1 -0
  161. package/dist/src/util/typed-array.js +289 -0
  162. package/dist/src/util/typed-array.js.map +1 -0
  163. package/dist/src/wire/bytes.d.ts +69 -0
  164. package/dist/src/wire/bytes.d.ts.map +1 -0
  165. package/dist/src/wire/bytes.js +361 -0
  166. package/dist/src/wire/bytes.js.map +1 -0
  167. package/dist/src/wire/carried-views.d.ts +111 -0
  168. package/dist/src/wire/carried-views.d.ts.map +1 -0
  169. package/dist/src/wire/carried-views.js +286 -0
  170. package/dist/src/wire/carried-views.js.map +1 -0
  171. package/dist/src/wire/from-wire.d.ts +144 -0
  172. package/dist/src/wire/from-wire.d.ts.map +1 -0
  173. package/dist/src/wire/from-wire.js +1425 -0
  174. package/dist/src/wire/from-wire.js.map +1 -0
  175. package/dist/src/wire/to-wire.d.ts +171 -0
  176. package/dist/src/wire/to-wire.d.ts.map +1 -0
  177. package/dist/src/wire/to-wire.js +652 -0
  178. package/dist/src/wire/to-wire.js.map +1 -0
  179. package/dist/tsconfig.build.tsbuildinfo +1 -0
  180. package/package.json +72 -7
  181. package/src/builder/arena.ts +192 -0
  182. package/src/builder/compact.ts +1811 -0
  183. package/src/builder/counting-sort.ts +506 -0
  184. package/src/builder/freeze.ts +689 -0
  185. package/src/builder/graph-builder.ts +2264 -0
  186. package/src/builder/options.ts +29 -0
  187. package/src/columns/bitmap.ts +196 -0
  188. package/src/columns/column.ts +2574 -0
  189. package/src/columns/dictionary.ts +114 -0
  190. package/src/columns/growable.ts +350 -0
  191. package/src/columns/infer.ts +285 -0
  192. package/src/columns/remap.ts +663 -0
  193. package/src/columns/table.ts +568 -0
  194. package/src/constants.ts +89 -0
  195. package/src/errors.ts +115 -0
  196. package/src/ids/edge-id-index.ts +179 -0
  197. package/src/ids/node-id-map.ts +1340 -0
  198. package/src/ids/string-store.ts +589 -0
  199. package/src/index.ts +139 -0
  200. package/src/lib-resizable-array-buffer.d.ts +34 -0
  201. package/src/populate/from-csr.ts +802 -0
  202. package/src/populate/from-edge-arrays.ts +257 -0
  203. package/src/populate/from-records.ts +536 -0
  204. package/src/snapshot/derived.ts +1486 -0
  205. package/src/snapshot/graph-meta.ts +167 -0
  206. package/src/snapshot/graph-snapshot.ts +1503 -0
  207. package/src/snapshot/hash.ts +241 -0
  208. package/src/snapshot/queries.ts +136 -0
  209. package/src/snapshot/validate.ts +1108 -0
  210. package/src/snapshot/views.ts +943 -0
  211. package/src/types/builder.ts +480 -0
  212. package/src/types/columns.ts +727 -0
  213. package/src/types/index.ts +14 -0
  214. package/src/types/internal.ts +195 -0
  215. package/src/types/snapshot.ts +886 -0
  216. package/src/types/wire.ts +207 -0
  217. package/src/util/mask.ts +89 -0
  218. package/src/util/options.ts +57 -0
  219. package/src/util/shared-buffers.ts +56 -0
  220. package/src/util/typed-array.ts +371 -0
  221. package/src/wire/bytes.ts +440 -0
  222. package/src/wire/carried-views.ts +327 -0
  223. package/src/wire/from-wire.ts +1690 -0
  224. package/src/wire/to-wire.ts +755 -0
@@ -0,0 +1,856 @@
1
+ /**
2
+ * The cached views of design section 7.2 as pure functions of a snapshot's core arrays, plus the
3
+ * per-arc / per-edge boundary helpers `foldArcs` and `expandEdges` of design section 7.5.
4
+ *
5
+ * Every function here computes one view from the public core (rowPtr, colIdx, weights, arcToEdge,
6
+ * edgeToArc, flags, counts) in O(n + m) or O(n log d) and returns a fresh object; GraphSnapshot owns
7
+ * the per-instance cache (invariant I17: computed once, never invalidated, SHARED -- a caller that
8
+ * needs scratch calls `.slice()`). Aliasing is deliberate and documented per view: `coo().dst` is
9
+ * `colIdx`, `reverse()` of an undirected snapshot is the forward arrays, an identity permutation
10
+ * lets `edgeList().weights` alias `weights`, and so on, so `viewByteLength()` counts only the bytes a
11
+ * view owns.
12
+ *
13
+ * Nothing here allocates an identity permutation that the snapshot has not materialised: view objects
14
+ * reach `arcToEdge` / `edgeToArc` through getters, so touching `coo()` on a directed identity
15
+ * snapshot costs one `src` array and nothing else until `coo().arcToEdge` is read.
16
+ */
17
+ import { GraphFormatError } from "../errors.js";
18
+ import { assertOneOf } from "../util/options.js";
19
+ import { arcRangeIn } from "./queries.js";
20
+ // ============================================================ reverse
21
+ /**
22
+ * The cuGraph degree tiers of `degreeOrder()`: a node with at least DEGREE_TIER_HIGH arcs is scheduled
23
+ * per workgroup, at least DEGREE_TIER_MID per subgroup, at least DEGREE_TIER_LOW per thread; degree 0
24
+ * nodes form the trailing segment (design section 7.2).
25
+ */
26
+ export const DEGREE_TIER_HIGH = 1024;
27
+ /** Lower bound of the middle degree tier. */
28
+ export const DEGREE_TIER_MID = 32;
29
+ /** Lower bound of the low degree tier. */
30
+ const DEGREE_TIER_LOW = 1;
31
+ /**
32
+ * The identity permutation of `length` entries as a fresh 4-byte-aligned array (invariant I10).
33
+ * @param length - the entry count
34
+ * @returns 0..length-1
35
+ */
36
+ export function identityPermutation(length) {
37
+ const out = new Uint32Array(length);
38
+ for (let i = 0; i < length; i++) {
39
+ out[i] = i;
40
+ }
41
+ return out;
42
+ }
43
+ /**
44
+ * The in-adjacency of a snapshot (design section 7.2): `ReverseView` with `fwdArc` (reverse arc k ->
45
+ * forward arc) and `arcToEdge` (`arcToEdge[fwdArc[k]]`) materialised lazily. For an undirected
46
+ * snapshot the forward arrays are shared (invariant I7), `fwdArc` is the identity (allocated on first
47
+ * read) and `arcToEdge` is the snapshot's own array. For a directed snapshot whose permutation is the
48
+ * identity, the reverse `arcToEdge` IS `fwdArc` (aliased, zero bytes).
49
+ */
50
+ export class ReverseAdjacency {
51
+ /**
52
+ * Wrap the reverse arrays. Used by `computeReverse()`; not meant to be constructed elsewhere.
53
+ * @param source - the snapshot the view belongs to
54
+ * @param rowPtr - the reverse row offsets
55
+ * @param colIdx - the reverse targets (forward sources)
56
+ * @param weights - the gathered weights, or null when unweighted
57
+ * @param fwdArc - the reverse arc -> forward arc map, or null when it is the identity (undirected)
58
+ */
59
+ constructor(source, rowPtr, colIdx, weights, fwdArc) {
60
+ this.source = source;
61
+ this.directed = source.directed;
62
+ this.nodeCount = source.nodeCount;
63
+ this.arcCount = source.arcCount;
64
+ this.rowPtr = rowPtr;
65
+ this.colIdx = colIdx;
66
+ this.weights = weights;
67
+ this.fwdArcCache = fwdArc;
68
+ this.fwdArcIsIdentity = fwdArc === null;
69
+ this.arcToEdgeCache = null;
70
+ this.arcToEdgeOwned = false;
71
+ }
72
+ /**
73
+ * Reverse arc k -> forward arc index; the identity for an undirected snapshot, materialised on first
74
+ * read outside any arena.
75
+ * @returns the map, arcCount entries
76
+ */
77
+ get fwdArc() {
78
+ this.fwdArcCache ?? (this.fwdArcCache = identityPermutation(this.arcCount));
79
+ return this.fwdArcCache;
80
+ }
81
+ /**
82
+ * Logical edge of every reverse arc: `source.arcToEdge[fwdArc[k]]`, materialised on first read.
83
+ * Aliases `source.arcToEdge` when undirected and `fwdArc` when the forward permutation is the
84
+ * identity.
85
+ * @returns the map, arcCount entries
86
+ */
87
+ get arcToEdge() {
88
+ if (this.arcToEdgeCache === null) {
89
+ if (this.fwdArcIsIdentity) {
90
+ this.arcToEdgeCache = this.source.arcToEdge;
91
+ }
92
+ else if (this.source.flags.arcToEdgeIsIdentity) {
93
+ this.arcToEdgeCache = this.fwdArc;
94
+ }
95
+ else {
96
+ const { fwdArc } = this;
97
+ const forward = this.source.arcToEdge;
98
+ const out = new Uint32Array(this.arcCount);
99
+ for (let k = 0; k < out.length; k++) {
100
+ out[k] = forward[fwdArc[k]];
101
+ }
102
+ this.arcToEdgeCache = out;
103
+ this.arcToEdgeOwned = true;
104
+ }
105
+ }
106
+ return this.arcToEdgeCache;
107
+ }
108
+ /**
109
+ * Bytes owned by this view (not shared with the snapshot's core): the reverse arrays when directed,
110
+ * a materialised identity `fwdArc`, and a gathered `arcToEdge`.
111
+ * @returns the byte count
112
+ */
113
+ get ownedByteLength() {
114
+ let bytes = 0;
115
+ if (this.directed) {
116
+ bytes += this.rowPtr.byteLength + this.colIdx.byteLength;
117
+ if (this.weights !== null) {
118
+ bytes += this.weights.byteLength;
119
+ }
120
+ }
121
+ if (this.fwdArcCache !== null) {
122
+ bytes += this.fwdArcCache.byteLength;
123
+ }
124
+ if (this.arcToEdgeOwned && this.arcToEdgeCache !== null) {
125
+ bytes += this.arcToEdgeCache.byteLength;
126
+ }
127
+ return bytes;
128
+ }
129
+ /**
130
+ * The arrays this view has materialised so far, for checksums and byte accounting.
131
+ * @returns the arrays, keyed by member name
132
+ */
133
+ materialised() {
134
+ const out = {
135
+ rowPtr: this.rowPtr,
136
+ colIdx: this.colIdx,
137
+ };
138
+ if (this.weights !== null) {
139
+ out.weights = this.weights;
140
+ }
141
+ if (this.fwdArcCache !== null) {
142
+ out.fwdArc = this.fwdArcCache;
143
+ }
144
+ if (this.arcToEdgeCache !== null) {
145
+ out.arcToEdge = this.arcToEdgeCache;
146
+ }
147
+ return out;
148
+ }
149
+ }
150
+ /**
151
+ * Compute the reverse view (design section 7.2): a stable counting sort of the forward arcs by target
152
+ * (O(n + m)) so that every reverse row is sorted by source with parallels in ascending edge order
153
+ * (invariant I4 for the reverse), weights gathered when weighted (Q37). Undirected: the forward
154
+ * arrays themselves (invariant I7).
155
+ * @param snapshot - the snapshot
156
+ * @returns the reverse view
157
+ */
158
+ export function computeReverse(snapshot) {
159
+ const { nodeCount, arcCount, rowPtr, colIdx, weights } = snapshot;
160
+ if (!snapshot.directed) {
161
+ return new ReverseAdjacency(snapshot, rowPtr, colIdx, weights, null);
162
+ }
163
+ const revPtr = new Uint32Array(nodeCount + 1);
164
+ for (let a = 0; a < arcCount; a++) {
165
+ revPtr[colIdx[a] + 1]++;
166
+ }
167
+ for (let v = 0; v < nodeCount; v++) {
168
+ revPtr[v + 1] += revPtr[v];
169
+ }
170
+ const revIdx = new Uint32Array(arcCount);
171
+ const fwdArc = new Uint32Array(arcCount);
172
+ const revWeights = weights === null ? null : new Float32Array(arcCount);
173
+ const cursor = revPtr.slice(0, nodeCount);
174
+ for (let u = 0; u < nodeCount; u++) {
175
+ const end = rowPtr[u + 1];
176
+ for (let a = rowPtr[u]; a < end; a++) {
177
+ const v = colIdx[a];
178
+ const k = cursor[v]++;
179
+ revIdx[k] = u;
180
+ fwdArc[k] = a;
181
+ if (revWeights !== null && weights !== null) {
182
+ revWeights[k] = weights[a];
183
+ }
184
+ }
185
+ }
186
+ return new ReverseAdjacency(snapshot, revPtr, revIdx, revWeights, fwdArc);
187
+ }
188
+ // ============================================================ coo and edge list
189
+ /**
190
+ * The source node of every arc: `rowPtr` expanded to arc length.
191
+ * @param rowPtr - the row offsets
192
+ * @param nodeCount - the node count
193
+ * @param arcCount - the arc count
194
+ * @returns a fresh Uint32Array(arcCount)
195
+ */
196
+ function expandRowPtr(rowPtr, nodeCount, arcCount) {
197
+ const src = new Uint32Array(arcCount);
198
+ for (let u = 0; u < nodeCount; u++) {
199
+ const end = rowPtr[u + 1];
200
+ for (let a = rowPtr[u]; a < end; a++) {
201
+ src[a] = u;
202
+ }
203
+ }
204
+ return src;
205
+ }
206
+ /**
207
+ * Compute the per-arc COO view (design section 7.2): `src` is the only new array; `dst` aliases
208
+ * `colIdx`, `weights` aliases `weights`, and `arcToEdge` reaches the snapshot's array through a
209
+ * getter (so an identity permutation is not materialised by building the view).
210
+ * @param snapshot - the snapshot
211
+ * @returns the COO view
212
+ */
213
+ export function computeCoo(snapshot) {
214
+ return cooViewOf(snapshot, expandRowPtr(snapshot.rowPtr, snapshot.nodeCount, snapshot.arcCount));
215
+ }
216
+ /**
217
+ * Wrap a per-arc source array as the COO view of a snapshot (the shape `computeCoo()` returns; also
218
+ * used to install a carried `src` from the wire).
219
+ * @param snapshot - the snapshot
220
+ * @param src - source node of every arc, arcCount entries
221
+ * @returns the COO view
222
+ */
223
+ export function cooViewOf(snapshot, src) {
224
+ return Object.freeze({
225
+ src,
226
+ dst: snapshot.colIdx,
227
+ weights: snapshot.weights,
228
+ get arcToEdge() {
229
+ return snapshot.arcToEdge;
230
+ },
231
+ });
232
+ }
233
+ /**
234
+ * Compute the edge list view (design section 7.2): every logical edge once in declared orientation.
235
+ * `src[e]` is the row holding `edgeToArc[e]`, `dst[e] === colIdx[edgeToArc[e]]`, `arc` aliases
236
+ * `edgeToArc` through a getter, `weights` is gathered through `edgeToArc` (aliased when the
237
+ * permutation is the identity, in which case `dst` aliases `colIdx` too).
238
+ * @param snapshot - the snapshot
239
+ * @returns the edge list view
240
+ */
241
+ export function computeEdgeList(snapshot) {
242
+ const { nodeCount, edgeCount, arcCount, rowPtr, colIdx, weights } = snapshot;
243
+ if (snapshot.flags.arcToEdgeIsIdentity) {
244
+ return edgeListViewOf(snapshot, expandRowPtr(rowPtr, nodeCount, arcCount), colIdx, weights);
245
+ }
246
+ const { arcToEdge, edgeToArc } = snapshot;
247
+ const src = new Uint32Array(edgeCount);
248
+ const dst = new Uint32Array(edgeCount);
249
+ for (let u = 0; u < nodeCount; u++) {
250
+ const end = rowPtr[u + 1];
251
+ for (let a = rowPtr[u]; a < end; a++) {
252
+ const e = arcToEdge[a];
253
+ if (edgeToArc[e] === a) {
254
+ src[e] = u;
255
+ dst[e] = colIdx[a];
256
+ }
257
+ }
258
+ }
259
+ let edgeWeights = null;
260
+ if (weights !== null) {
261
+ edgeWeights = new Float32Array(edgeCount);
262
+ for (let e = 0; e < edgeCount; e++) {
263
+ edgeWeights[e] = weights[edgeToArc[e]];
264
+ }
265
+ }
266
+ return edgeListViewOf(snapshot, src, dst, edgeWeights);
267
+ }
268
+ /**
269
+ * Wrap per-edge arrays as the edge list view of a snapshot (the shape `computeEdgeList()` returns;
270
+ * also used to install carried arrays from the wire). `arc` reaches `edgeToArc` through a getter so
271
+ * an identity permutation is not materialised by building the view.
272
+ * @param snapshot - the snapshot
273
+ * @param src - declared source of every edge, edgeCount entries
274
+ * @param dst - declared target of every edge, edgeCount entries (colIdx itself when the permutation is the identity)
275
+ * @param weights - per-edge weights, or null when unweighted
276
+ * @returns the edge list view
277
+ */
278
+ export function edgeListViewOf(snapshot, src, dst, weights) {
279
+ return Object.freeze({
280
+ src,
281
+ dst,
282
+ weights,
283
+ get arc() {
284
+ return snapshot.edgeToArc;
285
+ },
286
+ });
287
+ }
288
+ // ============================================================ degrees
289
+ /**
290
+ * Out-arc counts: `rowPtr[u + 1] - rowPtr[u]` (a self-loop counted once, design section 3.4).
291
+ * @param rowPtr - the row offsets
292
+ * @param nodeCount - the node count
293
+ * @returns a fresh Uint32Array(nodeCount)
294
+ */
295
+ export function rowLengths(rowPtr, nodeCount) {
296
+ const out = new Uint32Array(nodeCount);
297
+ for (let u = 0; u < nodeCount; u++) {
298
+ out[u] = rowPtr[u + 1] - rowPtr[u];
299
+ }
300
+ return out;
301
+ }
302
+ /**
303
+ * The graph-theoretic degree (design section 3.4): in + out when directed, out + self-loops when
304
+ * undirected (a loop counts twice, the NetworkX convention).
305
+ * @param outDegree - the out-degree view
306
+ * @param inOrLoops - inDegree() when directed, selfLoopsPerNode() when undirected
307
+ * @returns a fresh Uint32Array(n)
308
+ */
309
+ export function sumDegrees(outDegree, inOrLoops) {
310
+ const out = new Uint32Array(outDegree.length);
311
+ for (let u = 0; u < out.length; u++) {
312
+ out[u] = outDegree[u] + inOrLoops[u];
313
+ }
314
+ return out;
315
+ }
316
+ /**
317
+ * Row sums of an arc-aligned weight array as f64 (design section 7.2): a self-loop arc counted once;
318
+ * a row may sum to 0 because zero weights are legal.
319
+ * @param rowPtr - the row offsets of the adjacency being summed (forward or reverse)
320
+ * @param weights - the arc-aligned weights of that adjacency
321
+ * @param nodeCount - the node count
322
+ * @returns a fresh Float64Array(nodeCount)
323
+ */
324
+ export function rowWeightSums(rowPtr, weights, nodeCount) {
325
+ const out = new Float64Array(nodeCount);
326
+ for (let u = 0; u < nodeCount; u++) {
327
+ const end = rowPtr[u + 1];
328
+ let sum = 0;
329
+ for (let a = rowPtr[u]; a < end; a++) {
330
+ sum += weights[a];
331
+ }
332
+ out[u] = sum;
333
+ }
334
+ return out;
335
+ }
336
+ /**
337
+ * A u32 count vector widened to f64 (the unweighted case of the weighted-degree views).
338
+ * @param counts - the counts
339
+ * @returns a fresh Float64Array of the same length
340
+ */
341
+ export function widenToF64(counts) {
342
+ return new Float64Array(counts);
343
+ }
344
+ /**
345
+ * Element-wise sum of two f64 vectors of equal length.
346
+ * @param a - the first vector
347
+ * @param b - the second vector
348
+ * @returns a fresh Float64Array
349
+ */
350
+ export function sumF64(a, b) {
351
+ const out = new Float64Array(a.length);
352
+ for (let i = 0; i < out.length; i++) {
353
+ out[i] = a[i] + b[i];
354
+ }
355
+ return out;
356
+ }
357
+ /**
358
+ * The sum of weights over logical edges, each undirected edge once (design section 7.2), accumulated
359
+ * in f64. `edgeCount` when unweighted.
360
+ * @param snapshot - the snapshot
361
+ * @returns the total weight
362
+ */
363
+ export function computeTotalWeight(snapshot) {
364
+ const { weights, edgeCount } = snapshot;
365
+ if (weights === null) {
366
+ return edgeCount;
367
+ }
368
+ let sum = 0;
369
+ if (snapshot.flags.arcToEdgeIsIdentity) {
370
+ for (let a = 0; a < weights.length; a++) {
371
+ sum += weights[a];
372
+ }
373
+ return sum;
374
+ }
375
+ const { edgeToArc } = snapshot;
376
+ for (let e = 0; e < edgeCount; e++) {
377
+ sum += weights[edgeToArc[e]];
378
+ }
379
+ return sum;
380
+ }
381
+ /**
382
+ * Find every self-loop arc by a binary search per row (design section 7.2).
383
+ * @param snapshot - the snapshot
384
+ * @returns the loop arcs (length selfLoopCount) and the per-node counts
385
+ */
386
+ export function computeSelfLoops(snapshot) {
387
+ const { nodeCount, rowPtr, colIdx, selfLoopCount } = snapshot;
388
+ const perNode = new Uint32Array(nodeCount);
389
+ const arcs = new Uint32Array(selfLoopCount);
390
+ let at = 0;
391
+ for (let u = 0; u < nodeCount; u++) {
392
+ const [lo, hi] = arcRangeIn(rowPtr, colIdx, u, u);
393
+ perNode[u] = hi - lo;
394
+ for (let a = lo; a < hi && at < selfLoopCount; a++) {
395
+ arcs[at++] = a;
396
+ }
397
+ }
398
+ return { selfLoopArcs: arcs, selfLoopsPerNode: perNode };
399
+ }
400
+ /**
401
+ * Sum of weights over each node's self-loop arcs as f64; the loop counts widened when unweighted.
402
+ * @param snapshot - the snapshot
403
+ * @param loops - the self-loop views
404
+ * @returns a fresh Float64Array(n)
405
+ */
406
+ export function computeSelfLoopWeight(snapshot, loops) {
407
+ const { weights, nodeCount, rowPtr, colIdx } = snapshot;
408
+ if (weights === null) {
409
+ return widenToF64(loops.selfLoopsPerNode);
410
+ }
411
+ const out = new Float64Array(nodeCount);
412
+ for (let u = 0; u < nodeCount; u++) {
413
+ if (loops.selfLoopsPerNode[u] === 0) {
414
+ continue;
415
+ }
416
+ const [lo, hi] = arcRangeIn(rowPtr, colIdx, u, u);
417
+ let sum = 0;
418
+ for (let a = lo; a < hi; a++) {
419
+ sum += weights[a];
420
+ }
421
+ out[u] = sum;
422
+ }
423
+ return out;
424
+ }
425
+ // ============================================================ mate
426
+ /**
427
+ * The E_INVALID_SNAPSHOT error the mate walk raises when the doubled storage of invariant I7 does not
428
+ * hold (the row of the mate does not hold the expected source at the cursor).
429
+ * @param u - the row being walked
430
+ * @param v - the target whose mate group was expected
431
+ * @param arc - the arc where the walk failed
432
+ * @returns the error
433
+ */
434
+ function pairingError(u, v, arc) {
435
+ return new GraphFormatError("E_INVALID_SNAPSHOT", `invariant I7 violated: arc ${arc} in row ${u} targeting ${v} has no mate in row ${v}`, { invariant: "I7", row: u, arc, target: v });
436
+ }
437
+ /**
438
+ * For every arc of an undirected snapshot, the arc storing the opposite orientation of the same edge
439
+ * (a self-loop maps to itself), by the O(m) lockstep walk of design section 6.4: rows are visited in
440
+ * ascending order, every group of k parallel arcs u -> v (v > u) is paired k-th to k-th with the group
441
+ * v -> u at row v's cursor, and the cursor advances so that by the time row v is visited every
442
+ * target below v has been consumed. Throws E_DIRECTED on a directed snapshot.
443
+ * @param snapshot - an undirected snapshot
444
+ * @returns a fresh Uint32Array(arcCount)
445
+ */
446
+ export function computeMate(snapshot) {
447
+ if (snapshot.directed) {
448
+ throw new GraphFormatError("E_DIRECTED", "mate() is defined for undirected snapshots only", {
449
+ directed: true,
450
+ });
451
+ }
452
+ const { nodeCount, arcCount, rowPtr, colIdx } = snapshot;
453
+ const mate = new Uint32Array(arcCount);
454
+ const cursor = rowPtr.slice(0, nodeCount);
455
+ for (let u = 0; u < nodeCount; u++) {
456
+ const end = rowPtr[u + 1];
457
+ let a = cursor[u];
458
+ while (a < end) {
459
+ const v = colIdx[a];
460
+ let g = a + 1;
461
+ while (g < end && colIdx[g] === v) {
462
+ g++;
463
+ }
464
+ const k = g - a;
465
+ if (v === u) {
466
+ for (let i = 0; i < k; i++) {
467
+ mate[a + i] = a + i;
468
+ }
469
+ }
470
+ else {
471
+ const b = cursor[v];
472
+ if (v < u || b + k > rowPtr[v + 1] || colIdx[b] !== u || colIdx[b + k - 1] !== u) {
473
+ throw pairingError(u, v, a);
474
+ }
475
+ for (let i = 0; i < k; i++) {
476
+ mate[a + i] = b + i;
477
+ mate[b + i] = a + i;
478
+ }
479
+ cursor[v] = b + k;
480
+ }
481
+ a = g;
482
+ }
483
+ cursor[u] = end;
484
+ }
485
+ return mate;
486
+ }
487
+ // ============================================================ degree order
488
+ /**
489
+ * Nodes permuted by descending out-degree of an adjacency (counting sort, ties in ascending node
490
+ * index) with the cuGraph tier boundaries (design section 7.2): `segmentOffsets = [0, hiEnd,
491
+ * midEnd, lowEnd, n]` for the thresholds 1024 / 32 / 1.
492
+ * @param rowPtr - the row offsets of the adjacency to order by (forward, or reverse for the in-degree)
493
+ * @param nodeCount - the node count
494
+ * @returns the permutation and its tier offsets
495
+ */
496
+ export function computeDegreeOrder(rowPtr, nodeCount) {
497
+ const degree = rowLengths(rowPtr, nodeCount);
498
+ let maxDegree = 0;
499
+ for (let u = 0; u < nodeCount; u++) {
500
+ if (degree[u] > maxDegree) {
501
+ maxDegree = degree[u];
502
+ }
503
+ }
504
+ const start = new Uint32Array(maxDegree + 1);
505
+ for (let u = 0; u < nodeCount; u++) {
506
+ start[degree[u]]++;
507
+ }
508
+ // descending: the start of degree d is the number of nodes with a larger degree
509
+ let run = 0;
510
+ for (let d = maxDegree; d >= 0; d--) {
511
+ const count = start[d];
512
+ start[d] = run;
513
+ run += count;
514
+ }
515
+ const perm = new Uint32Array(nodeCount);
516
+ let hi = 0;
517
+ let mid = 0;
518
+ let low = 0;
519
+ for (let u = 0; u < nodeCount; u++) {
520
+ const d = degree[u];
521
+ perm[start[d]++] = u;
522
+ if (d >= DEGREE_TIER_HIGH) {
523
+ hi++;
524
+ }
525
+ else if (d >= DEGREE_TIER_MID) {
526
+ mid++;
527
+ }
528
+ else if (d >= DEGREE_TIER_LOW) {
529
+ low++;
530
+ }
531
+ }
532
+ const segmentOffsets = new Uint32Array([0, hi, hi + mid, hi + mid + low, nodeCount]);
533
+ return Object.freeze({ perm, segmentOffsets });
534
+ }
535
+ // ============================================================ symmetry
536
+ /**
537
+ * Whether a directed snapshot's arc set is closed under reversal with equal weights (design sections
538
+ * 3.6 and 7.2): forward row v and reverse row v hold the same targets (versus sources; both sorted,
539
+ * so one lockstep walk), and within each run of parallel arcs to one target the same multiset of
540
+ * weights. Parallel arcs are ordered by edge index in both rows, so their weights can arrive in a
541
+ * different order (an expanded undirected multigraph with unequal parallel weights, design section
542
+ * 6.6); each run is compared as a sorted multiset, which is positional for simple graphs. Undirected
543
+ * snapshots are symmetric by construction.
544
+ * @param snapshot - the snapshot
545
+ * @param reverse - its reverse view
546
+ * @returns true when symmetric
547
+ */
548
+ export function computeSymmetric(snapshot, reverse) {
549
+ if (!snapshot.directed) {
550
+ return true;
551
+ }
552
+ const { nodeCount, arcCount, rowPtr, colIdx, weights } = snapshot;
553
+ for (let v = 0; v <= nodeCount; v++) {
554
+ if (rowPtr[v] !== reverse.rowPtr[v]) {
555
+ return false;
556
+ }
557
+ }
558
+ const revIdx = reverse.colIdx;
559
+ for (let a = 0; a < arcCount; a++) {
560
+ if (colIdx[a] !== revIdx[a]) {
561
+ return false;
562
+ }
563
+ }
564
+ const revWeights = reverse.weights;
565
+ if (weights === null || revWeights === null) {
566
+ return true;
567
+ }
568
+ if (!snapshot.flags.multigraph) {
569
+ for (let a = 0; a < arcCount; a++) {
570
+ if (weights[a] !== revWeights[a]) {
571
+ return false;
572
+ }
573
+ }
574
+ return true;
575
+ }
576
+ // multigraph: compare each parallel run (adjacent by I4, equal targets in both rows) as a multiset
577
+ let a = 0;
578
+ while (a < arcCount) {
579
+ let b = a + 1;
580
+ while (b < arcCount && colIdx[b] === colIdx[a] && b < rowPtr[rowOf(rowPtr, nodeCount, a) + 1]) {
581
+ b++;
582
+ }
583
+ if (b - a === 1) {
584
+ if (weights[a] !== revWeights[a]) {
585
+ return false;
586
+ }
587
+ }
588
+ else if (!sameWeightMultiset(weights, revWeights, a, b)) {
589
+ return false;
590
+ }
591
+ a = b;
592
+ }
593
+ return true;
594
+ }
595
+ /**
596
+ * The row holding arc `a` (binary search on rowPtr).
597
+ * @param rowPtr - the row offsets
598
+ * @param nodeCount - the node count
599
+ * @param a - the arc
600
+ * @returns the row
601
+ */
602
+ function rowOf(rowPtr, nodeCount, a) {
603
+ let lo = 0;
604
+ let hi = nodeCount;
605
+ while (hi - lo > 1) {
606
+ const mid = (lo + hi) >>> 1;
607
+ if (rowPtr[mid] <= a) {
608
+ lo = mid;
609
+ }
610
+ else {
611
+ hi = mid;
612
+ }
613
+ }
614
+ return lo;
615
+ }
616
+ /**
617
+ * Whether two weight runs hold the same multiset (sorted scratch copies compared position by position).
618
+ * @param x - one weight array
619
+ * @param y - the other
620
+ * @param start - the first arc of the run
621
+ * @param end - one past the last arc of the run
622
+ * @returns true when the multisets agree
623
+ */
624
+ function sameWeightMultiset(x, y, start, end) {
625
+ const p = x.slice(start, end).sort();
626
+ const q = y.slice(start, end).sort();
627
+ for (let i = 0; i < p.length; i++) {
628
+ if (p[i] !== q[i] && !(Number.isNaN(p[i]) && Number.isNaN(q[i]))) {
629
+ return false;
630
+ }
631
+ }
632
+ return true;
633
+ }
634
+ // ============================================================ byte accounting
635
+ /**
636
+ * The bytes a cached view owns beyond the snapshot's core (aliased arrays count zero), for
637
+ * `byteLength({ views: true })` (design section 7.2).
638
+ * @param snapshot - the snapshot
639
+ * @param name - the view name
640
+ * @param value - the cached value
641
+ * @returns the byte count
642
+ */
643
+ export function viewByteLength(snapshot, name, value) {
644
+ switch (name) {
645
+ case "reverse":
646
+ return value instanceof ReverseAdjacency ? value.ownedByteLength : 0;
647
+ case "coo": {
648
+ const coo = value;
649
+ return coo.src.byteLength;
650
+ }
651
+ case "edgeList": {
652
+ const list = value;
653
+ let bytes = list.src.byteLength;
654
+ if (list.dst !== snapshot.colIdx) {
655
+ bytes += list.dst.byteLength;
656
+ }
657
+ if (list.weights !== null && list.weights !== snapshot.weights) {
658
+ bytes += list.weights.byteLength;
659
+ }
660
+ return bytes;
661
+ }
662
+ case "outDegree":
663
+ case "inDegree":
664
+ case "degree":
665
+ case "weightedOutDegree":
666
+ case "weightedInDegree":
667
+ case "weightedDegree":
668
+ case "selfLoopWeight":
669
+ case "selfLoopArcs":
670
+ case "selfLoopsPerNode":
671
+ case "mate":
672
+ return value.byteLength;
673
+ case "degreeOrder":
674
+ case "reverseDegreeOrder": {
675
+ const order = value;
676
+ return order.perm.byteLength + order.segmentOffsets.byteLength;
677
+ }
678
+ case "totalWeight":
679
+ case "symmetric":
680
+ return 0;
681
+ default: {
682
+ const unknown = name;
683
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown view ${String(unknown)}`, { view: unknown });
684
+ }
685
+ }
686
+ }
687
+ /**
688
+ * The typed arrays a cached view currently holds, keyed by member name, for the checksum records of
689
+ * design section 5.8 (aliased core arrays are included: a write through the alias is a violation
690
+ * too). Scalar views hold nothing.
691
+ * @param snapshot - the snapshot
692
+ * @param name - the view name
693
+ * @param value - the cached value
694
+ * @returns the arrays by member name
695
+ */
696
+ export function viewArrays(snapshot, name, value) {
697
+ switch (name) {
698
+ case "reverse":
699
+ return value instanceof ReverseAdjacency ? value.materialised() : {};
700
+ case "coo": {
701
+ const coo = value;
702
+ return { src: coo.src };
703
+ }
704
+ case "edgeList": {
705
+ const list = value;
706
+ const out = { src: list.src };
707
+ if (list.dst !== snapshot.colIdx) {
708
+ out.dst = list.dst;
709
+ }
710
+ if (list.weights !== null && list.weights !== snapshot.weights) {
711
+ out.weights = list.weights;
712
+ }
713
+ return out;
714
+ }
715
+ case "outDegree":
716
+ case "inDegree":
717
+ case "degree":
718
+ case "weightedOutDegree":
719
+ case "weightedInDegree":
720
+ case "weightedDegree":
721
+ case "selfLoopWeight":
722
+ case "selfLoopArcs":
723
+ case "selfLoopsPerNode":
724
+ case "mate":
725
+ return { data: value };
726
+ case "degreeOrder":
727
+ case "reverseDegreeOrder": {
728
+ const order = value;
729
+ return { perm: order.perm, segmentOffsets: order.segmentOffsets };
730
+ }
731
+ case "totalWeight":
732
+ case "symmetric":
733
+ return {};
734
+ default: {
735
+ const unknown = name;
736
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown view ${String(unknown)}`, { view: unknown });
737
+ }
738
+ }
739
+ }
740
+ // ============================================================ boundary helpers (7.5)
741
+ /**
742
+ * A zeroed vector of the same class as `like`.
743
+ * @param like - the vector whose class to match
744
+ * @param length - the element count
745
+ * @returns the new vector
746
+ */
747
+ function allocVector(like, length) {
748
+ if (like instanceof Float64Array) {
749
+ return new Float64Array(length);
750
+ }
751
+ if (like instanceof Float32Array) {
752
+ return new Float32Array(length);
753
+ }
754
+ if (like instanceof Int32Array) {
755
+ return new Int32Array(length);
756
+ }
757
+ return new Uint32Array(length);
758
+ }
759
+ /**
760
+ * Reduce a per-arc vector (arcCount entries) to a per-edge vector (edgeCount entries) through
761
+ * `arcToEdge` (design section 7.5): `"first"` takes the value of the arc holding the declared
762
+ * orientation (`edgeToArc[e]`), `"sum"` / `"max"` / `"min"` combine both arcs of an undirected edge.
763
+ * Returns `perArc` itself when the permutation is the identity and no `out` is given.
764
+ * @param snapshot - the snapshot the vector is aligned to
765
+ * @param perArc - the per-arc values, arcCount entries
766
+ * @param reducer - how the arcs of one edge combine
767
+ * @param out - an optional destination of edgeCount entries
768
+ * @returns the per-edge vector
769
+ */
770
+ export function foldArcs(snapshot, perArc, reducer, out) {
771
+ const { edgeCount, arcCount } = snapshot;
772
+ assertOneOf("reducer", reducer, ["first", "sum", "max", "min"]);
773
+ if (perArc.length !== arcCount) {
774
+ throw new GraphFormatError("E_COLUMN_LENGTH", `perArc has ${perArc.length} entries, expected ${arcCount}`, {
775
+ expected: arcCount,
776
+ found: perArc.length,
777
+ });
778
+ }
779
+ if (snapshot.flags.arcToEdgeIsIdentity) {
780
+ if (out === undefined) {
781
+ return perArc;
782
+ }
783
+ out.set(perArc);
784
+ return out;
785
+ }
786
+ const result = out ?? allocVector(perArc, edgeCount);
787
+ const { edgeToArc, arcToEdge } = snapshot;
788
+ for (let e = 0; e < edgeCount; e++) {
789
+ result[e] = perArc[edgeToArc[e]];
790
+ }
791
+ switch (reducer) {
792
+ case "first":
793
+ break;
794
+ case "sum":
795
+ for (let a = 0; a < arcCount; a++) {
796
+ const e = arcToEdge[a];
797
+ if (edgeToArc[e] !== a) {
798
+ result[e] += perArc[a];
799
+ }
800
+ }
801
+ break;
802
+ case "max":
803
+ for (let a = 0; a < arcCount; a++) {
804
+ const e = arcToEdge[a];
805
+ if (perArc[a] > result[e]) {
806
+ result[e] = perArc[a];
807
+ }
808
+ }
809
+ break;
810
+ case "min":
811
+ for (let a = 0; a < arcCount; a++) {
812
+ const e = arcToEdge[a];
813
+ if (perArc[a] < result[e]) {
814
+ result[e] = perArc[a];
815
+ }
816
+ }
817
+ break;
818
+ default: {
819
+ const unknown = reducer;
820
+ throw new GraphFormatError("E_UNSUPPORTED", `unknown reducer ${String(unknown)}`, { reducer: unknown });
821
+ }
822
+ }
823
+ return result;
824
+ }
825
+ /**
826
+ * Expand a per-edge vector (edgeCount entries) to a per-arc vector (arcCount entries) through
827
+ * `arcToEdge` (design section 7.5). Returns `perEdge` itself when the permutation is the identity and
828
+ * no `out` is given.
829
+ * @param snapshot - the snapshot the vector is aligned to
830
+ * @param perEdge - the per-edge values, edgeCount entries
831
+ * @param out - an optional destination of arcCount entries
832
+ * @returns the per-arc vector
833
+ */
834
+ export function expandEdges(snapshot, perEdge, out) {
835
+ const { edgeCount, arcCount } = snapshot;
836
+ if (perEdge.length !== edgeCount) {
837
+ throw new GraphFormatError("E_COLUMN_LENGTH", `perEdge has ${perEdge.length} entries, expected ${edgeCount}`, {
838
+ expected: edgeCount,
839
+ found: perEdge.length,
840
+ });
841
+ }
842
+ if (snapshot.flags.arcToEdgeIsIdentity) {
843
+ if (out === undefined) {
844
+ return perEdge;
845
+ }
846
+ out.set(perEdge);
847
+ return out;
848
+ }
849
+ const result = out ?? allocVector(perEdge, arcCount);
850
+ const { arcToEdge } = snapshot;
851
+ for (let a = 0; a < arcCount; a++) {
852
+ result[a] = perEdge[arcToEdge[a]];
853
+ }
854
+ return result;
855
+ }
856
+ //# sourceMappingURL=views.js.map