numbl 0.1.7 → 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 (91) hide show
  1. package/binding.gyp +53 -2
  2. package/dist-cli/cli.js +35560 -23939
  3. package/dist-lib/lib.js +42463 -31995
  4. package/dist-lib/numbl-core/executeCode.d.ts +20 -0
  5. package/dist-lib/numbl-core/helpers/reduction-helpers.d.ts +7 -2
  6. package/dist-lib/numbl-core/interpreter/builtins/datetime.d.ts +39 -0
  7. package/dist-lib/numbl-core/interpreter/builtins/index.d.ts +1 -0
  8. package/dist-lib/numbl-core/interpreter/builtins/time-system.d.ts +1 -0
  9. package/dist-lib/numbl-core/interpreter/builtins/types.d.ts +96 -5
  10. package/dist-lib/numbl-core/interpreter/interpreter.d.ts +37 -3
  11. package/dist-lib/numbl-core/interpreter/types.d.ts +1 -1
  12. package/dist-lib/numbl-core/jit/c/abi.d.ts +90 -0
  13. package/dist-lib/numbl-core/jit/c/assemble.d.ts +56 -0
  14. package/dist-lib/numbl-core/jit/c/classify.d.ts +70 -0
  15. package/dist-lib/numbl-core/jit/c/compile.d.ts +37 -0
  16. package/dist-lib/numbl-core/jit/c/context.d.ts +152 -0
  17. package/dist-lib/numbl-core/jit/c/emit/assign.d.ts +20 -0
  18. package/dist-lib/numbl-core/jit/c/emit/complexScalar.d.ts +18 -0
  19. package/dist-lib/numbl-core/jit/c/emit/fused.d.ts +42 -0
  20. package/dist-lib/numbl-core/jit/c/emit/helpers.d.ts +40 -0
  21. package/dist-lib/numbl-core/jit/c/emit/index.d.ts +14 -0
  22. package/dist-lib/numbl-core/jit/c/emit/scalar.d.ts +23 -0
  23. package/dist-lib/numbl-core/jit/c/emit/stmt.d.ts +25 -0
  24. package/dist-lib/numbl-core/jit/c/emit/tensor.d.ts +127 -0
  25. package/dist-lib/numbl-core/jit/c/emit/userCall.d.ts +58 -0
  26. package/dist-lib/numbl-core/jit/c/epilogue.d.ts +26 -0
  27. package/dist-lib/numbl-core/jit/c/feasibility.d.ts +44 -0
  28. package/dist-lib/numbl-core/jit/c/hybrid.d.ts +42 -0
  29. package/dist-lib/numbl-core/jit/c/install.d.ts +15 -0
  30. package/dist-lib/numbl-core/jit/c/parityError.d.ts +26 -0
  31. package/dist-lib/numbl-core/jit/c/prelude.d.ts +37 -0
  32. package/dist-lib/numbl-core/jit/c/registry.d.ts +51 -0
  33. package/dist-lib/numbl-core/jit/c/visit.d.ts +63 -0
  34. package/dist-lib/numbl-core/jit/e1/install.d.ts +13 -0
  35. package/dist-lib/numbl-core/jit/e1/kernelEmit.d.ts +54 -0
  36. package/dist-lib/numbl-core/jit/e1/openmpFlag.d.ts +13 -0
  37. package/dist-lib/numbl-core/jit/e1/scalarFnKernel.d.ts +44 -0
  38. package/dist-lib/numbl-core/jit/fusedChainHelpers.d.ts +65 -0
  39. package/dist-lib/numbl-core/jit/fusedScalarEmit.d.ts +61 -0
  40. package/dist-lib/numbl-core/jit/fusion.d.ts +71 -0
  41. package/dist-lib/numbl-core/jit/fusionOps.d.ts +25 -0
  42. package/dist-lib/numbl-core/{interpreter/jit → jit}/index.d.ts +2 -2
  43. package/dist-lib/numbl-core/jit/jitBailSafety.d.ts +41 -0
  44. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLoop.d.ts +2 -2
  45. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLoopAnalysis.d.ts +6 -1
  46. package/dist-lib/numbl-core/jit/jitLower.d.ts +122 -0
  47. package/dist-lib/numbl-core/jit/jitLowerExpr.d.ts +27 -0
  48. package/dist-lib/numbl-core/jit/jitLowerStmt.d.ts +9 -0
  49. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitLowerTypes.d.ts +7 -3
  50. package/dist-lib/numbl-core/jit/jitTopLevel.d.ts +22 -0
  51. package/dist-lib/numbl-core/{interpreter/jit → jit}/jitTypes.d.ts +133 -1
  52. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitCodegen.d.ts +2 -2
  53. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitCodegenHoist.d.ts +19 -1
  54. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpers.d.ts +15 -3
  55. package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpersIndex.d.ts +7 -0
  56. package/dist-lib/numbl-core/jit/js/jitHelpersTensor.d.ts +34 -0
  57. package/dist-lib/numbl-core/jit/js/jsFusedCodegen.d.ts +17 -0
  58. package/dist-lib/numbl-core/jit/scalarEmit.d.ts +58 -0
  59. package/dist-lib/numbl-core/lexer/types.d.ts +2 -1
  60. package/dist-lib/numbl-core/native/lapack-bridge.d.ts +39 -1
  61. package/dist-lib/numbl-core/ops/bessel.d.ts +18 -0
  62. package/dist-lib/numbl-core/ops/comparison.d.ts +11 -0
  63. package/dist-lib/numbl-core/ops/complexBinaryElemwise.d.ts +10 -0
  64. package/dist-lib/numbl-core/ops/complexUnaryElemwise.d.ts +8 -0
  65. package/dist-lib/numbl-core/ops/dispatch.d.ts +26 -0
  66. package/dist-lib/numbl-core/ops/index.d.ts +8 -0
  67. package/dist-lib/numbl-core/ops/opCodes.d.ts +70 -0
  68. package/dist-lib/numbl-core/ops/realBinaryElemwise.d.ts +8 -0
  69. package/dist-lib/numbl-core/ops/realUnaryElemwise.d.ts +5 -0
  70. package/dist-lib/numbl-core/ops/reduce.d.ts +6 -0
  71. package/dist-lib/numbl-core/parser/types.d.ts +6 -0
  72. package/dist-lib/numbl-core/runtime/alloc.d.ts +23 -0
  73. package/dist-lib/numbl-core/runtime/runtime.d.ts +1 -0
  74. package/dist-lib/numbl-core/version.d.ts +1 -1
  75. package/native/jit_runtime/jit_runtime.c +261 -0
  76. package/native/jit_runtime/jit_runtime.h +204 -0
  77. package/native/numbl_addon.cpp +53 -1
  78. package/native/ops/bessel.c +572 -0
  79. package/native/ops/comparison.c +150 -0
  80. package/native/ops/complex_binary_elemwise.c +192 -0
  81. package/native/ops/complex_unary_elemwise.c +152 -0
  82. package/native/ops/numbl_ops.c +66 -0
  83. package/native/ops/numbl_ops.h +262 -0
  84. package/native/ops/real_binary_elemwise.c +85 -0
  85. package/native/ops/real_unary_elemwise.c +104 -0
  86. package/native/ops/reduce.c +162 -0
  87. package/native/ops_napi.cpp +320 -0
  88. package/package.json +10 -9
  89. package/dist-lib/numbl-core/interpreter/jit/jitHelpersTensor.d.ts +0 -28
  90. package/dist-lib/numbl-core/interpreter/jit/jitLower.d.ts +0 -23
  91. /package/dist-lib/numbl-core/{interpreter/jit → jit/js}/jitHelpersComplex.d.ts +0 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Pure-TS implementation of Bessel tensor ops.
3
+ * Mirrors native/ops/bessel.c — fallback when the native addon is unavailable.
4
+ *
5
+ * Delegates the per-element math to src/numbl-core/helpers/bessel.ts so both
6
+ * the scalar interpreter path and the tensor path share the same algorithm.
7
+ */
8
+ /**
9
+ * out[i] = bessel<OP>(nu, z[i]). scale mirrors the behavior of numbl_bessel_real.
10
+ */
11
+ export declare function tsBesselReal(op: number, nu: number, n: number, z: Float64Array, scale: number, out: Float64Array): void;
12
+ /**
13
+ * Hankel function for real z:
14
+ * kKind=1 → out[i] = J_nu(z[i]) + i * Y_nu(z[i])
15
+ * kKind=2 → out[i] = J_nu(z[i]) - i * Y_nu(z[i])
16
+ * scaled=1 multiplies by exp(-i*z) (k=1) or exp(+i*z) (k=2).
17
+ */
18
+ export declare function tsBesselH(kKind: number, nu: number, n: number, z: Float64Array, scale: number, outRe: Float64Array, outIm: Float64Array): void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Pure-TS implementation of comparison ops.
3
+ * Mirrors native/ops/comparison.c.
4
+ *
5
+ * Output is 0.0 / 1.0 stored in a Float64Array (numbl's logical-tensor
6
+ * convention — caller wraps the result with _isLogical = true).
7
+ */
8
+ export declare function tsRealComparison(op: number, n: number, a: Float64Array, b: Float64Array, out: Float64Array): void;
9
+ export declare function tsRealScalarComparison(op: number, n: number, scalar: number, arr: Float64Array, scalarOnLeft: boolean, out: Float64Array): void;
10
+ export declare function tsComplexComparison(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, bRe: Float64Array, bIm: Float64Array | null, out: Float64Array): void;
11
+ export declare function tsComplexScalarComparison(op: number, n: number, sRe: number, sIm: number, arrRe: Float64Array, arrIm: Float64Array | null, scalarOnLeft: boolean, out: Float64Array): void;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Pure-TS implementation of complex binary element-wise ops, split storage.
3
+ * Mirrors native/ops/complex_binary_elemwise.c.
4
+ *
5
+ * Caller-allocated input/output buffers; never copies.
6
+ * aIm/bIm/arrIm may be null → treat as zero.
7
+ * outIm is always required (caller allocates even if result might be real).
8
+ */
9
+ export declare function tsComplexBinaryElemwise(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, bRe: Float64Array, bIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array): void;
10
+ export declare function tsComplexScalarBinaryElemwise(op: number, n: number, sRe: number, sIm: number, arrRe: Float64Array, arrIm: Float64Array | null, scalarOnLeft: boolean, outRe: Float64Array, outIm: Float64Array): void;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pure-TS implementation of complex unary element-wise ops, split storage.
3
+ * Mirrors native/ops/complex_unary_elemwise.c.
4
+ *
5
+ * ABS is intentionally unsupported (use tsComplexAbs — real-valued output).
6
+ */
7
+ export declare function tsComplexUnaryElemwise(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array): void;
8
+ export declare function tsComplexAbs(n: number, aRe: Float64Array, aIm: Float64Array | null, out: Float64Array): void;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Tensor-ops dispatcher: chooses native bridge or pure-TS fallback.
3
+ *
4
+ * All entry points have identical signatures across native and TS impls.
5
+ * Caller owns input AND output memory; functions never allocate output.
6
+ *
7
+ * Selection happens once on first use per category and is cached
8
+ * (the bridge identity is captured at module load via getLapackBridge()).
9
+ */
10
+ export declare const tensorOps: {
11
+ realBinaryElemwise(op: number, n: number, a: Float64Array, b: Float64Array, out: Float64Array): void;
12
+ realScalarBinaryElemwise(op: number, n: number, scalar: number, arr: Float64Array, scalarOnLeft: boolean, out: Float64Array): void;
13
+ complexBinaryElemwise(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, bRe: Float64Array, bIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array): void;
14
+ complexScalarBinaryElemwise(op: number, n: number, sRe: number, sIm: number, arrRe: Float64Array, arrIm: Float64Array | null, scalarOnLeft: boolean, outRe: Float64Array, outIm: Float64Array): void;
15
+ realUnaryElemwise(op: number, n: number, a: Float64Array, out: Float64Array): void;
16
+ complexUnaryElemwise(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array): void;
17
+ complexAbs(n: number, aRe: Float64Array, aIm: Float64Array | null, out: Float64Array): void;
18
+ realComparison(op: number, n: number, a: Float64Array, b: Float64Array, out: Float64Array): void;
19
+ realScalarComparison(op: number, n: number, scalar: number, arr: Float64Array, scalarOnLeft: boolean, out: Float64Array): void;
20
+ complexComparison(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, bRe: Float64Array, bIm: Float64Array | null, out: Float64Array): void;
21
+ complexScalarComparison(op: number, n: number, sRe: number, sIm: number, arrRe: Float64Array, arrIm: Float64Array | null, scalarOnLeft: boolean, out: Float64Array): void;
22
+ realFlatReduce(op: number, n: number, a: Float64Array, out: Float64Array): void;
23
+ complexFlatReduce(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array | null): void;
24
+ besselReal(op: number, nu: number, n: number, z: Float64Array, scale: number, out: Float64Array): void;
25
+ besselH(kKind: number, nu: number, n: number, z: Float64Array, scale: number, outRe: Float64Array, outIm: Float64Array): void;
26
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tensor-ops layer: pointer-based, op-code dispatched, native + TS parity.
3
+ *
4
+ * Every op has identical signatures across native (C) and TS implementations.
5
+ * Caller owns input AND output memory; ops never allocate output.
6
+ */
7
+ export { tensorOps } from "./dispatch.js";
8
+ export { OpRealBin, OpComplexBin, OpUnary, OpCmp, OpReduce, OpBessel, } from "./opCodes.js";
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Op-code enums for the tensor-ops layer.
3
+ *
4
+ * SINGLE SOURCE OF TRUTH on the TS side. Mirrors native/ops/numbl_ops.h.
5
+ * Drift detection: src/__tests__/op-codes-sync.test.ts compares this file's
6
+ * values against numbl_dump_op_codes() at CI time.
7
+ */
8
+ /** Real binary element-wise ops. */
9
+ export declare const OpRealBin: {
10
+ readonly ADD: 0;
11
+ readonly SUB: 1;
12
+ readonly MUL: 2;
13
+ readonly DIV: 3;
14
+ };
15
+ /** Complex binary element-wise ops. */
16
+ export declare const OpComplexBin: {
17
+ readonly ADD: 0;
18
+ readonly SUB: 1;
19
+ readonly MUL: 2;
20
+ readonly DIV: 3;
21
+ };
22
+ /** Flat reduction ops. */
23
+ export declare const OpReduce: {
24
+ readonly SUM: 0;
25
+ readonly PROD: 1;
26
+ readonly MAX: 2;
27
+ readonly MIN: 3;
28
+ readonly ANY: 4;
29
+ readonly ALL: 5;
30
+ readonly MEAN: 6;
31
+ };
32
+ /** Comparison ops (logical output). */
33
+ export declare const OpCmp: {
34
+ readonly EQ: 0;
35
+ readonly NE: 1;
36
+ readonly LT: 2;
37
+ readonly LE: 3;
38
+ readonly GT: 4;
39
+ readonly GE: 5;
40
+ };
41
+ /** Bessel function ops (real input). H-variants are in numbl_bessel_h (complex output). */
42
+ export declare const OpBessel: {
43
+ readonly J: 0;
44
+ readonly Y: 1;
45
+ readonly I: 2;
46
+ readonly K: 3;
47
+ };
48
+ /** Unary element-wise ops (shared numbering for real + complex). */
49
+ export declare const OpUnary: {
50
+ readonly EXP: 0;
51
+ readonly LOG: 1;
52
+ readonly LOG2: 2;
53
+ readonly LOG10: 3;
54
+ readonly SQRT: 4;
55
+ readonly ABS: 5;
56
+ readonly FLOOR: 6;
57
+ readonly CEIL: 7;
58
+ readonly ROUND: 8;
59
+ readonly TRUNC: 9;
60
+ readonly SIN: 10;
61
+ readonly COS: 11;
62
+ readonly TAN: 12;
63
+ readonly ASIN: 13;
64
+ readonly ACOS: 14;
65
+ readonly ATAN: 15;
66
+ readonly SINH: 16;
67
+ readonly COSH: 17;
68
+ readonly TANH: 18;
69
+ readonly SIGN: 19;
70
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Pure-TS implementation of real binary element-wise ops.
3
+ * Mirrors native/ops/real_binary_elemwise.c.
4
+ *
5
+ * Caller-allocated input/output buffers; never copies.
6
+ */
7
+ export declare function tsRealBinaryElemwise(op: number, n: number, a: Float64Array, b: Float64Array, out: Float64Array): void;
8
+ export declare function tsRealScalarBinaryElemwise(op: number, n: number, scalar: number, arr: Float64Array, scalarOnLeft: boolean, out: Float64Array): void;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Pure-TS implementation of real unary element-wise ops.
3
+ * Mirrors native/ops/real_unary_elemwise.c.
4
+ */
5
+ export declare function tsRealUnaryElemwise(op: number, n: number, a: Float64Array, out: Float64Array): void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Pure-TS flat reductions. Mirrors native/ops/reduce.c.
3
+ * Caller-allocated input and output (1-element).
4
+ */
5
+ export declare function tsRealFlatReduce(op: number, n: number, a: Float64Array, out: Float64Array): void;
6
+ export declare function tsComplexFlatReduce(op: number, n: number, aRe: Float64Array, aIm: Float64Array | null, outRe: Float64Array, outIm: Float64Array | null): void;
@@ -310,6 +310,12 @@ export type Stmt = {
310
310
  superClass: string | null;
311
311
  members: ClassMember[];
312
312
  span: Span;
313
+ } | {
314
+ /** Magic comment directive, e.g. `%!numbl:assert_jit` or `%!numbl:assert_jit c`. */
315
+ type: "Directive";
316
+ directive: string;
317
+ args: string[];
318
+ span: Span;
313
319
  };
314
320
  export interface AbstractSyntaxTree {
315
321
  body: Stmt[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Allocate an UNINITIALIZED typed array — skips the zero-fill that
3
+ * `new Float64Array(n)` / `new FloatXArray(n)` perform by default.
4
+ *
5
+ * On Node, `Buffer.allocUnsafe` returns un-zeroed memory; wrapping it
6
+ * in a TypedArray view costs ~10× less than the zero-fill for a 16 MB
7
+ * buffer (~45 µs vs ~470 µs at N=2M doubles).
8
+ *
9
+ * SAFETY CONTRACT (very important):
10
+ * The caller MUST write every element before reading it. Any element
11
+ * that is read before being written will contain arbitrary stale bytes
12
+ * from recently-freed memory. If you cannot guarantee full coverage,
13
+ * use `new Float64Array(n)` / `new FloatXArray(n)` instead.
14
+ *
15
+ * In non-Node environments (browser, Deno without node-compat, …) where
16
+ * `Buffer` is unavailable, we fall back to the zero-filling constructor
17
+ * — still correct, just slower.
18
+ */
19
+ import { FloatXArray } from "./types.js";
20
+ type FloatXInstance = InstanceType<typeof FloatXArray>;
21
+ export declare function uninitFloat64(n: number): Float64Array<ArrayBuffer>;
22
+ export declare function uninitFloatX(n: number): FloatXInstance;
23
+ export {};
@@ -147,6 +147,7 @@ export declare class Runtime {
147
147
  /** Scalar power that returns complex for negative base with fractional exponent */
148
148
  pow(a: number, b: number): number | RuntimeComplexNumber;
149
149
  share(v: unknown): RuntimeValue;
150
+ get displayResults(): boolean;
150
151
  displayResult(v: unknown): void;
151
152
  displayAssign(name: string, v: unknown): void;
152
153
  getConstant(name: string): RuntimeValue;
@@ -1,2 +1,2 @@
1
1
  /** Numbl version, used for JIT disk cache invalidation. */
2
- export declare const NUMBL_VERSION = "0.1.7";
2
+ export declare const NUMBL_VERSION = "0.2.0";
@@ -0,0 +1,261 @@
1
+ /**
2
+ * numbl_jit_runtime — see jit_runtime.h for the shape and invariants.
3
+ */
4
+
5
+ #include "jit_runtime.h"
6
+ #include "numbl_ops.h"
7
+
8
+ #include <math.h>
9
+ #include <string.h>
10
+ #include <time.h>
11
+
12
+ int numbl_jit_rt_version(void) {
13
+ return NUMBL_JIT_RT_VERSION;
14
+ }
15
+
16
+ double numbl_idx1r(const double* data, size_t len, double i, double* err_flag) {
17
+ /* Truncation-to-zero via int64 cast mirrors JS-JIT's `(i - 1) | 0`.
18
+ * Unsigned compare catches both negative idx and idx >= len in one
19
+ * branch. */
20
+ int64_t idx = (int64_t)(i - 1.0);
21
+ if ((uint64_t)idx >= (uint64_t)len) {
22
+ *err_flag = 1.0;
23
+ return 0.0;
24
+ }
25
+ return data[idx];
26
+ }
27
+
28
+ void numbl_set1r_h(double* data, size_t len, double i, double v,
29
+ double* err_flag) {
30
+ int64_t idx = (int64_t)(i - 1.0);
31
+ if ((uint64_t)idx >= (uint64_t)len) {
32
+ /* 2.0 = "growth needed → soft-bail to interpreter", distinct from
33
+ * 1.0 which the JS wrapper translates into a hard bounds error. */
34
+ *err_flag = 2.0;
35
+ return;
36
+ }
37
+ data[idx] = v;
38
+ }
39
+
40
+ double numbl_idx2r(const double* data, size_t len, size_t d0,
41
+ double i, double j, double* err_flag) {
42
+ int64_t r = (int64_t)(i - 1.0);
43
+ int64_t c = (int64_t)(j - 1.0);
44
+ /* d0 == 0 would mean an empty tensor — any index is OOB. */
45
+ if (d0 == 0 || (uint64_t)r >= (uint64_t)d0) {
46
+ *err_flag = 1.0;
47
+ return 0.0;
48
+ }
49
+ size_t cols = len / d0;
50
+ if ((uint64_t)c >= (uint64_t)cols) {
51
+ *err_flag = 1.0;
52
+ return 0.0;
53
+ }
54
+ return data[(size_t)c * d0 + (size_t)r];
55
+ }
56
+
57
+ double numbl_idx3r(const double* data, size_t len, size_t d0, size_t d1,
58
+ double i, double j, double k, double* err_flag) {
59
+ int64_t k0 = (int64_t)(i - 1.0);
60
+ int64_t k1 = (int64_t)(j - 1.0);
61
+ int64_t k2 = (int64_t)(k - 1.0);
62
+ if (d0 == 0 || d1 == 0 ||
63
+ (uint64_t)k0 >= (uint64_t)d0 ||
64
+ (uint64_t)k1 >= (uint64_t)d1) {
65
+ *err_flag = 1.0;
66
+ return 0.0;
67
+ }
68
+ size_t plane = d0 * d1;
69
+ size_t d2 = len / plane;
70
+ if ((uint64_t)k2 >= (uint64_t)d2) {
71
+ *err_flag = 1.0;
72
+ return 0.0;
73
+ }
74
+ return data[(size_t)k2 * plane + (size_t)k1 * d0 + (size_t)k0];
75
+ }
76
+
77
+ void numbl_set2r_h(double* data, size_t len, size_t d0,
78
+ double i, double j, double v, double* err_flag) {
79
+ int64_t r = (int64_t)(i - 1.0);
80
+ int64_t c = (int64_t)(j - 1.0);
81
+ if (d0 == 0) {
82
+ *err_flag = 2.0;
83
+ return;
84
+ }
85
+ size_t cols = len / d0;
86
+ if ((uint64_t)r >= (uint64_t)d0 || (uint64_t)c >= (uint64_t)cols) {
87
+ *err_flag = 2.0;
88
+ return;
89
+ }
90
+ data[(size_t)c * d0 + (size_t)r] = v;
91
+ }
92
+
93
+ void numbl_set3r_h(double* data, size_t len, size_t d0, size_t d1,
94
+ double i, double j, double k, double v,
95
+ double* err_flag) {
96
+ int64_t k0 = (int64_t)(i - 1.0);
97
+ int64_t k1 = (int64_t)(j - 1.0);
98
+ int64_t k2 = (int64_t)(k - 1.0);
99
+ if (d0 == 0 || d1 == 0) {
100
+ *err_flag = 2.0;
101
+ return;
102
+ }
103
+ size_t plane = d0 * d1;
104
+ size_t d2 = len / plane;
105
+ if ((uint64_t)k0 >= (uint64_t)d0 ||
106
+ (uint64_t)k1 >= (uint64_t)d1 ||
107
+ (uint64_t)k2 >= (uint64_t)d2) {
108
+ *err_flag = 2.0;
109
+ return;
110
+ }
111
+ data[(size_t)k2 * plane + (size_t)k1 * d0 + (size_t)k0] = v;
112
+ }
113
+
114
+ void numbl_setRange1r_h(double* dstData, size_t dstLen,
115
+ double dstStart, double dstEnd,
116
+ const double* srcData, size_t srcLen,
117
+ double srcStart, double srcEnd,
118
+ double* err_flag) {
119
+ int64_t dS = (int64_t)(dstStart - 1.0);
120
+ int64_t dE = (int64_t)(dstEnd - 1.0);
121
+ int64_t sS = (int64_t)(srcStart - 1.0);
122
+ int64_t sE = (int64_t)(srcEnd - 1.0);
123
+ int64_t dN = dE - dS + 1;
124
+ int64_t sN = sE - sS + 1;
125
+ if (dN != sN) {
126
+ /* 3.0 — length-mismatch error; JS wrapper translates to MATLAB's
127
+ * "Unable to perform assignment..." message. */
128
+ *err_flag = 3.0;
129
+ return;
130
+ }
131
+ if (dN <= 0) return;
132
+ if ((uint64_t)dS >= (uint64_t)dstLen ||
133
+ (uint64_t)dE >= (uint64_t)dstLen ||
134
+ (uint64_t)sS >= (uint64_t)srcLen ||
135
+ (uint64_t)sE >= (uint64_t)srcLen) {
136
+ *err_flag = 1.0;
137
+ return;
138
+ }
139
+ /* memmove handles overlap when src and dst alias. */
140
+ memmove(dstData + (size_t)dS, srcData + (size_t)sS,
141
+ (size_t)dN * sizeof(double));
142
+ }
143
+
144
+ void numbl_setCol2r_h(double* dstData, size_t dstRows, size_t dstLen,
145
+ double col,
146
+ const double* srcData, size_t srcLen,
147
+ double* err_flag) {
148
+ if (srcLen != dstRows) {
149
+ *err_flag = 3.0;
150
+ return;
151
+ }
152
+ int64_t j = (int64_t)(col - 1.0);
153
+ if (j < 0) {
154
+ *err_flag = 1.0;
155
+ return;
156
+ }
157
+ if (dstRows == 0) {
158
+ /* Empty dst with a nonempty src: growth territory — soft-bail. */
159
+ *err_flag = 2.0;
160
+ return;
161
+ }
162
+ size_t dstCols = dstLen / dstRows;
163
+ if ((uint64_t)j >= (uint64_t)dstCols) {
164
+ /* Growth on write — the interpreter handles growing dst; JS-JIT
165
+ * mirrors this with JitBailToInterpreter. */
166
+ *err_flag = 2.0;
167
+ return;
168
+ }
169
+ memcpy(dstData + (size_t)j * dstRows, srcData,
170
+ dstRows * sizeof(double));
171
+ }
172
+
173
+ void numbl_copyRange1r(const double* srcData, size_t srcLen,
174
+ double start, double end,
175
+ double* dstData,
176
+ double* err_flag) {
177
+ int64_t s = (int64_t)(start - 1.0);
178
+ int64_t e = (int64_t)(end - 1.0);
179
+ int64_t n = e - s + 1;
180
+ if (n <= 0) return;
181
+ if ((uint64_t)s >= (uint64_t)srcLen ||
182
+ (uint64_t)e >= (uint64_t)srcLen) {
183
+ *err_flag = 1.0;
184
+ return;
185
+ }
186
+ memcpy(dstData, srcData + (size_t)s, (size_t)n * sizeof(double));
187
+ }
188
+
189
+ double numbl_mod(double a, double b) {
190
+ if (b == 0.0) return a;
191
+ double r = fmod(a, b);
192
+ if (r != 0.0 && ((r < 0.0) != (b < 0.0))) r += b;
193
+ return r;
194
+ }
195
+
196
+ double numbl_sign(double x) {
197
+ if (x > 0.0) return 1.0;
198
+ if (x < 0.0) return -1.0;
199
+ return 0.0;
200
+ }
201
+
202
+ double numbl_reduce_flat(int op, const double* data, int64_t len) {
203
+ double out = 0.0;
204
+ numbl_real_flat_reduce(op, (size_t)len, data, &out);
205
+ return out;
206
+ }
207
+
208
+ double numbl_monotonic_time(void) {
209
+ struct timespec ts;
210
+ clock_gettime(CLOCK_MONOTONIC, &ts);
211
+ return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9;
212
+ }
213
+
214
+ double numbl_tic(double* state) {
215
+ double t = numbl_monotonic_time();
216
+ *state = t;
217
+ return t;
218
+ }
219
+
220
+ double numbl_toc(const double* state) {
221
+ return numbl_monotonic_time() - *state;
222
+ }
223
+
224
+ /* ── NaN / Inf / finite predicates ─────────────────────────────────────
225
+ *
226
+ * Bit-pattern inspection so the answer doesn't depend on the caller's
227
+ * `-ffast-math` / `-ffinite-math-only` posture. IEEE-754 binary64
228
+ * double: sign bit (1) | exponent (11) | mantissa (52). NaN has
229
+ * exponent all-1s and non-zero mantissa; ±Inf has exponent all-1s and
230
+ * zero mantissa; finite values have exponent != all-1s.
231
+ *
232
+ * memcpy is the portable way to reinterpret the bit pattern (type-
233
+ * punning through a union is UB in strict C; the compiler may still
234
+ * generate a type-punning read from memcpy as a single mov on any
235
+ * real platform).
236
+ */
237
+
238
+ static uint64_t bits_of(double x) {
239
+ uint64_t u;
240
+ memcpy(&u, &x, sizeof u);
241
+ return u;
242
+ }
243
+
244
+ #define NUMBL_DBL_EXP_MASK 0x7FF0000000000000ULL
245
+ #define NUMBL_DBL_MANT_MASK 0x000FFFFFFFFFFFFFULL
246
+
247
+ int numbl_is_nan(double x) {
248
+ uint64_t u = bits_of(x);
249
+ return (u & NUMBL_DBL_EXP_MASK) == NUMBL_DBL_EXP_MASK
250
+ && (u & NUMBL_DBL_MANT_MASK) != 0;
251
+ }
252
+
253
+ int numbl_is_inf(double x) {
254
+ uint64_t u = bits_of(x);
255
+ return (u & ~0x8000000000000000ULL) == NUMBL_DBL_EXP_MASK;
256
+ }
257
+
258
+ int numbl_is_finite(double x) {
259
+ uint64_t u = bits_of(x);
260
+ return (u & NUMBL_DBL_EXP_MASK) != NUMBL_DBL_EXP_MASK;
261
+ }