numbl 0.0.21 → 0.0.23

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/dist-cli/cli.js CHANGED
@@ -1214,6 +1214,10 @@ var RTV = {
1214
1214
  while (s.length > 2 && s[s.length - 1] === 1) s.pop();
1215
1215
  return { kind: "tensor", data: d, imag: im, shape: s, _rc: 1 };
1216
1216
  },
1217
+ /** Fast tensor constructor — data must be FloatXArray, shape already normalized (no trailing singletons). */
1218
+ tensorRaw(data, shape) {
1219
+ return { kind: "tensor", data, imag: void 0, shape, _rc: 1 };
1220
+ },
1217
1221
  /** Create a scalar tensor (1x1) */
1218
1222
  scalar(value) {
1219
1223
  return value;
@@ -1352,6 +1356,7 @@ var getItemTypeFromRuntimeValue = (value) => {
1352
1356
  };
1353
1357
 
1354
1358
  // src/numbl-core/native/lapack-bridge.ts
1359
+ var NATIVE_ADDON_EXPECTED_VERSION = 1;
1355
1360
  var _bridge = null;
1356
1361
  function setLapackBridge(bridge) {
1357
1362
  _bridge = bridge;
@@ -19027,7 +19032,91 @@ function complexBinaryOp(a, b, op) {
19027
19032
  `Matrix dimensions must agree: [${at.shape.join(",")}] vs [${bt.shape.join(",")}]`
19028
19033
  );
19029
19034
  }
19035
+ var ELEMWISE_ADD = 0;
19036
+ var ELEMWISE_SUB = 1;
19037
+ var ELEMWISE_MUL = 2;
19038
+ var ELEMWISE_DIV = 3;
19039
+ function matchSameShapeTensors(a, b) {
19040
+ if (typeof a !== "object" || a === null || a.kind !== "tensor" || typeof b !== "object" || b === null || b.kind !== "tensor")
19041
+ return null;
19042
+ const at = a;
19043
+ const bt = b;
19044
+ if (at.data.length !== bt.data.length || at.shape.length !== bt.shape.length || at.shape.some((d, i) => d !== bt.shape[i]))
19045
+ return null;
19046
+ return [at, bt];
19047
+ }
19048
+ function tryNativeElemwiseReal(at, bt, opCode) {
19049
+ const bridge = getLapackBridge();
19050
+ if (!bridge?.elemwise) return null;
19051
+ const result = bridge.elemwise(
19052
+ at.data,
19053
+ bt.data,
19054
+ opCode
19055
+ );
19056
+ return RTV.tensorRaw(result, at.shape);
19057
+ }
19058
+ function tensorElemwiseComplex(at, bt, opCode, jsOp) {
19059
+ const bridge = getLapackBridge();
19060
+ if (bridge?.elemwiseComplex) {
19061
+ const r = bridge.elemwiseComplex(
19062
+ at.data,
19063
+ at.imag ?? null,
19064
+ bt.data,
19065
+ bt.imag ?? null,
19066
+ opCode
19067
+ );
19068
+ if (r.im) return RTV.tensor(r.re, at.shape, r.im);
19069
+ return RTV.tensorRaw(r.re, at.shape);
19070
+ }
19071
+ const len = at.data.length;
19072
+ const aIm = at.imag;
19073
+ const bIm = bt.imag;
19074
+ const resultRe = new FloatXArray(len);
19075
+ const resultIm = new FloatXArray(len);
19076
+ if (aIm && bIm) {
19077
+ for (let i = 0; i < len; i++) {
19078
+ const r = jsOp(at.data[i], aIm[i], bt.data[i], bIm[i]);
19079
+ resultRe[i] = r.re;
19080
+ resultIm[i] = r.im;
19081
+ }
19082
+ } else if (aIm) {
19083
+ for (let i = 0; i < len; i++) {
19084
+ const r = jsOp(at.data[i], aIm[i], bt.data[i], 0);
19085
+ resultRe[i] = r.re;
19086
+ resultIm[i] = r.im;
19087
+ }
19088
+ } else {
19089
+ for (let i = 0; i < len; i++) {
19090
+ const r = jsOp(at.data[i], 0, bt.data[i], bIm[i]);
19091
+ resultRe[i] = r.re;
19092
+ resultIm[i] = r.im;
19093
+ }
19094
+ }
19095
+ const isReal = resultIm.every((x) => x === 0);
19096
+ return RTV.tensor(resultRe, at.shape, isReal ? void 0 : resultIm);
19097
+ }
19030
19098
  function mAdd(a, b) {
19099
+ const m = matchSameShapeTensors(a, b);
19100
+ if (m) {
19101
+ const [at, bt] = m;
19102
+ if (!at.imag && !bt.imag) {
19103
+ const nr = tryNativeElemwiseReal(at, bt, ELEMWISE_ADD);
19104
+ if (nr) return nr;
19105
+ const len = at.data.length;
19106
+ const result = new FloatXArray(len);
19107
+ for (let i = 0; i < len; i++) result[i] = at.data[i] + bt.data[i];
19108
+ return RTV.tensorRaw(result, at.shape);
19109
+ }
19110
+ return tensorElemwiseComplex(
19111
+ at,
19112
+ bt,
19113
+ ELEMWISE_ADD,
19114
+ (aRe, aIm, bRe, bIm) => ({
19115
+ re: aRe + bRe,
19116
+ im: aIm + bIm
19117
+ })
19118
+ );
19119
+ }
19031
19120
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19032
19121
  return mAddSparse(a, b);
19033
19122
  if (isComplexOrMixed(a, b)) {
@@ -19039,6 +19128,27 @@ function mAdd(a, b) {
19039
19128
  return binaryOp(a, b, (x, y) => x + y);
19040
19129
  }
19041
19130
  function mSub(a, b) {
19131
+ const m = matchSameShapeTensors(a, b);
19132
+ if (m) {
19133
+ const [at, bt] = m;
19134
+ if (!at.imag && !bt.imag) {
19135
+ const nr = tryNativeElemwiseReal(at, bt, ELEMWISE_SUB);
19136
+ if (nr) return nr;
19137
+ const len = at.data.length;
19138
+ const result = new FloatXArray(len);
19139
+ for (let i = 0; i < len; i++) result[i] = at.data[i] - bt.data[i];
19140
+ return RTV.tensorRaw(result, at.shape);
19141
+ }
19142
+ return tensorElemwiseComplex(
19143
+ at,
19144
+ bt,
19145
+ ELEMWISE_SUB,
19146
+ (aRe, aIm, bRe, bIm) => ({
19147
+ re: aRe - bRe,
19148
+ im: aIm - bIm
19149
+ })
19150
+ );
19151
+ }
19042
19152
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19043
19153
  return mSubSparse(a, b);
19044
19154
  if (isComplexOrMixed(a, b)) {
@@ -19064,6 +19174,27 @@ function mMul(a, b) {
19064
19174
  return binaryOp(a, b, (x, y) => x * y);
19065
19175
  }
19066
19176
  function mElemMul(a, b) {
19177
+ const m = matchSameShapeTensors(a, b);
19178
+ if (m) {
19179
+ const [at, bt] = m;
19180
+ if (!at.imag && !bt.imag) {
19181
+ const nr = tryNativeElemwiseReal(at, bt, ELEMWISE_MUL);
19182
+ if (nr) return nr;
19183
+ const len = at.data.length;
19184
+ const result = new FloatXArray(len);
19185
+ for (let i = 0; i < len; i++) result[i] = at.data[i] * bt.data[i];
19186
+ return RTV.tensorRaw(result, at.shape);
19187
+ }
19188
+ return tensorElemwiseComplex(
19189
+ at,
19190
+ bt,
19191
+ ELEMWISE_MUL,
19192
+ (aRe, aIm, bRe, bIm) => ({
19193
+ re: aRe * bRe - aIm * bIm,
19194
+ im: aRe * bIm + aIm * bRe
19195
+ })
19196
+ );
19197
+ }
19067
19198
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19068
19199
  return mElemMulSparse(a, b);
19069
19200
  if (isComplexOrMixed(a, b)) {
@@ -19090,6 +19221,19 @@ function mDiv(a, b) {
19090
19221
  return binaryOp(a, b, (x, y) => x / y);
19091
19222
  }
19092
19223
  function mElemDiv(a, b) {
19224
+ const m = matchSameShapeTensors(a, b);
19225
+ if (m) {
19226
+ const [at, bt] = m;
19227
+ if (!at.imag && !bt.imag) {
19228
+ const nr = tryNativeElemwiseReal(at, bt, ELEMWISE_DIV);
19229
+ if (nr) return nr;
19230
+ const len = at.data.length;
19231
+ const result = new FloatXArray(len);
19232
+ for (let i = 0; i < len; i++) result[i] = at.data[i] / bt.data[i];
19233
+ return RTV.tensorRaw(result, at.shape);
19234
+ }
19235
+ return tensorElemwiseComplex(at, bt, ELEMWISE_DIV, complexDivide);
19236
+ }
19093
19237
  if (isRuntimeSparseMatrix(a) || isRuntimeSparseMatrix(b))
19094
19238
  return mElemDivSparse(a, b);
19095
19239
  if (isComplexOrMixed(a, b)) {
@@ -19189,6 +19333,31 @@ function mPow(a, b) {
19189
19333
  return binaryOp(a, b, (x, y) => Math.pow(x, y));
19190
19334
  }
19191
19335
  function mElemPow(a, b) {
19336
+ if (isRuntimeTensor(a) && !a.imag && isRuntimeNumber(b)) {
19337
+ const exp = b;
19338
+ if (exp === 2) {
19339
+ const result = new FloatXArray(a.data.length);
19340
+ for (let i = 0; i < result.length; i++) result[i] = a.data[i] * a.data[i];
19341
+ return RTV.tensor(result, a.shape);
19342
+ }
19343
+ if (Number.isInteger(exp) || exp >= 0) {
19344
+ let hasNeg2 = false;
19345
+ if (!Number.isInteger(exp)) {
19346
+ for (let i = 0; i < a.data.length; i++) {
19347
+ if (a.data[i] < 0) {
19348
+ hasNeg2 = true;
19349
+ break;
19350
+ }
19351
+ }
19352
+ }
19353
+ if (!hasNeg2) {
19354
+ const result = new FloatXArray(a.data.length);
19355
+ for (let i = 0; i < result.length; i++)
19356
+ result[i] = Math.pow(a.data[i], exp);
19357
+ return RTV.tensor(result, a.shape);
19358
+ }
19359
+ }
19360
+ }
19192
19361
  const complexPow = (aRe, aIm, bRe, bIm) => {
19193
19362
  const r = Math.sqrt(aRe * aRe + aIm * aIm);
19194
19363
  if (r === 0) {
@@ -19239,17 +19408,22 @@ function mElemPow(a, b) {
19239
19408
  return binaryOp(a, b, (x, y) => Math.pow(x, y));
19240
19409
  }
19241
19410
  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];
19411
+ if (isRuntimeTensor(v)) {
19412
+ if (v.imag !== void 0) {
19413
+ const resultRe2 = new FloatXArray(v.data.length);
19414
+ const resultIm = new FloatXArray(v.imag.length);
19415
+ for (let i = 0; i < v.data.length; i++) {
19416
+ resultRe2[i] = -v.data[i];
19417
+ resultIm[i] = -v.imag[i];
19418
+ }
19419
+ return RTV.tensor(resultRe2, v.shape, resultIm);
19250
19420
  }
19251
- return RTV.tensor(resultRe, v.shape, resultIm);
19421
+ const resultRe = new FloatXArray(v.data.length);
19422
+ for (let i = 0; i < v.data.length; i++) resultRe[i] = -v.data[i];
19423
+ return RTV.tensor(resultRe, v.shape);
19252
19424
  }
19425
+ if (isRuntimeSparseMatrix(v)) return sparseNeg(v);
19426
+ if (isRuntimeComplexNumber(v)) return RTV.complex(-v.re, -v.im);
19253
19427
  return unaryOp(v, (x) => -x);
19254
19428
  }
19255
19429
  function transposeCellArray(v) {
@@ -19524,6 +19698,40 @@ function broadcastComparison(a, b, outShape, op) {
19524
19698
  return t;
19525
19699
  }
19526
19700
  function binaryOp(a, b, op) {
19701
+ if (isRuntimeTensor(a) && a.data.length > 1) {
19702
+ if (isRuntimeTensor(b) && b.data.length > 1) {
19703
+ if (a.data.length === b.data.length && a.shape.length === b.shape.length && a.shape.every((d, i) => d === b.shape[i])) {
19704
+ const result = new FloatXArray(a.data.length);
19705
+ for (let i = 0; i < result.length; i++) {
19706
+ result[i] = op(a.data[i], b.data[i]);
19707
+ }
19708
+ return RTV.tensor(result, a.shape);
19709
+ }
19710
+ const broadcastShape2 = getBroadcastShape(a.shape, b.shape);
19711
+ if (broadcastShape2 !== null) {
19712
+ return broadcastBinary(a, b, broadcastShape2, op);
19713
+ }
19714
+ throw new RuntimeError(
19715
+ `Matrix dimensions must agree: [${a.shape.join(",")}] vs [${b.shape.join(",")}]`
19716
+ );
19717
+ }
19718
+ if (isRuntimeNumber(b)) {
19719
+ const result = new FloatXArray(a.data.length);
19720
+ const sv = b;
19721
+ for (let i = 0; i < result.length; i++) {
19722
+ result[i] = op(a.data[i], sv);
19723
+ }
19724
+ return RTV.tensor(result, a.shape);
19725
+ }
19726
+ }
19727
+ if (isRuntimeTensor(b) && b.data.length > 1 && isRuntimeNumber(a)) {
19728
+ const result = new FloatXArray(b.data.length);
19729
+ const sv = a;
19730
+ for (let i = 0; i < result.length; i++) {
19731
+ result[i] = op(sv, b.data[i]);
19732
+ }
19733
+ return RTV.tensor(result, b.shape);
19734
+ }
19527
19735
  const an = asNumeric(a);
19528
19736
  const bn = asNumeric(b);
19529
19737
  if (an.scalar && bn.scalar) {
@@ -19567,6 +19775,13 @@ function binaryOp(a, b, op) {
19567
19775
  );
19568
19776
  }
19569
19777
  function unaryOp(v, op) {
19778
+ if (isRuntimeTensor(v) && v.data.length > 1) {
19779
+ const result2 = new FloatXArray(v.data.length);
19780
+ for (let i = 0; i < result2.length; i++) {
19781
+ result2[i] = op(v.data[i]);
19782
+ }
19783
+ return RTV.tensor(result2, v.shape);
19784
+ }
19570
19785
  const n = asNumeric(v);
19571
19786
  if (n.scalar) {
19572
19787
  const val = n.isComplex ? n.re : n.value;
@@ -19645,14 +19860,37 @@ function matMul(a, b) {
19645
19860
  }
19646
19861
  const isComplex2 = a.imag !== void 0 || b.imag !== void 0;
19647
19862
  if (!isComplex2) {
19648
- const bridge = getEffectiveBridge("matmul", "matmul");
19863
+ const bridge2 = getEffectiveBridge("matmul", "matmul");
19649
19864
  const f64A = a.data instanceof Float64Array ? a.data : new Float64Array(a.data);
19650
19865
  const f64B = b.data instanceof Float64Array ? b.data : new Float64Array(b.data);
19651
- const raw = bridge.matmul(f64A, aRows, aCols, f64B, bCols);
19866
+ const raw = bridge2.matmul(f64A, aRows, aCols, f64B, bCols);
19652
19867
  return unwrap1x1(RTV.tensor(new FloatXArray(raw), [aRows, bCols]));
19653
19868
  }
19654
19869
  const aIm = a.imag || new FloatXArray(a.data.length);
19655
19870
  const bIm = b.imag || new FloatXArray(b.data.length);
19871
+ const bridge = getEffectiveBridge("matmul", "matmulComplex");
19872
+ if (bridge.matmulComplex) {
19873
+ const f64ARe = a.data instanceof Float64Array ? a.data : new Float64Array(a.data);
19874
+ const f64AIm = aIm instanceof Float64Array ? aIm : new Float64Array(aIm);
19875
+ const f64BRe = b.data instanceof Float64Array ? b.data : new Float64Array(b.data);
19876
+ const f64BIm = bIm instanceof Float64Array ? bIm : new Float64Array(bIm);
19877
+ const raw = bridge.matmulComplex(
19878
+ f64ARe,
19879
+ f64AIm,
19880
+ aRows,
19881
+ aCols,
19882
+ f64BRe,
19883
+ f64BIm,
19884
+ bCols
19885
+ );
19886
+ return unwrap1x1(
19887
+ RTV.tensor(
19888
+ new FloatXArray(raw.re),
19889
+ [aRows, bCols],
19890
+ raw.im ? new FloatXArray(raw.im) : void 0
19891
+ )
19892
+ );
19893
+ }
19656
19894
  const resultRe = new FloatXArray(aRows * bCols);
19657
19895
  const resultIm = new FloatXArray(aRows * bCols);
19658
19896
  for (let i = 0; i < aRows; i++) {
@@ -20148,6 +20386,30 @@ function indexIntoTensor1D(base, idx) {
20148
20386
  }
20149
20387
  function indexIntoTensor2D(base, rowIdx, colIdx) {
20150
20388
  const [rows, cols] = tensorSize2D(base);
20389
+ if (isRuntimeNumber(rowIdx) && isColonIndex(colIdx)) {
20390
+ const r = Math.round(rowIdx) - 1;
20391
+ if (r < 0 || r >= rows)
20392
+ throw new RuntimeError("Index exceeds array bounds");
20393
+ const resultData2 = new FloatXArray(cols);
20394
+ const resultImag2 = base.imag ? new FloatXArray(cols) : void 0;
20395
+ for (let ci = 0; ci < cols; ci++) {
20396
+ resultData2[ci] = base.data[r + ci * rows];
20397
+ if (resultImag2 && base.imag) resultImag2[ci] = base.imag[r + ci * rows];
20398
+ }
20399
+ return RTV.tensor(resultData2, [1, cols], resultImag2);
20400
+ }
20401
+ if (isColonIndex(rowIdx) && isRuntimeNumber(colIdx)) {
20402
+ const c = Math.round(colIdx) - 1;
20403
+ if (c < 0 || c >= cols)
20404
+ throw new RuntimeError("Index exceeds array bounds");
20405
+ const offset = c * rows;
20406
+ const resultData2 = new FloatXArray(rows);
20407
+ for (let ri = 0; ri < rows; ri++) resultData2[ri] = base.data[offset + ri];
20408
+ const resultImag2 = base.imag ? new FloatXArray(rows) : void 0;
20409
+ if (resultImag2 && base.imag)
20410
+ for (let ri = 0; ri < rows; ri++) resultImag2[ri] = base.imag[offset + ri];
20411
+ return RTV.tensor(resultData2, [rows, 1], resultImag2);
20412
+ }
20151
20413
  const rowIdxArr = resolveIndex(rowIdx, rows);
20152
20414
  const colIdxArr = resolveIndex(colIdx, cols);
20153
20415
  const numR = rowIdxArr.length;
@@ -20486,6 +20748,11 @@ function storeIntoTensor(base, indices, rhs) {
20486
20748
  return deleteTensorElements(base, indices[0]);
20487
20749
  }
20488
20750
  if (isRuntimeTensor(rhs) && rhs.data.length === 0 && indices.length === 2) {
20751
+ const nrows = base.shape[0] ?? 1;
20752
+ const ncols = base.shape.length >= 2 ? base.shape[1] : 1;
20753
+ const rowCount = isColonIndex(indices[0]) ? nrows : resolveIndex(indices[0], nrows).length;
20754
+ const colCount = isColonIndex(indices[1]) ? ncols : resolveIndex(indices[1], ncols).length;
20755
+ if (rowCount === 0 || colCount === 0) return base;
20489
20756
  return deleteTensorRowsOrCols(base, indices);
20490
20757
  }
20491
20758
  if (base._rc > 1) {
@@ -20983,7 +21250,7 @@ function storeIntoTensorND(base, indices, rhs) {
20983
21250
  }
20984
21251
  return base;
20985
21252
  }
20986
- function storeIntoCell(base, indices, rhs) {
21253
+ function storeIntoCell(base, indices, rhs, parenAssign = false) {
20987
21254
  if (base._rc > 1) {
20988
21255
  base._rc--;
20989
21256
  const sharedData = base.data.map((elem) => shareRuntimeValue(elem));
@@ -20997,14 +21264,20 @@ function storeIntoCell(base, indices, rhs) {
20997
21264
  c.shape = isColVec ? [newLen, 1] : [1, newLen];
20998
21265
  };
20999
21266
  if (indices.length === 1) {
21000
- return storeIntoCell1D(base, indices[0], rhs, updateShapeAfterLinearAssign);
21267
+ return storeIntoCell1D(
21268
+ base,
21269
+ indices[0],
21270
+ rhs,
21271
+ updateShapeAfterLinearAssign,
21272
+ parenAssign
21273
+ );
21001
21274
  }
21002
21275
  if (indices.length === 2) {
21003
- return storeIntoCell2D(base, indices, rhs);
21276
+ return storeIntoCell2D(base, indices, rhs, parenAssign);
21004
21277
  }
21005
21278
  throw new RuntimeError(`Cannot index-assign into cell`);
21006
21279
  }
21007
- function storeIntoCell1D(base, idx, rhs, updateShape) {
21280
+ function storeIntoCell1D(base, idx, rhs, updateShape, parenAssign = false) {
21008
21281
  if (isRuntimeTensor(idx)) {
21009
21282
  let positions;
21010
21283
  if (idx._isLogical) {
@@ -21052,11 +21325,11 @@ function storeIntoCell1D(base, idx, rhs, updateShape) {
21052
21325
  while (base.data.length <= i)
21053
21326
  base.data.push(RTV.tensor(new FloatXArray(0), [0, 0]));
21054
21327
  }
21055
- base.data[i] = isRuntimeCell(rhs) && rhs.data.length === 1 ? rhs.data[0] : rhs;
21328
+ base.data[i] = parenAssign && isRuntimeCell(rhs) && rhs.data.length === 1 ? rhs.data[0] : rhs;
21056
21329
  updateShape(oldLen);
21057
21330
  return base;
21058
21331
  }
21059
- function storeIntoCell2D(base, indices, rhs) {
21332
+ function storeIntoCell2D(base, indices, rhs, parenAssign = false) {
21060
21333
  let rows = base.shape[0];
21061
21334
  let cols = base.shape.length >= 2 ? base.shape[1] : 1;
21062
21335
  const rowIndices = resolveIndex(indices[0], rows, 0);
@@ -21082,7 +21355,7 @@ function storeIntoCell2D(base, indices, rhs) {
21082
21355
  const nSelectedRows = rowIndices.length;
21083
21356
  const nSelectedCols = colIndices.length;
21084
21357
  const totalSelected = nSelectedRows * nSelectedCols;
21085
- if (isRuntimeCell(rhs)) {
21358
+ if (parenAssign && isRuntimeCell(rhs)) {
21086
21359
  if (rhs.data.length !== totalSelected && rhs.data.length !== 1) {
21087
21360
  throw new RuntimeError("Subscripted assignment dimension mismatch");
21088
21361
  }
@@ -21100,7 +21373,7 @@ function storeIntoCell2D(base, indices, rhs) {
21100
21373
  }
21101
21374
  return base;
21102
21375
  }
21103
- function storeIntoRTValueIndex(base, indices, rhs) {
21376
+ function storeIntoRTValueIndex(base, indices, rhs, parenAssign = false) {
21104
21377
  if (isRuntimeSparseMatrix(base)) {
21105
21378
  return storeIntoSparse(base, indices, rhs);
21106
21379
  }
@@ -21121,7 +21394,7 @@ function storeIntoRTValueIndex(base, indices, rhs) {
21121
21394
  return storeIntoTensor(base, indices, rhs);
21122
21395
  }
21123
21396
  if (isRuntimeCell(base)) {
21124
- return storeIntoCell(base, indices, rhs);
21397
+ return storeIntoCell(base, indices, rhs, parenAssign);
21125
21398
  }
21126
21399
  throw new RuntimeError(`Cannot index-assign into ${kstr(base)}`);
21127
21400
  }
@@ -23031,11 +23304,8 @@ var CommandParser = class extends ExpressionParser {
23031
23304
  scanPos++;
23032
23305
  continue;
23033
23306
  }
23034
- if (ch === "\n" || ch === "\r" || ch === ";" || ch === "%") break;
23035
- if (ch === ",") {
23036
- scanPos++;
23037
- continue;
23038
- }
23307
+ if (ch === "\n" || ch === "\r" || ch === ";" || ch === "%" || ch === ",")
23308
+ break;
23039
23309
  if (ch === "." && scanPos + 2 < this.input.length && this.input[scanPos + 1] === "." && this.input[scanPos + 2] === ".") {
23040
23310
  scanPos += 3;
23041
23311
  while (scanPos < this.input.length && this.input[scanPos] !== "\n") {
@@ -25256,6 +25526,13 @@ function complexElemwise(realFn, complexFn, complexOutKind, o) {
25256
25526
  const isReal = complexOutKind === "Number" || resultIm2.every((x) => x === 0);
25257
25527
  return RTV.tensor(resultRe2, v.shape, isReal ? void 0 : resultIm2);
25258
25528
  }
25529
+ if (o?.realSafe) {
25530
+ const resultRe2 = new FloatXArray(v.data.length);
25531
+ for (let i = 0; i < v.data.length; i++) {
25532
+ resultRe2[i] = realFn(v.data[i]);
25533
+ }
25534
+ return RTV.tensor(resultRe2, v.shape);
25535
+ }
25259
25536
  const resultRe = new FloatXArray(v.data.length);
25260
25537
  const resultIm = new FloatXArray(v.data.length);
25261
25538
  let hasImag = false;
@@ -25430,7 +25707,7 @@ function registerMathFunctions() {
25430
25707
  im: Math.cos(re) * Math.sinh(im)
25431
25708
  }),
25432
25709
  "ComplexNumber",
25433
- { nativeJs: "Math.sin" }
25710
+ { nativeJs: "Math.sin", realSafe: true }
25434
25711
  ),
25435
25712
  1
25436
25713
  );
@@ -25443,7 +25720,7 @@ function registerMathFunctions() {
25443
25720
  im: -Math.sin(re) * Math.sinh(im)
25444
25721
  }),
25445
25722
  "ComplexNumber",
25446
- { nativeJs: "Math.cos" }
25723
+ { nativeJs: "Math.cos", realSafe: true }
25447
25724
  ),
25448
25725
  1
25449
25726
  );
@@ -25456,7 +25733,7 @@ function registerMathFunctions() {
25456
25733
  return { re: Math.sin(2 * re) / denom, im: Math.sinh(2 * im) / denom };
25457
25734
  },
25458
25735
  "ComplexNumber",
25459
- { nativeJs: "Math.tan" }
25736
+ { nativeJs: "Math.tan", realSafe: true }
25460
25737
  ),
25461
25738
  1
25462
25739
  );
@@ -25502,7 +25779,7 @@ function registerMathFunctions() {
25502
25779
  im: Math.cosh(re) * Math.sin(im)
25503
25780
  }),
25504
25781
  "ComplexNumber",
25505
- { nativeJs: "Math.sinh" }
25782
+ { nativeJs: "Math.sinh", realSafe: true }
25506
25783
  ),
25507
25784
  1
25508
25785
  );
@@ -25515,7 +25792,7 @@ function registerMathFunctions() {
25515
25792
  im: Math.sinh(re) * Math.sin(im)
25516
25793
  }),
25517
25794
  "ComplexNumber",
25518
- { nativeJs: "Math.cosh" }
25795
+ { nativeJs: "Math.cosh", realSafe: true }
25519
25796
  ),
25520
25797
  1
25521
25798
  );
@@ -25531,7 +25808,7 @@ function registerMathFunctions() {
25531
25808
  };
25532
25809
  },
25533
25810
  "ComplexNumber",
25534
- { nativeJs: "Math.tanh" }
25811
+ { nativeJs: "Math.tanh", realSafe: true }
25535
25812
  ),
25536
25813
  1
25537
25814
  );
@@ -25581,7 +25858,7 @@ function registerMathFunctions() {
25581
25858
  im: Math.exp(re) * Math.sin(im)
25582
25859
  }),
25583
25860
  "ComplexNumber",
25584
- { nativeJs: "Math.exp" }
25861
+ { nativeJs: "Math.exp", realSafe: true }
25585
25862
  ),
25586
25863
  1
25587
25864
  );
@@ -25683,7 +25960,7 @@ function registerMathFunctions() {
25683
25960
  Math.abs,
25684
25961
  (re, im) => ({ re: Math.sqrt(re * re + im * im), im: 0 }),
25685
25962
  "Number",
25686
- { nativeJs: "Math.abs" }
25963
+ { nativeJs: "Math.abs", realSafe: true }
25687
25964
  ),
25688
25965
  1
25689
25966
  );
@@ -27476,6 +27753,19 @@ function registerArrayManipulationFunctions() {
27476
27753
  if (n !== data.length) {
27477
27754
  throw new RuntimeError("reshape: number of elements must not change");
27478
27755
  }
27756
+ if (isRuntimeTensor(v)) {
27757
+ v._rc++;
27758
+ const s = [...shape];
27759
+ while (s.length > 2 && s[s.length - 1] === 1) s.pop();
27760
+ return {
27761
+ kind: "tensor",
27762
+ data,
27763
+ imag,
27764
+ shape: s,
27765
+ _isLogical: v._isLogical,
27766
+ _rc: v._rc
27767
+ };
27768
+ }
27479
27769
  return RTV.tensor(
27480
27770
  new FloatXArray(data),
27481
27771
  shape,
@@ -29011,6 +29301,27 @@ function dimReduce(v, dim, reduceFn, initialValue, finalizeFn) {
29011
29301
  const imOut = resultImag && resultImag.some((x) => x !== 0) ? resultImag : void 0;
29012
29302
  return RTV.tensor(result, info.resultShape, imOut);
29013
29303
  }
29304
+ function dimReduceOmitNaN(v, dim, reduceFn, initialValue, finalizeFn) {
29305
+ if (!isRuntimeTensor(v))
29306
+ throw new RuntimeError("dimReduceOmitNaN: argument must be a tensor");
29307
+ const info = forEachSlice(v.shape, dim, () => {
29308
+ });
29309
+ if (!info) return copyTensor(v);
29310
+ const result = new FloatXArray(info.totalElems);
29311
+ forEachSlice(v.shape, dim, (outIdx, srcIndices) => {
29312
+ let acc = initialValue;
29313
+ let count = 0;
29314
+ for (let k = 0; k < srcIndices.length; k++) {
29315
+ const val = v.data[srcIndices[k]];
29316
+ if (!isNaN(val)) {
29317
+ acc = reduceFn(acc, val);
29318
+ count++;
29319
+ }
29320
+ }
29321
+ result[outIdx] = finalizeFn ? finalizeFn(acc, count) : acc;
29322
+ });
29323
+ return RTV.tensor(result, info.resultShape);
29324
+ }
29014
29325
  function sliceDimReduce(v, dim, sliceFn) {
29015
29326
  const info = forEachSlice(v.shape, dim, () => {
29016
29327
  });
@@ -29236,23 +29547,44 @@ function preserveTypeCheck(argTypes, nargout) {
29236
29547
  if (nargout === 2) return { outputTypes: [outType, outType] };
29237
29548
  return null;
29238
29549
  }
29239
- function makeReduction(name, kernel) {
29550
+ function parseNanFlag(args) {
29551
+ if (args.length >= 2) {
29552
+ const last = args[args.length - 1];
29553
+ if (isRuntimeChar(last)) {
29554
+ const s = toString(last).toLowerCase();
29555
+ if (s === "omitnan") return { args: args.slice(0, -1), omitNaN: true };
29556
+ if (s === "includenan")
29557
+ return { args: args.slice(0, -1), omitNaN: false };
29558
+ }
29559
+ }
29560
+ return { args, omitNaN: false };
29561
+ }
29562
+ function filterNaN(arr) {
29563
+ const out2 = [];
29564
+ for (let i = 0; i < arr.length; i++) {
29565
+ if (!isNaN(arr[i])) out2.push(arr[i]);
29566
+ }
29567
+ return new Float64Array(out2);
29568
+ }
29569
+ function makeReduction(name, kernel, omitNaNKernel) {
29240
29570
  return {
29241
29571
  check: reductionCheck,
29242
- apply: (args) => {
29243
- if (args.length < 1)
29572
+ apply: (rawArgs) => {
29573
+ if (rawArgs.length < 1)
29244
29574
  throw new RuntimeError(`${name} requires at least 1 argument`);
29575
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29576
+ const k = omitNaN && omitNaNKernel ? omitNaNKernel : kernel;
29245
29577
  const v = args[0];
29246
29578
  if (isRuntimeNumber(v)) return v;
29247
29579
  if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
29248
29580
  if (isRuntimeTensor(v)) {
29249
29581
  if (args.length >= 2) {
29250
29582
  if (isRuntimeChar(args[1]) && toString(args[1]) === "all")
29251
- return kernel.reduceAll(v);
29252
- return kernel.reduceDim(v, Math.round(toNumber(args[1])));
29583
+ return k.reduceAll(v);
29584
+ return k.reduceDim(v, Math.round(toNumber(args[1])));
29253
29585
  }
29254
29586
  const d = firstReduceDim(v.shape);
29255
- return d === 0 ? kernel.reduceAll(v) : kernel.reduceDim(v, d);
29587
+ return d === 0 ? k.reduceAll(v) : k.reduceDim(v, d);
29256
29588
  }
29257
29589
  throw new RuntimeError(`${name}: argument must be numeric`);
29258
29590
  }
@@ -29282,6 +29614,42 @@ function sliceKernel(sliceFn) {
29282
29614
  reduceDim: (v, dim) => sliceDimReduce(v, dim, sliceFn)
29283
29615
  };
29284
29616
  }
29617
+ function accumKernelOmitNaN(reduceFn, initial, finalizeFn) {
29618
+ return {
29619
+ reduceAll: (v) => {
29620
+ let acc = initial;
29621
+ let count = 0;
29622
+ for (let i = 0; i < v.data.length; i++) {
29623
+ if (!isNaN(v.data[i])) {
29624
+ acc = reduceFn(acc, v.data[i]);
29625
+ count++;
29626
+ }
29627
+ }
29628
+ const re = finalizeFn ? finalizeFn(acc, count) : acc;
29629
+ if (v.imag) {
29630
+ let accIm = initial;
29631
+ let countIm = 0;
29632
+ for (let i = 0; i < v.imag.length; i++) {
29633
+ if (!isNaN(v.imag[i])) {
29634
+ accIm = reduceFn(accIm, v.imag[i]);
29635
+ countIm++;
29636
+ }
29637
+ }
29638
+ const im = finalizeFn ? finalizeFn(accIm, countIm) : accIm;
29639
+ if (im !== 0) return RTV.complex(re, im);
29640
+ }
29641
+ return RTV.num(re);
29642
+ },
29643
+ reduceDim: (v, dim) => dimReduceOmitNaN(v, dim, reduceFn, initial, finalizeFn)
29644
+ };
29645
+ }
29646
+ function sliceKernelOmitNaN(sliceFn) {
29647
+ const filteredFn = (slice) => sliceFn(filterNaN(slice));
29648
+ return {
29649
+ reduceAll: (v) => RTV.num(filteredFn(v.data)),
29650
+ reduceDim: (v, dim) => sliceDimReduce(v, dim, filteredFn)
29651
+ };
29652
+ }
29285
29653
  function toNumArray(v, name) {
29286
29654
  if (isRuntimeNumber(v)) return [v];
29287
29655
  if (isRuntimeTensor(v)) return Array.from(v.data);
@@ -29293,15 +29661,16 @@ function registerBasicReductions() {
29293
29661
  register("sum", [
29294
29662
  {
29295
29663
  check: reductionCheck,
29296
- apply: (args) => {
29297
- if (args.length < 1)
29664
+ apply: (rawArgs) => {
29665
+ if (rawArgs.length < 1)
29298
29666
  throw new RuntimeError("sum requires at least 1 argument");
29667
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29299
29668
  const v = args[0];
29300
29669
  if (isRuntimeSparseMatrix(v)) {
29301
29670
  const dim = args.length >= 2 ? Math.round(toNumber(args[1])) : v.m > 1 ? 1 : v.n > 1 ? 2 : 1;
29302
29671
  return sparseSum(v, dim);
29303
29672
  }
29304
- const kernel = accumKernel((acc, val) => acc + val, 0);
29673
+ const kernel = omitNaN ? accumKernelOmitNaN((acc, val) => acc + val, 0) : accumKernel((acc, val) => acc + val, 0);
29305
29674
  if (isRuntimeNumber(v)) return v;
29306
29675
  if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
29307
29676
  if (isRuntimeTensor(v)) {
@@ -29317,13 +29686,14 @@ function registerBasicReductions() {
29317
29686
  }
29318
29687
  }
29319
29688
  ]);
29320
- const prodKernel = accumKernel((acc, val) => acc * val, 1);
29321
29689
  register("prod", [
29322
29690
  {
29323
29691
  check: reductionCheck,
29324
- apply: (args) => {
29325
- if (args.length < 1)
29692
+ apply: (rawArgs) => {
29693
+ if (rawArgs.length < 1)
29326
29694
  throw new RuntimeError("prod requires at least 1 argument");
29695
+ const { args: parsedArgs, omitNaN } = parseNanFlag(rawArgs);
29696
+ let args = parsedArgs;
29327
29697
  let v = args[0];
29328
29698
  if (isRuntimeSparseMatrix(v)) {
29329
29699
  v = sparseToDense(v);
@@ -29337,13 +29707,14 @@ function registerBasicReductions() {
29337
29707
  args.length >= 2 ? Math.round(toNumber(args[1])) : void 0
29338
29708
  );
29339
29709
  }
29710
+ const kernel = omitNaN ? accumKernelOmitNaN((acc, val) => acc * val, 1) : accumKernel((acc, val) => acc * val, 1);
29340
29711
  if (args.length >= 2) {
29341
29712
  if (isRuntimeChar(args[1]) && toString(args[1]) === "all")
29342
- return prodKernel.reduceAll(v);
29343
- return prodKernel.reduceDim(v, Math.round(toNumber(args[1])));
29713
+ return kernel.reduceAll(v);
29714
+ return kernel.reduceDim(v, Math.round(toNumber(args[1])));
29344
29715
  }
29345
29716
  const d = firstReduceDim(v.shape);
29346
- return d === 0 ? prodKernel.reduceAll(v) : prodKernel.reduceDim(v, d);
29717
+ return d === 0 ? kernel.reduceAll(v) : kernel.reduceDim(v, d);
29347
29718
  }
29348
29719
  throw new RuntimeError("prod: argument must be numeric");
29349
29720
  }
@@ -29356,31 +29727,40 @@ function registerBasicReductions() {
29356
29727
  (acc, val) => acc + val,
29357
29728
  0,
29358
29729
  (sum, count) => sum / count
29730
+ ),
29731
+ accumKernelOmitNaN(
29732
+ (acc, val) => acc + val,
29733
+ 0,
29734
+ (sum, count) => count === 0 ? NaN : sum / count
29359
29735
  )
29360
29736
  )
29361
29737
  ]);
29362
- const varianceOf = (slice, w) => {
29363
- const n = slice.length;
29738
+ const varianceOf = (slice, w, omitNaN) => {
29739
+ let data = slice;
29740
+ if (omitNaN) data = filterNaN(slice);
29741
+ const n = data.length;
29742
+ if (n === 0) return NaN;
29364
29743
  if (n <= 1 && w === 0) return 0;
29365
29744
  let s = 0;
29366
- for (let i = 0; i < n; i++) s += slice[i];
29745
+ for (let i = 0; i < n; i++) s += data[i];
29367
29746
  const m = s / n;
29368
29747
  let ss = 0;
29369
- for (let i = 0; i < n; i++) ss += (slice[i] - m) ** 2;
29748
+ for (let i = 0; i < n; i++) ss += (data[i] - m) ** 2;
29370
29749
  const denom = w === 1 ? n : n - 1;
29371
29750
  return ss / denom;
29372
29751
  };
29373
29752
  const stdVarApply = (name, transform) => {
29374
- return (args) => {
29375
- if (args.length < 1)
29753
+ return (rawArgs) => {
29754
+ if (rawArgs.length < 1)
29376
29755
  throw new RuntimeError(`${name} requires at least 1 argument`);
29756
+ const { args, omitNaN } = parseNanFlag(rawArgs);
29377
29757
  const v = args[0];
29378
29758
  const w = args.length >= 2 ? toNumber(args[1]) : 0;
29379
29759
  const dimArg = args.length >= 3 ? Math.round(toNumber(args[2])) : 0;
29380
29760
  if (isRuntimeNumber(v)) return RTV.num(0);
29381
29761
  if (isRuntimeTensor(v)) {
29382
29762
  const kernel = sliceKernel(
29383
- (slice) => transform(varianceOf(slice, w))
29763
+ (slice) => transform(varianceOf(slice, w, omitNaN))
29384
29764
  );
29385
29765
  if (dimArg > 0) return kernel.reduceDim(v, dimArg);
29386
29766
  const d = firstReduceDim(v.shape);
@@ -29408,7 +29788,13 @@ function registerBasicReductions() {
29408
29788
  if (n % 2 === 1) return sorted[(n - 1) / 2];
29409
29789
  return (sorted[n / 2 - 1] + sorted[n / 2]) / 2;
29410
29790
  };
29411
- register("median", [makeReduction("median", sliceKernel(medianOf))]);
29791
+ register("median", [
29792
+ makeReduction(
29793
+ "median",
29794
+ sliceKernel(medianOf),
29795
+ sliceKernelOmitNaN(medianOf)
29796
+ )
29797
+ ]);
29412
29798
  const modeOf = (arr) => {
29413
29799
  const counts = /* @__PURE__ */ new Map();
29414
29800
  for (let i = 0; i < arr.length; i++) {
@@ -29423,7 +29809,9 @@ function registerBasicReductions() {
29423
29809
  }
29424
29810
  return bestVal;
29425
29811
  };
29426
- register("mode", [makeReduction("mode", sliceKernel(modeOf))]);
29812
+ register("mode", [
29813
+ makeReduction("mode", sliceKernel(modeOf), sliceKernelOmitNaN(modeOf))
29814
+ ]);
29427
29815
  }
29428
29816
 
29429
29817
  // src/numbl-core/builtins/reduction/min-max.ts
@@ -30051,6 +30439,11 @@ function registerSortUnique() {
30051
30439
  dim = idx >= 0 ? idx + 1 : 1;
30052
30440
  }
30053
30441
  const dimIdx = dim - 1;
30442
+ if (!im && !descend && nargout <= 1 && re.length === shape[dimIdx]) {
30443
+ const sorted2 = new FloatXArray(re);
30444
+ sorted2.sort();
30445
+ return RTV.tensor(sorted2, [...shape]);
30446
+ }
30054
30447
  if (dimIdx >= shape.length) {
30055
30448
  const cp = RTV.tensor(
30056
30449
  new FloatXArray(re),
@@ -30265,10 +30658,10 @@ function registerSortUnique() {
30265
30658
  }
30266
30659
  function uniqueByRows(v, nargout, stable) {
30267
30660
  const [rows, cols] = tensorSize2D(v);
30268
- const rowKey = (r) => {
30269
- const parts = [];
30270
- for (let c = 0; c < cols; c++) parts.push(v.data[c * rows + r]);
30271
- return parts.join(",");
30661
+ const rowKey = cols === 2 ? (r) => v.data[r] + "," + v.data[rows + r] : (r) => {
30662
+ let key = "" + v.data[r];
30663
+ for (let c = 1; c < cols; c++) key += "," + v.data[c * rows + r];
30664
+ return key;
30272
30665
  };
30273
30666
  const rowHasNaN = (r) => {
30274
30667
  for (let c = 0; c < cols; c++) {
@@ -30321,10 +30714,21 @@ function uniqueByRows(v, nargout, stable) {
30321
30714
  1
30322
30715
  ]);
30323
30716
  if (!stable) {
30324
- const sortedKeyOrder = uniqueRowOrder.map((r) => rowKey(r));
30717
+ const sortedKeyToPos = /* @__PURE__ */ new Map();
30718
+ for (let u = 0; u < nUnique; u++) {
30719
+ sortedKeyToPos.set(rowKey(uniqueRowOrder[u]), u + 1);
30720
+ }
30325
30721
  for (let r = 0; r < rows; r++) {
30326
- const key = rowKey(r);
30327
- ic[r] = sortedKeyOrder.indexOf(key) + 1;
30722
+ if (rowHasNaN(r)) {
30723
+ for (let u = 0; u < nUnique; u++) {
30724
+ if (uniqueRowOrder[u] === r) {
30725
+ ic[r] = u + 1;
30726
+ break;
30727
+ }
30728
+ }
30729
+ } else {
30730
+ ic[r] = sortedKeyToPos.get(rowKey(r));
30731
+ }
30328
30732
  }
30329
30733
  }
30330
30734
  const icTensor = RTV.tensor(ic, [rows, 1]);
@@ -32884,51 +33288,61 @@ function registerDet() {
32884
33288
  register("cross", [
32885
33289
  {
32886
33290
  check: (argTypes, nargout) => {
32887
- if (argTypes.length !== 2 || nargout !== 1) return null;
33291
+ if (argTypes.length !== 2 && argTypes.length !== 3 || nargout !== 1)
33292
+ return null;
32888
33293
  return { outputTypes: [IType.Unknown] };
32889
33294
  },
32890
33295
  apply: (args) => {
32891
- if (args.length !== 2)
32892
- throw new RuntimeError("cross requires 2 arguments");
33296
+ if (args.length < 2 || args.length > 3)
33297
+ throw new RuntimeError("cross requires 2 or 3 arguments");
32893
33298
  const a = args[0], b = args[1];
32894
33299
  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]);
33300
+ throw new RuntimeError("cross: arguments must be vectors or arrays");
33301
+ const shape = a.shape;
33302
+ if (shape.length !== b.shape.length || shape.some((s, i) => s !== b.shape[i]))
33303
+ throw new RuntimeError("cross: A and B must have the same size");
33304
+ let dim;
33305
+ if (args.length === 3) {
33306
+ dim = toNumber(args[2]);
33307
+ if (!Number.isInteger(dim) || dim < 1)
33308
+ throw new RuntimeError("cross: dim must be a positive integer");
32927
33309
  } else {
33310
+ dim = shape.indexOf(3) + 1;
33311
+ if (dim === 0)
33312
+ throw new RuntimeError(
33313
+ "cross: A and B must have at least one dimension of length 3"
33314
+ );
33315
+ }
33316
+ const dimIdx = dim - 1;
33317
+ if (dimIdx >= shape.length || shape[dimIdx] !== 3)
32928
33318
  throw new RuntimeError(
32929
- "cross: inputs must be 3-element vectors or 3xN matrices"
33319
+ `cross: size(A,${dim}) and size(B,${dim}) must be 3`
32930
33320
  );
32931
- }
33321
+ const totalLen = a.data.length;
33322
+ const result = new FloatXArray(totalLen);
33323
+ const strides = new Array(shape.length);
33324
+ strides[0] = 1;
33325
+ for (let d = 1; d < shape.length; d++)
33326
+ strides[d] = strides[d - 1] * shape[d - 1];
33327
+ const dimStride = strides[dimIdx];
33328
+ const outerStride = dimIdx + 1 < shape.length ? strides[dimIdx + 1] : totalLen;
33329
+ const innerSize = dimStride;
33330
+ const numOuter = totalLen / outerStride;
33331
+ for (let outer = 0; outer < numOuter; outer++) {
33332
+ const blockBase = outer * outerStride;
33333
+ for (let inner = 0; inner < innerSize; inner++) {
33334
+ const base = blockBase + inner;
33335
+ const i0 = base;
33336
+ const i1 = base + dimStride;
33337
+ const i2 = base + 2 * dimStride;
33338
+ const ax = a.data[i0], ay = a.data[i1], az = a.data[i2];
33339
+ const bx = b.data[i0], by = b.data[i1], bz = b.data[i2];
33340
+ result[i0] = ay * bz - az * by;
33341
+ result[i1] = az * bx - ax * bz;
33342
+ result[i2] = ax * by - ay * bx;
33343
+ }
33344
+ }
33345
+ return RTV.tensor(result, [...shape]);
32932
33346
  }
32933
33347
  }
32934
33348
  ]);
@@ -37301,56 +37715,61 @@ function not(v) {
37301
37715
  return RTV.logical(false);
37302
37716
  }
37303
37717
  function binop(op, a, b) {
37304
- const an = asNumber(a);
37305
- const bn = asNumber(b);
37306
- if (an !== null && bn !== null) {
37718
+ if (typeof a === "number" && typeof b === "number") {
37307
37719
  switch (op) {
37308
37720
  case "Add" /* Add */:
37309
- return an + bn;
37721
+ return a + b;
37310
37722
  case "Sub" /* Sub */:
37311
- return an - bn;
37723
+ return a - b;
37312
37724
  case "Mul" /* Mul */:
37313
- return an * bn;
37725
+ return a * b;
37314
37726
  case "Div" /* Div */:
37315
- return an / bn;
37727
+ return a / b;
37316
37728
  case "Pow" /* Pow */: {
37317
- const r = Math.pow(an, bn);
37318
- if (isNaN(r) && !isNaN(an) && !isNaN(bn)) break;
37729
+ const r = Math.pow(a, b);
37730
+ if (isNaN(r) && !isNaN(a) && !isNaN(b)) break;
37319
37731
  return r;
37320
37732
  }
37321
37733
  case "ElemMul" /* ElemMul */:
37322
- return an * bn;
37734
+ return a * b;
37323
37735
  case "ElemDiv" /* ElemDiv */:
37324
- return an / bn;
37736
+ return a / b;
37325
37737
  case "ElemPow" /* ElemPow */: {
37326
- const r = Math.pow(an, bn);
37327
- if (isNaN(r) && !isNaN(an) && !isNaN(bn)) break;
37738
+ const r = Math.pow(a, b);
37739
+ if (isNaN(r) && !isNaN(a) && !isNaN(b)) break;
37328
37740
  return r;
37329
37741
  }
37330
37742
  case "LeftDiv" /* LeftDiv */:
37331
- return bn / an;
37743
+ return b / a;
37332
37744
  case "ElemLeftDiv" /* ElemLeftDiv */:
37333
- return bn / an;
37745
+ return b / a;
37334
37746
  case "Equal" /* Equal */:
37335
- return RTV.logical(an === bn);
37747
+ return RTV.logical(a === b);
37336
37748
  case "NotEqual" /* NotEqual */:
37337
- return RTV.logical(an !== bn);
37749
+ return RTV.logical(a !== b);
37338
37750
  case "Less" /* Less */:
37339
- return RTV.logical(an < bn);
37751
+ return RTV.logical(a < b);
37340
37752
  case "LessEqual" /* LessEqual */:
37341
- return RTV.logical(an <= bn);
37753
+ return RTV.logical(a <= b);
37342
37754
  case "Greater" /* Greater */:
37343
- return RTV.logical(an > bn);
37755
+ return RTV.logical(a > b);
37344
37756
  case "GreaterEqual" /* GreaterEqual */:
37345
- return RTV.logical(an >= bn);
37757
+ return RTV.logical(a >= b);
37346
37758
  case "BitAnd" /* BitAnd */:
37347
- return RTV.logical(an !== 0 && bn !== 0);
37759
+ return RTV.logical(a !== 0 && b !== 0);
37348
37760
  case "BitOr" /* BitOr */:
37349
- return RTV.logical(an !== 0 || bn !== 0);
37761
+ return RTV.logical(a !== 0 || b !== 0);
37762
+ }
37763
+ }
37764
+ if (typeof a !== "object" || typeof b !== "object") {
37765
+ const an = asNumber(a);
37766
+ const bn = asNumber(b);
37767
+ if (an !== null && bn !== null) {
37768
+ return binop(op, an, bn);
37350
37769
  }
37351
37770
  }
37352
- const ma = ensureRuntimeValue(a);
37353
- const mb = ensureRuntimeValue(b);
37771
+ const ma = typeof a === "object" && a !== null && "kind" in a ? a : ensureRuntimeValue(a);
37772
+ const mb = typeof b === "object" && b !== null && "kind" in b ? b : ensureRuntimeValue(b);
37354
37773
  let result;
37355
37774
  switch (op) {
37356
37775
  case "Add" /* Add */:
@@ -37794,13 +38213,6 @@ function callBuiltin(rt, name, nargout, args) {
37794
38213
  if (builtin) return builtin(nargout, args);
37795
38214
  throw new RuntimeError(`'${name}' is not a builtin function`);
37796
38215
  }
37797
- function callBuiltinSync(rt, name, nargout, args) {
37798
- const plotResult = dispatchPlotCall(rt, name, args);
37799
- if (plotResult !== void 0) return plotResult;
37800
- const builtin = rt.builtins[name];
37801
- if (builtin) return builtin(nargout, args);
37802
- throw new RuntimeError(`'${name}' is not a builtin function`);
37803
- }
37804
38216
  function callClassMethod(rt, className, methodName, nargout, args) {
37805
38217
  return dispatch(rt, methodName, nargout, args, className);
37806
38218
  }
@@ -38087,6 +38499,22 @@ function structfunImpl(rt, _nargout, args) {
38087
38499
  return RTV.struct(fields);
38088
38500
  }
38089
38501
  }
38502
+ var bsxfunOpMap = {
38503
+ plus: mAdd,
38504
+ minus: mSub,
38505
+ times: mElemMul,
38506
+ rdivide: mElemDiv
38507
+ };
38508
+ function resolveKnownBsxfunOp(fnArg) {
38509
+ if (typeof fnArg === "function") {
38510
+ return void 0;
38511
+ }
38512
+ const mv = ensureRuntimeValue(fnArg);
38513
+ if (isRuntimeFunction(mv) && mv.impl === "builtin") {
38514
+ return bsxfunOpMap[mv.name];
38515
+ }
38516
+ return void 0;
38517
+ }
38090
38518
  function bsxfunImpl(rt, _nargout, args) {
38091
38519
  if (args.length !== 3)
38092
38520
  throw new RuntimeError("bsxfun requires exactly 3 arguments");
@@ -38104,6 +38532,10 @@ function bsxfunImpl(rt, _nargout, args) {
38104
38532
  );
38105
38533
  }
38106
38534
  }
38535
+ const knownOp = resolveKnownBsxfunOp(fnArg);
38536
+ if (knownOp) {
38537
+ return knownOp(ensureRuntimeValue(args[1]), ensureRuntimeValue(args[2]));
38538
+ }
38107
38539
  const rawA = ensureRuntimeValue(args[1]);
38108
38540
  const rawB = ensureRuntimeValue(args[2]);
38109
38541
  const a = coerceToTensor2(rawA, "bsxfun", "first");
@@ -38367,6 +38799,67 @@ function endResolver(mv, numIndices) {
38367
38799
  };
38368
38800
  }
38369
38801
  function index(rt, base, indices, nargout = 1, skipSubsref = false) {
38802
+ if (typeof base === "object" && base !== null && base.kind === "tensor") {
38803
+ const t = base;
38804
+ const nIdx = indices.length;
38805
+ if (nIdx === 1) {
38806
+ const idx = indices[0];
38807
+ if (typeof idx === "number") {
38808
+ const i = Math.round(idx) - 1;
38809
+ if (i < 0 || i >= t.data.length)
38810
+ throw new RuntimeError("Index exceeds array bounds");
38811
+ if (t.imag !== void 0) {
38812
+ const im = t.imag[i];
38813
+ return im === 0 ? t.data[i] : RTV.complex(t.data[i], im);
38814
+ }
38815
+ return t.data[i];
38816
+ }
38817
+ } else if (nIdx === 2) {
38818
+ const ri = indices[0];
38819
+ const ci = indices[1];
38820
+ if (typeof ri === "number" && typeof ci === "number") {
38821
+ const s = t.shape;
38822
+ const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
38823
+ const cols = s.length === 0 ? 1 : s.length === 1 ? s[0] : s[1];
38824
+ const r = Math.round(ri) - 1;
38825
+ const c = Math.round(ci) - 1;
38826
+ if (r < 0 || r >= rows || c < 0 || c >= cols)
38827
+ throw new RuntimeError("Index exceeds array bounds");
38828
+ const lin = c * rows + r;
38829
+ if (t.imag !== void 0) {
38830
+ const im = t.imag[lin];
38831
+ return im === 0 ? t.data[lin] : RTV.complex(t.data[lin], im);
38832
+ }
38833
+ return t.data[lin];
38834
+ }
38835
+ } else if (nIdx >= 3) {
38836
+ let allNumeric = true;
38837
+ for (let k = 0; k < nIdx; k++) {
38838
+ if (typeof indices[k] !== "number") {
38839
+ allNumeric = false;
38840
+ break;
38841
+ }
38842
+ }
38843
+ if (allNumeric) {
38844
+ const s = t.shape;
38845
+ let lin = 0;
38846
+ let stride = 1;
38847
+ for (let k = 0; k < nIdx; k++) {
38848
+ const dimSize = k < s.length ? s[k] : 1;
38849
+ const sub = Math.round(indices[k]) - 1;
38850
+ if (sub < 0 || sub >= dimSize)
38851
+ throw new RuntimeError("Index exceeds array bounds");
38852
+ lin += sub * stride;
38853
+ stride *= dimSize;
38854
+ }
38855
+ if (t.imag !== void 0) {
38856
+ const im = t.imag[lin];
38857
+ return im === 0 ? t.data[lin] : RTV.complex(t.data[lin], im);
38858
+ }
38859
+ return t.data[lin];
38860
+ }
38861
+ }
38862
+ }
38370
38863
  if (typeof base === "function") {
38371
38864
  return base(...indices);
38372
38865
  }
@@ -38451,6 +38944,13 @@ function resolveIndicesForClassInstance(rt, mv, base, indices) {
38451
38944
  });
38452
38945
  }
38453
38946
  function indexCell(rt, base, indices) {
38947
+ if (indices.length === 1 && typeof indices[0] === "number" && typeof base === "object" && base !== null && base.kind === "cell") {
38948
+ const cell = base;
38949
+ const i = Math.round(indices[0]) - 1;
38950
+ if (i < 0 || i >= cell.data.length)
38951
+ throw new RuntimeError("Cell index exceeds bounds");
38952
+ return cell.data[i];
38953
+ }
38454
38954
  const mv = ensureRuntimeValue(base);
38455
38955
  if (isRuntimeClassInstance(mv)) {
38456
38956
  const subsrefFn = rt.cachedResolveClassMethod(mv.className, "subsref");
@@ -38589,7 +39089,7 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
38589
39089
  if (isRuntimeCell(mv)) {
38590
39090
  const idxMvals2 = resolveIndices(indices, endResolver(mv, indices.length));
38591
39091
  const rhsMv2 = ensureRuntimeValue(rhs);
38592
- return storeIntoRTValueIndex(mv, idxMvals2, rhsMv2);
39092
+ return storeIntoRTValueIndex(mv, idxMvals2, rhsMv2, true);
38593
39093
  }
38594
39094
  if (isRuntimeStruct(mv)) {
38595
39095
  return ensureRuntimeValue(rhs);
@@ -38802,7 +39302,7 @@ function registerSpecialBuiltins(rt) {
38802
39302
  if (fn) {
38803
39303
  return fn(nargout, args.slice(1));
38804
39304
  }
38805
- return rt.callBuiltinSync(fnName, nargout, args.slice(1));
39305
+ return rt.callBuiltin(fnName, nargout, args.slice(1));
38806
39306
  };
38807
39307
  const requireFileIO = () => {
38808
39308
  if (!rt.fileIO)
@@ -40148,15 +40648,23 @@ var Runtime = class _Runtime {
40148
40648
  // ── Builtin initialization ──────────────────────────────────────────
40149
40649
  initBuiltins() {
40150
40650
  for (const name of getAllBuiltinNames()) {
40651
+ const builtin = getBuiltin(name);
40652
+ const singleBranch = builtin.length === 1 ? builtin[0] : null;
40151
40653
  this.builtins[name] = (nargout, args) => {
40152
- const builtin = getBuiltin(name);
40153
40654
  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;
40655
+ let branch;
40656
+ if (singleBranch) {
40657
+ branch = singleBranch;
40658
+ } else {
40659
+ const argItemTypes = margs.map(
40660
+ (arg) => getItemTypeFromRuntimeValue(arg)
40661
+ );
40662
+ branch = builtin[0];
40663
+ for (let i = 0; i < builtin.length; i++) {
40664
+ if (builtin[i].check(argItemTypes, nargout)) {
40665
+ branch = builtin[i];
40666
+ break;
40667
+ }
40160
40668
  }
40161
40669
  }
40162
40670
  if (this.profilingEnabled) {
@@ -40788,9 +41296,6 @@ var Runtime = class _Runtime {
40788
41296
  }
40789
41297
  return binop(op, a, b);
40790
41298
  }
40791
- binopSync(op, a, b) {
40792
- return binop(op, a, b);
40793
- }
40794
41299
  range(start, step, end) {
40795
41300
  return range(start, step, end);
40796
41301
  }
@@ -40910,9 +41415,6 @@ var Runtime = class _Runtime {
40910
41415
  callBuiltin(name, nargout, args) {
40911
41416
  return callBuiltin(this, name, nargout, args);
40912
41417
  }
40913
- callBuiltinSync(name, nargout, args) {
40914
- return callBuiltinSync(this, name, nargout, args);
40915
- }
40916
41418
  callClassMethod(className, methodName, nargout, args) {
40917
41419
  return callClassMethod(this, className, methodName, nargout, args);
40918
41420
  }
@@ -44052,9 +44554,6 @@ function genBinary(cg, kind) {
44052
44554
  break;
44053
44555
  }
44054
44556
  }
44055
- if (leftType.kind !== "Unknown" && rightType.kind !== "Unknown" && leftType.kind !== "ClassInstance" && rightType.kind !== "ClassInstance") {
44056
- return `$rt.binopSync(${JSON.stringify(kind.op)}, ${left}, ${right})`;
44057
- }
44058
44557
  return `$rt.binop(${JSON.stringify(kind.op)}, ${left}, ${right})`;
44059
44558
  }
44060
44559
  function genTensor(cg, kind) {
@@ -44579,8 +45078,6 @@ function genClassInstantiation(cg, kind) {
44579
45078
  return createExpr;
44580
45079
  }
44581
45080
  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
45081
  abs: "Math.abs",
44585
45082
  floor: "Math.floor",
44586
45083
  ceil: "Math.ceil",
@@ -44594,6 +45091,14 @@ var NATIVE_MATH_1 = {
44594
45091
  log10: "Math.log10",
44595
45092
  sign: "Math.sign"
44596
45093
  };
45094
+ var GUARDED_MATH_1 = {
45095
+ sqrt: { fn: "Math.sqrt", guard: "$x >= 0" },
45096
+ log: { fn: "Math.log", guard: "$x > 0" },
45097
+ asin: { fn: "Math.asin", guard: "$x >= -1 && $x <= 1" },
45098
+ acos: { fn: "Math.acos", guard: "$x >= -1 && $x <= 1" },
45099
+ acosh: { fn: "Math.acosh", guard: "$x >= 1" },
45100
+ atanh: { fn: "Math.atanh", guard: "$x > -1 && $x < 1" }
45101
+ };
44597
45102
  var NATIVE_MATH_2 = {
44598
45103
  // Note: max/min are NOT here because Math.max/Math.min propagate NaN,
44599
45104
  // but max/min ignore NaN (returning the non-NaN value).
@@ -44605,6 +45110,15 @@ function tryNativeMathCodegen(cg, name, nargout, irArgs, jsArgs) {
44605
45110
  if (jsArgs.length === 1 && itemTypeForExprKind(irArgs[0].kind, cg.typeEnv).kind === "Number") {
44606
45111
  const fn = NATIVE_MATH_1[name];
44607
45112
  if (fn) return `${fn}(${jsArgs[0]})`;
45113
+ const guarded = GUARDED_MATH_1[name];
45114
+ if (guarded) {
45115
+ const arg = jsArgs[0];
45116
+ const tmp = cg.freshTemp("$gm");
45117
+ cg.emit(`var ${tmp};`);
45118
+ const guard = guarded.guard.replace(/\$x/g, tmp);
45119
+ const fallback = `${cg.useBuiltin(name)}(1, [${tmp}])`;
45120
+ return `(${tmp} = ${arg}, ${guard} ? ${guarded.fn}(${tmp}) : ${fallback})`;
45121
+ }
44608
45122
  }
44609
45123
  if (jsArgs.length === 2 && itemTypeForExprKind(irArgs[0].kind, cg.typeEnv).kind === "Number" && itemTypeForExprKind(irArgs[1].kind, cg.typeEnv).kind === "Number") {
44610
45124
  const fn = NATIVE_MATH_2[name];
@@ -46318,6 +46832,21 @@ function instantiateWasm(wasmData) {
46318
46832
  }
46319
46833
  return new WebAssembly.Instance(wasmModule, importObject);
46320
46834
  }
46835
+ function resolveBindings(file, directives, getWasmInstance, nativeBridge2) {
46836
+ const wasmInstance = directives.wasm ? getWasmInstance(directives.wasm) : void 0;
46837
+ let nativeLib;
46838
+ if (directives.native && nativeBridge2) {
46839
+ const libFile = nativeLibFilename(directives.native);
46840
+ const dir = file.name.substring(0, file.name.lastIndexOf("/") + 1);
46841
+ const libPath = dir + libFile;
46842
+ try {
46843
+ nativeLib = nativeBridge2.load(libPath);
46844
+ } catch {
46845
+ }
46846
+ }
46847
+ return { wasmInstance, nativeLib };
46848
+ }
46849
+ var LOADING = /* @__PURE__ */ Symbol("loading");
46321
46850
  function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46322
46851
  const result = /* @__PURE__ */ new Map();
46323
46852
  const wasmMap = wasmFiles ? buildWasmMap(wasmFiles) : /* @__PURE__ */ new Map();
@@ -46331,7 +46860,68 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46331
46860
  wasmInstanceCache.set(name, instance);
46332
46861
  return instance;
46333
46862
  }
46863
+ const libraryFiles = /* @__PURE__ */ new Map();
46864
+ const functionFiles = [];
46334
46865
  for (const file of jsFiles) {
46866
+ const base = file.name.split("/").pop();
46867
+ if (base.startsWith("_")) {
46868
+ const libName = base.replace(/\.js$/, "");
46869
+ libraryFiles.set(libName, file);
46870
+ } else {
46871
+ functionFiles.push(file);
46872
+ }
46873
+ }
46874
+ const libCache = /* @__PURE__ */ new Map();
46875
+ function importJS(name) {
46876
+ const cached = libCache.get(name);
46877
+ if (cached === LOADING) {
46878
+ throw new RuntimeError(`Circular dependency detected: ${name}.js`);
46879
+ }
46880
+ if (libCache.has(name)) return cached;
46881
+ const libFile = libraryFiles.get(name);
46882
+ if (!libFile) {
46883
+ throw new RuntimeError(
46884
+ `importJS: library '${name}.js' not found in workspace`
46885
+ );
46886
+ }
46887
+ libCache.set(name, LOADING);
46888
+ const directives = parseDirectives(libFile.source);
46889
+ const { wasmInstance, nativeLib } = resolveBindings(
46890
+ libFile,
46891
+ directives,
46892
+ getWasmInstance,
46893
+ nativeBridge2
46894
+ );
46895
+ const dummyRegister = () => {
46896
+ throw new RuntimeError(
46897
+ `Library file '${name}.js' must not call register(). Use return {...} to export values.`
46898
+ );
46899
+ };
46900
+ const factory = new Function(
46901
+ "RTV",
46902
+ "RuntimeError",
46903
+ "FloatXArray",
46904
+ "IType",
46905
+ "register",
46906
+ "wasm",
46907
+ "native",
46908
+ "importJS",
46909
+ libFile.source
46910
+ );
46911
+ const exports = factory(
46912
+ RTV,
46913
+ RuntimeError,
46914
+ FloatXArray,
46915
+ IType,
46916
+ dummyRegister,
46917
+ wasmInstance,
46918
+ nativeLib,
46919
+ importJS
46920
+ );
46921
+ libCache.set(name, exports);
46922
+ return exports;
46923
+ }
46924
+ for (const file of functionFiles) {
46335
46925
  const funcName = funcNameFromFile(file.name);
46336
46926
  try {
46337
46927
  const branches = [];
@@ -46345,17 +46935,12 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46345
46935
  });
46346
46936
  };
46347
46937
  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
- }
46938
+ const { wasmInstance, nativeLib } = resolveBindings(
46939
+ file,
46940
+ directives,
46941
+ getWasmInstance,
46942
+ nativeBridge2
46943
+ );
46359
46944
  const factory = new Function(
46360
46945
  "RTV",
46361
46946
  "RuntimeError",
@@ -46364,6 +46949,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46364
46949
  "register",
46365
46950
  "wasm",
46366
46951
  "native",
46952
+ "importJS",
46367
46953
  file.source
46368
46954
  );
46369
46955
  factory(
@@ -46373,7 +46959,8 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
46373
46959
  IType,
46374
46960
  registerFn,
46375
46961
  wasmInstance,
46376
- nativeLib
46962
+ nativeLib,
46963
+ importJS
46377
46964
  );
46378
46965
  if (branches.length === 0) {
46379
46966
  throw new Error(
@@ -47189,7 +47776,7 @@ Call stack (most recent call first):`;
47189
47776
  }
47190
47777
 
47191
47778
  // src/numbl-core/version.ts
47192
- var NUMBL_VERSION = "0.0.21";
47779
+ var NUMBL_VERSION = "0.0.23";
47193
47780
 
47194
47781
  // src/cli-repl.ts
47195
47782
  import { createInterface } from "readline";
@@ -47820,16 +48407,26 @@ var NodeFileIOAdapter = class {
47820
48407
  var __filename = fileURLToPath2(import.meta.url);
47821
48408
  var __dirname = dirname3(__filename);
47822
48409
  var packageDir2 = join6(__dirname, "..");
47823
- var addonPath = join6(packageDir2, "build", "Release", "lapack_addon.node");
48410
+ var addonPath = join6(packageDir2, "build", "Release", "numbl_addon.node");
47824
48411
  var nativeAddonLoaded = false;
47825
48412
  if (!process.env.NUMBL_NO_NATIVE) {
47826
48413
  try {
47827
48414
  const req = createRequire(import.meta.url);
47828
48415
  const addon = req(addonPath);
47829
- setLapackBridge(addon);
47830
- setLapackBridge(addon);
47831
- nativeAddonLoaded = true;
48416
+ const addonVer = typeof addon.addonVersion === "function" ? addon.addonVersion() : 0;
48417
+ if (addonVer !== NATIVE_ADDON_EXPECTED_VERSION) {
48418
+ console.error(
48419
+ `Warning: native addon version mismatch (got ${addonVer}, expected ${NATIVE_ADDON_EXPECTED_VERSION}). Run "npx numbl build-addon" to rebuild. Using JS fallbacks.`
48420
+ );
48421
+ } else {
48422
+ setLapackBridge(addon);
48423
+ setLapackBridge(addon);
48424
+ nativeAddonLoaded = true;
48425
+ }
47832
48426
  } catch {
48427
+ console.error(
48428
+ `Warning: native addon not found. Run "npx numbl build-addon" to build it. Using JS fallbacks.`
48429
+ );
47833
48430
  }
47834
48431
  }
47835
48432
  var nativeBridge;