@c9up/atom 0.1.12 → 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;
@@ -0,0 +1,86 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export function add(a: string, b: string): string;
5
+
6
+ export function cmp(a: string, b: string): number;
7
+
8
+ export function div(a: string, b: string, precision: number): string;
9
+
10
+ export function dot(a: string[], b: string[]): string;
11
+
12
+ export function mul(a: string, b: string): string;
13
+
14
+ export function pow(a: string, exp: number, precision: number): string;
15
+
16
+ export function rem(a: string, b: string): string;
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
+
28
+ export function sqrt(a: string, precision: number): string;
29
+
30
+ export function sub(a: string, b: string): string;
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
+
41
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
42
+
43
+ export interface InitOutput {
44
+ readonly memory: WebAssembly.Memory;
45
+ readonly add: (a: number, b: number, c: number, d: number) => [number, number, number, number];
46
+ readonly cmp: (a: number, b: number, c: number, d: number) => [number, number, number];
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];
49
+ readonly mul: (a: number, b: number, c: number, d: number) => [number, number, number, number];
50
+ readonly pow: (a: number, b: number, c: number, d: number) => [number, number, number, number];
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];
53
+ readonly sqrt: (a: number, b: number, c: number) => [number, number, number, number];
54
+ readonly sub: (a: number, b: number, c: number, d: number) => [number, number, number, number];
55
+ readonly sum: (a: number, b: number) => [number, number, number, number];
56
+ readonly __wbindgen_malloc: (a: number, b: number) => number;
57
+ readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
58
+ readonly __wbindgen_externrefs: WebAssembly.Table;
59
+ readonly __externref_table_dealloc: (a: number) => void;
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;
63
+ readonly __wbindgen_start: () => void;
64
+ }
65
+
66
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
67
+
68
+ /**
69
+ * Instantiates the given `module`, which can either be bytes or
70
+ * a precompiled `WebAssembly.Module`.
71
+ *
72
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
73
+ *
74
+ * @returns {InitOutput}
75
+ */
76
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
77
+
78
+ /**
79
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
80
+ * for everything else, calls `WebAssembly.instantiate` directly.
81
+ *
82
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
83
+ *
84
+ * @returns {Promise<InitOutput>}
85
+ */
86
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;