numbl 0.0.20 → 0.0.22

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 (3) hide show
  1. package/README.md +8 -0
  2. package/dist-cli/cli.js +509 -114
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -53,6 +53,10 @@ Commands:
53
53
  mip <subcommand> Package manager (install, uninstall, list, avail, info)
54
54
  (no command) Start interactive REPL
55
55
 
56
+ Global options:
57
+ --version, -V Print version and exit
58
+ --help, -h Print this help message
59
+
56
60
  Options (for REPL):
57
61
  --plot Enable plot server
58
62
  --plot-port <port> Set plot server port (implies --plot)
@@ -73,6 +77,10 @@ Environment variables:
73
77
  ```
74
78
  <!-- END CLI HELP -->
75
79
 
80
+ ## VS Code extension
81
+
82
+ The [Numbl extension for VS Code](https://marketplace.visualstudio.com/items?itemName=jmagland.numbl) lets you run `.m` scripts directly in the editor with inline error diagnostics and a built-in figure viewer.
83
+
76
84
  ## Upgrading
77
85
 
78
86
  ```bash
package/dist-cli/cli.js CHANGED
@@ -19028,6 +19028,16 @@ function complexBinaryOp(a, b, op) {
19028
19028
  );
19029
19029
  }
19030
19030
  function mAdd(a, b) {
19031
+ if (typeof a === "object" && a !== null && a.kind === "tensor" && typeof b === "object" && b !== null && b.kind === "tensor") {
19032
+ const at = a;
19033
+ const bt = b;
19034
+ if (!at.imag && !bt.imag && at.data.length === bt.data.length && at.shape.length === bt.shape.length && at.shape.every((d, i) => d === bt.shape[i])) {
19035
+ const result = new FloatXArray(at.data.length);
19036
+ for (let i = 0; i < result.length; i++)
19037
+ result[i] = at.data[i] + bt.data[i];
19038
+ return RTV.tensor(result, at.shape);
19039
+ }
19040
+ }
19031
19041
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19032
19042
  return mAddSparse(a, b);
19033
19043
  if (isComplexOrMixed(a, b)) {
@@ -19064,6 +19074,16 @@ function mMul(a, b) {
19064
19074
  return binaryOp(a, b, (x, y) => x * y);
19065
19075
  }
19066
19076
  function mElemMul(a, b) {
19077
+ if (typeof a === "object" && a !== null && a.kind === "tensor" && typeof b === "object" && b !== null && b.kind === "tensor") {
19078
+ const at = a;
19079
+ const bt = b;
19080
+ if (!at.imag && !bt.imag && at.data.length === bt.data.length && at.shape.length === bt.shape.length && at.shape.every((d, i) => d === bt.shape[i])) {
19081
+ const result = new FloatXArray(at.data.length);
19082
+ for (let i = 0; i < result.length; i++)
19083
+ result[i] = at.data[i] * bt.data[i];
19084
+ return RTV.tensor(result, at.shape);
19085
+ }
19086
+ }
19067
19087
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19068
19088
  return mElemMulSparse(a, b);
19069
19089
  if (isComplexOrMixed(a, b)) {
@@ -19189,6 +19209,31 @@ function mPow(a, b) {
19189
19209
  return binaryOp(a, b, (x, y) => Math.pow(x, y));
19190
19210
  }
19191
19211
  function mElemPow(a, b) {
19212
+ if (isRuntimeTensor(a) && !a.imag && isRuntimeNumber(b)) {
19213
+ const exp = b;
19214
+ if (exp === 2) {
19215
+ const result = new FloatXArray(a.data.length);
19216
+ for (let i = 0; i < result.length; i++) result[i] = a.data[i] * a.data[i];
19217
+ return RTV.tensor(result, a.shape);
19218
+ }
19219
+ if (Number.isInteger(exp) || exp >= 0) {
19220
+ let hasNeg2 = false;
19221
+ if (!Number.isInteger(exp)) {
19222
+ for (let i = 0; i < a.data.length; i++) {
19223
+ if (a.data[i] < 0) {
19224
+ hasNeg2 = true;
19225
+ break;
19226
+ }
19227
+ }
19228
+ }
19229
+ if (!hasNeg2) {
19230
+ const result = new FloatXArray(a.data.length);
19231
+ for (let i = 0; i < result.length; i++)
19232
+ result[i] = Math.pow(a.data[i], exp);
19233
+ return RTV.tensor(result, a.shape);
19234
+ }
19235
+ }
19236
+ }
19192
19237
  const complexPow = (aRe, aIm, bRe, bIm) => {
19193
19238
  const r = Math.sqrt(aRe * aRe + aIm * aIm);
19194
19239
  if (r === 0) {
@@ -19239,17 +19284,22 @@ function mElemPow(a, b) {
19239
19284
  return binaryOp(a, b, (x, y) => Math.pow(x, y));
19240
19285
  }
19241
19286
  function mNeg(v) {
19242
- if (isRuntimeSparseMatrix(v)) return sparseNeg(v);
19243
- if (isRuntimeComplexNumber(v)) return RTV.complex(-v.re, -v.im);
19244
- if (isRuntimeTensor(v) && v.imag !== void 0) {
19245
- const resultRe = new FloatXArray(v.data.length);
19246
- const resultIm = new FloatXArray(v.imag.length);
19247
- for (let i = 0; i < v.data.length; i++) {
19248
- resultRe[i] = -v.data[i];
19249
- resultIm[i] = -v.imag[i];
19287
+ if (isRuntimeTensor(v)) {
19288
+ if (v.imag !== void 0) {
19289
+ const resultRe2 = new FloatXArray(v.data.length);
19290
+ const resultIm = new FloatXArray(v.imag.length);
19291
+ for (let i = 0; i < v.data.length; i++) {
19292
+ resultRe2[i] = -v.data[i];
19293
+ resultIm[i] = -v.imag[i];
19294
+ }
19295
+ return RTV.tensor(resultRe2, v.shape, resultIm);
19250
19296
  }
19251
- return RTV.tensor(resultRe, v.shape, resultIm);
19297
+ const resultRe = new FloatXArray(v.data.length);
19298
+ for (let i = 0; i < v.data.length; i++) resultRe[i] = -v.data[i];
19299
+ return RTV.tensor(resultRe, v.shape);
19252
19300
  }
19301
+ if (isRuntimeSparseMatrix(v)) return sparseNeg(v);
19302
+ if (isRuntimeComplexNumber(v)) return RTV.complex(-v.re, -v.im);
19253
19303
  return unaryOp(v, (x) => -x);
19254
19304
  }
19255
19305
  function transposeCellArray(v) {
@@ -19524,6 +19574,40 @@ function broadcastComparison(a, b, outShape, op) {
19524
19574
  return t;
19525
19575
  }
19526
19576
  function binaryOp(a, b, op) {
19577
+ if (isRuntimeTensor(a) && a.data.length > 1) {
19578
+ if (isRuntimeTensor(b) && b.data.length > 1) {
19579
+ if (a.data.length === b.data.length && a.shape.length === b.shape.length && a.shape.every((d, i) => d === b.shape[i])) {
19580
+ const result = new FloatXArray(a.data.length);
19581
+ for (let i = 0; i < result.length; i++) {
19582
+ result[i] = op(a.data[i], b.data[i]);
19583
+ }
19584
+ return RTV.tensor(result, a.shape);
19585
+ }
19586
+ const broadcastShape2 = getBroadcastShape(a.shape, b.shape);
19587
+ if (broadcastShape2 !== null) {
19588
+ return broadcastBinary(a, b, broadcastShape2, op);
19589
+ }
19590
+ throw new RuntimeError(
19591
+ `Matrix dimensions must agree: [${a.shape.join(",")}] vs [${b.shape.join(",")}]`
19592
+ );
19593
+ }
19594
+ if (isRuntimeNumber(b)) {
19595
+ const result = new FloatXArray(a.data.length);
19596
+ const sv = b;
19597
+ for (let i = 0; i < result.length; i++) {
19598
+ result[i] = op(a.data[i], sv);
19599
+ }
19600
+ return RTV.tensor(result, a.shape);
19601
+ }
19602
+ }
19603
+ if (isRuntimeTensor(b) && b.data.length > 1 && isRuntimeNumber(a)) {
19604
+ const result = new FloatXArray(b.data.length);
19605
+ const sv = a;
19606
+ for (let i = 0; i < result.length; i++) {
19607
+ result[i] = op(sv, b.data[i]);
19608
+ }
19609
+ return RTV.tensor(result, b.shape);
19610
+ }
19527
19611
  const an = asNumeric(a);
19528
19612
  const bn = asNumeric(b);
19529
19613
  if (an.scalar && bn.scalar) {
@@ -19567,6 +19651,13 @@ function binaryOp(a, b, op) {
19567
19651
  );
19568
19652
  }
19569
19653
  function unaryOp(v, op) {
19654
+ if (isRuntimeTensor(v) && v.data.length > 1) {
19655
+ const result2 = new FloatXArray(v.data.length);
19656
+ for (let i = 0; i < result2.length; i++) {
19657
+ result2[i] = op(v.data[i]);
19658
+ }
19659
+ return RTV.tensor(result2, v.shape);
19660
+ }
19570
19661
  const n = asNumeric(v);
19571
19662
  if (n.scalar) {
19572
19663
  const val = n.isComplex ? n.re : n.value;
@@ -20486,6 +20577,11 @@ function storeIntoTensor(base, indices, rhs) {
20486
20577
  return deleteTensorElements(base, indices[0]);
20487
20578
  }
20488
20579
  if (isRuntimeTensor(rhs) && rhs.data.length === 0 && indices.length === 2) {
20580
+ const nrows = base.shape[0] ?? 1;
20581
+ const ncols = base.shape.length >= 2 ? base.shape[1] : 1;
20582
+ const rowCount = isColonIndex(indices[0]) ? nrows : resolveIndex(indices[0], nrows).length;
20583
+ const colCount = isColonIndex(indices[1]) ? ncols : resolveIndex(indices[1], ncols).length;
20584
+ if (rowCount === 0 || colCount === 0) return base;
20489
20585
  return deleteTensorRowsOrCols(base, indices);
20490
20586
  }
20491
20587
  if (base._rc > 1) {
@@ -20983,7 +21079,7 @@ function storeIntoTensorND(base, indices, rhs) {
20983
21079
  }
20984
21080
  return base;
20985
21081
  }
20986
- function storeIntoCell(base, indices, rhs) {
21082
+ function storeIntoCell(base, indices, rhs, parenAssign = false) {
20987
21083
  if (base._rc > 1) {
20988
21084
  base._rc--;
20989
21085
  const sharedData = base.data.map((elem) => shareRuntimeValue(elem));
@@ -20997,14 +21093,20 @@ function storeIntoCell(base, indices, rhs) {
20997
21093
  c.shape = isColVec ? [newLen, 1] : [1, newLen];
20998
21094
  };
20999
21095
  if (indices.length === 1) {
21000
- return storeIntoCell1D(base, indices[0], rhs, updateShapeAfterLinearAssign);
21096
+ return storeIntoCell1D(
21097
+ base,
21098
+ indices[0],
21099
+ rhs,
21100
+ updateShapeAfterLinearAssign,
21101
+ parenAssign
21102
+ );
21001
21103
  }
21002
21104
  if (indices.length === 2) {
21003
- return storeIntoCell2D(base, indices, rhs);
21105
+ return storeIntoCell2D(base, indices, rhs, parenAssign);
21004
21106
  }
21005
21107
  throw new RuntimeError(`Cannot index-assign into cell`);
21006
21108
  }
21007
- function storeIntoCell1D(base, idx, rhs, updateShape) {
21109
+ function storeIntoCell1D(base, idx, rhs, updateShape, parenAssign = false) {
21008
21110
  if (isRuntimeTensor(idx)) {
21009
21111
  let positions;
21010
21112
  if (idx._isLogical) {
@@ -21052,11 +21154,11 @@ function storeIntoCell1D(base, idx, rhs, updateShape) {
21052
21154
  while (base.data.length <= i)
21053
21155
  base.data.push(RTV.tensor(new FloatXArray(0), [0, 0]));
21054
21156
  }
21055
- base.data[i] = isRuntimeCell(rhs) && rhs.data.length === 1 ? rhs.data[0] : rhs;
21157
+ base.data[i] = parenAssign && isRuntimeCell(rhs) && rhs.data.length === 1 ? rhs.data[0] : rhs;
21056
21158
  updateShape(oldLen);
21057
21159
  return base;
21058
21160
  }
21059
- function storeIntoCell2D(base, indices, rhs) {
21161
+ function storeIntoCell2D(base, indices, rhs, parenAssign = false) {
21060
21162
  let rows = base.shape[0];
21061
21163
  let cols = base.shape.length >= 2 ? base.shape[1] : 1;
21062
21164
  const rowIndices = resolveIndex(indices[0], rows, 0);
@@ -21082,7 +21184,7 @@ function storeIntoCell2D(base, indices, rhs) {
21082
21184
  const nSelectedRows = rowIndices.length;
21083
21185
  const nSelectedCols = colIndices.length;
21084
21186
  const totalSelected = nSelectedRows * nSelectedCols;
21085
- if (isRuntimeCell(rhs)) {
21187
+ if (parenAssign && isRuntimeCell(rhs)) {
21086
21188
  if (rhs.data.length !== totalSelected && rhs.data.length !== 1) {
21087
21189
  throw new RuntimeError("Subscripted assignment dimension mismatch");
21088
21190
  }
@@ -21100,7 +21202,7 @@ function storeIntoCell2D(base, indices, rhs) {
21100
21202
  }
21101
21203
  return base;
21102
21204
  }
21103
- function storeIntoRTValueIndex(base, indices, rhs) {
21205
+ function storeIntoRTValueIndex(base, indices, rhs, parenAssign = false) {
21104
21206
  if (isRuntimeSparseMatrix(base)) {
21105
21207
  return storeIntoSparse(base, indices, rhs);
21106
21208
  }
@@ -21121,7 +21223,7 @@ function storeIntoRTValueIndex(base, indices, rhs) {
21121
21223
  return storeIntoTensor(base, indices, rhs);
21122
21224
  }
21123
21225
  if (isRuntimeCell(base)) {
21124
- return storeIntoCell(base, indices, rhs);
21226
+ return storeIntoCell(base, indices, rhs, parenAssign);
21125
21227
  }
21126
21228
  throw new RuntimeError(`Cannot index-assign into ${kstr(base)}`);
21127
21229
  }
@@ -23031,11 +23133,8 @@ var CommandParser = class extends ExpressionParser {
23031
23133
  scanPos++;
23032
23134
  continue;
23033
23135
  }
23034
- if (ch === "\n" || ch === "\r" || ch === ";" || ch === "%") break;
23035
- if (ch === ",") {
23036
- scanPos++;
23037
- continue;
23038
- }
23136
+ if (ch === "\n" || ch === "\r" || ch === ";" || ch === "%" || ch === ",")
23137
+ break;
23039
23138
  if (ch === "." && scanPos + 2 < this.input.length && this.input[scanPos + 1] === "." && this.input[scanPos + 2] === ".") {
23040
23139
  scanPos += 3;
23041
23140
  while (scanPos < this.input.length && this.input[scanPos] !== "\n") {
@@ -25256,6 +25355,13 @@ function complexElemwise(realFn, complexFn, complexOutKind, o) {
25256
25355
  const isReal = complexOutKind === "Number" || resultIm2.every((x) => x === 0);
25257
25356
  return RTV.tensor(resultRe2, v.shape, isReal ? void 0 : resultIm2);
25258
25357
  }
25358
+ if (o?.realSafe) {
25359
+ const resultRe2 = new FloatXArray(v.data.length);
25360
+ for (let i = 0; i < v.data.length; i++) {
25361
+ resultRe2[i] = realFn(v.data[i]);
25362
+ }
25363
+ return RTV.tensor(resultRe2, v.shape);
25364
+ }
25259
25365
  const resultRe = new FloatXArray(v.data.length);
25260
25366
  const resultIm = new FloatXArray(v.data.length);
25261
25367
  let hasImag = false;
@@ -25430,7 +25536,7 @@ function registerMathFunctions() {
25430
25536
  im: Math.cos(re) * Math.sinh(im)
25431
25537
  }),
25432
25538
  "ComplexNumber",
25433
- { nativeJs: "Math.sin" }
25539
+ { nativeJs: "Math.sin", realSafe: true }
25434
25540
  ),
25435
25541
  1
25436
25542
  );
@@ -25443,7 +25549,7 @@ function registerMathFunctions() {
25443
25549
  im: -Math.sin(re) * Math.sinh(im)
25444
25550
  }),
25445
25551
  "ComplexNumber",
25446
- { nativeJs: "Math.cos" }
25552
+ { nativeJs: "Math.cos", realSafe: true }
25447
25553
  ),
25448
25554
  1
25449
25555
  );
@@ -25456,7 +25562,7 @@ function registerMathFunctions() {
25456
25562
  return { re: Math.sin(2 * re) / denom, im: Math.sinh(2 * im) / denom };
25457
25563
  },
25458
25564
  "ComplexNumber",
25459
- { nativeJs: "Math.tan" }
25565
+ { nativeJs: "Math.tan", realSafe: true }
25460
25566
  ),
25461
25567
  1
25462
25568
  );
@@ -25502,7 +25608,7 @@ function registerMathFunctions() {
25502
25608
  im: Math.cosh(re) * Math.sin(im)
25503
25609
  }),
25504
25610
  "ComplexNumber",
25505
- { nativeJs: "Math.sinh" }
25611
+ { nativeJs: "Math.sinh", realSafe: true }
25506
25612
  ),
25507
25613
  1
25508
25614
  );
@@ -25515,7 +25621,7 @@ function registerMathFunctions() {
25515
25621
  im: Math.sinh(re) * Math.sin(im)
25516
25622
  }),
25517
25623
  "ComplexNumber",
25518
- { nativeJs: "Math.cosh" }
25624
+ { nativeJs: "Math.cosh", realSafe: true }
25519
25625
  ),
25520
25626
  1
25521
25627
  );
@@ -25531,7 +25637,7 @@ function registerMathFunctions() {
25531
25637
  };
25532
25638
  },
25533
25639
  "ComplexNumber",
25534
- { nativeJs: "Math.tanh" }
25640
+ { nativeJs: "Math.tanh", realSafe: true }
25535
25641
  ),
25536
25642
  1
25537
25643
  );
@@ -25581,7 +25687,7 @@ function registerMathFunctions() {
25581
25687
  im: Math.exp(re) * Math.sin(im)
25582
25688
  }),
25583
25689
  "ComplexNumber",
25584
- { nativeJs: "Math.exp" }
25690
+ { nativeJs: "Math.exp", realSafe: true }
25585
25691
  ),
25586
25692
  1
25587
25693
  );
@@ -25683,7 +25789,7 @@ function registerMathFunctions() {
25683
25789
  Math.abs,
25684
25790
  (re, im) => ({ re: Math.sqrt(re * re + im * im), im: 0 }),
25685
25791
  "Number",
25686
- { nativeJs: "Math.abs" }
25792
+ { nativeJs: "Math.abs", realSafe: true }
25687
25793
  ),
25688
25794
  1
25689
25795
  );
@@ -29011,6 +29117,27 @@ function dimReduce(v, dim, reduceFn, initialValue, finalizeFn) {
29011
29117
  const imOut = resultImag && resultImag.some((x) => x !== 0) ? resultImag : void 0;
29012
29118
  return RTV.tensor(result, info.resultShape, imOut);
29013
29119
  }
29120
+ function dimReduceOmitNaN(v, dim, reduceFn, initialValue, finalizeFn) {
29121
+ if (!isRuntimeTensor(v))
29122
+ throw new RuntimeError("dimReduceOmitNaN: argument must be a tensor");
29123
+ const info = forEachSlice(v.shape, dim, () => {
29124
+ });
29125
+ if (!info) return copyTensor(v);
29126
+ const result = new FloatXArray(info.totalElems);
29127
+ forEachSlice(v.shape, dim, (outIdx, srcIndices) => {
29128
+ let acc = initialValue;
29129
+ let count = 0;
29130
+ for (let k = 0; k < srcIndices.length; k++) {
29131
+ const val = v.data[srcIndices[k]];
29132
+ if (!isNaN(val)) {
29133
+ acc = reduceFn(acc, val);
29134
+ count++;
29135
+ }
29136
+ }
29137
+ result[outIdx] = finalizeFn ? finalizeFn(acc, count) : acc;
29138
+ });
29139
+ return RTV.tensor(result, info.resultShape);
29140
+ }
29014
29141
  function sliceDimReduce(v, dim, sliceFn) {
29015
29142
  const info = forEachSlice(v.shape, dim, () => {
29016
29143
  });
@@ -29236,23 +29363,44 @@ function preserveTypeCheck(argTypes, nargout) {
29236
29363
  if (nargout === 2) return { outputTypes: [outType, outType] };
29237
29364
  return null;
29238
29365
  }
29239
- function makeReduction(name, kernel) {
29366
+ function parseNanFlag(args) {
29367
+ if (args.length >= 2) {
29368
+ const last = args[args.length - 1];
29369
+ if (isRuntimeChar(last)) {
29370
+ const s = toString(last).toLowerCase();
29371
+ if (s === "omitnan") return { args: args.slice(0, -1), omitNaN: true };
29372
+ if (s === "includenan")
29373
+ return { args: args.slice(0, -1), omitNaN: false };
29374
+ }
29375
+ }
29376
+ return { args, omitNaN: false };
29377
+ }
29378
+ function filterNaN(arr) {
29379
+ const out2 = [];
29380
+ for (let i = 0; i < arr.length; i++) {
29381
+ if (!isNaN(arr[i])) out2.push(arr[i]);
29382
+ }
29383
+ return new Float64Array(out2);
29384
+ }
29385
+ function makeReduction(name, kernel, omitNaNKernel) {
29240
29386
  return {
29241
29387
  check: reductionCheck,
29242
- apply: (args) => {
29243
- if (args.length < 1)
29388
+ apply: (rawArgs) => {
29389
+ if (rawArgs.length < 1)
29244
29390
  throw new RuntimeError(`${name} requires at least 1 argument`);
29391
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29392
+ const k = omitNaN && omitNaNKernel ? omitNaNKernel : kernel;
29245
29393
  const v = args[0];
29246
29394
  if (isRuntimeNumber(v)) return v;
29247
29395
  if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
29248
29396
  if (isRuntimeTensor(v)) {
29249
29397
  if (args.length >= 2) {
29250
29398
  if (isRuntimeChar(args[1]) && toString(args[1]) === "all")
29251
- return kernel.reduceAll(v);
29252
- return kernel.reduceDim(v, Math.round(toNumber(args[1])));
29399
+ return k.reduceAll(v);
29400
+ return k.reduceDim(v, Math.round(toNumber(args[1])));
29253
29401
  }
29254
29402
  const d = firstReduceDim(v.shape);
29255
- return d === 0 ? kernel.reduceAll(v) : kernel.reduceDim(v, d);
29403
+ return d === 0 ? k.reduceAll(v) : k.reduceDim(v, d);
29256
29404
  }
29257
29405
  throw new RuntimeError(`${name}: argument must be numeric`);
29258
29406
  }
@@ -29282,6 +29430,42 @@ function sliceKernel(sliceFn) {
29282
29430
  reduceDim: (v, dim) => sliceDimReduce(v, dim, sliceFn)
29283
29431
  };
29284
29432
  }
29433
+ function accumKernelOmitNaN(reduceFn, initial, finalizeFn) {
29434
+ return {
29435
+ reduceAll: (v) => {
29436
+ let acc = initial;
29437
+ let count = 0;
29438
+ for (let i = 0; i < v.data.length; i++) {
29439
+ if (!isNaN(v.data[i])) {
29440
+ acc = reduceFn(acc, v.data[i]);
29441
+ count++;
29442
+ }
29443
+ }
29444
+ const re = finalizeFn ? finalizeFn(acc, count) : acc;
29445
+ if (v.imag) {
29446
+ let accIm = initial;
29447
+ let countIm = 0;
29448
+ for (let i = 0; i < v.imag.length; i++) {
29449
+ if (!isNaN(v.imag[i])) {
29450
+ accIm = reduceFn(accIm, v.imag[i]);
29451
+ countIm++;
29452
+ }
29453
+ }
29454
+ const im = finalizeFn ? finalizeFn(accIm, countIm) : accIm;
29455
+ if (im !== 0) return RTV.complex(re, im);
29456
+ }
29457
+ return RTV.num(re);
29458
+ },
29459
+ reduceDim: (v, dim) => dimReduceOmitNaN(v, dim, reduceFn, initial, finalizeFn)
29460
+ };
29461
+ }
29462
+ function sliceKernelOmitNaN(sliceFn) {
29463
+ const filteredFn = (slice) => sliceFn(filterNaN(slice));
29464
+ return {
29465
+ reduceAll: (v) => RTV.num(filteredFn(v.data)),
29466
+ reduceDim: (v, dim) => sliceDimReduce(v, dim, filteredFn)
29467
+ };
29468
+ }
29285
29469
  function toNumArray(v, name) {
29286
29470
  if (isRuntimeNumber(v)) return [v];
29287
29471
  if (isRuntimeTensor(v)) return Array.from(v.data);
@@ -29293,15 +29477,16 @@ function registerBasicReductions() {
29293
29477
  register("sum", [
29294
29478
  {
29295
29479
  check: reductionCheck,
29296
- apply: (args) => {
29297
- if (args.length < 1)
29480
+ apply: (rawArgs) => {
29481
+ if (rawArgs.length < 1)
29298
29482
  throw new RuntimeError("sum requires at least 1 argument");
29483
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29299
29484
  const v = args[0];
29300
29485
  if (isRuntimeSparseMatrix(v)) {
29301
29486
  const dim = args.length >= 2 ? Math.round(toNumber(args[1])) : v.m > 1 ? 1 : v.n > 1 ? 2 : 1;
29302
29487
  return sparseSum(v, dim);
29303
29488
  }
29304
- const kernel = accumKernel((acc, val) => acc + val, 0);
29489
+ const kernel = omitNaN ? accumKernelOmitNaN((acc, val) => acc + val, 0) : accumKernel((acc, val) => acc + val, 0);
29305
29490
  if (isRuntimeNumber(v)) return v;
29306
29491
  if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
29307
29492
  if (isRuntimeTensor(v)) {
@@ -29317,13 +29502,14 @@ function registerBasicReductions() {
29317
29502
  }
29318
29503
  }
29319
29504
  ]);
29320
- const prodKernel = accumKernel((acc, val) => acc * val, 1);
29321
29505
  register("prod", [
29322
29506
  {
29323
29507
  check: reductionCheck,
29324
- apply: (args) => {
29325
- if (args.length < 1)
29508
+ apply: (rawArgs) => {
29509
+ if (rawArgs.length < 1)
29326
29510
  throw new RuntimeError("prod requires at least 1 argument");
29511
+ const { args: parsedArgs, omitNaN } = parseNanFlag(rawArgs);
29512
+ let args = parsedArgs;
29327
29513
  let v = args[0];
29328
29514
  if (isRuntimeSparseMatrix(v)) {
29329
29515
  v = sparseToDense(v);
@@ -29337,13 +29523,14 @@ function registerBasicReductions() {
29337
29523
  args.length >= 2 ? Math.round(toNumber(args[1])) : void 0
29338
29524
  );
29339
29525
  }
29526
+ const kernel = omitNaN ? accumKernelOmitNaN((acc, val) => acc * val, 1) : accumKernel((acc, val) => acc * val, 1);
29340
29527
  if (args.length >= 2) {
29341
29528
  if (isRuntimeChar(args[1]) && toString(args[1]) === "all")
29342
- return prodKernel.reduceAll(v);
29343
- return prodKernel.reduceDim(v, Math.round(toNumber(args[1])));
29529
+ return kernel.reduceAll(v);
29530
+ return kernel.reduceDim(v, Math.round(toNumber(args[1])));
29344
29531
  }
29345
29532
  const d = firstReduceDim(v.shape);
29346
- return d === 0 ? prodKernel.reduceAll(v) : prodKernel.reduceDim(v, d);
29533
+ return d === 0 ? kernel.reduceAll(v) : kernel.reduceDim(v, d);
29347
29534
  }
29348
29535
  throw new RuntimeError("prod: argument must be numeric");
29349
29536
  }
@@ -29356,31 +29543,40 @@ function registerBasicReductions() {
29356
29543
  (acc, val) => acc + val,
29357
29544
  0,
29358
29545
  (sum, count) => sum / count
29546
+ ),
29547
+ accumKernelOmitNaN(
29548
+ (acc, val) => acc + val,
29549
+ 0,
29550
+ (sum, count) => count === 0 ? NaN : sum / count
29359
29551
  )
29360
29552
  )
29361
29553
  ]);
29362
- const varianceOf = (slice, w) => {
29363
- const n = slice.length;
29554
+ const varianceOf = (slice, w, omitNaN) => {
29555
+ let data = slice;
29556
+ if (omitNaN) data = filterNaN(slice);
29557
+ const n = data.length;
29558
+ if (n === 0) return NaN;
29364
29559
  if (n <= 1 && w === 0) return 0;
29365
29560
  let s = 0;
29366
- for (let i = 0; i < n; i++) s += slice[i];
29561
+ for (let i = 0; i < n; i++) s += data[i];
29367
29562
  const m = s / n;
29368
29563
  let ss = 0;
29369
- for (let i = 0; i < n; i++) ss += (slice[i] - m) ** 2;
29564
+ for (let i = 0; i < n; i++) ss += (data[i] - m) ** 2;
29370
29565
  const denom = w === 1 ? n : n - 1;
29371
29566
  return ss / denom;
29372
29567
  };
29373
29568
  const stdVarApply = (name, transform) => {
29374
- return (args) => {
29375
- if (args.length < 1)
29569
+ return (rawArgs) => {
29570
+ if (rawArgs.length < 1)
29376
29571
  throw new RuntimeError(`${name} requires at least 1 argument`);
29572
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29377
29573
  const v = args[0];
29378
29574
  const w = args.length >= 2 ? toNumber(args[1]) : 0;
29379
29575
  const dimArg = args.length >= 3 ? Math.round(toNumber(args[2])) : 0;
29380
29576
  if (isRuntimeNumber(v)) return RTV.num(0);
29381
29577
  if (isRuntimeTensor(v)) {
29382
29578
  const kernel = sliceKernel(
29383
- (slice) => transform(varianceOf(slice, w))
29579
+ (slice) => transform(varianceOf(slice, w, omitNaN))
29384
29580
  );
29385
29581
  if (dimArg > 0) return kernel.reduceDim(v, dimArg);
29386
29582
  const d = firstReduceDim(v.shape);
@@ -29408,7 +29604,13 @@ function registerBasicReductions() {
29408
29604
  if (n % 2 === 1) return sorted[(n - 1) / 2];
29409
29605
  return (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
29410
29606
  };
29411
- register("median", [makeReduction("median", sliceKernel(medianOf))]);
29607
+ register("median", [
29608
+ makeReduction(
29609
+ "median",
29610
+ sliceKernel(medianOf),
29611
+ sliceKernelOmitNaN(medianOf)
29612
+ )
29613
+ ]);
29412
29614
  const modeOf = (arr) => {
29413
29615
  const counts = /* @__PURE__ */ new Map();
29414
29616
  for (let i = 0; i < arr.length; i++) {
@@ -29423,7 +29625,9 @@ function registerBasicReductions() {
29423
29625
  }
29424
29626
  return bestVal;
29425
29627
  };
29426
- register("mode", [makeReduction("mode", sliceKernel(modeOf))]);
29628
+ register("mode", [
29629
+ makeReduction("mode", sliceKernel(modeOf), sliceKernelOmitNaN(modeOf))
29630
+ ]);
29427
29631
  }
29428
29632
 
29429
29633
  // src/numbl-core/builtins/reduction/min-max.ts
@@ -30051,6 +30255,11 @@ function registerSortUnique() {
30051
30255
  dim = idx >= 0 ? idx + 1 : 1;
30052
30256
  }
30053
30257
  const dimIdx = dim - 1;
30258
+ if (!im && !descend && nargout <= 1 && re.length === shape[dimIdx]) {
30259
+ const sorted2 = new FloatXArray(re);
30260
+ sorted2.sort();
30261
+ return RTV.tensor(sorted2, [...shape]);
30262
+ }
30054
30263
  if (dimIdx >= shape.length) {
30055
30264
  const cp = RTV.tensor(
30056
30265
  new FloatXArray(re),
@@ -32884,51 +33093,61 @@ function registerDet() {
32884
33093
  register("cross", [
32885
33094
  {
32886
33095
  check: (argTypes, nargout) => {
32887
- if (argTypes.length !== 2 || nargout !== 1) return null;
33096
+ if (argTypes.length !== 2 && argTypes.length !== 3 || nargout !== 1)
33097
+ return null;
32888
33098
  return { outputTypes: [IType.Unknown] };
32889
33099
  },
32890
33100
  apply: (args) => {
32891
- if (args.length !== 2)
32892
- throw new RuntimeError("cross requires 2 arguments");
33101
+ if (args.length < 2 || args.length > 3)
33102
+ throw new RuntimeError("cross requires 2 or 3 arguments");
32893
33103
  const a = args[0], b = args[1];
32894
33104
  if (!isRuntimeTensor(a) || !isRuntimeTensor(b))
32895
- throw new RuntimeError("cross: arguments must be vectors");
32896
- const aRows = a.shape[0];
32897
- const aCols = a.shape.length >= 2 ? a.shape[1] : 1;
32898
- const bRows = b.shape[0];
32899
- const bCols = b.shape.length >= 2 ? b.shape[1] : 1;
32900
- const isColVector = aRows === 3 && aCols === 1;
32901
- const isRowVector = aRows === 1 && aCols === 3;
32902
- const isMatrix = aRows === 3 && aCols > 1;
32903
- if (isColVector || isRowVector) {
32904
- if (a.data.length !== 3 || b.data.length !== 3)
32905
- throw new RuntimeError("cross: vectors must have 3 elements");
32906
- const ax = a.data[0], ay = a.data[1], az = a.data[2];
32907
- const bx = b.data[0], by = b.data[1], bz = b.data[2];
32908
- const result = new FloatXArray(3);
32909
- result[0] = ay * bz - az * by;
32910
- result[1] = az * bx - ax * bz;
32911
- result[2] = ax * by - ay * bx;
32912
- return RTV.tensor(result, isColVector ? [3, 1] : [1, 3]);
32913
- } else if (isMatrix) {
32914
- if (bRows !== 3 || bCols !== aCols)
32915
- throw new RuntimeError("cross: matrix dimensions must agree");
32916
- const cols = aCols;
32917
- const result = new FloatXArray(3 * cols);
32918
- for (let c = 0; c < cols; c++) {
32919
- const off = c * 3;
32920
- const ax = a.data[off], ay = a.data[off + 1], az = a.data[off + 2];
32921
- const bx = b.data[off], by = b.data[off + 1], bz = b.data[off + 2];
32922
- result[off] = ay * bz - az * by;
32923
- result[off + 1] = az * bx - ax * bz;
32924
- result[off + 2] = ax * by - ay * bx;
32925
- }
32926
- return RTV.tensor(result, [3, cols]);
33105
+ throw new RuntimeError("cross: arguments must be vectors or arrays");
33106
+ const shape = a.shape;
33107
+ if (shape.length !== b.shape.length || shape.some((s, i) => s !== b.shape[i]))
33108
+ throw new RuntimeError("cross: A and B must have the same size");
33109
+ let dim;
33110
+ if (args.length === 3) {
33111
+ dim = toNumber(args[2]);
33112
+ if (!Number.isInteger(dim) || dim < 1)
33113
+ throw new RuntimeError("cross: dim must be a positive integer");
32927
33114
  } else {
33115
+ dim = shape.indexOf(3) + 1;
33116
+ if (dim === 0)
33117
+ throw new RuntimeError(
33118
+ "cross: A and B must have at least one dimension of length 3"
33119
+ );
33120
+ }
33121
+ const dimIdx = dim - 1;
33122
+ if (dimIdx >= shape.length || shape[dimIdx] !== 3)
32928
33123
  throw new RuntimeError(
32929
- "cross: inputs must be 3-element vectors or 3xN matrices"
33124
+ `cross: size(A,${dim}) and size(B,${dim}) must be 3`
32930
33125
  );
32931
- }
33126
+ const totalLen = a.data.length;
33127
+ const result = new FloatXArray(totalLen);
33128
+ const strides = new Array(shape.length);
33129
+ strides[0] = 1;
33130
+ for (let d = 1; d < shape.length; d++)
33131
+ strides[d] = strides[d - 1] * shape[d - 1];
33132
+ const dimStride = strides[dimIdx];
33133
+ const outerStride = dimIdx + 1 < shape.length ? strides[dimIdx + 1] : totalLen;
33134
+ const innerSize = dimStride;
33135
+ const numOuter = totalLen / outerStride;
33136
+ for (let outer = 0; outer < numOuter; outer++) {
33137
+ const blockBase = outer * outerStride;
33138
+ for (let inner = 0; inner < innerSize; inner++) {
33139
+ const base = blockBase + inner;
33140
+ const i0 = base;
33141
+ const i1 = base + dimStride;
33142
+ const i2 = base + 2 * dimStride;
33143
+ const ax = a.data[i0], ay = a.data[i1], az = a.data[i2];
33144
+ const bx = b.data[i0], by = b.data[i1], bz = b.data[i2];
33145
+ result[i0] = ay * bz - az * by;
33146
+ result[i1] = az * bx - ax * bz;
33147
+ result[i2] = ax * by - ay * bx;
33148
+ }
33149
+ }
33150
+ return RTV.tensor(result, [...shape]);
32932
33151
  }
32933
33152
  }
32934
33153
  ]);
@@ -38367,6 +38586,67 @@ function endResolver(mv, numIndices) {
38367
38586
  };
38368
38587
  }
38369
38588
  function index(rt, base, indices, nargout = 1, skipSubsref = false) {
38589
+ if (typeof base === "object" && base !== null && base.kind === "tensor") {
38590
+ const t = base;
38591
+ const nIdx = indices.length;
38592
+ if (nIdx === 1) {
38593
+ const idx = indices[0];
38594
+ if (typeof idx === "number") {
38595
+ const i = Math.round(idx) - 1;
38596
+ if (i < 0 || i >= t.data.length)
38597
+ throw new RuntimeError("Index exceeds array bounds");
38598
+ if (t.imag !== void 0) {
38599
+ const im = t.imag[i];
38600
+ return im === 0 ? t.data[i] : RTV.complex(t.data[i], im);
38601
+ }
38602
+ return t.data[i];
38603
+ }
38604
+ } else if (nIdx === 2) {
38605
+ const ri = indices[0];
38606
+ const ci = indices[1];
38607
+ if (typeof ri === "number" && typeof ci === "number") {
38608
+ const s = t.shape;
38609
+ const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
38610
+ const cols = s.length === 0 ? 1 : s.length === 1 ? s[0] : s[1];
38611
+ const r = Math.round(ri) - 1;
38612
+ const c = Math.round(ci) - 1;
38613
+ if (r < 0 || r >= rows || c < 0 || c >= cols)
38614
+ throw new RuntimeError("Index exceeds array bounds");
38615
+ const lin = c * rows + r;
38616
+ if (t.imag !== void 0) {
38617
+ const im = t.imag[lin];
38618
+ return im === 0 ? t.data[lin] : RTV.complex(t.data[lin], im);
38619
+ }
38620
+ return t.data[lin];
38621
+ }
38622
+ } else if (nIdx >= 3) {
38623
+ let allNumeric = true;
38624
+ for (let k = 0; k < nIdx; k++) {
38625
+ if (typeof indices[k] !== "number") {
38626
+ allNumeric = false;
38627
+ break;
38628
+ }
38629
+ }
38630
+ if (allNumeric) {
38631
+ const s = t.shape;
38632
+ let lin = 0;
38633
+ let stride = 1;
38634
+ for (let k = 0; k < nIdx; k++) {
38635
+ const dimSize = k < s.length ? s[k] : 1;
38636
+ const sub = Math.round(indices[k]) - 1;
38637
+ if (sub < 0 || sub >= dimSize)
38638
+ throw new RuntimeError("Index exceeds array bounds");
38639
+ lin += sub * stride;
38640
+ stride *= dimSize;
38641
+ }
38642
+ if (t.imag !== void 0) {
38643
+ const im = t.imag[lin];
38644
+ return im === 0 ? t.data[lin] : RTV.complex(t.data[lin], im);
38645
+ }
38646
+ return t.data[lin];
38647
+ }
38648
+ }
38649
+ }
38370
38650
  if (typeof base === "function") {
38371
38651
  return base(...indices);
38372
38652
  }
@@ -38451,6 +38731,13 @@ function resolveIndicesForClassInstance(rt, mv, base, indices) {
38451
38731
  });
38452
38732
  }
38453
38733
  function indexCell(rt, base, indices) {
38734
+ if (indices.length === 1 && typeof indices[0] === "number" && typeof base === "object" && base !== null && base.kind === "cell") {
38735
+ const cell = base;
38736
+ const i = Math.round(indices[0]) - 1;
38737
+ if (i < 0 || i >= cell.data.length)
38738
+ throw new RuntimeError("Cell index exceeds bounds");
38739
+ return cell.data[i];
38740
+ }
38454
38741
  const mv = ensureRuntimeValue(base);
38455
38742
  if (isRuntimeClassInstance(mv)) {
38456
38743
  const subsrefFn = rt.cachedResolveClassMethod(mv.className, "subsref");
@@ -38589,7 +38876,7 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
38589
38876
  if (isRuntimeCell(mv)) {
38590
38877
  const idxMvals2 = resolveIndices(indices, endResolver(mv, indices.length));
38591
38878
  const rhsMv2 = ensureRuntimeValue(rhs);
38592
- return storeIntoRTValueIndex(mv, idxMvals2, rhsMv2);
38879
+ return storeIntoRTValueIndex(mv, idxMvals2, rhsMv2, true);
38593
38880
  }
38594
38881
  if (isRuntimeStruct(mv)) {
38595
38882
  return ensureRuntimeValue(rhs);
@@ -40148,15 +40435,23 @@ var Runtime = class _Runtime {
40148
40435
  // ── Builtin initialization ──────────────────────────────────────────
40149
40436
  initBuiltins() {
40150
40437
  for (const name of getAllBuiltinNames()) {
40438
+ const builtin = getBuiltin(name);
40439
+ const singleBranch = builtin.length === 1 ? builtin[0] : null;
40151
40440
  this.builtins[name] = (nargout, args) => {
40152
- const builtin = getBuiltin(name);
40153
40441
  const margs = args.map((a) => ensureRuntimeValue(a));
40154
- const argItemTypes = margs.map((arg) => getItemTypeFromRuntimeValue(arg));
40155
- let branch = builtin[0];
40156
- for (let i = 0; i < builtin.length; i++) {
40157
- if (builtin[i].check(argItemTypes, nargout)) {
40158
- branch = builtin[i];
40159
- break;
40442
+ let branch;
40443
+ if (singleBranch) {
40444
+ branch = singleBranch;
40445
+ } else {
40446
+ const argItemTypes = margs.map(
40447
+ (arg) => getItemTypeFromRuntimeValue(arg)
40448
+ );
40449
+ branch = builtin[0];
40450
+ for (let i = 0; i < builtin.length; i++) {
40451
+ if (builtin[i].check(argItemTypes, nargout)) {
40452
+ branch = builtin[i];
40453
+ break;
40454
+ }
40160
40455
  }
40161
40456
  }
40162
40457
  if (this.profilingEnabled) {
@@ -44579,8 +44874,6 @@ function genClassInstantiation(cg, kind) {
44579
44874
  return createExpr;
44580
44875
  }
44581
44876
  var NATIVE_MATH_1 = {
44582
- // Note: sqrt, asin, acos, log are NOT here because they can produce
44583
- // complex results from real inputs (e.g., sqrt(-1) = 1i).
44584
44877
  abs: "Math.abs",
44585
44878
  floor: "Math.floor",
44586
44879
  ceil: "Math.ceil",
@@ -44594,6 +44887,14 @@ var NATIVE_MATH_1 = {
44594
44887
  log10: "Math.log10",
44595
44888
  sign: "Math.sign"
44596
44889
  };
44890
+ var GUARDED_MATH_1 = {
44891
+ sqrt: { fn: "Math.sqrt", guard: "$x >= 0" },
44892
+ log: { fn: "Math.log", guard: "$x > 0" },
44893
+ asin: { fn: "Math.asin", guard: "$x >= -1 && $x <= 1" },
44894
+ acos: { fn: "Math.acos", guard: "$x >= -1 && $x <= 1" },
44895
+ acosh: { fn: "Math.acosh", guard: "$x >= 1" },
44896
+ atanh: { fn: "Math.atanh", guard: "$x > -1 && $x < 1" }
44897
+ };
44597
44898
  var NATIVE_MATH_2 = {
44598
44899
  // Note: max/min are NOT here because Math.max/Math.min propagate NaN,
44599
44900
  // but max/min ignore NaN (returning the non-NaN value).
@@ -44605,6 +44906,15 @@ function tryNativeMathCodegen(cg, name, nargout, irArgs, jsArgs) {
44605
44906
  if (jsArgs.length === 1 && itemTypeForExprKind(irArgs[0].kind, cg.typeEnv).kind === "Number") {
44606
44907
  const fn = NATIVE_MATH_1[name];
44607
44908
  if (fn) return `${fn}(${jsArgs[0]})`;
44909
+ const guarded = GUARDED_MATH_1[name];
44910
+ if (guarded) {
44911
+ const arg = jsArgs[0];
44912
+ const tmp = cg.freshTemp("$gm");
44913
+ cg.emit(`var ${tmp};`);
44914
+ const guard = guarded.guard.replace(/\$x/g, tmp);
44915
+ const fallback = `${cg.useBuiltin(name)}(1, [${tmp}])`;
44916
+ return `(${tmp} = ${arg}, ${guard} ? ${guarded.fn}(${tmp}) : ${fallback})`;
44917
+ }
44608
44918
  }
44609
44919
  if (jsArgs.length === 2 && itemTypeForExprKind(irArgs[0].kind, cg.typeEnv).kind === "Number" && itemTypeForExprKind(irArgs[1].kind, cg.typeEnv).kind === "Number") {
44610
44920
  const fn = NATIVE_MATH_2[name];
@@ -46318,6 +46628,21 @@ function instantiateWasm(wasmData) {
46318
46628
  }
46319
46629
  return new WebAssembly.Instance(wasmModule, importObject);
46320
46630
  }
46631
+ function resolveBindings(file, directives, getWasmInstance, nativeBridge2) {
46632
+ const wasmInstance = directives.wasm ? getWasmInstance(directives.wasm) : void 0;
46633
+ let nativeLib;
46634
+ if (directives.native && nativeBridge2) {
46635
+ const libFile = nativeLibFilename(directives.native);
46636
+ const dir = file.name.substring(0, file.name.lastIndexOf("/") + 1);
46637
+ const libPath = dir + libFile;
46638
+ try {
46639
+ nativeLib = nativeBridge2.load(libPath);
46640
+ } catch {
46641
+ }
46642
+ }
46643
+ return { wasmInstance, nativeLib };
46644
+ }
46645
+ var LOADING = /* @__PURE__ */ Symbol("loading");
46321
46646
  function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46322
46647
  const result = /* @__PURE__ */ new Map();
46323
46648
  const wasmMap = wasmFiles ? buildWasmMap(wasmFiles) : /* @__PURE__ */ new Map();
@@ -46331,7 +46656,68 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46331
46656
  wasmInstanceCache.set(name, instance);
46332
46657
  return instance;
46333
46658
  }
46659
+ const libraryFiles = /* @__PURE__ */ new Map();
46660
+ const functionFiles = [];
46334
46661
  for (const file of jsFiles) {
46662
+ const base = file.name.split("/").pop();
46663
+ if (base.startsWith("_")) {
46664
+ const libName = base.replace(/\.js$/, "");
46665
+ libraryFiles.set(libName, file);
46666
+ } else {
46667
+ functionFiles.push(file);
46668
+ }
46669
+ }
46670
+ const libCache = /* @__PURE__ */ new Map();
46671
+ function importJS(name) {
46672
+ const cached = libCache.get(name);
46673
+ if (cached === LOADING) {
46674
+ throw new RuntimeError(`Circular dependency detected: ${name}.js`);
46675
+ }
46676
+ if (libCache.has(name)) return cached;
46677
+ const libFile = libraryFiles.get(name);
46678
+ if (!libFile) {
46679
+ throw new RuntimeError(
46680
+ `importJS: library '${name}.js' not found in workspace`
46681
+ );
46682
+ }
46683
+ libCache.set(name, LOADING);
46684
+ const directives = parseDirectives(libFile.source);
46685
+ const { wasmInstance, nativeLib } = resolveBindings(
46686
+ libFile,
46687
+ directives,
46688
+ getWasmInstance,
46689
+ nativeBridge2
46690
+ );
46691
+ const dummyRegister = () => {
46692
+ throw new RuntimeError(
46693
+ `Library file '${name}.js' must not call register(). Use return {...} to export values.`
46694
+ );
46695
+ };
46696
+ const factory = new Function(
46697
+ "RTV",
46698
+ "RuntimeError",
46699
+ "FloatXArray",
46700
+ "IType",
46701
+ "register",
46702
+ "wasm",
46703
+ "native",
46704
+ "importJS",
46705
+ libFile.source
46706
+ );
46707
+ const exports = factory(
46708
+ RTV,
46709
+ RuntimeError,
46710
+ FloatXArray,
46711
+ IType,
46712
+ dummyRegister,
46713
+ wasmInstance,
46714
+ nativeLib,
46715
+ importJS
46716
+ );
46717
+ libCache.set(name, exports);
46718
+ return exports;
46719
+ }
46720
+ for (const file of functionFiles) {
46335
46721
  const funcName = funcNameFromFile(file.name);
46336
46722
  try {
46337
46723
  const branches = [];
@@ -46345,17 +46731,12 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46345
46731
  });
46346
46732
  };
46347
46733
  const directives = parseDirectives(file.source);
46348
- const wasmInstance = directives.wasm ? getWasmInstance(directives.wasm) : void 0;
46349
- let nativeLib;
46350
- if (directives.native && nativeBridge2) {
46351
- const libFile = nativeLibFilename(directives.native);
46352
- const dir = file.name.substring(0, file.name.lastIndexOf("/") + 1);
46353
- const libPath = dir + libFile;
46354
- try {
46355
- nativeLib = nativeBridge2.load(libPath);
46356
- } catch {
46357
- }
46358
- }
46734
+ const { wasmInstance, nativeLib } = resolveBindings(
46735
+ file,
46736
+ directives,
46737
+ getWasmInstance,
46738
+ nativeBridge2
46739
+ );
46359
46740
  const factory = new Function(
46360
46741
  "RTV",
46361
46742
  "RuntimeError",
@@ -46364,6 +46745,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46364
46745
  "register",
46365
46746
  "wasm",
46366
46747
  "native",
46748
+ "importJS",
46367
46749
  file.source
46368
46750
  );
46369
46751
  factory(
@@ -46373,7 +46755,8 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46373
46755
  IType,
46374
46756
  registerFn,
46375
46757
  wasmInstance,
46376
- nativeLib
46758
+ nativeLib,
46759
+ importJS
46377
46760
  );
46378
46761
  if (branches.length === 0) {
46379
46762
  throw new Error(
@@ -47188,6 +47571,9 @@ Call stack (most recent call first):`;
47188
47571
  return result;
47189
47572
  }
47190
47573
 
47574
+ // src/numbl-core/version.ts
47575
+ var NUMBL_VERSION = "0.0.22";
47576
+
47191
47577
  // src/cli-repl.ts
47192
47578
  import { createInterface } from "readline";
47193
47579
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, appendFileSync } from "fs";
@@ -47982,6 +48368,10 @@ Commands:
47982
48368
  mip <subcommand> Package manager (install, uninstall, list, avail, info)
47983
48369
  (no command) Start interactive REPL
47984
48370
 
48371
+ Global options:
48372
+ --version, -V Print version and exit
48373
+ --help, -h Print this help message
48374
+
47985
48375
  Options (for REPL):
47986
48376
  --plot Enable plot server
47987
48377
  --plot-port <port> Set plot server port (implies --plot)
@@ -48376,6 +48766,7 @@ async function cmdBuildAddon() {
48376
48766
  function cmdInfo() {
48377
48767
  process.stdout.write(
48378
48768
  JSON.stringify({
48769
+ version: NUMBL_VERSION,
48379
48770
  nativeAddon: nativeAddonLoaded,
48380
48771
  nativeAddonPath: addonPath,
48381
48772
  packageDir: packageDir2
@@ -48534,6 +48925,10 @@ function cmdShowProfile(args) {
48534
48925
  }
48535
48926
  async function main() {
48536
48927
  const args = process.argv.slice(2);
48928
+ if (args.includes("--version") || args.includes("-V")) {
48929
+ console.log(NUMBL_VERSION);
48930
+ process.exit(0);
48931
+ }
48537
48932
  if (args.includes("--help") || args.includes("-h")) {
48538
48933
  printHelp();
48539
48934
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numbl",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Run .m source files in the browser and on the command line by compiling to JavaScript",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -106,10 +106,11 @@
106
106
  "vitest": "^4.0.18"
107
107
  },
108
108
  "lint-staged": {
109
- "*.{js,jsx,ts,tsx}": [
109
+ "*.{ts,tsx}": [
110
110
  "prettier --write",
111
111
  "eslint"
112
112
  ],
113
+ "*.{js,jsx}": "prettier --write",
113
114
  "*.{json,css,md,yml}": "prettier --write"
114
115
  },
115
116
  "optionalDependencies": {