@c9up/atom 0.1.13 → 0.1.14

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.
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  * pure TypeScript BigInt fallback for unsupported platforms.
5
5
  */
6
6
 
7
+ export type { BulkDivOptions, BulkStddevOptions } from "./bulk.js";
8
+ export { Bulk, BulkColumn, BulkScalar, bulk } from "./bulk.js";
7
9
  export type { AtomContext, AtomContextOptions } from "./context.js";
8
10
  export {
9
11
  configureAtomContext,
@@ -34,6 +36,7 @@ export type {
34
36
  export { Money, money } from "./Money.js";
35
37
  export { isNativeAvailable } from "./native.js";
36
38
 
39
+ import { bulk as bulkFn } from "./bulk.js";
37
40
  import { defaultPrecision, defaultRoundMode } from "./context.js";
38
41
  import type { MedianOptions, StddevOptions } from "./Decimal.js";
39
42
  import { Decimal, type DecimalInput } from "./Decimal.js";
@@ -82,21 +85,28 @@ function resolveValues(args: readonly unknown[]): Iterable<DecimalInput> {
82
85
  return args.map(toDecimalValue);
83
86
  }
84
87
 
88
+ /**
89
+ * Exact sum — through the engine's BATCH entry point, not a fold.
90
+ *
91
+ * The result is identical either way; the cost is not. Every `plus` from
92
+ * JavaScript serialises both operands, crosses into the native engine and
93
+ * comes back with a string, and that crossing is where the time goes — around
94
+ * a microsecond an operation, against nanoseconds for the arithmetic itself.
95
+ * `Decimal.sum` hands the whole list over once, so summing a column of a
96
+ * hundred thousand figures costs one crossing rather than a hundred thousand.
97
+ *
98
+ * Nothing changed for callers: this is what `Atom.sum` always meant.
99
+ */
85
100
  function sumImpl(values: Iterable<DecimalInput>): Decimal {
86
- let total = Decimal.zero();
87
- for (const value of values) {
88
- total = total.plus(value);
89
- }
90
- return total;
101
+ return Decimal.sum(values);
91
102
  }
92
103
 
93
104
  function avgImpl(values: Iterable<DecimalInput>): Decimal {
94
- let total = Decimal.zero();
95
- let count = 0;
96
- for (const value of values) {
97
- total = total.plus(value);
98
- count++;
99
- }
105
+ // Materialised so the count and the batch see the same values: an iterable
106
+ // consumed by one is not there for the other.
107
+ const list = [...values];
108
+ const total = Decimal.sum(list);
109
+ const count = list.length;
100
110
  if (count === 0) {
101
111
  throw new Error("Atom.avg requires at least one value");
102
112
  }
@@ -183,11 +193,10 @@ function stddevImpl(
183
193
  throw new Error("Atom.stddev sample mode requires at least two values");
184
194
  }
185
195
  const mean = avgImpl(list);
186
- let sumSquares = Decimal.zero();
187
- for (const value of list) {
188
- const diff = value.minus(mean);
189
- sumSquares = sumSquares.plus(diff.times(diff));
190
- }
196
+ // diff²` is a dot product of the deviations with themselves: one
197
+ // crossing, where the fold made two per value a multiply and an add.
198
+ const deviations = list.map((value) => value.minus(mean));
199
+ const sumSquares = Decimal.dot(deviations, deviations);
191
200
  const variance = sumSquares.div(String(divisor), {
192
201
  precision: precision + 8,
193
202
  });
@@ -278,6 +287,15 @@ function stddevFn(
278
287
  export const Atom = {
279
288
  /** Construct a `Decimal` from a string / number / bigint / Decimal. Alias for `Decimal.from`. */
280
289
  decimal,
290
+ /**
291
+ * Plan a computation and run it in one crossing.
292
+ *
293
+ * For chains of dependent operations — a schedule, a rate solver, a
294
+ * statistic over a column — where the cost is the boundary rather than the
295
+ * arithmetic. Not for a lone sum: below about three operations per element a
296
+ * plain loop wins.
297
+ */
298
+ bulk: bulkFn,
281
299
  /** Exact sum of N values. Empty input → `Decimal('0')`. */
282
300
  sum: sumFn,
283
301
  /** Arithmetic mean of N values. Throws on empty input. */
@@ -4,6 +4,25 @@
4
4
  // output. Editing this file by hand puts it back where it started: a
5
5
  // description that can disagree with the code it describes.
6
6
 
7
+ /**
8
+ * Run a bulk program over a resident column buffer.
9
+ *
10
+ * `values` holds every column laid end to end, `rows` values each, followed by
11
+ * the scalar literals the program refers to by absolute index. `program` is
12
+ * six-word bytecode. Returns the scalars the program emitted, in order.
13
+ *
14
+ * An overflow of the 128-bit working width is reported with a message opening
15
+ * `[ATOM_BULK_OVERFLOW]`, which is the caller's signal to re-run the same
16
+ * program on the BigInt executor rather than to give up: the fast path is
17
+ * allowed to be too narrow, never to be wrong.
18
+ */
19
+
20
+ export declare function runBulk(
21
+ values: BigInt64Array,
22
+ rows: number,
23
+ program: Int32Array,
24
+ ): Array<string>;
25
+
7
26
  export declare function add(a: string, b: string): string;
8
27
 
9
28
  export declare function sub(a: string, b: string): string;
@@ -18,4 +37,18 @@ export declare function pow(a: string, exp: number, precision: number): string;
18
37
 
19
38
  export declare function sqrt(a: string, precision: number): string;
20
39
 
40
+ /**
41
+ * Batch addition — one crossing for the whole list.
42
+ *
43
+ * `Vec<String>` rather than a stream of calls, because the crossing IS the
44
+ * cost: parsing and formatting a decimal is cheap, and doing it once per
45
+ * pair from JavaScript is what makes a long fold expensive.
46
+ */
47
+
48
+ export declare function sum(values: Array<string>): string;
49
+
50
+ /** Batch `Σ aᵢ·bᵢ` — the shape of every valuation, in one crossing. */
51
+
52
+ export declare function dot(a: Array<string>, b: Array<string>): string;
53
+
21
54
  export declare function cmp(a: string, b: string): number;
@@ -7,16 +7,37 @@ export function cmp(a: string, b: string): number;
7
7
 
8
8
  export function div(a: string, b: string, precision: number): string;
9
9
 
10
+ export function dot(a: string[], b: string[]): string;
11
+
10
12
  export function mul(a: string, b: string): string;
11
13
 
12
14
  export function pow(a: string, exp: number, precision: number): string;
13
15
 
14
16
  export function rem(a: string, b: string): string;
15
17
 
18
+ /**
19
+ * Run a bulk program over a resident column buffer.
20
+ *
21
+ * Held to the same shape as the N-API build so the two engines cannot quietly
22
+ * diverge — the browser has to export what Node does. `Vec<i64>` and `Vec<i32>`
23
+ * cross as `BigInt64Array` and `Int32Array`, which is what the compiler in
24
+ * TypeScript produces anyway.
25
+ */
26
+ export function runBulk(values: BigInt64Array, rows: number, program: Int32Array): string[];
27
+
16
28
  export function sqrt(a: string, precision: number): string;
17
29
 
18
30
  export function sub(a: string, b: string): string;
19
31
 
32
+ /**
33
+ * The batch pair, held to the same shape as the NAPI build so the two
34
+ * engines cannot quietly diverge — the browser has to export what Node does.
35
+ *
36
+ * `Vec<String>` crosses the wasm boundary as a JS array of strings, which is
37
+ * exactly what the facade holds anyway.
38
+ */
39
+ export function sum(values: string[]): string;
40
+
20
41
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
21
42
 
22
43
  export interface InitOutput {
@@ -24,16 +45,21 @@ export interface InitOutput {
24
45
  readonly add: (a: number, b: number, c: number, d: number) => [number, number, number, number];
25
46
  readonly cmp: (a: number, b: number, c: number, d: number) => [number, number, number];
26
47
  readonly div: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
48
+ readonly dot: (a: number, b: number, c: number, d: number) => [number, number, number, number];
27
49
  readonly mul: (a: number, b: number, c: number, d: number) => [number, number, number, number];
28
50
  readonly pow: (a: number, b: number, c: number, d: number) => [number, number, number, number];
29
51
  readonly rem: (a: number, b: number, c: number, d: number) => [number, number, number, number];
52
+ readonly runBulk: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
30
53
  readonly sqrt: (a: number, b: number, c: number) => [number, number, number, number];
31
54
  readonly sub: (a: number, b: number, c: number, d: number) => [number, number, number, number];
32
- readonly __wbindgen_externrefs: WebAssembly.Table;
55
+ readonly sum: (a: number, b: number) => [number, number, number, number];
33
56
  readonly __wbindgen_malloc: (a: number, b: number) => number;
34
57
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
58
+ readonly __wbindgen_externrefs: WebAssembly.Table;
35
59
  readonly __externref_table_dealloc: (a: number) => void;
36
60
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
61
+ readonly __externref_table_alloc: () => number;
62
+ readonly __externref_drop_slice: (a: number, b: number) => void;
37
63
  readonly __wbindgen_start: () => void;
38
64
  }
39
65
 
@@ -74,6 +74,34 @@ export function div(a, b, precision) {
74
74
  }
75
75
  }
76
76
 
77
+ /**
78
+ * @param {string[]} a
79
+ * @param {string[]} b
80
+ * @returns {string}
81
+ */
82
+ export function dot(a, b) {
83
+ let deferred4_0;
84
+ let deferred4_1;
85
+ try {
86
+ const ptr0 = passArrayJsValueToWasm0(a, wasm.__wbindgen_malloc);
87
+ const len0 = WASM_VECTOR_LEN;
88
+ const ptr1 = passArrayJsValueToWasm0(b, wasm.__wbindgen_malloc);
89
+ const len1 = WASM_VECTOR_LEN;
90
+ const ret = wasm.dot(ptr0, len0, ptr1, len1);
91
+ var ptr3 = ret[0];
92
+ var len3 = ret[1];
93
+ if (ret[3]) {
94
+ ptr3 = 0; len3 = 0;
95
+ throw takeFromExternrefTable0(ret[2]);
96
+ }
97
+ deferred4_0 = ptr3;
98
+ deferred4_1 = len3;
99
+ return getStringFromWasm0(ptr3, len3);
100
+ } finally {
101
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
102
+ }
103
+ }
104
+
77
105
  /**
78
106
  * @param {string} a
79
107
  * @param {string} b
@@ -157,6 +185,32 @@ export function rem(a, b) {
157
185
  }
158
186
  }
159
187
 
188
+ /**
189
+ * Run a bulk program over a resident column buffer.
190
+ *
191
+ * Held to the same shape as the N-API build so the two engines cannot quietly
192
+ * diverge — the browser has to export what Node does. `Vec<i64>` and `Vec<i32>`
193
+ * cross as `BigInt64Array` and `Int32Array`, which is what the compiler in
194
+ * TypeScript produces anyway.
195
+ * @param {BigInt64Array} values
196
+ * @param {number} rows
197
+ * @param {Int32Array} program
198
+ * @returns {string[]}
199
+ */
200
+ export function runBulk(values, rows, program) {
201
+ const ptr0 = passArray64ToWasm0(values, wasm.__wbindgen_malloc);
202
+ const len0 = WASM_VECTOR_LEN;
203
+ const ptr1 = passArray32ToWasm0(program, wasm.__wbindgen_malloc);
204
+ const len1 = WASM_VECTOR_LEN;
205
+ const ret = wasm.runBulk(ptr0, len0, rows, ptr1, len1);
206
+ if (ret[3]) {
207
+ throw takeFromExternrefTable0(ret[2]);
208
+ }
209
+ var v3 = getArrayJsValueFromWasm0(ret[0], ret[1]);
210
+ wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
211
+ return v3;
212
+ }
213
+
160
214
  /**
161
215
  * @param {string} a
162
216
  * @param {number} precision
@@ -210,9 +264,50 @@ export function sub(a, b) {
210
264
  wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
211
265
  }
212
266
  }
267
+
268
+ /**
269
+ * The batch pair, held to the same shape as the NAPI build so the two
270
+ * engines cannot quietly diverge — the browser has to export what Node does.
271
+ *
272
+ * `Vec<String>` crosses the wasm boundary as a JS array of strings, which is
273
+ * exactly what the facade holds anyway.
274
+ * @param {string[]} values
275
+ * @returns {string}
276
+ */
277
+ export function sum(values) {
278
+ let deferred3_0;
279
+ let deferred3_1;
280
+ try {
281
+ const ptr0 = passArrayJsValueToWasm0(values, wasm.__wbindgen_malloc);
282
+ const len0 = WASM_VECTOR_LEN;
283
+ const ret = wasm.sum(ptr0, len0);
284
+ var ptr2 = ret[0];
285
+ var len2 = ret[1];
286
+ if (ret[3]) {
287
+ ptr2 = 0; len2 = 0;
288
+ throw takeFromExternrefTable0(ret[2]);
289
+ }
290
+ deferred3_0 = ptr2;
291
+ deferred3_1 = len2;
292
+ return getStringFromWasm0(ptr2, len2);
293
+ } finally {
294
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
295
+ }
296
+ }
213
297
  function __wbg_get_imports() {
214
298
  const import0 = {
215
299
  __proto__: null,
300
+ __wbg___wbindgen_string_get_d154f1e671052120: function(arg0, arg1) {
301
+ const obj = arg1;
302
+ const ret = typeof(obj) === 'string' ? obj : undefined;
303
+ var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
304
+ var len1 = WASM_VECTOR_LEN;
305
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
306
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
307
+ },
308
+ __wbg___wbindgen_throw_bb96b2010945f0bc: function(arg0, arg1) {
309
+ throw new Error(getStringFromWasm0(arg0, arg1));
310
+ },
216
311
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
217
312
  // Cast intrinsic for `Ref(String) -> Externref`.
218
313
  const ret = getStringFromWasm0(arg0, arg1);
@@ -234,10 +329,51 @@ function __wbg_get_imports() {
234
329
  };
235
330
  }
236
331
 
332
+ function addToExternrefTable0(obj) {
333
+ const idx = wasm.__externref_table_alloc();
334
+ wasm.__wbindgen_externrefs.set(idx, obj);
335
+ return idx;
336
+ }
337
+
338
+ function getArrayJsValueFromWasm0(ptr, len) {
339
+ ptr = ptr >>> 0;
340
+ const mem = getDataViewMemory0();
341
+ const result = [];
342
+ for (let i = ptr; i < ptr + 4 * len; i += 4) {
343
+ result.push(wasm.__wbindgen_externrefs.get(mem.getUint32(i, true)));
344
+ }
345
+ wasm.__externref_drop_slice(ptr, len);
346
+ return result;
347
+ }
348
+
349
+ let cachedBigUint64ArrayMemory0 = null;
350
+ function getBigUint64ArrayMemory0() {
351
+ if (cachedBigUint64ArrayMemory0 === null || cachedBigUint64ArrayMemory0.byteLength === 0) {
352
+ cachedBigUint64ArrayMemory0 = new BigUint64Array(wasm.memory.buffer);
353
+ }
354
+ return cachedBigUint64ArrayMemory0;
355
+ }
356
+
357
+ let cachedDataViewMemory0 = null;
358
+ function getDataViewMemory0() {
359
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
360
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
361
+ }
362
+ return cachedDataViewMemory0;
363
+ }
364
+
237
365
  function getStringFromWasm0(ptr, len) {
238
366
  return decodeText(ptr >>> 0, len);
239
367
  }
240
368
 
369
+ let cachedUint32ArrayMemory0 = null;
370
+ function getUint32ArrayMemory0() {
371
+ if (cachedUint32ArrayMemory0 === null || cachedUint32ArrayMemory0.byteLength === 0) {
372
+ cachedUint32ArrayMemory0 = new Uint32Array(wasm.memory.buffer);
373
+ }
374
+ return cachedUint32ArrayMemory0;
375
+ }
376
+
241
377
  let cachedUint8ArrayMemory0 = null;
242
378
  function getUint8ArrayMemory0() {
243
379
  if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
@@ -246,6 +382,34 @@ function getUint8ArrayMemory0() {
246
382
  return cachedUint8ArrayMemory0;
247
383
  }
248
384
 
385
+ function isLikeNone(x) {
386
+ return x === undefined || x === null;
387
+ }
388
+
389
+ function passArray32ToWasm0(arg, malloc) {
390
+ const ptr = malloc(arg.length * 4, 4) >>> 0;
391
+ getUint32ArrayMemory0().set(arg, ptr / 4);
392
+ WASM_VECTOR_LEN = arg.length;
393
+ return ptr;
394
+ }
395
+
396
+ function passArray64ToWasm0(arg, malloc) {
397
+ const ptr = malloc(arg.length * 8, 8) >>> 0;
398
+ getBigUint64ArrayMemory0().set(arg, ptr / 8);
399
+ WASM_VECTOR_LEN = arg.length;
400
+ return ptr;
401
+ }
402
+
403
+ function passArrayJsValueToWasm0(array, malloc) {
404
+ const ptr = malloc(array.length * 4, 4) >>> 0;
405
+ for (let i = 0; i < array.length; i++) {
406
+ const add = addToExternrefTable0(array[i]);
407
+ getDataViewMemory0().setUint32(ptr + 4 * i, add, true);
408
+ }
409
+ WASM_VECTOR_LEN = array.length;
410
+ return ptr;
411
+ }
412
+
249
413
  function passStringToWasm0(arg, malloc, realloc) {
250
414
  if (realloc === undefined) {
251
415
  const buf = cachedTextEncoder.encode(arg);
@@ -323,6 +487,9 @@ function __wbg_finalize_init(instance, module) {
323
487
  wasmInstance = instance;
324
488
  wasm = instance.exports;
325
489
  wasmModule = module;
490
+ cachedBigUint64ArrayMemory0 = null;
491
+ cachedDataViewMemory0 = null;
492
+ cachedUint32ArrayMemory0 = null;
326
493
  cachedUint8ArrayMemory0 = null;
327
494
  wasm.__wbindgen_start();
328
495
  return wasm;
Binary file
@@ -4,14 +4,19 @@ export const memory: WebAssembly.Memory;
4
4
  export const add: (a: number, b: number, c: number, d: number) => [number, number, number, number];
5
5
  export const cmp: (a: number, b: number, c: number, d: number) => [number, number, number];
6
6
  export const div: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
7
+ export const dot: (a: number, b: number, c: number, d: number) => [number, number, number, number];
7
8
  export const mul: (a: number, b: number, c: number, d: number) => [number, number, number, number];
8
9
  export const pow: (a: number, b: number, c: number, d: number) => [number, number, number, number];
9
10
  export const rem: (a: number, b: number, c: number, d: number) => [number, number, number, number];
11
+ export const runBulk: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
10
12
  export const sqrt: (a: number, b: number, c: number) => [number, number, number, number];
11
13
  export const sub: (a: number, b: number, c: number, d: number) => [number, number, number, number];
12
- export const __wbindgen_externrefs: WebAssembly.Table;
14
+ export const sum: (a: number, b: number) => [number, number, number, number];
13
15
  export const __wbindgen_malloc: (a: number, b: number) => number;
14
16
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
17
+ export const __wbindgen_externrefs: WebAssembly.Table;
15
18
  export const __externref_table_dealloc: (a: number) => void;
16
19
  export const __wbindgen_free: (a: number, b: number, c: number) => void;
20
+ export const __externref_table_alloc: () => number;
21
+ export const __externref_drop_slice: (a: number, b: number) => void;
17
22
  export const __wbindgen_start: () => void;