numbl 0.4.6 → 0.4.8

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 (59) hide show
  1. package/dist-cli/cli.js +2600 -228
  2. package/dist-graphics/graphics/FigureView.d.ts +6 -0
  3. package/dist-graphics/graphics/SurfView.d.ts +18 -0
  4. package/dist-graphics/graphics/axisLimits.d.ts +23 -0
  5. package/dist-graphics/graphics/drawPlot.d.ts +2 -0
  6. package/dist-graphics/graphics/exportFigureHdf5.d.ts +21 -0
  7. package/dist-graphics/graphics/figureHashTransport.d.ts +24 -0
  8. package/dist-graphics/graphics/figureHdf5Schema.d.ts +19 -0
  9. package/dist-graphics/graphics/figureUpload.d.ts +45 -0
  10. package/dist-graphics/graphics/figuresReducer.d.ts +86 -0
  11. package/dist-graphics/graphics/importFigureHdf5.d.ts +9 -0
  12. package/dist-graphics/graphics/openInFigureViewer.d.ts +26 -0
  13. package/dist-graphics/graphics/plotHelpers.d.ts +6 -0
  14. package/dist-graphics/graphics/plotLegend.d.ts +2 -0
  15. package/dist-graphics/graphics/plotMarkers.d.ts +2 -0
  16. package/dist-graphics/graphics/restoreNaNs.d.ts +2 -0
  17. package/dist-graphics/graphics/surfColormap.d.ts +2 -0
  18. package/dist-graphics/graphics/types.d.ts +423 -0
  19. package/dist-graphics/graphics/uihtmlSrcDoc.d.ts +23 -0
  20. package/dist-graphics/graphics-lib.d.ts +21 -0
  21. package/dist-graphics/index.js +35334 -0
  22. package/dist-lib/lib.d.ts +4 -0
  23. package/dist-lib/lib.js +3286 -222
  24. package/dist-lib/numbl-core/executors/jit/hostHelpers.d.ts +4 -0
  25. package/dist-lib/numbl-core/fileIOAdapter.d.ts +13 -0
  26. package/dist-lib/numbl-core/interpreter/builtins/gallery.d.ts +7 -0
  27. package/dist-lib/numbl-core/interpreter/builtins/geometry.d.ts +3 -3
  28. package/dist-lib/numbl-core/interpreter/builtins/graph.d.ts +29 -0
  29. package/dist-lib/numbl-core/interpreter/builtins/index.d.ts +2 -0
  30. package/dist-lib/numbl-core/interpreter/interpreter.d.ts +5 -0
  31. package/dist-lib/numbl-core/interpreter/interpreterExec.d.ts +14 -1
  32. package/dist-lib/numbl-core/interpreter/interpreterFunctions.d.ts +6 -0
  33. package/dist-lib/numbl-core/interpreter/interpreterSpecialBuiltins.d.ts +4 -0
  34. package/dist-lib/numbl-core/interpreter/types.d.ts +13 -0
  35. package/dist-lib/numbl-core/jit/builtins/defs/math/rand.d.ts +2 -0
  36. package/dist-lib/numbl-core/jitDeclineDiagnostics.d.ts +32 -0
  37. package/dist-lib/numbl-core/jsUserFunctions.d.ts +13 -0
  38. package/dist-lib/numbl-core/lowering/classInfo.d.ts +19 -2
  39. package/dist-lib/numbl-core/native/geometry-bridge.d.ts +10 -0
  40. package/dist-lib/numbl-core/parser/ArgumentsParser.d.ts +6 -1
  41. package/dist-lib/numbl-core/runtime/constructors.d.ts +2 -1
  42. package/dist-lib/numbl-core/runtime/runtime.d.ts +2 -1
  43. package/dist-lib/numbl-core/runtime/runtimeMemberAccess.d.ts +1 -1
  44. package/dist-lib/numbl-core/version.d.ts +1 -1
  45. package/dist-lib/vfs/BrowserFileIOAdapter.d.ts +54 -0
  46. package/dist-lib/vfs/BrowserSystemAdapter.d.ts +27 -0
  47. package/dist-lib/vfs/VirtualFileSystem.d.ts +66 -0
  48. package/dist-plot-viewer/assets/hdf5_hl-C9YUKPMe.js +24296 -0
  49. package/dist-plot-viewer/assets/index-YeXWXIxH.js +4504 -0
  50. package/dist-plot-viewer/index.html +1 -1
  51. package/dist-site-viewer/assets/hdf5_hl-C9YUKPMe.js +24296 -0
  52. package/dist-site-viewer/assets/index-C-wfkZK0.js +4829 -0
  53. package/dist-site-viewer/assets/numbl-worker-Bnbz2rMQ.js +5228 -0
  54. package/dist-site-viewer/index.html +1 -1
  55. package/native/lapack_svd.cpp +50 -0
  56. package/package.json +21 -2
  57. package/dist-plot-viewer/assets/index-D4grNz8m.js +0 -4504
  58. package/dist-site-viewer/assets/index-B2mCFC55.js +0 -4826
  59. package/dist-site-viewer/assets/numbl-worker-DWZE29ck.js +0 -5116
package/dist-cli/cli.js CHANGED
@@ -320,6 +320,7 @@ import { homedir as homedir4 } from "os";
320
320
  import { fileURLToPath as fileURLToPath2 } from "url";
321
321
  import { createRequire } from "module";
322
322
  import { execSync as execSync2 } from "child_process";
323
+ import { createHash as createHash2 } from "crypto";
323
324
 
324
325
  // src/cli-plot-server.ts
325
326
  import { createServer } from "node:http";
@@ -2628,6 +2629,9 @@ var RTV = {
2628
2629
  }
2629
2630
  return new RuntimeClassInstance(className, fields, isHandleClass2);
2630
2631
  },
2632
+ classInstanceArray(className, elements, shape) {
2633
+ return new RuntimeClassInstanceArray(className, elements, shape);
2634
+ },
2631
2635
  complex(re, im) {
2632
2636
  return new RuntimeComplexNumber(re, im);
2633
2637
  },
@@ -21187,6 +21191,24 @@ function lu(data, m, n) {
21187
21191
  }
21188
21192
  function svd(data, m, n, econ, computeUV) {
21189
21193
  const k = Math.min(m, n);
21194
+ let finite = true;
21195
+ for (let i = 0; i < data.length; i++) {
21196
+ if (!Number.isFinite(data[i])) {
21197
+ finite = false;
21198
+ break;
21199
+ }
21200
+ }
21201
+ if (!finite) {
21202
+ const sNan = allocFloat64Array(k).fill(NaN);
21203
+ if (!computeUV) return { S: sNan };
21204
+ const uCols2 = econ ? k : m;
21205
+ const vCols2 = econ ? k : n;
21206
+ return {
21207
+ U: allocFloat64Array(m * uCols2).fill(NaN),
21208
+ S: sNan,
21209
+ V: allocFloat64Array(n * vCols2).fill(NaN)
21210
+ };
21211
+ }
21190
21212
  const a = allocFloat64Array(data);
21191
21213
  const s = allocFloat64Array(k);
21192
21214
  const JOBU_A2 = 0;
@@ -22413,8 +22435,8 @@ function indexIntoScalar(base, indices) {
22413
22435
  const idx = indices[0];
22414
22436
  if (isRuntimeTensor(idx)) {
22415
22437
  const is0x0 = idx.shape.length === 2 && idx.shape[0] === 0 && idx.shape[1] === 0;
22416
- const outShape = is0x0 ? [0, 0] : [...idx.shape];
22417
- return RTV.tensor(allocFloat64Array(0), outShape);
22438
+ const outShape2 = is0x0 ? [0, 0] : [...idx.shape];
22439
+ return RTV.tensor(allocFloat64Array(0), outShape2);
22418
22440
  }
22419
22441
  }
22420
22442
  if (indices.length === 1) {
@@ -22444,22 +22466,46 @@ function indexIntoScalar(base, indices) {
22444
22466
  if (k !== 1) throw new RuntimeError("Index exceeds array bounds");
22445
22467
  }
22446
22468
  const n = idx.data.length;
22447
- const scalarRe = isRuntimeNumber(base) ? base : base.re;
22448
- const data = allocFloat64Array(n);
22449
- data.fill(scalarRe);
22469
+ const scalarRe2 = isRuntimeNumber(base) ? base : base.re;
22470
+ const data2 = allocFloat64Array(n);
22471
+ data2.fill(scalarRe2);
22450
22472
  if (isRuntimeComplexNumber(base) && base.im !== 0) {
22451
22473
  const im = allocFloat64Array(n);
22452
22474
  im.fill(base.im);
22453
- return RTV.tensor(data, [...idx.shape], im);
22475
+ return RTV.tensor(data2, [...idx.shape], im);
22454
22476
  }
22455
- return RTV.tensor(data, [...idx.shape]);
22477
+ return RTV.tensor(data2, [...idx.shape]);
22456
22478
  }
22479
+ const dimLengths = [];
22480
+ let anyTensor = false;
22457
22481
  for (const idx of indices) {
22458
- if (isColonIndex(idx)) continue;
22459
- const i = toNumber(idx);
22460
- if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
22482
+ if (isColonIndex(idx)) {
22483
+ dimLengths.push(1);
22484
+ } else if (isRuntimeTensor(idx)) {
22485
+ anyTensor = true;
22486
+ for (let i = 0; i < idx.data.length; i++) {
22487
+ if (Math.round(idx.data[i]) !== 1)
22488
+ throw new RuntimeError("Index exceeds array bounds");
22489
+ }
22490
+ dimLengths.push(idx.data.length);
22491
+ } else {
22492
+ if (Math.round(toNumber(idx)) !== 1)
22493
+ throw new RuntimeError("Index exceeds array bounds");
22494
+ dimLengths.push(1);
22495
+ }
22461
22496
  }
22462
- return base;
22497
+ if (!anyTensor) return base;
22498
+ const total = dimLengths.reduce((a, b) => a * b, 1);
22499
+ const scalarRe = isRuntimeNumber(base) ? base : base.re;
22500
+ const data = allocFloat64Array(total);
22501
+ data.fill(scalarRe);
22502
+ const outShape = dimLengths.length >= 2 ? dimLengths : [dimLengths[0], 1];
22503
+ if (isRuntimeComplexNumber(base) && base.im !== 0) {
22504
+ const im = allocFloat64Array(total);
22505
+ im.fill(base.im);
22506
+ return RTV.tensor(data, outShape, im);
22507
+ }
22508
+ return RTV.tensor(data, outShape);
22463
22509
  }
22464
22510
  function indexIntoTensor(base, indices) {
22465
22511
  if (indices.length === 1) {
@@ -22494,11 +22540,13 @@ function indexIntoTensor(base, indices) {
22494
22540
  function indexIntoTensor1D(base, idx) {
22495
22541
  if (isColonIndex(idx)) {
22496
22542
  const imag2 = base.imag ? allocFloat64Array(base.imag) : void 0;
22497
- return RTV.tensor(
22543
+ const result = RTV.tensor(
22498
22544
  allocFloat64Array(base.data),
22499
22545
  [base.data.length, 1],
22500
22546
  imag2
22501
22547
  );
22548
+ if (base._isLogical === true) result._isLogical = true;
22549
+ return result;
22502
22550
  }
22503
22551
  if (isRuntimeLogical(idx)) {
22504
22552
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
@@ -22696,6 +22744,17 @@ function indexIntoChar(base, indices) {
22696
22744
  if (indices.length === 1) {
22697
22745
  const idx = indices[0];
22698
22746
  if (isColonIndex(idx)) return base;
22747
+ if (isRuntimeTensor(idx) && idx._isLogical) {
22748
+ let result = "";
22749
+ for (let k = 0; k < idx.data.length; k++) {
22750
+ if (idx.data[k] !== 0) {
22751
+ if (k >= base.value.length)
22752
+ throw new RuntimeError("Index exceeds char array length");
22753
+ result += base.value[k];
22754
+ }
22755
+ }
22756
+ return RTV.char(result);
22757
+ }
22699
22758
  if (isRuntimeTensor(idx)) {
22700
22759
  let result = "";
22701
22760
  for (let k = 0; k < idx.data.length; k++) {
@@ -22714,22 +22773,19 @@ function indexIntoChar(base, indices) {
22714
22773
  if (indices.length === 2) {
22715
22774
  const rowIdx = indices[0];
22716
22775
  const colIdx = indices[1];
22717
- let rows;
22718
- if (isColonIndex(rowIdx)) {
22719
- rows = Array.from({ length: nRows }, (_, i) => i);
22720
- } else if (isRuntimeTensor(rowIdx)) {
22721
- rows = Array.from(rowIdx.data, (v) => Math.round(v) - 1);
22722
- } else {
22723
- rows = [Math.round(toNumber(rowIdx)) - 1];
22724
- }
22725
- let cols;
22726
- if (isColonIndex(colIdx)) {
22727
- cols = Array.from({ length: nCols }, (_, i) => i);
22728
- } else if (isRuntimeTensor(colIdx)) {
22729
- cols = Array.from(colIdx.data, (v) => Math.round(v) - 1);
22730
- } else {
22731
- cols = [Math.round(toNumber(colIdx)) - 1];
22732
- }
22776
+ const toPositions = (idx) => {
22777
+ if (isRuntimeTensor(idx) && idx._isLogical) {
22778
+ const out = [];
22779
+ for (let i = 0; i < idx.data.length; i++)
22780
+ if (idx.data[i] !== 0) out.push(i);
22781
+ return out;
22782
+ }
22783
+ if (isRuntimeTensor(idx))
22784
+ return Array.from(idx.data, (v) => Math.round(v) - 1);
22785
+ return [Math.round(toNumber(idx)) - 1];
22786
+ };
22787
+ const rows = isColonIndex(rowIdx) ? Array.from({ length: nRows }, (_, i) => i) : toPositions(rowIdx);
22788
+ const cols = isColonIndex(colIdx) ? Array.from({ length: nCols }, (_, i) => i) : toPositions(colIdx);
22733
22789
  let result = "";
22734
22790
  for (const r of rows) {
22735
22791
  for (const c of cols) {
@@ -22755,11 +22811,11 @@ function indexIntoLogical(base, indices) {
22755
22811
  const vi = Math.round(idx.data[i2]);
22756
22812
  if (vi !== 1) throw new RuntimeError("Index exceeds array bounds");
22757
22813
  }
22758
- const data = allocFloat64Array(idx.data.length);
22759
- data.fill(base ? 1 : 0);
22760
- const result = RTV.tensor(data, [1, idx.data.length]);
22761
- result._isLogical = true;
22762
- return result;
22814
+ const data2 = allocFloat64Array(idx.data.length);
22815
+ data2.fill(base ? 1 : 0);
22816
+ const result2 = RTV.tensor(data2, [...idx.shape]);
22817
+ result2._isLogical = true;
22818
+ return result2;
22763
22819
  }
22764
22820
  if (isRuntimeLogical(idx)) {
22765
22821
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
@@ -22769,16 +22825,35 @@ function indexIntoLogical(base, indices) {
22769
22825
  if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
22770
22826
  return base;
22771
22827
  }
22828
+ const dimLengths = [];
22829
+ let anyTensor = false;
22772
22830
  for (const idx of indices) {
22773
- if (isColonIndex(idx)) continue;
22774
- if (isRuntimeLogical(idx)) {
22831
+ if (isColonIndex(idx)) {
22832
+ dimLengths.push(1);
22833
+ } else if (isRuntimeLogical(idx)) {
22775
22834
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
22776
- continue;
22835
+ dimLengths.push(1);
22836
+ } else if (isRuntimeTensor(idx)) {
22837
+ anyTensor = true;
22838
+ for (let i = 0; i < idx.data.length; i++) {
22839
+ if (Math.round(idx.data[i]) !== 1)
22840
+ throw new RuntimeError("Index exceeds array bounds");
22841
+ }
22842
+ dimLengths.push(idx.data.length);
22843
+ } else {
22844
+ if (Math.round(toNumber(idx)) !== 1)
22845
+ throw new RuntimeError("Index exceeds array bounds");
22846
+ dimLengths.push(1);
22777
22847
  }
22778
- const i = Math.round(toNumber(idx));
22779
- if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
22780
22848
  }
22781
- return base;
22849
+ if (!anyTensor) return base;
22850
+ const total = dimLengths.reduce((a, b) => a * b, 1);
22851
+ const data = allocFloat64Array(total);
22852
+ data.fill(base ? 1 : 0);
22853
+ const outShape = dimLengths.length >= 2 ? dimLengths : [dimLengths[0], 1];
22854
+ const result = RTV.tensor(data, outShape);
22855
+ result._isLogical = true;
22856
+ return result;
22782
22857
  }
22783
22858
  function indexIntoTensorWithTensor(base, idx) {
22784
22859
  const baseLogical = base._isLogical === true;
@@ -22858,11 +22933,15 @@ function indexIntoRTValue(base, indices) {
22858
22933
  return indexIntoLogical(base, indices);
22859
22934
  }
22860
22935
  if (isRuntimeStruct(base) || isRuntimeClassInstance(base)) {
22861
- if (indices.length === 1) {
22862
- const i = Math.round(toNumber(indices[0]));
22863
- if (i !== 1) throw new RuntimeError("Index exceeds struct dimensions");
22864
- return base;
22865
- }
22936
+ const allOnes = indices.length >= 1 && indices.every((idx) => {
22937
+ if (isRuntimeNumber(idx)) return Math.round(idx) === 1;
22938
+ if (isRuntimeLogical(idx)) return idx === true;
22939
+ if (isRuntimeTensor(idx))
22940
+ return idx.data.length === 1 && Math.round(idx.data[0]) === 1;
22941
+ return false;
22942
+ });
22943
+ if (allOnes) return base;
22944
+ throw new RuntimeError("Index exceeds struct dimensions");
22866
22945
  }
22867
22946
  if (isRuntimeSparseMatrix(base)) {
22868
22947
  return indexIntoSparse(base, indices);
@@ -22912,7 +22991,13 @@ function storeIntoTensor(base, indices, rhs, _rt) {
22912
22991
  }
22913
22992
  if (isShared(base)) {
22914
22993
  const cowImag = base.imag ? allocFloat64Array(base.imag) : void 0;
22915
- base = RTV.tensor(allocFloat64Array(base.data), [...base.shape], cowImag);
22994
+ const copy = RTV.tensor(
22995
+ allocFloat64Array(base.data),
22996
+ [...base.shape],
22997
+ cowImag
22998
+ );
22999
+ copy._isLogical = base._isLogical;
23000
+ base = copy;
22916
23001
  }
22917
23002
  if (indices.length === 1) {
22918
23003
  return storeIntoTensor1D(base, indices[0], rhs);
@@ -22940,7 +23025,9 @@ function deleteTensorElements(base, idx) {
22940
23025
  toDelete.add(Math.round(idx.data[i]) - 1);
22941
23026
  }
22942
23027
  } else if (isColonIndex(idx)) {
22943
- return RTV.tensor(allocFloat64Array(0), [0, 0]);
23028
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
23029
+ empty._isLogical = base._isLogical;
23030
+ return empty;
22944
23031
  }
22945
23032
  if (toDelete.size === 0) return base;
22946
23033
  const newData = [];
@@ -22955,7 +23042,9 @@ function deleteTensorElements(base, idx) {
22955
23042
  const baseIsColVec = base.shape.length >= 2 && base.shape[1] === 1 && base.shape[0] !== 1;
22956
23043
  const outShape = baseIsColVec ? [newData.length, 1] : [1, newData.length];
22957
23044
  const imOut = hasImag && newIm.some((x) => x !== 0) ? allocFloat64Array(newIm) : void 0;
22958
- return RTV.tensor(allocFloat64Array(newData), outShape, imOut);
23045
+ const result = RTV.tensor(allocFloat64Array(newData), outShape, imOut);
23046
+ result._isLogical = base._isLogical;
23047
+ return result;
22959
23048
  }
22960
23049
  function collectDelIndices(idx, dimLen) {
22961
23050
  const s = /* @__PURE__ */ new Set();
@@ -22995,7 +23084,9 @@ function deleteTensorRowsOrCols(base, indices) {
22995
23084
  if (newIm && base.imag) newIm[dstIdx] = base.imag[srcIdx];
22996
23085
  }
22997
23086
  }
22998
- return RTV.tensor(newData, [newNrows, ncols], newIm);
23087
+ const result = RTV.tensor(newData, [newNrows, ncols], newIm);
23088
+ result._isLogical = base._isLogical;
23089
+ return result;
22999
23090
  }
23000
23091
  if (isColonIndex(indices[0])) {
23001
23092
  const delCols = collectDelIndices(indices[1], ncols);
@@ -23014,7 +23105,9 @@ function deleteTensorRowsOrCols(base, indices) {
23014
23105
  if (newIm && base.imag) newIm[dstIdx] = base.imag[srcIdx];
23015
23106
  }
23016
23107
  }
23017
- return RTV.tensor(newData, [nrows, newNcols], newIm);
23108
+ const result = RTV.tensor(newData, [nrows, newNcols], newIm);
23109
+ result._isLogical = base._isLogical;
23110
+ return result;
23018
23111
  }
23019
23112
  throw new RuntimeError("Cannot delete from both row and column dimensions");
23020
23113
  }
@@ -23084,6 +23177,9 @@ function storeIntoTensor1D(base, idx, rhs) {
23084
23177
  }
23085
23178
  function storeIntoTensorByVector(base, idx, rhs) {
23086
23179
  if (idx._isLogical) {
23180
+ if (isRuntimeTensor(rhs) && rhs.data.length === 1) {
23181
+ rhs = rhs.imag && rhs.imag[0] !== 0 ? RTV.complex(rhs.data[0], rhs.imag[0]) : RTV.num(rhs.data[0]);
23182
+ }
23087
23183
  const { re: rhsRe, im: rhsIm } = isRuntimeTensor(rhs) ? { re: null, im: null } : toReIm(rhs);
23088
23184
  let maxTruthy = -1;
23089
23185
  for (let i = 0; i < idx.data.length; i++) {
@@ -23333,7 +23429,8 @@ function storeIntoTensorND(base, indices, rhs) {
23333
23429
  const dimIndices = indices.map((idx, dim) => {
23334
23430
  const dimSize = dim < shape.length ? shape[dim] : 1;
23335
23431
  if (isColonIndex(idx)) {
23336
- if (dimSize === 0 && rhsShape) {
23432
+ const dimSizeUnknown = dimSize === 0 || dim >= shape.length;
23433
+ if (dimSizeUnknown && rhsShape) {
23337
23434
  const rDim = rhsDimCursor < rhsShape.length ? rhsShape[rhsDimCursor] : 1;
23338
23435
  rhsDimCursor++;
23339
23436
  return Array.from({ length: rDim }, (_, i) => i);
@@ -23650,6 +23747,9 @@ function horzcat(...values) {
23650
23747
  if (values.some((v) => isRuntimeSparseMatrix(v))) {
23651
23748
  return sparseCatAlongDim(values, 1);
23652
23749
  }
23750
+ if (values.some((v) => isRuntimeCell(v))) {
23751
+ return cellCatAlongDim(values, 1);
23752
+ }
23653
23753
  if (values.some((v) => isRuntimeChar(v))) {
23654
23754
  let result = "";
23655
23755
  for (const v of values) {
@@ -23670,9 +23770,6 @@ function horzcat(...values) {
23670
23770
  }
23671
23771
  return RTV.string(result);
23672
23772
  }
23673
- if (values.some((v) => isRuntimeCell(v))) {
23674
- return cellCatAlongDim(values, 1);
23675
- }
23676
23773
  if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
23677
23774
  return structCat(values);
23678
23775
  }
@@ -23684,12 +23781,12 @@ function vertcat(...values) {
23684
23781
  if (values.some((v) => isRuntimeSparseMatrix(v))) {
23685
23782
  return sparseCatAlongDim(values, 0);
23686
23783
  }
23687
- if (values.some((v) => isRuntimeChar(v))) {
23688
- return vertcatChars(values);
23689
- }
23690
23784
  if (values.some((v) => isRuntimeCell(v))) {
23691
23785
  return cellCatAlongDim(values, 0);
23692
23786
  }
23787
+ if (values.some((v) => isRuntimeChar(v))) {
23788
+ return vertcatChars(values);
23789
+ }
23693
23790
  if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
23694
23791
  return structCat(values);
23695
23792
  }
@@ -24035,11 +24132,22 @@ function catAlongDim(values, dimIdx) {
24035
24132
  if (allLogical) result._isLogical = true;
24036
24133
  return result;
24037
24134
  }
24135
+ function isEmptyNonCellOperand(v) {
24136
+ if (isRuntimeChar(v)) return v.value.length === 0;
24137
+ if (isRuntimeTensor(v)) return v.data.length === 0;
24138
+ return false;
24139
+ }
24038
24140
  function cellCatAlongDim(values, dimIdx) {
24039
- let cells = values.map((v) => {
24040
- if (isRuntimeCell(v)) return v;
24041
- return RTV.cell([v], [1, 1]);
24042
- });
24141
+ let cells = [];
24142
+ for (const v of values) {
24143
+ if (isRuntimeCell(v)) {
24144
+ cells.push(v);
24145
+ } else if (isEmptyNonCellOperand(v)) {
24146
+ continue;
24147
+ } else {
24148
+ cells.push(RTV.cell([v], [1, 1]));
24149
+ }
24150
+ }
24043
24151
  cells = cells.filter((c) => c.data.length > 0);
24044
24152
  if (cells.length === 0) return RTV.cell([], [0, 0]);
24045
24153
  if (cells.length === 1) return cells[0];
@@ -27229,6 +27337,24 @@ function sprintfFormat(fmt, args) {
27229
27337
  case "t":
27230
27338
  result += " ";
27231
27339
  break;
27340
+ case "r":
27341
+ result += "\r";
27342
+ break;
27343
+ case "a":
27344
+ result += "\x07";
27345
+ break;
27346
+ case "b":
27347
+ result += "\b";
27348
+ break;
27349
+ case "f":
27350
+ result += "\f";
27351
+ break;
27352
+ case "v":
27353
+ result += "\v";
27354
+ break;
27355
+ case "0":
27356
+ result += "\0";
27357
+ break;
27232
27358
  case "\\":
27233
27359
  result += "\\";
27234
27360
  break;
@@ -31459,7 +31585,7 @@ var ExpressionParser = class extends ParserBase {
31459
31585
  }
31460
31586
  } else if (expr.type === "Ident" && this.peekToken() === 41 /* At */ && this.peekTokenAt(1) === 4 /* Ident */) {
31461
31587
  this.pos++;
31462
- const className = this.expectIdent();
31588
+ const className = this.parseQualifiedName();
31463
31589
  if (!this.consume(50 /* LParen */)) {
31464
31590
  throw this.error("expected '(' after superclass name in super call");
31465
31591
  }
@@ -32060,7 +32186,10 @@ var ControlFlowParser = class extends CommandParser {
32060
32186
  let catchBody = [];
32061
32187
  if (this.consume(27 /* Catch */)) {
32062
32188
  if (this.peekToken() === 4 /* Ident */) {
32063
- catchVar = this.expectIdent();
32189
+ const after = this.peekTokenAt(1);
32190
+ if (after === void 0 || after === 68 /* Newline */ || after === 49 /* Semicolon */ || after === 47 /* Comma */ || after === 15 /* End */) {
32191
+ catchVar = this.expectIdent();
32192
+ }
32064
32193
  }
32065
32194
  catchBody = this.parseBlock((t) => t === 15 /* End */);
32066
32195
  }
@@ -32185,8 +32314,12 @@ var ArgumentsParser = class extends ControlFlowParser {
32185
32314
  dimensions = this.parseArgDimensions();
32186
32315
  }
32187
32316
  let className = null;
32188
- if (this.peekToken() === 4 /* Ident */ && this.peekToken() !== 68 /* Newline */ && this.peekToken() !== 49 /* Semicolon */) {
32317
+ if (this.peekToken() === 4 /* Ident */) {
32189
32318
  className = this.next().lexeme;
32319
+ while (this.peekToken() === 45 /* Dot */) {
32320
+ this.consume(45 /* Dot */);
32321
+ className += "." + this.expectIdent();
32322
+ }
32190
32323
  }
32191
32324
  let validators = [];
32192
32325
  if (this.peekToken() === 54 /* LBrace */) {
@@ -32222,18 +32355,28 @@ var ArgumentsParser = class extends ControlFlowParser {
32222
32355
  return dims;
32223
32356
  }
32224
32357
  /**
32225
- * Parse validator list: {mustBeNumeric, mustBePositive}
32358
+ * Parse validator list: {mustBeNumeric, mustBePositive}. Validators may be
32359
+ * full function calls with arguments, e.g.
32360
+ * `{mustBeMember(x,["a","b"]), mustBeInRange(x,0,7)}`. The validator bodies
32361
+ * are not enforced at runtime, so we capture the top-level validator name
32362
+ * tokens and skip past any nested `(...)`, `[...]`, `{...}` so parsing
32363
+ * resumes correctly at the default value (`= expr`) or end of line.
32226
32364
  */
32227
32365
  parseArgValidators() {
32228
32366
  this.consume(54 /* LBrace */);
32229
32367
  const validators = [];
32230
- while (this.peekToken() !== 55 /* RBrace */ && this.peekToken() !== void 0) {
32231
- if (this.consume(47 /* Comma */)) continue;
32232
- if (this.peekToken() === 4 /* Ident */) {
32233
- validators.push(this.next().lexeme);
32234
- } else {
32235
- break;
32368
+ let depth = 0;
32369
+ while (this.peekToken() !== void 0) {
32370
+ const tok = this.peekToken();
32371
+ if (depth === 0 && tok === 55 /* RBrace */) break;
32372
+ if (tok === 50 /* LParen */ || tok === 52 /* LBracket */ || tok === 54 /* LBrace */) {
32373
+ depth++;
32374
+ } else if (tok === 51 /* RParen */ || tok === 53 /* RBracket */ || tok === 55 /* RBrace */) {
32375
+ depth--;
32376
+ } else if (depth === 0 && tok === 4 /* Ident */) {
32377
+ validators.push(this.peek().lexeme);
32236
32378
  }
32379
+ this.next();
32237
32380
  }
32238
32381
  this.consume(55 /* RBrace */);
32239
32382
  return validators;
@@ -32264,7 +32407,8 @@ var FunctionParser = class extends ArgumentsParser {
32264
32407
  if (this.consume(52 /* LBracket */)) {
32265
32408
  if (this.peekToken() !== 53 /* RBracket */) {
32266
32409
  outputs.push(this.expectIdentOrTilde());
32267
- while (this.consume(47 /* Comma */)) {
32410
+ while (this.peekToken() === 47 /* Comma */ || this.peekToken() === 4 /* Ident */ || this.peekToken() === 40 /* Tilde */) {
32411
+ this.consume(47 /* Comma */);
32268
32412
  outputs.push(this.expectIdentOrTilde());
32269
32413
  }
32270
32414
  }
@@ -32372,6 +32516,14 @@ var FunctionParser = class extends ArgumentsParser {
32372
32516
  };
32373
32517
 
32374
32518
  // src/numbl-core/parser/ClassParser.ts
32519
+ var HANDLE_BASE_CLASSES = /* @__PURE__ */ new Set([
32520
+ "handle",
32521
+ "dynamicprops",
32522
+ "matlab.mixin.Copyable",
32523
+ "matlab.mixin.SetGet",
32524
+ "matlab.mixin.SetGetExactNames",
32525
+ "hgsetget"
32526
+ ]);
32375
32527
  var ClassParser = class extends FunctionParser {
32376
32528
  // ── Imports & ClassDef ───────────────────────────────────────────────
32377
32529
  parseImport() {
@@ -32406,7 +32558,11 @@ var ClassParser = class extends FunctionParser {
32406
32558
  const name = this.parseQualifiedName();
32407
32559
  let superClass = null;
32408
32560
  if (this.consume(43 /* Less */)) {
32409
- superClass = this.parseQualifiedName();
32561
+ const supers = [this.parseQualifiedName()];
32562
+ while (this.consume(38 /* And */)) {
32563
+ supers.push(this.parseQualifiedName());
32564
+ }
32565
+ superClass = supers.some((s) => HANDLE_BASE_CLASSES.has(s)) ? "handle" : supers[0];
32410
32566
  }
32411
32567
  const members = [];
32412
32568
  while (true) {
@@ -33281,7 +33437,7 @@ function insertFunctionEndTokens(tokens) {
33281
33437
  }
33282
33438
  if (BLOCK_OPENERS.has(tok.token)) {
33283
33439
  depth++;
33284
- } else if (tok.token === 15 /* End */ && groupDepth === 0) {
33440
+ } else if (tok.token === 15 /* End */ && groupDepth === 0 && tokens[i - 1]?.token !== 45 /* Dot */) {
33285
33441
  depth--;
33286
33442
  if (depth === 0 && inFunction) {
33287
33443
  inFunction = false;
@@ -34806,7 +34962,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
34806
34962
  return true;
34807
34963
  }
34808
34964
  case "close": {
34809
- if (args.length > 0 && toString(args[0]) === "all") {
34965
+ const a0 = args[0];
34966
+ const isText3 = a0 !== void 0 && (isRuntimeChar(a0) || isRuntimeString(a0));
34967
+ if (isText3 && toString(a0) === "all") {
34810
34968
  plotInstr(instructions, { type: "close_all" });
34811
34969
  } else {
34812
34970
  plotInstr(instructions, { type: "close" });
@@ -35414,43 +35572,40 @@ function methodDispatch(rt, name, nargout, args) {
35414
35572
  }
35415
35573
  }
35416
35574
  }
35417
- try {
35418
- return callClassMethod(rt, firstRV.className, name, nargout, args);
35419
- } catch (e) {
35420
- if (e instanceof RuntimeError) {
35421
- const guardKey = `${firstRV.className}.subsref`;
35422
- if (!rt.activeAccessors.has(guardKey)) {
35423
- const subsrefFn = rt.cachedResolveClassMethod(
35424
- firstRV.className,
35425
- "subsref"
35426
- );
35427
- if (subsrefFn) {
35428
- const remaining = args.slice(1);
35429
- const sEntries = [
35430
- RTV.struct({
35431
- type: RTV.char("."),
35432
- subs: RTV.char(name)
35433
- }),
35434
- RTV.struct({
35435
- type: RTV.char("()"),
35436
- subs: RTV.cell(
35437
- remaining.map((a) => ensureRuntimeValue(a)),
35438
- [1, remaining.length]
35439
- )
35440
- })
35441
- ];
35442
- const S = RTV.structArray(["type", "subs"], sEntries);
35443
- rt.activeAccessors.add(guardKey);
35444
- try {
35445
- return subsrefFn(nargout, first, S);
35446
- } finally {
35447
- rt.activeAccessors.delete(guardKey);
35448
- }
35575
+ const methodExists = rt.cachedResolveClassMethod(firstRV.className, name) !== null;
35576
+ if (!methodExists) {
35577
+ const guardKey = `${firstRV.className}.subsref`;
35578
+ if (!rt.activeAccessors.has(guardKey)) {
35579
+ const subsrefFn = rt.cachedResolveClassMethod(
35580
+ firstRV.className,
35581
+ "subsref"
35582
+ );
35583
+ if (subsrefFn) {
35584
+ const remaining = args.slice(1);
35585
+ const sEntries = [
35586
+ RTV.struct({
35587
+ type: RTV.char("."),
35588
+ subs: RTV.char(name)
35589
+ }),
35590
+ RTV.struct({
35591
+ type: RTV.char("()"),
35592
+ subs: RTV.cell(
35593
+ remaining.map((a) => ensureRuntimeValue(a)),
35594
+ [1, remaining.length]
35595
+ )
35596
+ })
35597
+ ];
35598
+ const S = RTV.structArray(["type", "subs"], sEntries);
35599
+ rt.activeAccessors.add(guardKey);
35600
+ try {
35601
+ return subsrefFn(nargout, first, S);
35602
+ } finally {
35603
+ rt.activeAccessors.delete(guardKey);
35449
35604
  }
35450
35605
  }
35451
35606
  }
35452
- throw e;
35453
35607
  }
35608
+ return callClassMethod(rt, firstRV.className, name, nargout, args);
35454
35609
  }
35455
35610
  }
35456
35611
  const builtin = rt.builtins[name];
@@ -35507,11 +35662,13 @@ function arrayfunCellfunImpl(rt, name, nargout, args) {
35507
35662
  const getElem = (arr, i) => {
35508
35663
  if (isRuntimeCell(arr)) return arr.data[i];
35509
35664
  if (isRuntimeTensor(arr)) return arr.data[i];
35665
+ if (isRuntimeStructArray(arr)) return arr.elements[i];
35510
35666
  return arr;
35511
35667
  };
35512
35668
  const getLen = (arr) => {
35513
35669
  if (isRuntimeCell(arr)) return arr.data.length;
35514
35670
  if (isRuntimeTensor(arr)) return arr.data.length;
35671
+ if (isRuntimeStructArray(arr)) return arr.elements.length;
35515
35672
  return 1;
35516
35673
  };
35517
35674
  const collectArgs = (i) => {
@@ -35547,9 +35704,9 @@ function arrayfunCellfunImpl(rt, name, nargout, args) {
35547
35704
  if (allLogical && arrArg.data.length > 0) result._isLogical = true;
35548
35705
  return result;
35549
35706
  }
35550
- if (isRuntimeCell(arrArg) || extraInputs.length > 0 || nargout > 1) {
35707
+ if (isRuntimeCell(arrArg) || isRuntimeStructArray(arrArg) || extraInputs.length > 0 || nargout > 1) {
35551
35708
  const len = getLen(arrArg);
35552
- const shape = isRuntimeCell(arrArg) ? [...arrArg.shape] : isRuntimeTensor(arrArg) ? [...arrArg.shape] : [1, len];
35709
+ const shape = isRuntimeCell(arrArg) || isRuntimeTensor(arrArg) ? [...arrArg.shape] : [1, len];
35553
35710
  if (nargout > 1) {
35554
35711
  const allResults = Array.from(
35555
35712
  { length: nargout },
@@ -36770,6 +36927,28 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
36770
36927
  if (isRuntimeStruct(mv)) {
36771
36928
  return ensureRuntimeValue(rhs);
36772
36929
  }
36930
+ if (indices.length === 1 && isRuntimeClassInstance(ensureRuntimeValue(rhs)) && // Object-array growth uses a numeric position. A char/string index (e.g. a
36931
+ // key into a containers.Map whose value happens to be a class instance)
36932
+ // must fall through to the subsasgn dispatch below, not be coerced to a
36933
+ // number — `toNumber` on a multi-char value throws.
36934
+ !isRuntimeChar(ensureRuntimeValue(indices[0]))) {
36935
+ const k = Math.round(toNumber(ensureRuntimeValue(indices[0]))) - 1;
36936
+ const growsArray = isRuntimeClassInstanceArray(mv) || isRuntimeTensor(mv) && mv.data.length === 0 || isRuntimeClassInstance(mv) && k >= 1;
36937
+ if (growsArray) {
36938
+ if (k < 0) throw new RuntimeError("Index must be a positive integer");
36939
+ const rhsInst = ensureRuntimeValue(rhs);
36940
+ const existing = isRuntimeClassInstanceArray(mv) ? [...mv.elements] : isRuntimeClassInstance(mv) ? [mv] : [];
36941
+ const className = isRuntimeClassInstanceArray(mv) ? mv.className : isRuntimeClassInstance(mv) ? mv.className : rhsInst.className;
36942
+ while (existing.length < k) {
36943
+ existing.push(
36944
+ RTV.classInstance(className, [...rhsInst.fields.keys()], false)
36945
+ );
36946
+ }
36947
+ existing[k] = rhsInst;
36948
+ if (existing.length === 1) return existing[0];
36949
+ return RTV.classInstanceArray(className, existing, [1, existing.length]);
36950
+ }
36951
+ }
36773
36952
  if (isRuntimeClassInstance(mv)) {
36774
36953
  const guardKey = `${mv.className}.subsasgn`;
36775
36954
  if (!skipSubsasgn && !rt.activeAccessors.has(guardKey)) {
@@ -37190,6 +37369,11 @@ function validateDim(x) {
37190
37369
  throw new RuntimeError("Size inputs must be nonnegative integers.");
37191
37370
  return Math.max(0, x);
37192
37371
  }
37372
+ function toScalarDim(v) {
37373
+ if (isRuntimeTensor(v) && v.data.length !== 1)
37374
+ throw new RuntimeError("Size inputs must be scalar.");
37375
+ return validateDim(toNumber(v));
37376
+ }
37193
37377
  function parseShapeArgs(args) {
37194
37378
  if (args.length === 1 && isRuntimeTensor(args[0])) {
37195
37379
  const t = args[0];
@@ -37197,7 +37381,7 @@ function parseShapeArgs(args) {
37197
37381
  for (let i = 0; i < t.data.length; i++) shape.push(validateDim(t.data[i]));
37198
37382
  return shape;
37199
37383
  }
37200
- return args.map((a) => validateDim(toNumber(a)));
37384
+ return args.map(toScalarDim);
37201
37385
  }
37202
37386
  function arrayConstructorCases(fillFn, scalarValue, opts) {
37203
37387
  const cases = [
@@ -37238,12 +37422,14 @@ function arrayConstructorCases(fillFn, scalarValue, opts) {
37238
37422
  return fillFn(shape);
37239
37423
  }
37240
37424
  },
37241
- // Multiple scalar args
37425
+ // Multiple scalar args. A 1×1 array counts as a scalar dimension, so
37426
+ // accept `tensor` here too (apply-time validates each is scalar).
37242
37427
  {
37243
37428
  match: (argTypes) => {
37244
37429
  if (argTypes.length <= 1) return null;
37245
37430
  for (const a of argTypes) {
37246
- if (a.kind !== "number" && a.kind !== "boolean") return null;
37431
+ if (a.kind !== "number" && a.kind !== "boolean" && a.kind !== "tensor")
37432
+ return null;
37247
37433
  }
37248
37434
  const shape = argTypes.map(
37249
37435
  (a) => a.kind === "number" && typeof a.exact === "number" ? a.exact : -1
@@ -37803,6 +37989,13 @@ registerIBuiltin({
37803
37989
  apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
37804
37990
  })
37805
37991
  });
37992
+ registerIBuiltin({
37993
+ name: "waitbar",
37994
+ resolve: () => ({
37995
+ outputTypes: [{ kind: "unknown" }],
37996
+ apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
37997
+ })
37998
+ });
37806
37999
  registerIBuiltin({
37807
38000
  name: "get",
37808
38001
  resolve: () => ({
@@ -39001,6 +39194,8 @@ var SPECIAL_BUILTIN_NAMES = [
39001
39194
  "delete",
39002
39195
  "rmdir",
39003
39196
  "movefile",
39197
+ "copyfile",
39198
+ "fileattrib",
39004
39199
  "unzip",
39005
39200
  "dir",
39006
39201
  "warning",
@@ -39010,6 +39205,7 @@ var SPECIAL_BUILTIN_NAMES = [
39010
39205
  "userpath",
39011
39206
  "getenv",
39012
39207
  "setenv",
39208
+ "maxNumCompThreads",
39013
39209
  "pwd",
39014
39210
  "cd",
39015
39211
  "ode45",
@@ -39117,8 +39313,8 @@ function registerSpecialBuiltins(rt) {
39117
39313
  if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
39118
39314
  const margs = args.map((a) => ensureRuntimeValue(a));
39119
39315
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
39120
- const state = toString(margs[0]);
39121
- if (state === "on" || state === "off") {
39316
+ const mode = toString(margs[0]);
39317
+ if (mode === "on" || mode === "off") {
39122
39318
  if (nargout === 0) return void 0;
39123
39319
  return RTV.struct(
39124
39320
  /* @__PURE__ */ new Map([
@@ -39127,6 +39323,14 @@ function registerSpecialBuiltins(rt) {
39127
39323
  ])
39128
39324
  );
39129
39325
  }
39326
+ if (mode === "query") {
39327
+ return RTV.struct(
39328
+ /* @__PURE__ */ new Map([
39329
+ ["state", RTV.char("on")],
39330
+ ["identifier", margs[1]]
39331
+ ])
39332
+ );
39333
+ }
39130
39334
  }
39131
39335
  let fmtIdx = 0;
39132
39336
  if (margs.length >= 2 && isRuntimeChar(margs[0]) && toString(margs[0]).includes(":")) {
@@ -39943,6 +40147,65 @@ function registerSpecialBuiltins(rt) {
39943
40147
  RTV.char("")
39944
40148
  ];
39945
40149
  });
40150
+ registerSpecial("copyfile", (nargout, args) => {
40151
+ const io = requireFileIO();
40152
+ if (!io.copyfile)
40153
+ throw new RuntimeError("copyfile is not available in this environment");
40154
+ const margs = args.map((a) => ensureRuntimeValue(a));
40155
+ if (margs.length < 1)
40156
+ throw new RuntimeError("copyfile requires at least 1 argument");
40157
+ const source = toString(margs[0]);
40158
+ const destination = margs.length >= 2 ? toString(margs[1]) : rt.system?.cwd() ?? ".";
40159
+ let force = false;
40160
+ if (margs.length >= 3) {
40161
+ const third = toString(margs[2]);
40162
+ if (third.toLowerCase() === "f") force = true;
40163
+ }
40164
+ const ok = io.copyfile(source, destination, force);
40165
+ if (nargout === 0) {
40166
+ if (!ok)
40167
+ throw new RuntimeError(
40168
+ `copyfile: cannot copy '${source}' to '${destination}'`
40169
+ );
40170
+ return void 0;
40171
+ }
40172
+ return nargout <= 1 ? RTV.num(ok ? 1 : 0) : [
40173
+ RTV.num(ok ? 1 : 0),
40174
+ RTV.char(ok ? "" : `Cannot copy '${source}' to '${destination}'`),
40175
+ RTV.char("")
40176
+ ];
40177
+ });
40178
+ registerSpecial("fileattrib", (nargout, args) => {
40179
+ const io = requireFileIO();
40180
+ if (!io.fileattrib)
40181
+ throw new RuntimeError("fileattrib is not available in this environment");
40182
+ const margs = args.map((a) => ensureRuntimeValue(a));
40183
+ if (margs.length < 1)
40184
+ throw new RuntimeError("fileattrib requires at least 1 argument");
40185
+ const p2 = toString(margs[0]);
40186
+ const info = io.fileattrib(p2);
40187
+ if (nargout === 0) {
40188
+ if (!info)
40189
+ throw new RuntimeError(
40190
+ `fileattrib: cannot find '${p2}': No such file or directory`
40191
+ );
40192
+ return void 0;
40193
+ }
40194
+ const values = info ? RTV.struct(
40195
+ /* @__PURE__ */ new Map([
40196
+ ["Name", RTV.char(info.Name)],
40197
+ ["archive", RTV.num(0)],
40198
+ ["system", RTV.num(0)],
40199
+ ["hidden", RTV.num(0)],
40200
+ ["directory", RTV.num(info.directory ? 1 : 0)],
40201
+ ["UserRead", RTV.num(info.UserRead ? 1 : 0)],
40202
+ ["UserWrite", RTV.num(info.UserWrite ? 1 : 0)],
40203
+ ["UserExecute", RTV.num(info.UserExecute ? 1 : 0)]
40204
+ ])
40205
+ ) : RTV.char(`No such file or directory: ${p2}`);
40206
+ if (nargout <= 1) return RTV.num(info ? 1 : 0);
40207
+ return [RTV.num(info ? 1 : 0), values, RTV.char("")];
40208
+ });
39946
40209
  registerSpecial("unzip", (nargout, args) => {
39947
40210
  const io = requireFileIO();
39948
40211
  if (!io.unzip)
@@ -40268,6 +40531,7 @@ function registerSpecialBuiltins(rt) {
40268
40531
  }
40269
40532
  return RTV.char(sys?.getEnv(toString(args[0])) ?? "");
40270
40533
  });
40534
+ registerSpecial("maxNumCompThreads", () => RTV.num(1));
40271
40535
  registerSpecialVoid("setenv", (args) => {
40272
40536
  const sys = rt.system;
40273
40537
  if (args.length === 2) {
@@ -40649,6 +40913,10 @@ function _eigsImpl(rt, nargout, args) {
40649
40913
  const margs = args.map((a) => ensureRuntimeValue(a));
40650
40914
  if (margs.length < 1)
40651
40915
  throw new RuntimeError("eigs requires at least 1 argument");
40916
+ for (let i = 0; i < margs.length; i++) {
40917
+ const mi = margs[i];
40918
+ if (isRuntimeSparseMatrix(mi)) margs[i] = sparseToDense(mi);
40919
+ }
40652
40920
  let afun = null;
40653
40921
  let A = null;
40654
40922
  let n;
@@ -41566,12 +41834,17 @@ function getMember(rt, base, name) {
41566
41834
  `No property or method '${name}' for class '${mv.className}'`
41567
41835
  );
41568
41836
  }
41837
+ if (isRuntimeClassInstanceArray(mv)) {
41838
+ const values = mv.elements.map(
41839
+ (el) => ensureRuntimeValue(getMember(rt, el, name))
41840
+ );
41841
+ return horzcat(...values);
41842
+ }
41569
41843
  return getRTValueField(mv, name);
41570
41844
  }
41571
- function getMemberDynamic(base, nameExpr) {
41572
- const mv = ensureRuntimeValue(base);
41845
+ function getMemberDynamic(rt, base, nameExpr) {
41573
41846
  const name = toString(ensureRuntimeValue(nameExpr));
41574
- return getRTValueField(mv, name);
41847
+ return ensureRuntimeValue(getMember(rt, base, name));
41575
41848
  }
41576
41849
  function getMemberOrEmpty(base, name) {
41577
41850
  try {
@@ -41593,7 +41866,7 @@ function setMemberReturn(rt, base, name, rhs) {
41593
41866
  if (setter) {
41594
41867
  rt.activeAccessors.add(accessorKey);
41595
41868
  try {
41596
- const result = setter(1, base, rhs);
41869
+ const result = setter(0, base, rhs);
41597
41870
  return result !== void 0 ? result : base;
41598
41871
  } finally {
41599
41872
  rt.activeAccessors.delete(accessorKey);
@@ -41605,10 +41878,8 @@ function setMemberReturn(rt, base, name, rhs) {
41605
41878
  return setRTValueField(mv, name, rhsMv, rt);
41606
41879
  }
41607
41880
  function setMemberDynamicReturn(rt, base, nameExpr, rhs) {
41608
- const mv = ensureRuntimeValue(base);
41609
41881
  const name = toString(ensureRuntimeValue(nameExpr));
41610
- const rhsMv = ensureRuntimeValue(rhs);
41611
- return setRTValueField(mv, name, rhsMv, rt);
41882
+ return ensureRuntimeValue(setMemberReturn(rt, base, name, rhs));
41612
41883
  }
41613
41884
  function subsrefCall(rt, base, names) {
41614
41885
  const mv = ensureRuntimeValue(base);
@@ -44257,7 +44528,7 @@ var Runtime = class _Runtime {
44257
44528
  return getMember(this, base, name);
44258
44529
  }
44259
44530
  getMemberDynamic(base, nameExpr) {
44260
- return getMemberDynamic(base, nameExpr);
44531
+ return getMemberDynamic(this, base, nameExpr);
44261
44532
  }
44262
44533
  getMemberOrEmpty(base, name) {
44263
44534
  return getMemberOrEmpty(base, name);
@@ -45564,6 +45835,8 @@ defineBuiltin({
45564
45835
  if (isRuntimeComplexNumber(v)) return v.im === 0;
45565
45836
  if (isRuntimeTensor(v)) return imagAllZero(v.imag);
45566
45837
  if (isRuntimeSparseMatrix(v)) return !v.pi || imagAllZero(v.pi);
45838
+ if (isRuntimeCell(v) || isRuntimeStruct(v) || isRuntimeStructArray(v) || isRuntimeString(v) || isRuntimeFunction(v))
45839
+ return false;
45567
45840
  return true;
45568
45841
  }
45569
45842
  }
@@ -46035,10 +46308,79 @@ defineBuiltin({
46035
46308
  name: "ischar",
46036
46309
  cases: [anyToLogicalCase((args) => isRuntimeChar(args[0]))]
46037
46310
  });
46311
+ defineBuiltin({
46312
+ name: "isstr",
46313
+ cases: [anyToLogicalCase((args) => isRuntimeChar(args[0]))]
46314
+ });
46038
46315
  defineBuiltin({
46039
46316
  name: "isstring",
46040
46317
  cases: [anyToLogicalCase((args) => isRuntimeString(args[0]))]
46041
46318
  });
46319
+ var MATLAB_KEYWORDS = /* @__PURE__ */ new Set([
46320
+ "break",
46321
+ "case",
46322
+ "catch",
46323
+ "classdef",
46324
+ "continue",
46325
+ "else",
46326
+ "elseif",
46327
+ "end",
46328
+ "for",
46329
+ "function",
46330
+ "global",
46331
+ "if",
46332
+ "otherwise",
46333
+ "parfor",
46334
+ "persistent",
46335
+ "return",
46336
+ "spmd",
46337
+ "switch",
46338
+ "try",
46339
+ "while"
46340
+ ]);
46341
+ function singleRowText(v) {
46342
+ if (isRuntimeChar(v)) {
46343
+ if (v.shape && v.shape.length >= 1 && v.shape[0] > 1) return null;
46344
+ return v.value;
46345
+ }
46346
+ if (isRuntimeString(v)) return v;
46347
+ return null;
46348
+ }
46349
+ defineBuiltin({
46350
+ name: "isvarname",
46351
+ cases: [
46352
+ anyToLogicalCase((args) => {
46353
+ const s = singleRowText(args[0]);
46354
+ if (s === null || s.length === 0 || s.length > 63) return false;
46355
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(s)) return false;
46356
+ return !MATLAB_KEYWORDS.has(s);
46357
+ })
46358
+ ]
46359
+ });
46360
+ defineBuiltin({
46361
+ name: "iskeyword",
46362
+ cases: [
46363
+ {
46364
+ // iskeyword() with no args returns the list of keywords (column cell).
46365
+ match: (argTypes) => {
46366
+ if (argTypes.length === 0) return [{ kind: "cell" }];
46367
+ if (argTypes.length === 1) return [{ kind: "boolean" }];
46368
+ return null;
46369
+ },
46370
+ apply: (args) => {
46371
+ if (args.length === 0) {
46372
+ const names = [...MATLAB_KEYWORDS].sort();
46373
+ return RTV.cell(
46374
+ names.map((n) => RTV.char(n)),
46375
+ [names.length, 1]
46376
+ );
46377
+ }
46378
+ const s = singleRowText(args[0]);
46379
+ return RTV.logical(s !== null && MATLAB_KEYWORDS.has(s));
46380
+ }
46381
+ }
46382
+ ]
46383
+ });
46042
46384
  defineBuiltin({
46043
46385
  name: "iscell",
46044
46386
  cases: [anyToLogicalCase((args) => isRuntimeCell(args[0]))]
@@ -46055,6 +46397,82 @@ defineBuiltin({
46055
46397
  name: "issparse",
46056
46398
  cases: [anyToLogicalCase((args) => isRuntimeSparseMatrix(args[0]))]
46057
46399
  });
46400
+ defineBuiltin({
46401
+ name: "isobject",
46402
+ cases: [
46403
+ anyToLogicalCase(
46404
+ (args) => isRuntimeClassInstance(args[0]) || isRuntimeClassInstanceArray(args[0])
46405
+ )
46406
+ ]
46407
+ });
46408
+ defineBuiltin({
46409
+ name: "isprop",
46410
+ help: {
46411
+ signatures: ["tf = isprop(obj, PropertyName)"],
46412
+ description: "Return logical 1 where PropertyName is a property of object obj, else 0. The result has the same size as obj. Only class objects have properties: structs (even with that field), numeric, char and other built-in types always return false. Methods are not properties."
46413
+ },
46414
+ cases: [
46415
+ {
46416
+ match: (argTypes) => {
46417
+ if (argTypes.length !== 2) return null;
46418
+ return [{ kind: "boolean" }];
46419
+ },
46420
+ apply: (args) => {
46421
+ const v = args[0];
46422
+ const nameArg = args[1];
46423
+ let propName = null;
46424
+ if (isRuntimeChar(nameArg)) propName = nameArg.value;
46425
+ else if (isRuntimeString(nameArg)) propName = nameArg;
46426
+ let answer = false;
46427
+ if (propName !== null) {
46428
+ if (isRuntimeClassInstance(v)) answer = v.fields.has(propName);
46429
+ else if (isRuntimeClassInstanceArray(v))
46430
+ answer = v.elements.length > 0 && v.elements[0].fields.has(propName);
46431
+ }
46432
+ const shape = getShape(v);
46433
+ const n = shape.reduce((a, b) => a * b, 1);
46434
+ if (n === 1) return RTV.logical(answer);
46435
+ const data = allocFloat64Array(n);
46436
+ if (answer) data.fill(1);
46437
+ return new RuntimeTensor(data, shape, void 0, true);
46438
+ }
46439
+ }
46440
+ ]
46441
+ });
46442
+ defineBuiltin({
46443
+ name: "addprop",
46444
+ help: {
46445
+ signatures: ["p = addprop(obj, PropertyName)"],
46446
+ description: "Add a dynamic property named PropertyName to the dynamicprops (handle) object obj. The property can then be read and assigned via obj.PropertyName. Returns a meta.DynamicProperty describing the new property."
46447
+ },
46448
+ cases: [
46449
+ {
46450
+ match: (argTypes) => {
46451
+ if (argTypes.length !== 2) return null;
46452
+ return [{ kind: "unknown" }];
46453
+ },
46454
+ apply: (args) => {
46455
+ const obj = args[0];
46456
+ if (!isRuntimeClassInstance(obj))
46457
+ throw new RuntimeError(
46458
+ "addprop: first argument must be a dynamicprops object"
46459
+ );
46460
+ const name = toString(args[1]);
46461
+ if (!obj.fields.has(name)) {
46462
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
46463
+ incref(empty);
46464
+ obj.fields.set(name, empty);
46465
+ }
46466
+ return RTV.classInstance(
46467
+ "meta.DynamicProperty",
46468
+ ["Name"],
46469
+ true,
46470
+ /* @__PURE__ */ new Map([["Name", RTV.char(name)]])
46471
+ );
46472
+ }
46473
+ }
46474
+ ]
46475
+ });
46058
46476
  defineBuiltin({
46059
46477
  name: "isscalar",
46060
46478
  cases: [
@@ -46252,6 +46670,30 @@ function mkChar(value) {
46252
46670
  defineBuiltin({
46253
46671
  name: "class",
46254
46672
  cases: [
46673
+ // Old-style (pre-classdef) constructor form: class(structData, 'ClassName')
46674
+ // builds a value-type instance whose fields are the struct's fields. The
46675
+ // optional class(s,'Name',parent,...) inheritance form is not supported
46676
+ // (returns null → "unsupported argument types").
46677
+ {
46678
+ match: (argTypes) => {
46679
+ if (argTypes.length !== 2) return null;
46680
+ if (argTypes[0].kind !== "struct") return null;
46681
+ const k = argTypes[1].kind;
46682
+ if (k !== "char" && k !== "string") return null;
46683
+ return [{ kind: "unknown" }];
46684
+ },
46685
+ apply: (args) => {
46686
+ const s = args[0];
46687
+ if (!isRuntimeStruct(s))
46688
+ throw new RuntimeError(
46689
+ "class: first argument must be a scalar struct"
46690
+ );
46691
+ const nameVal = args[1];
46692
+ const className = isRuntimeChar(nameVal) ? nameVal.value : isRuntimeString(nameVal) ? nameVal : String(nameVal);
46693
+ const fieldNames = [...s.fields.keys()];
46694
+ return RTV.classInstance(className, fieldNames, false, s.fields);
46695
+ }
46696
+ },
46255
46697
  {
46256
46698
  match: (argTypes) => {
46257
46699
  if (argTypes.length !== 1) return null;
@@ -46289,6 +46731,26 @@ defineBuiltin({
46289
46731
  }
46290
46732
  ]
46291
46733
  });
46734
+ for (const name of ["superiorto", "inferiorto"]) {
46735
+ defineBuiltin({
46736
+ name,
46737
+ help: {
46738
+ signatures: [`${name}('Class1', 'Class2', ...)`],
46739
+ description: name === "superiorto" ? "Establish superior class relationship (old-style class precedence). Accepted as a no-op." : "Establish inferior class relationship (old-style class precedence). Accepted as a no-op."
46740
+ },
46741
+ cases: [
46742
+ {
46743
+ match: (argTypes) => {
46744
+ for (const t of argTypes) {
46745
+ if (t.kind !== "char" && t.kind !== "string") return null;
46746
+ }
46747
+ return [];
46748
+ },
46749
+ apply: () => void 0
46750
+ }
46751
+ ]
46752
+ });
46753
+ }
46292
46754
  function fieldnamesApply(args) {
46293
46755
  if (args.length !== 1)
46294
46756
  throw new RuntimeError("fieldnames requires 1 argument");
@@ -46856,7 +47318,6 @@ function anyAllApply(name, mode) {
46856
47318
  if (isRuntimeComplexNumber(v)) return RTV.logical(v.re !== 0 || v.im !== 0);
46857
47319
  if (isRuntimeTensor(v)) {
46858
47320
  if (args.length === 1) {
46859
- if (v.data.length === 0) return RTV.logical(mode === "all");
46860
47321
  const d = firstReduceDim(v.shape);
46861
47322
  if (d === 0) return RTV.logical(scanLogical(v.data, v.imag, mode));
46862
47323
  return logicalAlongDim(v, d, mode);
@@ -47126,7 +47587,12 @@ function applyTextFn(v, fn) {
47126
47587
  }
47127
47588
  return RTV.cell(out, [...v.shape]);
47128
47589
  }
47129
- if (isRuntimeChar(v)) return RTV.char(fn(v.value));
47590
+ if (isRuntimeChar(v)) {
47591
+ if (v.shape && (v.shape[0] ?? 1) > 1) {
47592
+ return charRowsToMatrix(valueToCharRows(v).map(fn));
47593
+ }
47594
+ return RTV.char(fn(v.value));
47595
+ }
47130
47596
  if (isRuntimeString(v)) return RTV.string(fn(toString(v)));
47131
47597
  return v;
47132
47598
  }
@@ -47278,7 +47744,7 @@ registerIBuiltin({
47278
47744
  const str = toString(args[0]);
47279
47745
  const pat = toString(args[1]);
47280
47746
  const rep = toString(args[2]);
47281
- let flags = "g";
47747
+ let flags = "gs";
47282
47748
  for (let i = 3; i < args.length; i++) {
47283
47749
  const opt = toString(args[i]).toLowerCase();
47284
47750
  if (opt === "ignorecase") flags += "i";
@@ -47642,6 +48108,38 @@ registerIBuiltin({
47642
48108
  };
47643
48109
  }
47644
48110
  });
48111
+ registerIBuiltin({
48112
+ name: "isspace",
48113
+ help: {
48114
+ signatures: ["TF = isspace(str)"],
48115
+ description: "Return a logical array the same size as str, true where the character is whitespace. Numeric input is treated as Unicode code points."
48116
+ },
48117
+ resolve: (argTypes) => {
48118
+ if (argTypes.length !== 1) return null;
48119
+ const k = argTypes[0].kind;
48120
+ if (k !== "char" && k !== "string" && k !== "number" && k !== "boolean" && k !== "tensor") {
48121
+ return null;
48122
+ }
48123
+ const pred = (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
48124
+ return {
48125
+ outputTypes: [{ kind: "tensor", isComplex: false, isLogical: true }],
48126
+ apply: (args) => {
48127
+ const v = args[0];
48128
+ if (isRuntimeChar(v)) {
48129
+ return logicalRowFromString(
48130
+ v.value,
48131
+ pred,
48132
+ v.shape ? [...v.shape] : void 0
48133
+ );
48134
+ }
48135
+ if (isRuntimeString(v)) return logicalRowFromString(toString(v), pred);
48136
+ if (isRuntimeTensor(v))
48137
+ return logicalFromNumericTensor(v.data, v.shape, pred);
48138
+ return logicalFromNumericTensor([toNumber(v)], [1, 1], pred);
48139
+ }
48140
+ };
48141
+ }
48142
+ });
47645
48143
  registerIBuiltin({
47646
48144
  name: "contains",
47647
48145
  resolve: (argTypes) => {
@@ -48089,12 +48587,102 @@ registerIBuiltin({
48089
48587
  };
48090
48588
  }
48091
48589
  });
48590
+ function collectRows(v) {
48591
+ if (isRuntimeCell(v)) {
48592
+ const rows = [];
48593
+ for (const el of v.data) rows.push(...valueToCharRows(el));
48594
+ return rows;
48595
+ }
48596
+ return valueToCharRows(v);
48597
+ }
48598
+ registerIBuiltin({
48599
+ name: "strmatch",
48600
+ help: {
48601
+ signatures: [
48602
+ "x = strmatch(str, strarray)",
48603
+ "x = strmatch(str, strarray, 'exact')"
48604
+ ],
48605
+ description: "(Legacy) Find rows of STRARRAY that begin with STR, or that equal STR when 'exact' is given. Returns a column vector of matching row indices."
48606
+ },
48607
+ resolve: (argTypes) => {
48608
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
48609
+ if (!isTextType(argTypes[0])) return null;
48610
+ const sa = argTypes[1];
48611
+ if (sa.kind !== "char" && sa.kind !== "string" && sa.kind !== "cell")
48612
+ return null;
48613
+ return {
48614
+ outputTypes: [{ kind: "tensor", isComplex: false }],
48615
+ apply: (args) => {
48616
+ const str = toString(args[0]);
48617
+ let rows = collectRows(args[1]);
48618
+ const n = rows.length === 0 ? 0 : Math.max(...rows.map((r) => r.length));
48619
+ rows = rows.map((r) => r.padEnd(n, " "));
48620
+ const exactMatch = args.length === 3;
48621
+ let s = str;
48622
+ let len = s.length;
48623
+ if (len > n) {
48624
+ return RTV.tensor(allocFloat64Array(0), [0, 1]);
48625
+ }
48626
+ if (exactMatch && len < n) {
48627
+ const useNull = rows.some((r) => r.charCodeAt(n - 1) === 0);
48628
+ s = s.padEnd(n, useNull ? "\0" : " ");
48629
+ len = n;
48630
+ }
48631
+ const matches2 = [];
48632
+ for (let r = 0; r < rows.length; r++) {
48633
+ let ok = true;
48634
+ for (let i = 0; i < len; i++) {
48635
+ if (rows[r][i] !== s[i]) {
48636
+ ok = false;
48637
+ break;
48638
+ }
48639
+ }
48640
+ if (ok) matches2.push(r + 1);
48641
+ }
48642
+ return RTV.tensor(allocFloat64Array(matches2), [matches2.length, 1]);
48643
+ }
48644
+ };
48645
+ }
48646
+ });
48647
+ function valueToCharRows(v) {
48648
+ if (isRuntimeChar(v)) {
48649
+ const cols = v.shape ? v.shape[1] ?? v.value.length : v.value.length;
48650
+ const numRows = v.shape ? v.shape[0] ?? 1 : 1;
48651
+ if (numRows <= 1) return [v.value];
48652
+ const out = [];
48653
+ for (let r = 0; r < numRows; r++)
48654
+ out.push(v.value.slice(r * cols, (r + 1) * cols));
48655
+ return out;
48656
+ }
48657
+ if (isRuntimeString(v)) return [v];
48658
+ if (isRuntimeNumber(v)) return [String.fromCharCode(Math.round(v))];
48659
+ if (isRuntimeTensor(v)) {
48660
+ const rows = v.shape.length >= 2 ? v.shape[0] ?? 1 : 1;
48661
+ const cols = v.shape.length >= 2 ? v.shape[1] ?? 0 : v.data.length;
48662
+ const out = [];
48663
+ for (let r = 0; r < rows; r++) {
48664
+ let s = "";
48665
+ for (let c = 0; c < cols; c++)
48666
+ s += String.fromCharCode(Math.round(v.data[c * rows + r]));
48667
+ out.push(s);
48668
+ }
48669
+ return out;
48670
+ }
48671
+ throw new RuntimeError("char: unsupported cell element type");
48672
+ }
48673
+ function charRowsToMatrix(rows) {
48674
+ if (rows.length === 0) return RTV.char("");
48675
+ const width = Math.max(...rows.map((r) => r.length));
48676
+ const padded = rows.map((r) => r.padEnd(width, " "));
48677
+ if (rows.length === 1) return RTV.char(padded[0]);
48678
+ return new RuntimeChar(padded.join(""), [rows.length, width]);
48679
+ }
48092
48680
  registerIBuiltin({
48093
48681
  name: "char",
48094
48682
  resolve: (argTypes) => {
48095
48683
  if (argTypes.length !== 1) return null;
48096
48684
  const a = argTypes[0];
48097
- if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor")
48685
+ if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor" && a.kind !== "cell")
48098
48686
  return null;
48099
48687
  return {
48100
48688
  outputTypes: [{ kind: "char" }],
@@ -48111,6 +48699,11 @@ registerIBuiltin({
48111
48699
  }
48112
48700
  return RTV.char(chars.join(""));
48113
48701
  }
48702
+ if (isRuntimeCell(v)) {
48703
+ const rows = [];
48704
+ for (const el of v.data) rows.push(...valueToCharRows(el));
48705
+ return charRowsToMatrix(rows);
48706
+ }
48114
48707
  throw new RuntimeError("char: unsupported arguments");
48115
48708
  }
48116
48709
  };
@@ -48178,6 +48771,8 @@ registerIBuiltin({
48178
48771
  if (!isTextType(argTypes[0]) || !isTextType(argTypes[1])) return null;
48179
48772
  const outputTypes = [{ kind: "number" }];
48180
48773
  if (nargout >= 2) outputTypes.push({ kind: "number" });
48774
+ if (nargout >= 3) outputTypes.push({ kind: "char" });
48775
+ if (nargout >= 4) outputTypes.push({ kind: "number" });
48181
48776
  return {
48182
48777
  outputTypes,
48183
48778
  apply: (args, nout) => {
@@ -48187,6 +48782,7 @@ registerIBuiltin({
48187
48782
  const results = [];
48188
48783
  let strPos = 0;
48189
48784
  let fmtPos = 0;
48785
+ let matchFailure = false;
48190
48786
  while (fmtPos < fmt.length && strPos < str.length && results.length < maxCount) {
48191
48787
  if (fmt[fmtPos] === "%") {
48192
48788
  fmtPos++;
@@ -48198,22 +48794,34 @@ registerIBuiltin({
48198
48794
  }
48199
48795
  if (spec === "d" || spec === "i") {
48200
48796
  const m = str.slice(strPos).match(/^[+-]?\d+/);
48201
- if (!m) break;
48797
+ if (!m) {
48798
+ matchFailure = true;
48799
+ break;
48800
+ }
48202
48801
  results.push(parseInt(m[0], 10));
48203
48802
  strPos += m[0].length;
48204
48803
  } else if (spec === "f" || spec === "e" || spec === "g") {
48205
48804
  const m = str.slice(strPos).match(/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/);
48206
- if (!m) break;
48805
+ if (!m) {
48806
+ matchFailure = true;
48807
+ break;
48808
+ }
48207
48809
  results.push(parseFloat(m[0]));
48208
48810
  strPos += m[0].length;
48209
48811
  } else if (spec === "x") {
48210
48812
  const m = str.slice(strPos).match(/^[+-]?[0-9a-fA-F]+/);
48211
- if (!m) break;
48813
+ if (!m) {
48814
+ matchFailure = true;
48815
+ break;
48816
+ }
48212
48817
  results.push(parseInt(m[0], 16));
48213
48818
  strPos += m[0].length;
48214
48819
  } else if (spec === "o") {
48215
48820
  const m = str.slice(strPos).match(/^[+-]?[0-7]+/);
48216
- if (!m) break;
48821
+ if (!m) {
48822
+ matchFailure = true;
48823
+ break;
48824
+ }
48217
48825
  results.push(parseInt(m[0], 8));
48218
48826
  strPos += m[0].length;
48219
48827
  } else if (spec === "c") {
@@ -48221,7 +48829,10 @@ registerIBuiltin({
48221
48829
  strPos++;
48222
48830
  } else if (spec === "s") {
48223
48831
  const m = str.slice(strPos).match(/^\S+/);
48224
- if (!m) break;
48832
+ if (!m) {
48833
+ matchFailure = true;
48834
+ break;
48835
+ }
48225
48836
  for (let ci = 0; ci < m[0].length && results.length < maxCount; ci++) {
48226
48837
  results.push(m[0].charCodeAt(ci));
48227
48838
  }
@@ -48231,7 +48842,10 @@ registerIBuiltin({
48231
48842
  fmtPos++;
48232
48843
  while (strPos < str.length && /\s/.test(str[strPos])) strPos++;
48233
48844
  } else {
48234
- if (str[strPos] !== fmt[fmtPos]) break;
48845
+ if (str[strPos] !== fmt[fmtPos]) {
48846
+ matchFailure = true;
48847
+ break;
48848
+ }
48235
48849
  strPos++;
48236
48850
  fmtPos++;
48237
48851
  }
@@ -48241,7 +48855,16 @@ registerIBuiltin({
48241
48855
  }
48242
48856
  const vals = results.length === 1 ? RTV.num(results[0]) : RTV.tensor(allocFloat64Array(results), [results.length, 1]);
48243
48857
  if (nout >= 2) {
48244
- return [vals, RTV.num(results.length)];
48858
+ const out = [vals, RTV.num(results.length)];
48859
+ if (nout >= 3) {
48860
+ out.push(
48861
+ RTV.char(matchFailure ? "Matching failure in format." : "")
48862
+ );
48863
+ }
48864
+ if (nout >= 4) {
48865
+ out.push(RTV.num(strPos + 1));
48866
+ }
48867
+ return out;
48245
48868
  }
48246
48869
  return vals;
48247
48870
  }
@@ -49652,8 +50275,34 @@ function normImplTensor(v, args) {
49652
50275
  return RTV.num(maxRowSum);
49653
50276
  }
49654
50277
  if (p2 === 2) {
50278
+ let anyNaN = false;
50279
+ let anyInf = false;
50280
+ for (let i = 0; i < v.data.length; i++) {
50281
+ const re = v.data[i];
50282
+ const im = imag2 ? imag2[i] : 0;
50283
+ if (Number.isNaN(re) || Number.isNaN(im)) anyNaN = true;
50284
+ else if (!isFinite(re) || !isFinite(im)) anyInf = true;
50285
+ }
50286
+ if (anyNaN) return RTV.num(NaN);
50287
+ if (anyInf) return RTV.num(Infinity);
49655
50288
  const bridge = getEffectiveBridge("norm", "svd");
49656
50289
  if (bridge && bridge.svd) {
50290
+ if (imag2) {
50291
+ const R = allocFloat64Array(4 * rows * cols);
50292
+ const tm = 2 * rows;
50293
+ for (let j = 0; j < cols; j++) {
50294
+ for (let i = 0; i < rows; i++) {
50295
+ const a = v.data[j * rows + i];
50296
+ const b = imag2[j * rows + i];
50297
+ R[j * tm + i] = a;
50298
+ R[j * tm + (rows + i)] = b;
50299
+ R[(cols + j) * tm + i] = -b;
50300
+ R[(cols + j) * tm + (rows + i)] = a;
50301
+ }
50302
+ }
50303
+ const result2 = bridge.svd(R, tm, 2 * cols, false, false);
50304
+ return RTV.num(result2.S[0]);
50305
+ }
49657
50306
  const f64 = v.data instanceof Float64Array ? v.data : allocFloat64Array(v.data);
49658
50307
  const result = bridge.svd(f64, rows, cols, false, false);
49659
50308
  return RTV.num(result.S[0]);
@@ -50413,6 +51062,100 @@ function svdApply(args, nargout) {
50413
51062
  [k, 1]
50414
51063
  );
50415
51064
  }
51065
+ function nullApply(args) {
51066
+ let A = args[0];
51067
+ if (isRuntimeNumber(A) || isRuntimeComplexNumber(A)) {
51068
+ A = isRuntimeNumber(A) ? RTV.tensor(allocFloat64Array([A]), [1, 1]) : RTV.tensor(
51069
+ allocFloat64Array([A.re]),
51070
+ [1, 1],
51071
+ allocFloat64Array([A.im])
51072
+ );
51073
+ }
51074
+ if (!isRuntimeTensor(A))
51075
+ throw new RuntimeError("null: argument must be a numeric matrix");
51076
+ const [m, n] = tensorSize2D(A);
51077
+ const [, Sdiag, V] = svdApply([A], 3);
51078
+ const k = Math.min(m, n);
51079
+ const s = [];
51080
+ for (let i = 0; i < k; i++) s.push(Sdiag.data[colMajorIndex(i, i, m)]);
51081
+ const maxS = s.length > 0 ? Math.max(...s) : 0;
51082
+ const tol = args.length > 1 && args[1] !== void 0 ? toNumber(args[1]) : Math.max(m, n) * epsOf(maxS);
51083
+ let r = 0;
51084
+ for (const sv of s) if (sv > tol) r++;
51085
+ const cols = n - r;
51086
+ const out = allocFloat64Array(n * cols);
51087
+ const outImag = V.imag ? allocFloat64Array(n * cols) : void 0;
51088
+ for (let c = 0; c < cols; c++) {
51089
+ for (let i = 0; i < n; i++) {
51090
+ const src = colMajorIndex(i, r + c, n);
51091
+ out[colMajorIndex(i, c, n)] = V.data[src];
51092
+ if (outImag) outImag[colMajorIndex(i, c, n)] = V.imag[src];
51093
+ }
51094
+ }
51095
+ return RTV.tensor(out, [n, cols], outImag);
51096
+ }
51097
+ registerIBuiltin({
51098
+ name: "null",
51099
+ resolve: (argTypes, nargout) => {
51100
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2) return null;
51101
+ if (!isNumericJitType(argTypes[0])) return null;
51102
+ const isComplex = argTypes[0].kind === "tensor" && argTypes[0].isComplex;
51103
+ return {
51104
+ outputTypes: [tensorType(isComplex || void 0)],
51105
+ apply: (args) => nullApply(args)
51106
+ };
51107
+ }
51108
+ });
51109
+ function computeBandwidth(A) {
51110
+ let lower = 0;
51111
+ let upper = 0;
51112
+ const note = (i, j) => {
51113
+ if (i > j) lower = Math.max(lower, i - j);
51114
+ else if (j > i) upper = Math.max(upper, j - i);
51115
+ };
51116
+ if (isRuntimeNumber(A) || isRuntimeComplexNumber(A)) return [0, 0];
51117
+ if (isRuntimeSparseMatrix(A)) {
51118
+ for (let j = 0; j < A.n; j++) {
51119
+ for (let k = A.jc[j]; k < A.jc[j + 1]; k++) {
51120
+ if (A.pr[k] === 0 && (!A.pi || A.pi[k] === 0)) continue;
51121
+ note(A.ir[k], j);
51122
+ }
51123
+ }
51124
+ return [lower, upper];
51125
+ }
51126
+ if (isRuntimeTensor(A)) {
51127
+ const [m, n] = tensorSize2D(A);
51128
+ for (let j = 0; j < n; j++) {
51129
+ for (let i = 0; i < m; i++) {
51130
+ const idx = colMajorIndex(i, j, m);
51131
+ if (A.data[idx] === 0 && (!A.imag || A.imag[idx] === 0)) continue;
51132
+ note(i, j);
51133
+ }
51134
+ }
51135
+ return [lower, upper];
51136
+ }
51137
+ throw new RuntimeError("bandwidth: first argument must be a numeric matrix");
51138
+ }
51139
+ function bandwidthApply(args, nargout) {
51140
+ const [lower, upper] = computeBandwidth(args[0]);
51141
+ if (args.length >= 2 && args[1] !== void 0) {
51142
+ const type = parseStringArgLower(args[1]);
51143
+ if (type === "lower") return RTV.num(lower);
51144
+ if (type === "upper") return RTV.num(upper);
51145
+ throw new RuntimeError("bandwidth: TYPE must be 'lower' or 'upper'");
51146
+ }
51147
+ if (nargout >= 2) return [RTV.num(lower), RTV.num(upper)];
51148
+ return RTV.num(lower);
51149
+ }
51150
+ registerIBuiltin({
51151
+ name: "bandwidth",
51152
+ resolve: (argTypes, nargout) => {
51153
+ if (nargout > 2 || argTypes.length < 1 || argTypes.length > 2) return null;
51154
+ if (!isNumericJitType(argTypes[0])) return null;
51155
+ const outs = nargout >= 2 ? [NUM, NUM] : [NUM];
51156
+ return { outputTypes: outs, apply: (args, n) => bandwidthApply(args, n) };
51157
+ }
51158
+ });
50416
51159
  function computeATA(A_data, m, n) {
50417
51160
  const result = allocFloat64Array(n * n);
50418
51161
  for (let j = 0; j < n; j++)
@@ -52283,6 +53026,33 @@ function validateSizeArg(x) {
52283
53026
  }
52284
53027
  return x < 0 ? 0 : x;
52285
53028
  }
53029
+ function parseReshapeDims(args, total) {
53030
+ let rawDims;
53031
+ if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
53032
+ rawDims = Array.from(args[1].data).map((x) => validateSizeArg(x));
53033
+ } else {
53034
+ rawDims = args.slice(1).map((a) => {
53035
+ if (isRuntimeTensor(a) && a.data.length === 0) return null;
53036
+ return validateSizeArg(toNumber(a));
53037
+ });
53038
+ }
53039
+ const autoCount = rawDims.filter((d) => d === null).length;
53040
+ if (autoCount > 1)
53041
+ throw new RuntimeError("reshape: only one dimension size can be []");
53042
+ let shape;
53043
+ if (autoCount === 1) {
53044
+ const known = rawDims.filter((d) => d !== null);
53045
+ const knownProduct = known.reduce((a, b) => a * b, 1);
53046
+ if (knownProduct === 0 || total % knownProduct !== 0)
53047
+ throw new RuntimeError("reshape: number of elements must not change");
53048
+ shape = rawDims.map((d) => d === null ? total / knownProduct : d);
53049
+ } else {
53050
+ shape = rawDims;
53051
+ }
53052
+ if (numel(shape) !== total)
53053
+ throw new RuntimeError("reshape: number of elements must not change");
53054
+ return shape;
53055
+ }
52286
53056
  defineBuiltin({
52287
53057
  name: "reshape",
52288
53058
  cases: [
@@ -52294,31 +53064,31 @@ defineBuiltin({
52294
53064
  const v = args[0];
52295
53065
  if (isRuntimeSparseMatrix(v)) {
52296
53066
  const totalEl = v.m * v.n;
52297
- let rawDims2;
53067
+ let rawDims;
52298
53068
  if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
52299
- rawDims2 = Array.from(args[1].data).map((x) => Math.round(x));
53069
+ rawDims = Array.from(args[1].data).map((x) => Math.round(x));
52300
53070
  } else {
52301
- rawDims2 = args.slice(1).map((a) => {
53071
+ rawDims = args.slice(1).map((a) => {
52302
53072
  if (isRuntimeTensor(a) && a.data.length === 0) return null;
52303
53073
  return Math.round(toNumber(a));
52304
53074
  });
52305
53075
  }
52306
- const autoCount2 = rawDims2.filter((d) => d === null).length;
52307
- if (autoCount2 > 1)
53076
+ const autoCount = rawDims.filter((d) => d === null).length;
53077
+ if (autoCount > 1)
52308
53078
  throw new RuntimeError(
52309
53079
  "reshape: only one dimension size can be []"
52310
53080
  );
52311
53081
  let shape2;
52312
- if (autoCount2 === 1) {
52313
- const known = rawDims2.filter((d) => d !== null);
53082
+ if (autoCount === 1) {
53083
+ const known = rawDims.filter((d) => d !== null);
52314
53084
  const knownProduct = known.reduce((a, b) => a * b, 1);
52315
53085
  if (totalEl % knownProduct !== 0)
52316
53086
  throw new RuntimeError(
52317
53087
  "reshape: number of elements must not change"
52318
53088
  );
52319
- shape2 = rawDims2.map((d) => d === null ? totalEl / knownProduct : d);
53089
+ shape2 = rawDims.map((d) => d === null ? totalEl / knownProduct : d);
52320
53090
  } else {
52321
- shape2 = rawDims2;
53091
+ shape2 = rawDims;
52322
53092
  }
52323
53093
  if (shape2.length !== 2)
52324
53094
  throw new RuntimeError("reshape: sparse matrices must be 2-D");
@@ -52356,40 +53126,32 @@ defineBuiltin({
52356
53126
  jc[newN] = ti;
52357
53127
  return RTV.sparseMatrix(newM, newN, ir, jc, pr, pi2);
52358
53128
  }
53129
+ if (isRuntimeChar(v)) {
53130
+ const srcRows = v.shape ? v.shape[0] : 1;
53131
+ const srcCols = v.shape ? v.shape[1] ?? 0 : v.value.length;
53132
+ const total = v.value.length;
53133
+ const reqShape = parseReshapeDims(args, total);
53134
+ const s = [...reqShape];
53135
+ while (s.length > 2 && s[s.length - 1] === 1) s.pop();
53136
+ if (s.length > 2)
53137
+ throw new RuntimeError("reshape: char arrays must be 2-D");
53138
+ const nr = s[0];
53139
+ const nc = s.length >= 2 ? s[1] : 1;
53140
+ const linear = new Array(total);
53141
+ for (let j = 0; j < srcCols; j++)
53142
+ for (let i = 0; i < srcRows; i++)
53143
+ linear[j * srcRows + i] = v.value[i * srcCols + j];
53144
+ const chars = new Array(total);
53145
+ for (let j2 = 0; j2 < nc; j2++)
53146
+ for (let i2 = 0; i2 < nr; i2++)
53147
+ chars[i2 * nc + j2] = linear[j2 * nr + i2];
53148
+ return new RuntimeChar(chars.join(""), [nr, nc]);
53149
+ }
52359
53150
  if (!isRuntimeTensor(v) && !isRuntimeNumber(v) && !isRuntimeComplexNumber(v))
52360
53151
  throw new RuntimeError("reshape: first argument must be numeric");
52361
53152
  const data = isRuntimeTensor(v) ? v.data : isRuntimeComplexNumber(v) ? allocFloat64Array([v.re]) : allocFloat64Array([v]);
52362
53153
  const imag2 = isRuntimeTensor(v) ? v.imag : isRuntimeComplexNumber(v) ? allocFloat64Array([v.im]) : void 0;
52363
- let rawDims;
52364
- if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
52365
- rawDims = Array.from(args[1].data).map((x) => validateSizeArg(x));
52366
- } else {
52367
- rawDims = args.slice(1).map((a) => {
52368
- if (isRuntimeTensor(a) && a.data.length === 0) return null;
52369
- return validateSizeArg(toNumber(a));
52370
- });
52371
- }
52372
- const autoCount = rawDims.filter((d) => d === null).length;
52373
- if (autoCount > 1)
52374
- throw new RuntimeError("reshape: only one dimension size can be []");
52375
- let shape;
52376
- if (autoCount === 1) {
52377
- const known = rawDims.filter((d) => d !== null);
52378
- const knownProduct = known.reduce((a, b) => a * b, 1);
52379
- if (data.length % knownProduct !== 0)
52380
- throw new RuntimeError(
52381
- "reshape: number of elements must not change"
52382
- );
52383
- shape = rawDims.map(
52384
- (d) => d === null ? data.length / knownProduct : d
52385
- );
52386
- } else {
52387
- shape = rawDims;
52388
- }
52389
- const n = numel(shape);
52390
- if (n !== data.length) {
52391
- throw new RuntimeError("reshape: number of elements must not change");
52392
- }
53154
+ const shape = parseReshapeDims(args, data.length);
52393
53155
  if (isRuntimeTensor(v)) {
52394
53156
  const dataCopy = allocFloat64Array(data);
52395
53157
  const imagCopy = imag2 ? allocFloat64Array(imag2) : void 0;
@@ -52801,6 +53563,35 @@ defineBuiltin({
52801
53563
  }
52802
53564
  ]
52803
53565
  });
53566
+ function repmatObjects(v, repArgs) {
53567
+ const srcElements = isRuntimeClassInstanceArray(v) ? v.elements : [v];
53568
+ const [rows, cols] = isRuntimeClassInstanceArray(v) ? v.shape : [1, 1];
53569
+ const className = v.className;
53570
+ let reps;
53571
+ if (repArgs.length === 1) {
53572
+ const arg1 = repArgs[0];
53573
+ if (isRuntimeTensor(arg1)) {
53574
+ reps = Array.from(arg1.data).map((x) => validateSizeArg(x));
53575
+ } else {
53576
+ const n = validateSizeArg(toNumber(arg1));
53577
+ reps = [n, n];
53578
+ }
53579
+ } else {
53580
+ reps = repArgs.map((a) => validateSizeArg(toNumber(a)));
53581
+ }
53582
+ const rRep = reps[0] ?? 1;
53583
+ const cRep = reps.length >= 2 ? reps[1] : reps[0] ?? 1;
53584
+ const newRows = rows * rRep;
53585
+ const newCols = cols * cRep;
53586
+ const out = new Array(Math.max(0, newRows * newCols));
53587
+ for (let J = 0; J < newCols; J++) {
53588
+ for (let I = 0; I < newRows; I++) {
53589
+ const src = srcElements[I % rows + J % cols * rows];
53590
+ out[I + J * newRows] = copyClassInstance(src);
53591
+ }
53592
+ }
53593
+ return out.length === 1 ? out[0] : RTV.classInstanceArray(className, out, [newRows, newCols]);
53594
+ }
52804
53595
  defineBuiltin({
52805
53596
  name: "repmat",
52806
53597
  cases: [
@@ -52825,6 +53616,8 @@ defineBuiltin({
52825
53616
  if (args.length < 2)
52826
53617
  throw new RuntimeError("repmat requires at least 2 arguments");
52827
53618
  let v = args[0];
53619
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
53620
+ return repmatObjects(v, args.slice(1));
52828
53621
  if (isRuntimeSparseMatrix(v)) v = sparseToDense(v);
52829
53622
  let reps;
52830
53623
  if (args.length === 2) {
@@ -52989,6 +53782,62 @@ defineBuiltin({
52989
53782
  }
52990
53783
  ]
52991
53784
  });
53785
+ function copyClassInstance(inst) {
53786
+ if (inst.isHandleClass) return inst;
53787
+ return new RuntimeClassInstance(
53788
+ inst.className,
53789
+ new Map(inst.fields),
53790
+ inst.isHandleClass,
53791
+ inst._builtinData
53792
+ );
53793
+ }
53794
+ function repelemObjects(v, repArgs) {
53795
+ const srcElements = isRuntimeClassInstanceArray(v) ? v.elements : [v];
53796
+ const [rows, cols] = isRuntimeClassInstanceArray(v) ? v.shape : [1, 1];
53797
+ const className = v.className;
53798
+ const wrap = (elements, shape) => elements.length === 1 ? elements[0] : RTV.classInstanceArray(className, elements, shape);
53799
+ if (repArgs.length === 1) {
53800
+ const repArg = repArgs[0];
53801
+ if (isRuntimeTensor(repArg) && repArg.data.length > 1) {
53802
+ const counts = repArg.data;
53803
+ if (counts.length !== srcElements.length)
53804
+ throw new RuntimeError(
53805
+ `repelem: counts vector length (${counts.length}) must match the number of elements (${srcElements.length})`
53806
+ );
53807
+ const out3 = [];
53808
+ for (let i = 0; i < srcElements.length; i++) {
53809
+ const c = Math.max(0, Math.round(counts[i]));
53810
+ for (let j = 0; j < c; j++) out3.push(copyClassInstance(srcElements[i]));
53811
+ }
53812
+ const isCol2 = cols === 1 && rows !== 1;
53813
+ return wrap(out3, isCol2 ? [out3.length, 1] : [1, out3.length]);
53814
+ }
53815
+ const n = Math.max(0, Math.round(toNumber(repArg)));
53816
+ const out2 = [];
53817
+ for (const el of srcElements)
53818
+ for (let j = 0; j < n; j++) out2.push(copyClassInstance(el));
53819
+ const isCol = cols === 1 && rows !== 1;
53820
+ return wrap(out2, isCol ? [out2.length, 1] : [1, out2.length]);
53821
+ }
53822
+ const rRep = Math.max(0, Math.round(toNumber(repArgs[0])));
53823
+ const cRep = Math.max(0, Math.round(toNumber(repArgs[1])));
53824
+ const newRows = rows * rRep;
53825
+ const newCols = cols * cRep;
53826
+ const out = new Array(newRows * newCols);
53827
+ for (let c = 0; c < cols; c++) {
53828
+ for (let r = 0; r < rows; r++) {
53829
+ const src = srcElements[c * rows + r];
53830
+ for (let dc = 0; dc < cRep; dc++) {
53831
+ for (let dr = 0; dr < rRep; dr++) {
53832
+ const dstRow = r * rRep + dr;
53833
+ const dstCol = c * cRep + dc;
53834
+ out[dstCol * newRows + dstRow] = copyClassInstance(src);
53835
+ }
53836
+ }
53837
+ }
53838
+ }
53839
+ return wrap(out, [newRows, newCols]);
53840
+ }
52992
53841
  defineBuiltin({
52993
53842
  name: "repelem",
52994
53843
  cases: [
@@ -52998,6 +53847,8 @@ defineBuiltin({
52998
53847
  if (args.length < 2)
52999
53848
  throw new RuntimeError("repelem requires at least 2 arguments");
53000
53849
  const v = args[0];
53850
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
53851
+ return repelemObjects(v, args.slice(1));
53001
53852
  if (args.length === 2) {
53002
53853
  const repArg = args[1];
53003
53854
  if (isRuntimeTensor(repArg) && repArg.data.length > 1) {
@@ -53120,7 +53971,15 @@ defineBuiltin({
53120
53971
  }
53121
53972
  return RTV.tensor(dataCopy, newShape, imagCopy);
53122
53973
  }
53123
- throw new RuntimeError("squeeze: argument must be numeric");
53974
+ if (isRuntimeCell(v)) {
53975
+ const shape = [...v.shape];
53976
+ while (shape.length > 2 && shape[shape.length - 1] === 1) shape.pop();
53977
+ if (shape.length <= 2) return RTV.cell(v.data, shape);
53978
+ const newShape = shape.filter((d) => d !== 1);
53979
+ while (newShape.length < 2) newShape.push(1);
53980
+ return RTV.cell(v.data, newShape);
53981
+ }
53982
+ return v;
53124
53983
  }
53125
53984
  }
53126
53985
  ]
@@ -53790,6 +54649,8 @@ defineBuiltin({
53790
54649
  ]
53791
54650
  });
53792
54651
  function bitwiseOp(a, b, op, name) {
54652
+ if (typeof a === "boolean") a = a ? 1 : 0;
54653
+ if (typeof b === "boolean") b = b ? 1 : 0;
53793
54654
  if (isRuntimeNumber(a) && isRuntimeNumber(b)) {
53794
54655
  return RTV.num(op(Math.round(a), Math.round(b)));
53795
54656
  }
@@ -56057,6 +56918,56 @@ defineBuiltin({
56057
56918
  }
56058
56919
  ]
56059
56920
  });
56921
+ function froundArray(src) {
56922
+ const out = allocFloat64Array(src.length);
56923
+ for (let i = 0; i < src.length; i++) out[i] = Math.fround(src[i]);
56924
+ return out;
56925
+ }
56926
+ function singleApply(v) {
56927
+ if (isRuntimeChar(v)) {
56928
+ if (v.value.length === 0) return RTV.tensor(allocFloat64Array(0), [0, 0]);
56929
+ if (v.value.length === 1)
56930
+ return RTV.num(Math.fround(v.value.charCodeAt(0)));
56931
+ return RTV.row(Array.from(v.value).map((c) => Math.fround(c.charCodeAt(0))));
56932
+ }
56933
+ if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
56934
+ if (isRuntimeNumber(v)) return RTV.num(Math.fround(v));
56935
+ if (isRuntimeComplexNumber(v))
56936
+ return RTV.complex(Math.fround(v.re), Math.fround(v.im));
56937
+ if (isRuntimeTensor(v)) {
56938
+ const imag2 = v.imag ? froundArray(v.imag) : void 0;
56939
+ return RTV.tensor(froundArray(v.data), v.shape.slice(), imag2);
56940
+ }
56941
+ if (isRuntimeClassInstance(v) && v._builtinData !== void 0)
56942
+ return singleApply(v._builtinData);
56943
+ return RTV.num(Math.fround(toNumber(v)));
56944
+ }
56945
+ defineBuiltin({
56946
+ name: "single",
56947
+ cases: [
56948
+ {
56949
+ match: (argTypes) => {
56950
+ if (argTypes.length !== 1) return null;
56951
+ const a = argTypes[0];
56952
+ if (a.kind === "number" || a.kind === "boolean" || a.kind === "char" || a.kind === "class_instance")
56953
+ return [{ kind: "number" }];
56954
+ if (a.kind === "complex_or_number")
56955
+ return [{ kind: "complex_or_number" }];
56956
+ if (a.kind === "tensor")
56957
+ return [
56958
+ {
56959
+ kind: "tensor",
56960
+ isComplex: a.isComplex,
56961
+ shape: a.shape,
56962
+ ndim: a.ndim
56963
+ }
56964
+ ];
56965
+ return null;
56966
+ },
56967
+ apply: (args) => singleApply(args[0])
56968
+ }
56969
+ ]
56970
+ });
56060
56971
  var INT_RANGES = [
56061
56972
  { name: "int8", min: -128, max: 127 },
56062
56973
  { name: "int16", min: -32768, max: 32767 },
@@ -56611,11 +57522,20 @@ defineBuiltin({
56611
57522
  },
56612
57523
  apply: (args) => {
56613
57524
  const v = args[0];
56614
- if (isRuntimeStructArray(v))
56615
- return RTV.logical(v.fieldNames.includes(toString(args[1])));
56616
- if (!isRuntimeStruct(v) && !isRuntimeClassInstance(v))
56617
- return RTV.logical(false);
56618
- return RTV.logical(v.fields.has(toString(args[1])));
57525
+ const hasField = (name) => {
57526
+ if (isRuntimeStructArray(v)) return v.fieldNames.includes(name);
57527
+ if (!isRuntimeStruct(v) && !isRuntimeClassInstance(v)) return false;
57528
+ return v.fields.has(name);
57529
+ };
57530
+ if (isRuntimeCell(args[1])) {
57531
+ const cell = args[1];
57532
+ const result = allocFloat64Array(cell.data.length);
57533
+ for (let i = 0; i < cell.data.length; i++) {
57534
+ result[i] = hasField(toString(cell.data[i])) ? 1 : 0;
57535
+ }
57536
+ return new RuntimeTensor(result, cell.shape.slice(), void 0, true);
57537
+ }
57538
+ return RTV.logical(hasField(toString(args[1])));
56619
57539
  }
56620
57540
  }
56621
57541
  ]
@@ -56732,7 +57652,7 @@ function regexpImpl(caseSensitive, name, args, nargout) {
56732
57652
  if (outModes.length === 0) {
56733
57653
  outModes.push("start", "end", "tokenextents", "match", "tokens", "names");
56734
57654
  }
56735
- const flags = caseSensitive ? "g" : "gi";
57655
+ const flags = caseSensitive ? "gs" : "gis";
56736
57656
  const re = new RegExp(pat, flags);
56737
57657
  const starts = [];
56738
57658
  const ends = [];
@@ -57452,6 +58372,13 @@ registerIBuiltin({
57452
58372
  if (args.length === 1) return RTV.cell([A], [1, 1]);
57453
58373
  A = RTV.tensor(allocFloat64Array([A ? 1 : 0]), [1, 1]);
57454
58374
  }
58375
+ if (isRuntimeStruct(A)) {
58376
+ return RTV.cell([A], [1, 1]);
58377
+ }
58378
+ if (isRuntimeStructArray(A)) {
58379
+ const elems = A.elements;
58380
+ return RTV.cell([...elems], [1, elems.length]);
58381
+ }
57455
58382
  if (!isRuntimeTensor(A))
57456
58383
  throw new RuntimeError(
57457
58384
  "num2cell: first argument must be a numeric array"
@@ -57575,6 +58502,69 @@ registerIBuiltin({
57575
58502
  };
57576
58503
  }
57577
58504
  });
58505
+ registerIBuiltin({
58506
+ name: "getfield",
58507
+ resolve: (argTypes) => {
58508
+ if (argTypes.length < 2) return null;
58509
+ return {
58510
+ outputTypes: [{ kind: "unknown" }],
58511
+ apply: (args) => {
58512
+ let v = args[0];
58513
+ for (let i = 1; i < args.length; i++) {
58514
+ if (isRuntimeCell(args[i]))
58515
+ throw new RuntimeError(
58516
+ "getfield: index ({}) subscripts are not supported"
58517
+ );
58518
+ if (!isRuntimeStruct(v))
58519
+ throw new RuntimeError("getfield: argument must be a structure");
58520
+ const name = toString(args[i]);
58521
+ if (!v.fields.has(name))
58522
+ throw new RuntimeError(
58523
+ `Reference to non-existent field '${name}'.`
58524
+ );
58525
+ v = v.fields.get(name);
58526
+ }
58527
+ return v;
58528
+ }
58529
+ };
58530
+ }
58531
+ });
58532
+ registerIBuiltin({
58533
+ name: "setfield",
58534
+ resolve: (argTypes) => {
58535
+ if (argTypes.length < 3) return null;
58536
+ return {
58537
+ outputTypes: [{ kind: "struct", fields: {} }],
58538
+ apply: (args) => {
58539
+ const value = args[args.length - 1];
58540
+ const fieldArgs = args.slice(1, args.length - 1);
58541
+ const setChain = (s, depth) => {
58542
+ const fa = fieldArgs[depth];
58543
+ if (isRuntimeCell(fa))
58544
+ throw new RuntimeError(
58545
+ "setfield: index ({}) subscripts are not supported"
58546
+ );
58547
+ const name = toString(fa);
58548
+ const base = isRuntimeStruct(s) ? s.fields : /* @__PURE__ */ new Map();
58549
+ if (!isRuntimeStruct(s) && !(isRuntimeTensor(s) && s.data.length === 0))
58550
+ throw new RuntimeError("setfield: argument must be a structure");
58551
+ const newFields = new Map(base);
58552
+ if (depth === fieldArgs.length - 1) {
58553
+ newFields.set(name, value);
58554
+ } else {
58555
+ const child = newFields.get(name);
58556
+ newFields.set(
58557
+ name,
58558
+ setChain(child ?? RTV.struct(/* @__PURE__ */ new Map()), depth + 1)
58559
+ );
58560
+ }
58561
+ return RTV.struct(newFields);
58562
+ };
58563
+ return setChain(args[0], 0);
58564
+ }
58565
+ };
58566
+ }
58567
+ });
57578
58568
  registerIBuiltin({
57579
58569
  name: "namedargs2cell",
57580
58570
  resolve: (argTypes) => {
@@ -57596,6 +58586,10 @@ registerIBuiltin({
57596
58586
  };
57597
58587
  }
57598
58588
  });
58589
+ function rmfieldNames(arg) {
58590
+ if (isRuntimeCell(arg)) return arg.data.map(toString);
58591
+ return [toString(arg)];
58592
+ }
57599
58593
  registerIBuiltin({
57600
58594
  name: "rmfield",
57601
58595
  resolve: (argTypes) => {
@@ -57604,25 +58598,26 @@ registerIBuiltin({
57604
58598
  outputTypes: [{ kind: "struct", fields: {} }],
57605
58599
  apply: (args) => {
57606
58600
  const v = args[0];
58601
+ const names = rmfieldNames(args[1]);
57607
58602
  if (isRuntimeStructArray(v)) {
57608
- const name2 = toString(args[1]);
57609
- if (!v.fieldNames.includes(name2))
57610
- throw new RuntimeError(`rmfield: field '${name2}' does not exist`);
57611
- const newFieldNames = v.fieldNames.filter((n) => n !== name2);
58603
+ for (const name of names)
58604
+ if (!v.fieldNames.includes(name))
58605
+ throw new RuntimeError(`rmfield: field '${name}' does not exist`);
58606
+ const newFieldNames = v.fieldNames.filter((n) => !names.includes(n));
57612
58607
  const newElements = v.elements.map((el) => {
57613
58608
  const newFields2 = new Map(el.fields);
57614
- newFields2.delete(name2);
58609
+ for (const name of names) newFields2.delete(name);
57615
58610
  return RTV.struct(newFields2);
57616
58611
  });
57617
58612
  return RTV.structArray(newFieldNames, newElements);
57618
58613
  }
57619
58614
  if (!isRuntimeStruct(v))
57620
58615
  throw new RuntimeError("rmfield: first argument must be a struct");
57621
- const name = toString(args[1]);
57622
- if (!v.fields.has(name))
57623
- throw new RuntimeError(`rmfield: field '${name}' does not exist`);
58616
+ for (const name of names)
58617
+ if (!v.fields.has(name))
58618
+ throw new RuntimeError(`rmfield: field '${name}' does not exist`);
57624
58619
  const newFields = new Map(v.fields);
57625
- newFields.delete(name);
58620
+ for (const name of names) newFields.delete(name);
57626
58621
  return RTV.struct(newFields);
57627
58622
  }
57628
58623
  };
@@ -57843,6 +58838,23 @@ registerIBuiltin({
57843
58838
  }
57844
58839
  })
57845
58840
  });
58841
+ registerIBuiltin({
58842
+ name: "spalloc",
58843
+ resolve: () => ({
58844
+ outputTypes: [{ kind: "unknown" }],
58845
+ apply: (args) => {
58846
+ const m = args.length >= 1 ? Math.round(toNumber(args[0])) : 0;
58847
+ const n = args.length >= 2 ? Math.round(toNumber(args[1])) : 0;
58848
+ return RTV.sparseMatrix(
58849
+ m,
58850
+ n,
58851
+ new Int32Array(0),
58852
+ new Int32Array(n + 1),
58853
+ allocFloat64Array(0)
58854
+ );
58855
+ }
58856
+ })
58857
+ });
57846
58858
  registerIBuiltin({
57847
58859
  name: "speye",
57848
58860
  resolve: () => ({
@@ -58128,6 +59140,598 @@ registerIBuiltin({
58128
59140
  }
58129
59141
  })
58130
59142
  });
59143
+ var SPPARMS_KEYS = [
59144
+ "spumoni",
59145
+ "thr_rel",
59146
+ "thr_abs",
59147
+ "exact_d",
59148
+ "supernd",
59149
+ "rreduce",
59150
+ "wh_frac",
59151
+ "autommd",
59152
+ "autoamd",
59153
+ "piv_tol",
59154
+ "bandden",
59155
+ "umfpack",
59156
+ "sym_tol",
59157
+ "ldl_tol",
59158
+ "usema57",
59159
+ "spqrtol",
59160
+ "sp_ctor",
59161
+ "reorder",
59162
+ "no_redo"
59163
+ ];
59164
+ var SPPARMS_DEFAULTS = [
59165
+ 0,
59166
+ 1.1,
59167
+ 1,
59168
+ 0,
59169
+ 3,
59170
+ 3,
59171
+ 0.5,
59172
+ 1,
59173
+ 1,
59174
+ 0.1,
59175
+ 0.5,
59176
+ 1,
59177
+ 1e-3,
59178
+ 0.01,
59179
+ 1,
59180
+ -2,
59181
+ 0,
59182
+ 0,
59183
+ 0
59184
+ ];
59185
+ var spparmsState = Float64Array.from(SPPARMS_DEFAULTS);
59186
+ function spparmsVector() {
59187
+ return RTV.tensor(allocFloat64Array(Array.from(spparmsState)), [
59188
+ spparmsState.length,
59189
+ 1
59190
+ ]);
59191
+ }
59192
+ registerIBuiltin({
59193
+ name: "spparms",
59194
+ resolve: (argTypes, nargout) => {
59195
+ if (argTypes.length > 2) return null;
59196
+ const tensorOut = { kind: "tensor", isComplex: false };
59197
+ const outputTypes = nargout >= 2 ? [{ kind: "char" }, tensorOut] : [tensorOut];
59198
+ return {
59199
+ outputTypes,
59200
+ apply: (args, n) => {
59201
+ if (args.length === 0) {
59202
+ if (n >= 2) {
59203
+ const width = Math.max(...SPPARMS_KEYS.map((k) => k.length));
59204
+ const padded = SPPARMS_KEYS.map((k) => k.padEnd(width)).join("");
59205
+ return [
59206
+ new RuntimeChar(padded, [SPPARMS_KEYS.length, width]),
59207
+ spparmsVector()
59208
+ ];
59209
+ }
59210
+ return spparmsVector();
59211
+ }
59212
+ const first = args[0];
59213
+ if (isRuntimeChar(first) || isRuntimeString(first)) {
59214
+ const key = parseStringArgLower(first);
59215
+ if (args.length >= 2) {
59216
+ const idx2 = SPPARMS_KEYS.indexOf(key);
59217
+ if (idx2 >= 0) spparmsState[idx2] = toNumber(args[1]);
59218
+ return RTV.num(0);
59219
+ }
59220
+ if (key === "default") {
59221
+ spparmsState = Float64Array.from(SPPARMS_DEFAULTS);
59222
+ return RTV.num(0);
59223
+ }
59224
+ if (key === "tight") {
59225
+ return RTV.num(0);
59226
+ }
59227
+ const idx = SPPARMS_KEYS.indexOf(key);
59228
+ if (idx >= 0) return RTV.num(spparmsState[idx]);
59229
+ throw new RuntimeError(`spparms: unknown parameter '${key}'`);
59230
+ }
59231
+ const vals = isRuntimeNumber(first) ? [first] : isRuntimeTensor(first) ? Array.from(first.data) : null;
59232
+ if (vals === null)
59233
+ throw new RuntimeError(
59234
+ "spparms: argument must be a parameter name or value vector"
59235
+ );
59236
+ const next = Float64Array.from(spparmsState);
59237
+ for (let i = 0; i < Math.min(vals.length, next.length); i++)
59238
+ next[i] = vals[i];
59239
+ spparmsState = next;
59240
+ return RTV.num(0);
59241
+ }
59242
+ };
59243
+ }
59244
+ });
59245
+
59246
+ // src/numbl-core/interpreter/builtins/graph.ts
59247
+ function isGraph(v) {
59248
+ return isRuntimeClassInstance(v) && v.className === "graph";
59249
+ }
59250
+ function requireGraph(v, fn) {
59251
+ if (!isGraph(v)) {
59252
+ throw new RuntimeError(`${fn}: first argument must be a graph`);
59253
+ }
59254
+ return v;
59255
+ }
59256
+ function graphN(g) {
59257
+ return toNumber(g.fields.get("_n") ?? 0);
59258
+ }
59259
+ function graphWeighted(g) {
59260
+ const w = g.fields.get("_weighted");
59261
+ return w === true;
59262
+ }
59263
+ function graphAdj(g) {
59264
+ const a = g.fields.get("_A");
59265
+ if (a === void 0 || !isRuntimeSparseMatrix(a)) {
59266
+ throw new RuntimeError("graph: corrupt internal adjacency");
59267
+ }
59268
+ return a;
59269
+ }
59270
+ function buildSymAdj(n, edges) {
59271
+ const triplets = [];
59272
+ for (const e of edges) {
59273
+ if (e.w === 0) continue;
59274
+ const u = e.u - 1;
59275
+ const v = e.v - 1;
59276
+ if (u === v) {
59277
+ triplets.push({ col: u, row: u, val: e.w });
59278
+ } else {
59279
+ triplets.push({ col: v, row: u, val: e.w });
59280
+ triplets.push({ col: u, row: v, val: e.w });
59281
+ }
59282
+ }
59283
+ triplets.sort((a, b) => a.col - b.col || a.row - b.row);
59284
+ const ir = [];
59285
+ const pr = [];
59286
+ const cols = [];
59287
+ let prevCol = -1;
59288
+ let prevRow = -1;
59289
+ for (const t of triplets) {
59290
+ if (t.col === prevCol && t.row === prevRow) {
59291
+ pr[pr.length - 1] += t.val;
59292
+ } else {
59293
+ ir.push(t.row);
59294
+ pr.push(t.val);
59295
+ cols.push(t.col);
59296
+ prevCol = t.col;
59297
+ prevRow = t.row;
59298
+ }
59299
+ }
59300
+ const jc = new Int32Array(n + 1);
59301
+ let ci = 0;
59302
+ for (let c = 0; c < n; c++) {
59303
+ jc[c] = ci;
59304
+ while (ci < cols.length && cols[ci] === c) ci++;
59305
+ }
59306
+ jc[n] = ci;
59307
+ return RTV.sparseMatrix(n, n, new Int32Array(ir), jc, allocFloat64Array(pr));
59308
+ }
59309
+ function makeGraph(n, edges, weighted) {
59310
+ const fields = /* @__PURE__ */ new Map();
59311
+ fields.set("_n", n);
59312
+ fields.set("_A", buildSymAdj(n, edges));
59313
+ fields.set("_weighted", weighted);
59314
+ return new RuntimeClassInstance("graph", fields, false);
59315
+ }
59316
+ function eachNeighbor(A, c, cb) {
59317
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) {
59318
+ const r = A.ir[k];
59319
+ if (r !== c) cb(r, A.pr[k]);
59320
+ }
59321
+ }
59322
+ function edgesFromAdj(A) {
59323
+ const edges = [];
59324
+ for (let c = 0; c < A.n; c++) {
59325
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) {
59326
+ const r = A.ir[k];
59327
+ if (r < c) edges.push({ u: r + 1, v: c + 1, w: A.pr[k] });
59328
+ else if (r === c) edges.push({ u: r + 1, v: r + 1, w: A.pr[k] });
59329
+ }
59330
+ }
59331
+ return edges;
59332
+ }
59333
+ function squareDim(A, fn) {
59334
+ if (isRuntimeNumber(A) || isRuntimeLogical(A)) return 1;
59335
+ if (isRuntimeSparseMatrix(A)) {
59336
+ if (A.m !== A.n) throw new RuntimeError(`${fn}: adjacency must be square`);
59337
+ return A.n;
59338
+ }
59339
+ if (isRuntimeTensor(A)) {
59340
+ const rows = A.shape[0] ?? 1;
59341
+ const cols = A.shape[1] ?? 1;
59342
+ if (rows !== cols)
59343
+ throw new RuntimeError(`${fn}: adjacency must be square`);
59344
+ return rows;
59345
+ }
59346
+ throw new RuntimeError(`${fn}: adjacency must be a numeric matrix`);
59347
+ }
59348
+ function takeEntry(r, c, triangle) {
59349
+ if (r === c) return true;
59350
+ if (triangle === "lower") return r > c;
59351
+ return r < c;
59352
+ }
59353
+ function adjToEdges(A, omitSelfLoops, triangle, fn) {
59354
+ const n = squareDim(A, fn);
59355
+ const weighted = !(isRuntimeTensor(A) && A._isLogical);
59356
+ const edges = [];
59357
+ const push = (r, c, val) => {
59358
+ if (val === 0) return;
59359
+ if (r === c) {
59360
+ if (!omitSelfLoops) edges.push({ u: r + 1, v: r + 1, w: val });
59361
+ } else if (takeEntry(r, c, triangle)) {
59362
+ edges.push({ u: Math.min(r, c) + 1, v: Math.max(r, c) + 1, w: val });
59363
+ }
59364
+ };
59365
+ if (isRuntimeSparseMatrix(A)) {
59366
+ for (let c = 0; c < A.n; c++) {
59367
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) push(A.ir[k], c, A.pr[k]);
59368
+ }
59369
+ } else if (isRuntimeTensor(A)) {
59370
+ const m = A.shape[0] ?? 1;
59371
+ for (let c = 0; c < n; c++) {
59372
+ for (let r = 0; r < n; r++) {
59373
+ const val = A.data[c * m + r];
59374
+ if (val !== 0) push(r, c, val);
59375
+ }
59376
+ }
59377
+ } else if (isRuntimeNumber(A) || isRuntimeLogical(A)) {
59378
+ const val = isRuntimeNumber(A) ? A : A ? 1 : 0;
59379
+ if (val !== 0) push(0, 0, val);
59380
+ }
59381
+ return { n, edges, weighted };
59382
+ }
59383
+ function isNumericArg3(v) {
59384
+ return isRuntimeNumber(v) || isRuntimeLogical(v) || isRuntimeTensor(v) || isRuntimeSparseMatrix(v);
59385
+ }
59386
+ function toNumArray2(v) {
59387
+ if (isRuntimeNumber(v)) return [v];
59388
+ if (isRuntimeLogical(v)) return [v ? 1 : 0];
59389
+ if (isRuntimeTensor(v)) return Array.from(v.data);
59390
+ throw new RuntimeError("graph: node/weight arguments must be numeric");
59391
+ }
59392
+ function argString(v) {
59393
+ if (isRuntimeString(v)) return v;
59394
+ if (isRuntimeChar(v)) return v.value;
59395
+ return null;
59396
+ }
59397
+ registerIBuiltin({
59398
+ name: "graph",
59399
+ help: {
59400
+ signatures: [
59401
+ "G = graph(A)",
59402
+ "G = graph(A, 'omitselfloops')",
59403
+ "G = graph(A, 'upper' | 'lower')",
59404
+ "G = graph(s, t)",
59405
+ "G = graph(s, t, w)",
59406
+ "G = graph(s, t, w, num)"
59407
+ ],
59408
+ description: "Create an undirected graph from an adjacency matrix A (which must be symmetric) or from edge endpoint lists s and t with optional weights w."
59409
+ },
59410
+ resolve: () => ({
59411
+ outputTypes: [
59412
+ {
59413
+ kind: "class_instance",
59414
+ className: "graph",
59415
+ isHandleClass: false,
59416
+ fields: {}
59417
+ }
59418
+ ],
59419
+ apply: (args) => {
59420
+ if (args.length === 0) return makeGraph(0, [], false);
59421
+ if (args.length >= 2 && isNumericArg3(args[1])) {
59422
+ const s = toNumArray2(args[0]);
59423
+ const t = toNumArray2(args[1]);
59424
+ if (s.length !== t.length) {
59425
+ throw new RuntimeError("graph: s and t must have the same length");
59426
+ }
59427
+ let weighted2 = false;
59428
+ let w = null;
59429
+ let num = null;
59430
+ let omitSelfLoops2 = false;
59431
+ if (args.length >= 3 && isNumericArg3(args[2])) {
59432
+ w = toNumArray2(args[2]);
59433
+ weighted2 = true;
59434
+ if (args.length >= 4 && isNumericArg3(args[3])) {
59435
+ num = Math.floor(toNumber(args[3]));
59436
+ }
59437
+ }
59438
+ for (let i = 2; i < args.length; i++) {
59439
+ const str = argString(args[i]);
59440
+ if (str && str.toLowerCase() === "omitselfloops")
59441
+ omitSelfLoops2 = true;
59442
+ }
59443
+ let n2 = num ?? 0;
59444
+ for (let i = 0; i < s.length; i++) n2 = Math.max(n2, s[i], t[i]);
59445
+ const edges2 = [];
59446
+ for (let i = 0; i < s.length; i++) {
59447
+ const u = Math.min(s[i], t[i]);
59448
+ const v = Math.max(s[i], t[i]);
59449
+ if (omitSelfLoops2 && u === v) continue;
59450
+ edges2.push({ u, v, w: w ? w[w.length === 1 ? 0 : i] : 1 });
59451
+ }
59452
+ return makeGraph(n2, edges2, weighted2);
59453
+ }
59454
+ let omitSelfLoops = false;
59455
+ let triangle = "sym";
59456
+ for (let i = 1; i < args.length; i++) {
59457
+ const str = argString(args[i]);
59458
+ if (!str) continue;
59459
+ const lc = str.toLowerCase();
59460
+ if (lc === "omitselfloops") omitSelfLoops = true;
59461
+ else if (lc === "upper") triangle = "upper";
59462
+ else if (lc === "lower") triangle = "lower";
59463
+ }
59464
+ const { n, edges, weighted } = adjToEdges(
59465
+ args[0],
59466
+ omitSelfLoops,
59467
+ triangle,
59468
+ "graph"
59469
+ );
59470
+ return makeGraph(n, edges, weighted);
59471
+ }
59472
+ })
59473
+ });
59474
+ registerIBuiltin({
59475
+ name: "conncomp",
59476
+ help: {
59477
+ signatures: ["bins = conncomp(G)", "[bins, binsizes] = conncomp(G)"],
59478
+ description: "Connected components of an undirected graph G. Returns a row vector labeling each node with its component index, and optionally the size of each component."
59479
+ },
59480
+ resolve: () => ({
59481
+ outputTypes: [{ kind: "unknown" }, { kind: "unknown" }],
59482
+ apply: (args, nargout) => {
59483
+ const g = requireGraph(args[0], "conncomp");
59484
+ const n = graphN(g);
59485
+ const A = graphAdj(g);
59486
+ const bins = new Float64Array(n);
59487
+ const sizes = [];
59488
+ let comp = 0;
59489
+ const stack = [];
59490
+ for (let start = 0; start < n; start++) {
59491
+ if (bins[start] !== 0) continue;
59492
+ comp++;
59493
+ let count = 0;
59494
+ bins[start] = comp;
59495
+ stack.length = 0;
59496
+ stack.push(start);
59497
+ while (stack.length > 0) {
59498
+ const node = stack.pop();
59499
+ count++;
59500
+ eachNeighbor(A, node, (r) => {
59501
+ if (bins[r] === 0) {
59502
+ bins[r] = comp;
59503
+ stack.push(r);
59504
+ }
59505
+ });
59506
+ }
59507
+ sizes.push(count);
59508
+ }
59509
+ const binsT = RTV.tensor(bins, [1, n]);
59510
+ if (nargout >= 2) {
59511
+ return [binsT, RTV.tensor(allocFloat64Array(sizes), [1, sizes.length])];
59512
+ }
59513
+ return binsT;
59514
+ }
59515
+ })
59516
+ });
59517
+ registerIBuiltin({
59518
+ name: "laplacian",
59519
+ help: {
59520
+ signatures: ["L = laplacian(G)"],
59521
+ description: "Graph Laplacian matrix L = D - A of an undirected graph G, where A is the (binary, unweighted) adjacency matrix and D the diagonal node-degree matrix. Returns a sparse matrix."
59522
+ },
59523
+ resolve: () => ({
59524
+ outputTypes: [{ kind: "unknown" }],
59525
+ apply: (args) => {
59526
+ const g = requireGraph(args[0], "laplacian");
59527
+ const n = graphN(g);
59528
+ const A = graphAdj(g);
59529
+ const edges = [];
59530
+ for (let c = 0; c < n; c++) {
59531
+ let degree = 0;
59532
+ eachNeighbor(A, c, (r) => {
59533
+ degree++;
59534
+ if (r < c) edges.push({ u: r + 1, v: c + 1, w: -1 });
59535
+ });
59536
+ edges.push({ u: c + 1, v: c + 1, w: degree });
59537
+ }
59538
+ return buildSymAdj(n, edges);
59539
+ }
59540
+ })
59541
+ });
59542
+ registerIBuiltin({
59543
+ name: "addedge",
59544
+ help: {
59545
+ signatures: ["H = addedge(G, s, t)", "H = addedge(G, s, t, w)"],
59546
+ description: "Add one or more edges between nodes s and t to graph G, returning a new graph. Weights w are required when G is a weighted graph."
59547
+ },
59548
+ resolve: () => ({
59549
+ outputTypes: [
59550
+ {
59551
+ kind: "class_instance",
59552
+ className: "graph",
59553
+ isHandleClass: false,
59554
+ fields: {}
59555
+ }
59556
+ ],
59557
+ apply: (args) => {
59558
+ const g = requireGraph(args[0], "addedge");
59559
+ if (args.length < 3) {
59560
+ throw new RuntimeError("addedge: requires nodes s and t");
59561
+ }
59562
+ const weighted = graphWeighted(g);
59563
+ const s = toNumArray2(args[1]);
59564
+ const t = toNumArray2(args[2]);
59565
+ if (s.length !== t.length) {
59566
+ throw new RuntimeError("addedge: s and t must have the same length");
59567
+ }
59568
+ let w = null;
59569
+ if (args.length >= 4) {
59570
+ w = toNumArray2(args[3]);
59571
+ } else if (weighted) {
59572
+ throw new RuntimeError(
59573
+ "addedge: Must specify weights when adding an edge to a weighted graph."
59574
+ );
59575
+ }
59576
+ const edges = edgesFromAdj(graphAdj(g));
59577
+ let n = graphN(g);
59578
+ for (let i = 0; i < s.length; i++) {
59579
+ const u = Math.min(s[i], t[i]);
59580
+ const v = Math.max(s[i], t[i]);
59581
+ n = Math.max(n, u, v);
59582
+ edges.push({ u, v, w: w ? w[w.length === 1 ? 0 : i] : 1 });
59583
+ }
59584
+ return makeGraph(n, edges, weighted);
59585
+ }
59586
+ })
59587
+ });
59588
+ registerIBuiltin({
59589
+ name: "numnodes",
59590
+ help: {
59591
+ signatures: ["n = numnodes(G)"],
59592
+ description: "Number of nodes in graph G."
59593
+ },
59594
+ resolve: () => ({
59595
+ outputTypes: [{ kind: "number" }],
59596
+ apply: (args) => RTV.num(graphN(requireGraph(args[0], "numnodes")))
59597
+ })
59598
+ });
59599
+ registerIBuiltin({
59600
+ name: "numedges",
59601
+ help: {
59602
+ signatures: ["m = numedges(G)"],
59603
+ description: "Number of edges in graph G."
59604
+ },
59605
+ resolve: () => ({
59606
+ outputTypes: [{ kind: "number" }],
59607
+ apply: (args) => {
59608
+ const g = requireGraph(args[0], "numedges");
59609
+ return RTV.num(edgesFromAdj(graphAdj(g)).length);
59610
+ }
59611
+ })
59612
+ });
59613
+ registerIBuiltin({
59614
+ name: "degree",
59615
+ help: {
59616
+ signatures: ["d = degree(G)"],
59617
+ description: "Degree of each node in graph G, returned as a column vector of edge counts (self-loops excluded)."
59618
+ },
59619
+ resolve: () => ({
59620
+ outputTypes: [{ kind: "unknown" }],
59621
+ apply: (args) => {
59622
+ const g = requireGraph(args[0], "degree");
59623
+ const n = graphN(g);
59624
+ const A = graphAdj(g);
59625
+ const d = new Float64Array(n);
59626
+ for (let c = 0; c < n; c++) eachNeighbor(A, c, () => d[c] += 1);
59627
+ return RTV.tensor(d, [n, 1]);
59628
+ }
59629
+ })
59630
+ });
59631
+
59632
+ // src/numbl-core/interpreter/builtins/gallery.ts
59633
+ function toNumArray3(v, what) {
59634
+ if (isRuntimeNumber(v)) return [v];
59635
+ if (isRuntimeLogical(v)) return [v ? 1 : 0];
59636
+ if (isRuntimeTensor(v)) return Array.from(v.data);
59637
+ if (isRuntimeComplexNumber(v)) return [v.re];
59638
+ throw new RuntimeError(`gallery: ${what} must be numeric`);
59639
+ }
59640
+ function buildTridiag(sub2, diag2, sup) {
59641
+ const n = diag2.length;
59642
+ if (sub2.length !== n - 1 || sup.length !== n - 1) {
59643
+ throw new RuntimeError(
59644
+ "gallery: tridiag sub/superdiagonal must have length one less than the diagonal"
59645
+ );
59646
+ }
59647
+ const ir = [];
59648
+ const pr = [];
59649
+ const jc = new Int32Array(n + 1);
59650
+ for (let c = 0; c < n; c++) {
59651
+ jc[c] = ir.length;
59652
+ if (c >= 1 && sup[c - 1] !== 0) {
59653
+ ir.push(c - 1);
59654
+ pr.push(sup[c - 1]);
59655
+ }
59656
+ if (diag2[c] !== 0) {
59657
+ ir.push(c);
59658
+ pr.push(diag2[c]);
59659
+ }
59660
+ if (c <= n - 2 && sub2[c] !== 0) {
59661
+ ir.push(c + 1);
59662
+ pr.push(sub2[c]);
59663
+ }
59664
+ }
59665
+ jc[n] = ir.length;
59666
+ return RTV.sparseMatrix(n, n, new Int32Array(ir), jc, allocFloat64Array(pr));
59667
+ }
59668
+ function buildTridiagToeplitz(n, c, d, e) {
59669
+ const off = Math.max(0, n - 1);
59670
+ return buildTridiag(
59671
+ new Array(off).fill(c),
59672
+ new Array(n).fill(d),
59673
+ new Array(off).fill(e)
59674
+ );
59675
+ }
59676
+ function galleryTridiag(rest) {
59677
+ if (rest.length === 1) {
59678
+ return buildTridiagToeplitz(Math.round(toNumber(rest[0])), -1, 2, -1);
59679
+ }
59680
+ if (rest.length === 4) {
59681
+ return buildTridiagToeplitz(
59682
+ Math.round(toNumber(rest[0])),
59683
+ toNumber(rest[1]),
59684
+ toNumber(rest[2]),
59685
+ toNumber(rest[3])
59686
+ );
59687
+ }
59688
+ if (rest.length === 3) {
59689
+ const sub2 = toNumArray3(rest[0], "subdiagonal");
59690
+ const diag2 = toNumArray3(rest[1], "diagonal");
59691
+ const sup = toNumArray3(rest[2], "superdiagonal");
59692
+ if (sub2.length === 1 && diag2.length === 1 && sup.length === 1) {
59693
+ return buildTridiag([], diag2, []);
59694
+ }
59695
+ return buildTridiag(sub2, diag2, sup);
59696
+ }
59697
+ throw new RuntimeError("gallery: tridiag expects (n), (n,c,d,e), or (x,y,z)");
59698
+ }
59699
+ registerIBuiltin({
59700
+ name: "gallery",
59701
+ help: {
59702
+ signatures: [
59703
+ "A = gallery('tridiag', n)",
59704
+ "A = gallery('tridiag', n, c, d, e)",
59705
+ "A = gallery('tridiag', x, y, z)"
59706
+ ],
59707
+ description: "Test matrices. gallery('tridiag', ...) returns a sparse tridiagonal matrix: gallery('tridiag', n) is the order-n matrix with -1 on the sub/superdiagonals and 2 on the diagonal (the negative second-difference matrix); gallery('tridiag', n, c, d, e) is the order-n Toeplitz tridiagonal with scalar sub c, diagonal d, super e; gallery('tridiag', x, y, z) uses vectors x (subdiagonal), y (diagonal), z (superdiagonal), where x and z have length one less than y."
59708
+ },
59709
+ resolve: () => ({
59710
+ outputTypes: [{ kind: "unknown" }],
59711
+ apply: (args) => {
59712
+ if (args.length < 1) {
59713
+ throw new RuntimeError("gallery: not enough input arguments");
59714
+ }
59715
+ const name = parseStringArgLower(args[0]);
59716
+ let rest = args.slice(1);
59717
+ if (rest.length >= 1) {
59718
+ const last = rest[rest.length - 1];
59719
+ if (isRuntimeString(last) || isRuntimeChar(last)) {
59720
+ const cn = parseStringArgLower(last);
59721
+ if (cn === "single" || cn === "double") rest = rest.slice(0, -1);
59722
+ }
59723
+ }
59724
+ switch (name) {
59725
+ case "tridiag":
59726
+ return galleryTridiag(rest);
59727
+ default:
59728
+ throw new RuntimeError(
59729
+ `gallery: matrix family '${name}' is not supported`
59730
+ );
59731
+ }
59732
+ }
59733
+ })
59734
+ });
58131
59735
 
58132
59736
  // src/numbl-core/interpreter/builtins/special-math.ts
58133
59737
  function binaryApply(a, b, fn) {
@@ -58611,12 +60215,19 @@ registerIBuiltin({
58611
60215
 
58612
60216
  // src/numbl-core/native/geometry-bridge.ts
58613
60217
  var backend = null;
60218
+ var hullBackend = null;
58614
60219
  function setDelaunayBackend(fn) {
58615
60220
  backend = fn;
58616
60221
  }
58617
60222
  function getDelaunayBackend() {
58618
60223
  return backend;
58619
60224
  }
60225
+ function setConvexHullBackend(fn) {
60226
+ hullBackend = fn;
60227
+ }
60228
+ function getConvexHullBackend() {
60229
+ return hullBackend;
60230
+ }
58620
60231
 
58621
60232
  // src/numbl-core/interpreter/builtins/geometry.ts
58622
60233
  function toFlatArray(v, name) {
@@ -58669,7 +60280,7 @@ function simplexVolume(points, cell, dim) {
58669
60280
  for (let k = 2; k <= dim; k++) fact *= k;
58670
60281
  return Math.abs(det) / fact;
58671
60282
  }
58672
- function triangulateToTensor(points, dim) {
60283
+ function triangulateToTensor(points, dim, orientCCW = false) {
58673
60284
  const backend2 = getDelaunayBackend();
58674
60285
  if (!backend2)
58675
60286
  throw new RuntimeError(
@@ -58689,6 +60300,16 @@ function triangulateToTensor(points, dim) {
58689
60300
  const cells = volTol > 0 ? raw.filter((cell) => simplexVolume(points, cell, dim) > volTol) : raw;
58690
60301
  const numCells = cells.length;
58691
60302
  const cols = dim + 1;
60303
+ if (orientCCW && dim === 2) {
60304
+ for (const cell of cells) {
60305
+ const [a, b, c] = cell;
60306
+ const signedArea2 = (points[b][0] - points[a][0]) * (points[c][1] - points[a][1]) - (points[c][0] - points[a][0]) * (points[b][1] - points[a][1]);
60307
+ if (signedArea2 < 0) {
60308
+ cell[1] = c;
60309
+ cell[2] = b;
60310
+ }
60311
+ }
60312
+ }
58692
60313
  const out = allocFloat64Array(numCells * cols);
58693
60314
  for (let i = 0; i < numCells; i++) {
58694
60315
  const cell = cells[i];
@@ -58742,7 +60363,7 @@ defineBuiltin({
58742
60363
  throw new RuntimeError(
58743
60364
  `delaunay: need at least ${dim + 1} points for a ${dim}-D triangulation`
58744
60365
  );
58745
- return triangulateToTensor(points, dim);
60366
+ return triangulateToTensor(points, dim, true);
58746
60367
  }
58747
60368
  }
58748
60369
  ]
@@ -58785,6 +60406,203 @@ defineBuiltin({
58785
60406
  }
58786
60407
  ]
58787
60408
  });
60409
+ var CONVHULLN_MAX_DIM = 3;
60410
+ function convexHullFacets(points, dim, name) {
60411
+ const backend2 = getConvexHullBackend();
60412
+ if (!backend2)
60413
+ throw new RuntimeError(
60414
+ `${name}: convex-hull backend not initialized. In Node call loadQhullNodeBackend() (the CLI and library do this automatically); in the browser worker it loads on startup.`
60415
+ );
60416
+ return backend2(points, dim);
60417
+ }
60418
+ function hullInteriorPoint(points, facets, dim) {
60419
+ const used = /* @__PURE__ */ new Set();
60420
+ for (const f of facets) for (const idx of f) used.add(idx);
60421
+ const c = new Array(dim).fill(0);
60422
+ for (const idx of used) {
60423
+ const p2 = points[idx];
60424
+ for (let k = 0; k < dim; k++) c[k] += p2[k];
60425
+ }
60426
+ const m = used.size || 1;
60427
+ for (let k = 0; k < dim; k++) c[k] /= m;
60428
+ return c;
60429
+ }
60430
+ function hullMeasure(points, facets, dim) {
60431
+ const c = hullInteriorPoint(points, facets, dim);
60432
+ let total = 0;
60433
+ if (dim === 2) {
60434
+ for (const f of facets) {
60435
+ const a = points[f[0]];
60436
+ const b = points[f[1]];
60437
+ const ax = a[0] - c[0], ay = a[1] - c[1];
60438
+ const bx = b[0] - c[0], by = b[1] - c[1];
60439
+ total += Math.abs(ax * by - ay * bx) / 2;
60440
+ }
60441
+ } else {
60442
+ for (const f of facets) {
60443
+ const a = points[f[0]];
60444
+ const b = points[f[1]];
60445
+ const d = points[f[2]];
60446
+ const ax = a[0] - c[0], ay = a[1] - c[1], az = a[2] - c[2];
60447
+ const bx = b[0] - c[0], by = b[1] - c[1], bz = b[2] - c[2];
60448
+ const dx = d[0] - c[0], dy = d[1] - c[1], dz = d[2] - c[2];
60449
+ const det = ax * (by * dz - bz * dy) - ay * (bx * dz - bz * dx) + az * (bx * dy - by * dx);
60450
+ total += Math.abs(det) / 6;
60451
+ }
60452
+ }
60453
+ return total;
60454
+ }
60455
+ function facetsToTensor(facets, cols) {
60456
+ const n = facets.length;
60457
+ const out = allocFloat64Array(n * cols);
60458
+ for (let i = 0; i < n; i++) {
60459
+ const f = facets[i];
60460
+ for (let j = 0; j < cols; j++) out[j * n + i] = f[j] + 1;
60461
+ }
60462
+ return RTV.tensor(out, [n, cols]);
60463
+ }
60464
+ function orderHull2D(facets, points) {
60465
+ const adj = /* @__PURE__ */ new Map();
60466
+ for (const [a, b] of facets) {
60467
+ (adj.get(a) ?? adj.set(a, []).get(a)).push(b);
60468
+ (adj.get(b) ?? adj.set(b, []).get(b)).push(a);
60469
+ }
60470
+ const start = facets[0][0];
60471
+ const loop = [start];
60472
+ let prev = -1;
60473
+ let cur = start;
60474
+ for (let guard = 0; guard <= facets.length; guard++) {
60475
+ const nbrs = adj.get(cur);
60476
+ const next = nbrs[0] === prev ? nbrs[1] : nbrs[0];
60477
+ if (next === void 0 || next === start) break;
60478
+ loop.push(next);
60479
+ prev = cur;
60480
+ cur = next;
60481
+ }
60482
+ let signed = 0;
60483
+ for (let i = 0; i < loop.length; i++) {
60484
+ const p2 = points[loop[i]];
60485
+ const q = points[loop[(i + 1) % loop.length]];
60486
+ signed += p2[0] * q[1] - q[0] * p2[1];
60487
+ }
60488
+ if (signed < 0) loop.reverse();
60489
+ loop.push(loop[0]);
60490
+ const out = allocFloat64Array(loop.length);
60491
+ for (let i = 0; i < loop.length; i++) out[i] = loop[i] + 1;
60492
+ return RTV.tensor(out, [loop.length, 1]);
60493
+ }
60494
+ defineBuiltin({
60495
+ name: "convhull",
60496
+ help: {
60497
+ signatures: [
60498
+ "k = convhull(P)",
60499
+ "k = convhull(x,y)",
60500
+ "k = convhull(x,y,z)",
60501
+ "k = convhull(___,'Simplify',tf)",
60502
+ "[k,av] = convhull(___)"
60503
+ ],
60504
+ description: "Convex hull of a set of 2-D or 3-D points. With a single matrix P (N-by-2 or N-by-3), or coordinate vectors x,y (2-D) or x,y,z (3-D). For 2-D input, k is a column vector of 1-based point indices tracing the hull boundary counter-clockwise (closed: k(1)==k(end)). For 3-D input, k is a numFacets-by-3 matrix of triangle vertex indices. The second output av is the area (2-D) or volume (3-D). The 'Simplify' name-value pair is accepted for compatibility; the hull is always returned in minimal (simplified) form."
60505
+ },
60506
+ cases: [
60507
+ {
60508
+ match: (argTypes, nargout) => {
60509
+ if (nargout > 2) return null;
60510
+ if (argTypes.length < 1 || argTypes.length > 5) return null;
60511
+ const ok = (t) => t.kind === "number" || t.kind === "boolean" || t.kind === "tensor" || t.kind === "string" || t.kind === "char";
60512
+ if (!argTypes.every(ok)) return null;
60513
+ const k = { kind: "tensor", isComplex: false };
60514
+ return nargout > 1 ? [k, { kind: "number" }] : [k];
60515
+ },
60516
+ apply: (args, nargout) => {
60517
+ let coordArgs = args;
60518
+ const tail = args[args.length - 2];
60519
+ const isSimplifyName = (v) => typeof v === "string" && v.toLowerCase() === "simplify" || isRuntimeChar(v) && v.value.toLowerCase() === "simplify";
60520
+ if (args.length >= 3 && isSimplifyName(tail)) {
60521
+ coordArgs = args.slice(0, args.length - 2);
60522
+ }
60523
+ let points;
60524
+ let dim;
60525
+ if (coordArgs.length === 1) {
60526
+ const P = coordArgs[0];
60527
+ if (!isRuntimeTensor(P))
60528
+ throw new RuntimeError(
60529
+ "convhull: P must be an N-by-2 or N-by-3 matrix"
60530
+ );
60531
+ const [, d] = tensorSize2D(P);
60532
+ if (d !== 2 && d !== 3)
60533
+ throw new RuntimeError("convhull: P must have 2 or 3 columns");
60534
+ points = matrixToPoints(P, "convhull");
60535
+ dim = d;
60536
+ } else if (coordArgs.length === 2 || coordArgs.length === 3) {
60537
+ const coords = coordArgs.map((a) => toFlatArray(a, "convhull"));
60538
+ const n = coords[0].length;
60539
+ if (coords.some((c) => c.length !== n))
60540
+ throw new RuntimeError(
60541
+ "convhull: coordinate vectors must have the same length"
60542
+ );
60543
+ points = new Array(n);
60544
+ for (let i = 0; i < n; i++) points[i] = coords.map((c) => c[i]);
60545
+ dim = coordArgs.length;
60546
+ } else {
60547
+ throw new RuntimeError("convhull: invalid arguments");
60548
+ }
60549
+ if (points.length < dim + 1)
60550
+ throw new RuntimeError(
60551
+ `convhull: need at least ${dim + 1} points for a ${dim}-D hull`
60552
+ );
60553
+ const facets = convexHullFacets(points, dim, "convhull");
60554
+ const k = dim === 2 ? orderHull2D(facets, points) : facetsToTensor(facets, 3);
60555
+ if (nargout > 1) return [k, hullMeasure(points, facets, dim)];
60556
+ return k;
60557
+ }
60558
+ }
60559
+ ]
60560
+ });
60561
+ defineBuiltin({
60562
+ name: "convhulln",
60563
+ help: {
60564
+ signatures: [
60565
+ "k = convhulln(P)",
60566
+ "k = convhulln(P,opts)",
60567
+ "[k,vol] = convhulln(___)"
60568
+ ],
60569
+ description: "N-D convex hull. P is an m-by-n matrix of m points in n-dimensional space. Returns a numFacets-by-n matrix where each row holds the 1-based point indices of one simplicial facet (edges for n=2, triangles for n=3). The second output is the area (n=2) or volume (n=3). The opts argument (Qhull options) is accepted for MATLAB compatibility but ignored. Note: only n = 2 and n = 3 are supported (see source comments)."
60570
+ },
60571
+ cases: [
60572
+ {
60573
+ match: (argTypes, nargout) => {
60574
+ if (nargout > 2) return null;
60575
+ if (argTypes.length < 1 || argTypes.length > 2) return null;
60576
+ const x = argTypes[0];
60577
+ if (x.kind !== "tensor" && x.kind !== "number" && x.kind !== "boolean")
60578
+ return null;
60579
+ const k = { kind: "tensor", isComplex: false };
60580
+ return nargout > 1 ? [k, { kind: "number" }] : [k];
60581
+ },
60582
+ apply: (args, nargout) => {
60583
+ const P = args[0];
60584
+ if (!isRuntimeTensor(P))
60585
+ throw new RuntimeError("convhulln: P must be an m-by-n matrix");
60586
+ const [m, n] = tensorSize2D(P);
60587
+ if (n < 2)
60588
+ throw new RuntimeError("convhulln: P must have at least 2 columns");
60589
+ if (n > CONVHULLN_MAX_DIM)
60590
+ throw new RuntimeError(
60591
+ `convhulln: only 2-D and 3-D hulls are supported (got ${n}-D); higher dimensions are not yet validated`
60592
+ );
60593
+ if (m < n + 1)
60594
+ throw new RuntimeError(
60595
+ `convhulln: need at least ${n + 1} points for a ${n}-D hull`
60596
+ );
60597
+ const points = matrixToPoints(P, "convhulln");
60598
+ const facets = convexHullFacets(points, n, "convhulln");
60599
+ const k = facetsToTensor(facets, n);
60600
+ if (nargout > 1) return [k, hullMeasure(points, facets, n)];
60601
+ return k;
60602
+ }
60603
+ }
60604
+ ]
60605
+ });
58788
60606
  function getQueryValues(v, name) {
58789
60607
  if (typeof v === "number") return { data: [v], shape: null };
58790
60608
  if (typeof v === "boolean") return { data: [v ? 1 : 0], shape: null };
@@ -59268,6 +61086,10 @@ var H = {
59268
61086
  signatures: ["C = class(A)"],
59269
61087
  description: "Returns the class name of A as a character vector."
59270
61088
  },
61089
+ superclasses: {
61090
+ signatures: ["S = superclasses(ClassName)", "S = superclasses(obj)"],
61091
+ description: "Names of the superclasses of a class, returned as a column cell array of character vectors. Accepts a class name (char/string) or an object. Returns an empty cell for built-in types or classes with no superclasses."
61092
+ },
59271
61093
  fieldnames: {
59272
61094
  signatures: ["F = fieldnames(S)"],
59273
61095
  description: "Returns cell array of field names of struct or object S."
@@ -59671,6 +61493,14 @@ var H = {
59671
61493
  signatures: ["B = pinv(A)", "B = pinv(A, TOL)"],
59672
61494
  description: "Moore-Penrose pseudoinverse."
59673
61495
  },
61496
+ null: {
61497
+ signatures: ["Z = null(A)", "Z = null(A, TOL)"],
61498
+ description: "Orthonormal basis for the null space of A (columns of Z), computed via the SVD. TOL overrides the singular-value tolerance."
61499
+ },
61500
+ bandwidth: {
61501
+ signatures: ["[lower, upper] = bandwidth(A)", "B = bandwidth(A, TYPE)"],
61502
+ description: "Lower and upper matrix bandwidth. TYPE is 'lower' or 'upper'; a single output without TYPE returns the lower bandwidth."
61503
+ },
59674
61504
  cond: {
59675
61505
  signatures: ["C = cond(A)", "C = cond(A, P)"],
59676
61506
  description: "Condition number of matrix."
@@ -59900,6 +61730,10 @@ var H = {
59900
61730
  signatures: ["Y = double(X)"],
59901
61731
  description: "Convert to double precision."
59902
61732
  },
61733
+ single: {
61734
+ signatures: ["Y = single(X)"],
61735
+ description: "Round to single precision. numbl stores all numerics as double, so the result reports class 'double', but the low-order bits are dropped as in MATLAB."
61736
+ },
59903
61737
  logical: {
59904
61738
  signatures: ["Y = logical(X)"],
59905
61739
  description: "Convert to logical (boolean) array."
@@ -60270,6 +62104,18 @@ var H = {
60270
62104
  ],
60271
62105
  description: "Extract or create sparse banded/diagonal matrices."
60272
62106
  },
62107
+ spparms: {
62108
+ signatures: [
62109
+ "spparms",
62110
+ "values = spparms",
62111
+ "[keys, values] = spparms",
62112
+ "value = spparms(KEY)",
62113
+ "spparms(KEY, VALUE)",
62114
+ "spparms('default')",
62115
+ "spparms(values)"
62116
+ ],
62117
+ description: "Get/set sparse direct-solver parameters. numbl's sparse solver ignores these knobs; values are stored so they round-trip through get/set/restore but have no effect."
62118
+ },
60273
62119
  // ── Linear algebra extras ─────────────────────────────────────────────
60274
62120
  qz: {
60275
62121
  signatures: [
@@ -61071,7 +62917,7 @@ function getSourceLine(getSource, file, line) {
61071
62917
  }
61072
62918
 
61073
62919
  // src/numbl-core/version.ts
61074
- var NUMBL_VERSION = "0.4.6";
62920
+ var NUMBL_VERSION = "0.4.8";
61075
62921
 
61076
62922
  // src/cli-repl.ts
61077
62923
  import { createInterface } from "readline";
@@ -61087,6 +62933,13 @@ import { join as join4 } from "path";
61087
62933
  import { homedir as homedir2 } from "os";
61088
62934
 
61089
62935
  // src/numbl-core/jsUserFunctions.ts
62936
+ function callHandle(handle, args, nargout = 1) {
62937
+ const rt = getCurrentRuntime();
62938
+ if (!rt) {
62939
+ throw new RuntimeError("callHandle: no active runtime to invoke handle");
62940
+ }
62941
+ return rt.index(handle, args, nargout);
62942
+ }
61090
62943
  function funcNameFromFile(fileName) {
61091
62944
  const base = fileName.split("/").pop();
61092
62945
  return base.replace(/\.numbl\.js$/, "");
@@ -61132,6 +62985,8 @@ function instantiateWasm(wasmData) {
61132
62985
  const moduleImports = WebAssembly.Module.imports(wasmModule);
61133
62986
  const importObject = {};
61134
62987
  const neededModules = new Set(moduleImports.map((i) => i.module));
62988
+ const callbacks = /* @__PURE__ */ new Map();
62989
+ let nextCbId = 1;
61135
62990
  if (neededModules.has("wasi_snapshot_preview1")) {
61136
62991
  importObject.wasi_snapshot_preview1 = {
61137
62992
  fd_write: () => 0,
@@ -61151,10 +63006,36 @@ function instantiateWasm(wasmData) {
61151
63006
  if (neededModules.has("env")) {
61152
63007
  importObject.env = {
61153
63008
  emscripten_notify_memory_growth: () => {
63009
+ },
63010
+ // Scalar callback: WASM calls back into a registered handle with one
63011
+ // f64 and receives an f64. Exceptions thrown here (incl. a missing id)
63012
+ // propagate out through the WASM call into the apply.
63013
+ numbl_cb_d: (id, x) => {
63014
+ const fn = callbacks.get(id);
63015
+ if (!fn) {
63016
+ throw new RuntimeError(
63017
+ `numbl_cb_d: no callback registered for id ${id}`
63018
+ );
63019
+ }
63020
+ return fn(x);
61154
63021
  }
61155
63022
  };
61156
63023
  }
61157
- return new WebAssembly.Instance(wasmModule, importObject);
63024
+ const instance = new WebAssembly.Instance(
63025
+ wasmModule,
63026
+ importObject
63027
+ );
63028
+ instance.callbacks = {
63029
+ add(fn) {
63030
+ const id = nextCbId++;
63031
+ callbacks.set(id, fn);
63032
+ return id;
63033
+ },
63034
+ remove(id) {
63035
+ callbacks.delete(id);
63036
+ }
63037
+ };
63038
+ return instance;
61158
63039
  }
61159
63040
  function resolveBindings(file, directives, getWasmInstance, nativeBridge2) {
61160
63041
  const wasmInstance = directives.wasm ? getWasmInstance(directives.wasm) : void 0;
@@ -61229,6 +63110,8 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
61229
63110
  "wasm",
61230
63111
  "native",
61231
63112
  "importJS",
63113
+ "callHandle",
63114
+ "toNumber",
61232
63115
  libFile.source
61233
63116
  );
61234
63117
  const exports = factory(
@@ -61238,7 +63121,9 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
61238
63121
  dummyRegister,
61239
63122
  wasmInstance,
61240
63123
  nativeLib,
61241
- importJS
63124
+ importJS,
63125
+ callHandle,
63126
+ toNumber
61242
63127
  );
61243
63128
  libCache.set(name, exports);
61244
63129
  return exports;
@@ -61276,6 +63161,8 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
61276
63161
  "wasm",
61277
63162
  "native",
61278
63163
  "importJS",
63164
+ "callHandle",
63165
+ "toNumber",
61279
63166
  file.source
61280
63167
  );
61281
63168
  factory(
@@ -61285,7 +63172,9 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
61285
63172
  registerFn,
61286
63173
  wasmInstance,
61287
63174
  nativeLib,
61288
- importJS
63175
+ importJS,
63176
+ callHandle,
63177
+ toNumber
61289
63178
  );
61290
63179
  if (!builtin) {
61291
63180
  throw new Error(
@@ -61537,6 +63426,7 @@ function resolveFunctionImpl(name, argTypes, callSite, index2) {
61537
63426
  for (const argType of argTypes) {
61538
63427
  if (argType?.kind === "ClassInstance") {
61539
63428
  const className = argType.className;
63429
+ if (index2.classConstructors.get(className) === name) continue;
61540
63430
  if (!candidates.includes(className) && (index2.classInstanceMethods.get(className)?.has(name) || index2.classStaticMethods.get(className)?.has(name))) {
61541
63431
  candidates.push(className);
61542
63432
  }
@@ -61645,12 +63535,25 @@ var Environment = class _Environment {
61645
63535
  }
61646
63536
  /** Function ID for persistent variable storage */
61647
63537
  persistentFuncId;
63538
+ /** Call-site variable names of this frame's arguments, for `inputname`.
63539
+ * Entry i is the name of the variable passed as argument i+1, or '' if
63540
+ * that argument was not a plain variable. Undefined when the call did
63541
+ * not originate from an interpreted call expression (e.g. feval, JIT).
63542
+ * Read directly off the executing frame — the interpreter has only
63543
+ * function-level scoping, so `this.env` is the frame while a body runs. */
63544
+ inputArgNames;
61648
63545
  /** Back-reference to the runtime (needed for global/persistent access) */
61649
63546
  rt = null;
61650
63547
  /** Set when a `@nestedFn` handle has been created that captures this env
61651
63548
  * (or an ancestor). Tells the function-exit cleanup that clearing this
61652
63549
  * env would strand the handle's closure, so locals must be left alive. */
61653
63550
  nestedHandleCreated = false;
63551
+ /** For a nested-function frame: the names of this function's own formal
63552
+ * input/output arguments. These are always local — a write must never be
63553
+ * redirected to a same-named variable in the parent, even before the
63554
+ * output has been assigned. (MATLAB scopes a nested function's formal
63555
+ * arguments to that function; only other variables are shared.) */
63556
+ nestedLocalNames;
61654
63557
  get(name) {
61655
63558
  if (this._globalNames !== void 0 && this._globalNames.has(name) && this.rt) {
61656
63559
  const v = this.rt.$g[name];
@@ -61669,7 +63572,7 @@ var Environment = class _Environment {
61669
63572
  if (old !== void 0) decref(this.rt, old);
61670
63573
  return;
61671
63574
  }
61672
- if (this.isNested && !this.vars.has(name) && this.parent) {
63575
+ if (this.isNested && !this.vars.has(name) && this.parent && !this.nestedLocalNames?.has(name)) {
61673
63576
  const owner = this.findOwner(name);
61674
63577
  if (owner) {
61675
63578
  owner.setLocal(name, value);
@@ -62864,6 +64767,7 @@ function makeRootContext(interp, registry3) {
62864
64767
  var interpreterExec_exports = {};
62865
64768
  __export(interpreterExec_exports, {
62866
64769
  assignLValue: () => assignLValue,
64770
+ computeInputNames: () => computeInputNames,
62867
64771
  evalAnonFunc: () => evalAnonFunc,
62868
64772
  evalArgs: () => evalArgs,
62869
64773
  evalBinary: () => evalBinary,
@@ -62890,6 +64794,15 @@ __export(interpreterExec_exports, {
62890
64794
  writeLValueBase: () => writeLValueBase
62891
64795
  });
62892
64796
 
64797
+ // src/numbl-core/jitDeclineDiagnostics.ts
64798
+ var lastDecline = null;
64799
+ function recordJitDecline(d) {
64800
+ lastDecline = d;
64801
+ }
64802
+ function getLastJitDecline() {
64803
+ return lastDecline;
64804
+ }
64805
+
62893
64806
  // src/numbl-core/runtime/cow.ts
62894
64807
  function cowCopy(v) {
62895
64808
  if (isRuntimeTensor(v)) {
@@ -63166,6 +65079,11 @@ function execStmtInner(stmt) {
63166
65079
  case "Global": {
63167
65080
  for (const name of stmt.names) {
63168
65081
  this.env.globalNames.add(name);
65082
+ if (this.rt && !(name in this.rt.$g)) {
65083
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
65084
+ incref(empty);
65085
+ this.rt.$g[name] = empty;
65086
+ }
63169
65087
  }
63170
65088
  return null;
63171
65089
  }
@@ -63187,14 +65105,16 @@ function execStmtInner(stmt) {
63187
65105
  case "Directive": {
63188
65106
  if (stmt.directive === "assert_jit") {
63189
65107
  const wantC = stmt.args.includes("c");
65108
+ const decline = getLastJitDecline();
65109
+ const why = decline ? ` Most recent JIT decline (${decline.where}, ${decline.kind}): ${decline.message}` : ` (no JIT decline reason was recorded \u2014 the unit may have been declined before lowering, e.g. an unsupported input type.)`;
63190
65110
  if (this.optimization === "1") {
63191
65111
  throw new RuntimeError(
63192
- `%!numbl:assert_jit: expected the enclosing loop/function/script to be JS-JIT-compiled at --opt 1, but it ran in the interpreter. (Run with --opt 0 to silence.)`
65112
+ `%!numbl:assert_jit: expected the enclosing loop/function/script to be JS-JIT-compiled at --opt 1, but it ran in the interpreter.${why} (Run with --opt 0 to silence.)`
63193
65113
  );
63194
65114
  }
63195
65115
  if (this.optimization === "2" && wantC) {
63196
65116
  throw new RuntimeError(
63197
- `%!numbl:assert_jit c: expected the enclosing loop/function/script to be C-JIT-compiled at --opt 2, but it ran in the interpreter. (Run with --opt 0 to silence.)`
65117
+ `%!numbl:assert_jit c: expected the enclosing loop/function/script to be C-JIT-compiled at --opt 2, but it ran in the interpreter.${why} (Run with --opt 0 to silence.)`
63198
65118
  );
63199
65119
  }
63200
65120
  }
@@ -63277,6 +65197,10 @@ function evalExprNargout(expr, nargout) {
63277
65197
  if (isRuntimeStructArray(rv)) {
63278
65198
  return rv.elements.length;
63279
65199
  }
65200
+ if (isRuntimeClassInstanceArray(rv)) {
65201
+ if (ctx.numIndices === 1) return rv.elements.length;
65202
+ return ctx.dimIndex < rv.shape.length ? rv.shape[ctx.dimIndex] : 1;
65203
+ }
63280
65204
  if (isRuntimeSparseMatrix(rv)) {
63281
65205
  if (ctx.numIndices === 1) return rv.m * rv.n;
63282
65206
  return ctx.dimIndex === 0 ? rv.m : ctx.dimIndex === 1 ? rv.n : 1;
@@ -63437,6 +65361,23 @@ function evalExprNargout(expr, nargout) {
63437
65361
  throw new RuntimeError("Interpreter does not yet support meta.class");
63438
65362
  }
63439
65363
  }
65364
+ function computeInputNames(argExprs, callerEnv) {
65365
+ const names = [];
65366
+ let blanked = false;
65367
+ for (const a of argExprs) {
65368
+ if (blanked) {
65369
+ names.push("");
65370
+ } else if (a.type === "Ident" && callerEnv.has(a.name)) {
65371
+ names.push(a.name);
65372
+ } else if (a.type === "IndexCell" || a.type === "Member" || a.type === "MemberDynamic") {
65373
+ names.push("");
65374
+ blanked = true;
65375
+ } else {
65376
+ names.push("");
65377
+ }
65378
+ }
65379
+ return names;
65380
+ }
63440
65381
  function evalArgs(argExprs) {
63441
65382
  const args = [];
63442
65383
  for (const a of argExprs) {
@@ -63566,6 +65507,7 @@ function evalFuncCall(expr, nargout) {
63566
65507
  const c = getConstant(expr.name);
63567
65508
  if (c !== void 0) return c;
63568
65509
  }
65510
+ this.pendingInputNames = expr.args.length > 0 ? computeInputNames(expr.args, this.env) : void 0;
63569
65511
  return this.callFunction(expr.name, args, nargout);
63570
65512
  }
63571
65513
  function evalMember(expr, nargout) {
@@ -63745,7 +65687,14 @@ function makeFuncHandle(name) {
63745
65687
  );
63746
65688
  };
63747
65689
  fn.jsFnExpectsNargout = true;
63748
- const narg = getIBuiltinNargin(name);
65690
+ let narg = getIBuiltinNargin(name);
65691
+ if (narg === void 0) {
65692
+ try {
65693
+ narg = this.declaredNargin(name);
65694
+ } catch {
65695
+ narg = void 0;
65696
+ }
65697
+ }
63749
65698
  if (narg !== void 0) fn.nargin = narg;
63750
65699
  if (isNested) {
63751
65700
  fn.releaseExtra = () => capturedEnv.clearLocals();
@@ -64034,6 +65983,7 @@ __export(interpreterFunctions_exports, {
64034
65983
  callNestedFunction: () => callNestedFunction,
64035
65984
  callUserFunction: () => callUserFunction,
64036
65985
  collectClassProperties: () => collectClassProperties,
65986
+ declaredNargin: () => declaredNargin,
64037
65987
  evalInLocalScope: () => evalInLocalScope,
64038
65988
  findExternalMethod: () => findExternalMethod,
64039
65989
  findFunctionInClassFile: () => findFunctionInClassFile,
@@ -64336,6 +66286,30 @@ register("isa", (ctx, args) => {
64336
66286
  if (args.length !== 2) return FALL_THROUGH;
64337
66287
  return ctx.rt.isa(args[0], args[1]);
64338
66288
  });
66289
+ register("superclasses", (ctx, args) => {
66290
+ if (args.length !== 1) return FALL_THROUGH;
66291
+ const v = ensureRuntimeValue(args[0]);
66292
+ let className;
66293
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v)) {
66294
+ className = v.className;
66295
+ } else if (isRuntimeChar(v) || isRuntimeString(v)) {
66296
+ className = toString(v);
66297
+ }
66298
+ const names = [];
66299
+ if (className) {
66300
+ const seen = /* @__PURE__ */ new Set([className]);
66301
+ let current = ctx.rt.getClassParentName(className);
66302
+ while (current && !seen.has(current)) {
66303
+ names.push(current);
66304
+ seen.add(current);
66305
+ current = ctx.rt.getClassParentName(current);
66306
+ }
66307
+ }
66308
+ return RTV.cell(
66309
+ names.map((n) => RTV.string(n)),
66310
+ [names.length, 1]
66311
+ );
66312
+ });
64339
66313
  register("__inferred_type_str", (_ctx, args) => {
64340
66314
  if (args.length !== 1) return FALL_THROUGH;
64341
66315
  const rv = ensureRuntimeValue(args[0]);
@@ -64360,6 +66334,44 @@ register("nargout", (ctx, args) => {
64360
66334
  const v = ctx.env.get("$nargout");
64361
66335
  return v !== void 0 ? v : 0;
64362
66336
  });
66337
+ register("inputname", (ctx, args) => {
66338
+ if (args.length !== 1) return FALL_THROUGH;
66339
+ const narginVal = ctx.env.get("$nargin");
66340
+ if (typeof narginVal !== "number") {
66341
+ throw new RuntimeError(
66342
+ "You can only call 'inputname' from within a MATLAB function."
66343
+ );
66344
+ }
66345
+ const k = toNumber(ensureRuntimeValue(args[0]));
66346
+ if (!Number.isInteger(k) || k < 1) {
66347
+ throw new RuntimeError(
66348
+ "Argument number must be a positive integer scalar."
66349
+ );
66350
+ }
66351
+ if (k > narginVal) {
66352
+ throw new RuntimeError(
66353
+ "Argument number exceeds number of function input arguments."
66354
+ );
66355
+ }
66356
+ const names = ctx.env.inputArgNames;
66357
+ const name = names && k <= names.length ? names[k - 1] : "";
66358
+ return RTV.char(name);
66359
+ });
66360
+ register("properties", (ctx, args) => {
66361
+ if (args.length !== 1) return FALL_THROUGH;
66362
+ const v = ensureRuntimeValue(args[0]);
66363
+ let names;
66364
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v)) {
66365
+ names = ctx.classPublicProperties(v.className);
66366
+ } else if (isRuntimeChar(v) || isRuntimeString(v)) {
66367
+ names = ctx.classPublicProperties(toString(v));
66368
+ }
66369
+ if (!names) return RTV.cell([], [0, 1]);
66370
+ return RTV.cell(
66371
+ names.map((n) => RTV.string(n)),
66372
+ [names.length, 1]
66373
+ );
66374
+ });
64363
66375
  register("narginchk", (ctx, args) => {
64364
66376
  if (args.length !== 2) return FALL_THROUGH;
64365
66377
  const narginVal = ctx.env.get("$nargin");
@@ -64464,10 +66476,14 @@ function callFunction(name, args, nargout) {
64464
66476
  source: ""
64465
66477
  };
64466
66478
  return void 0;
64467
- }
66479
+ },
66480
+ classPublicProperties: (n) => classPublicProperties(this, n)
64468
66481
  };
64469
66482
  const result = specialHandler(ctx, args, nargout);
64470
- if (result !== FALL_THROUGH) return result;
66483
+ if (result !== FALL_THROUGH) {
66484
+ this.pendingInputNames = void 0;
66485
+ return result;
66486
+ }
64471
66487
  }
64472
66488
  const nested = this.env.getNestedFunction(name);
64473
66489
  if (nested) {
@@ -64487,6 +66503,75 @@ function callFunction(name, args, nargout) {
64487
66503
  }
64488
66504
  throw new RuntimeError(`Undefined function or variable '${name}'`);
64489
66505
  }
66506
+ function narginFromParams(params) {
66507
+ const hasVarargin = params.length > 0 && params[params.length - 1] === "varargin";
66508
+ return hasVarargin ? -params.length : params.length;
66509
+ }
66510
+ function paramsInFile(interp, fileName, funcName) {
66511
+ const ast = interp.ctx.getCachedAST(fileName);
66512
+ for (const stmt of ast.body) {
66513
+ if (stmt.type === "Function" && stmt.name === funcName) return stmt.params;
66514
+ }
66515
+ return void 0;
66516
+ }
66517
+ function declaredNargin(name) {
66518
+ const nested = this.env.getNestedFunction(name);
66519
+ if (nested) return narginFromParams(nested.fn.params);
66520
+ const callSite = {
66521
+ file: this.currentFile,
66522
+ ...this.currentClassName ? { className: this.currentClassName } : {},
66523
+ ...this.currentMethodName ? { methodName: this.currentMethodName } : {}
66524
+ };
66525
+ const target = resolveFunction(name, [], callSite, this.functionIndex);
66526
+ if (!target) return void 0;
66527
+ let params;
66528
+ switch (target.kind) {
66529
+ case "localFunction": {
66530
+ const { source } = target;
66531
+ if (source.from === "main") {
66532
+ params = this.mainLocalFunctions.get(target.name)?.params;
66533
+ } else if (source.from === "workspaceFile") {
66534
+ params = this.findFunctionInWorkspaceFile(source.wsName, target.name)?.params ?? void 0;
66535
+ } else if (source.from === "classFile") {
66536
+ params = this.findFunctionInClassFile(
66537
+ source.className,
66538
+ target.name,
66539
+ source.methodScope
66540
+ )?.params ?? void 0;
66541
+ } else if (source.from === "privateFile") {
66542
+ params = paramsInFile(this, source.callerFile, target.name);
66543
+ }
66544
+ break;
66545
+ }
66546
+ case "workspaceFunction": {
66547
+ const dotIdx = target.name.lastIndexOf(".");
66548
+ const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
66549
+ params = this.findFunctionInWorkspaceFile(target.name, primaryName)?.params ?? void 0;
66550
+ if (params === void 0) {
66551
+ const entry = this.ctx.registry.filesByFuncName.get(target.name);
66552
+ if (entry) {
66553
+ const ast = this.ctx.getCachedAST(entry.fileName);
66554
+ for (const stmt of ast.body) {
66555
+ if (stmt.type === "Function") {
66556
+ params = stmt.params;
66557
+ break;
66558
+ }
66559
+ }
66560
+ }
66561
+ }
66562
+ break;
66563
+ }
66564
+ case "privateFunction": {
66565
+ const entry = this.ctx.getPrivateFileEntry(
66566
+ target.callerFile,
66567
+ target.name
66568
+ );
66569
+ if (entry) params = paramsInFile(this, entry.fileName, target.name);
66570
+ break;
66571
+ }
66572
+ }
66573
+ return params !== void 0 ? narginFromParams(params) : void 0;
66574
+ }
64490
66575
  function interpretTarget(target, args, nargout) {
64491
66576
  switch (target.kind) {
64492
66577
  case "builtin": {
@@ -64655,18 +66740,12 @@ function interpretWorkspaceFunction(target, args, nargout) {
64655
66740
  if (entry) {
64656
66741
  const ast = this.ctx.getCachedAST(entry.fileName);
64657
66742
  return this.withFileContext(entry.fileName, void 0, void 0, () => {
64658
- const savedEnv = this.env;
64659
- this.env = new Environment();
64660
- try {
64661
- for (const stmt of ast.body) {
64662
- if (stmt.type === "Function") continue;
64663
- const signal = this.execStmt(stmt);
64664
- if (signal instanceof ReturnSignal) break;
64665
- }
64666
- return this.ans;
64667
- } finally {
64668
- this.env = savedEnv;
66743
+ for (const stmt of ast.body) {
66744
+ if (stmt.type === "Function") continue;
66745
+ const signal = this.execStmt(stmt);
66746
+ if (signal instanceof ReturnSignal) break;
64669
66747
  }
66748
+ return this.ans;
64670
66749
  });
64671
66750
  }
64672
66751
  throw new RuntimeError(`Workspace function '${target.name}' not found`);
@@ -64733,6 +66812,21 @@ function instantiateClass(className, args, nargout) {
64733
66812
  if (!classInfo) {
64734
66813
  return this.rt.callClassMethod(className, className, nargout, args);
64735
66814
  }
66815
+ if (classInfo.isOldStyle) {
66816
+ const ctorName = classInfo.constructorName ?? className;
66817
+ const ctorFn = this.findExternalMethod(classInfo, ctorName);
66818
+ if (!ctorFn)
66819
+ throw new RuntimeError(
66820
+ `Constructor for old-style class '${className}' not found`
66821
+ );
66822
+ const fileName = classInfo.externalMethodFiles.get(ctorName)?.fileName ?? classInfo.fileName;
66823
+ return this.withFileContext(
66824
+ fileName,
66825
+ className,
66826
+ ctorName,
66827
+ () => this.callUserFunction(ctorFn, args, nargout)
66828
+ );
66829
+ }
64736
66830
  const { propertyNames, propertyDefaults } = this.collectClassProperties(classInfo);
64737
66831
  const defaults = /* @__PURE__ */ new Map();
64738
66832
  for (const [propName, defaultExpr] of propertyDefaults) {
@@ -64756,7 +66850,7 @@ function instantiateClass(className, args, nargout) {
64756
66850
  function interpretConstructor(classInfo, args, nargout) {
64757
66851
  const constructorName = classInfo.constructorName;
64758
66852
  if (!constructorName) return args[0];
64759
- for (const member of classInfo.ast.members) {
66853
+ for (const member of classInfo.ast?.members ?? []) {
64760
66854
  if (member.type !== "Methods") continue;
64761
66855
  for (const methodStmt of member.body) {
64762
66856
  if (methodStmt.type === "Function" && methodStmt.name === constructorName) {
@@ -64798,6 +66892,8 @@ function callUserFunction(fn, args, nargout, narginOverride) {
64798
66892
  if (!hasVarargoutDecl && nargout > declaredRegularOutputs) {
64799
66893
  throw new RuntimeError("Too many output arguments.");
64800
66894
  }
66895
+ const callInputNames = this.pendingInputNames;
66896
+ this.pendingInputNames = void 0;
64801
66897
  const sharedArgs = args;
64802
66898
  if (narginOverride === void 0 && this.registry.size > 0) {
64803
66899
  const r = this.registry.dispatchCall(fn, sharedArgs, nargout, this);
@@ -64831,6 +66927,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
64831
66927
  }
64832
66928
  fnEnv.set("$nargin", narginOverride ?? args.length);
64833
66929
  fnEnv.set("$nargout", nargout);
66930
+ fnEnv.inputArgNames = callInputNames;
64834
66931
  for (const stmt of fn.body) {
64835
66932
  if (stmt.type === "Function") {
64836
66933
  fnEnv.nestedFunctions.set(stmt.name, {
@@ -64907,10 +67004,13 @@ function callUserFunction(fn, args, nargout, narginOverride) {
64907
67004
  }
64908
67005
  }
64909
67006
  function callNestedFunction(fn, parentEnv, args, nargout) {
67007
+ const callInputNames = this.pendingInputNames;
67008
+ this.pendingInputNames = void 0;
64910
67009
  const fnEnv = new Environment(parentEnv);
64911
67010
  fnEnv.isNested = true;
64912
67011
  fnEnv.rt = this.rt;
64913
67012
  fnEnv.persistentFuncId = `${this.currentFile}:${fn.name}`;
67013
+ fnEnv.nestedLocalNames = /* @__PURE__ */ new Set([...fn.params, ...fn.outputs]);
64914
67014
  const hasVarargin = fn.params.length > 0 && fn.params[fn.params.length - 1] === "varargin";
64915
67015
  const regularParams = hasVarargin ? fn.params.slice(0, -1) : fn.params;
64916
67016
  for (let i = 0; i < regularParams.length; i++) {
@@ -64924,6 +67024,15 @@ function callNestedFunction(fn, parentEnv, args, nargout) {
64924
67024
  }
64925
67025
  fnEnv.setLocal("$nargin", args.length);
64926
67026
  fnEnv.setLocal("$nargout", nargout);
67027
+ fnEnv.inputArgNames = callInputNames;
67028
+ for (const stmt of fn.body) {
67029
+ if (stmt.type === "Function") {
67030
+ fnEnv.nestedFunctions.set(stmt.name, {
67031
+ fn: funcDefFromStmt(stmt),
67032
+ env: fnEnv
67033
+ });
67034
+ }
67035
+ }
64927
67036
  const savedEnv = this.env;
64928
67037
  this.env = fnEnv;
64929
67038
  this.rt.pushCleanupScope();
@@ -65062,7 +67171,7 @@ function findMethodInClass(classInfo, methodName) {
65062
67171
  const cacheKey = `method:${classInfo.name}:${methodName}`;
65063
67172
  const cached = this.functionDefCache.get(cacheKey);
65064
67173
  if (cached) return cached;
65065
- for (const member of classInfo.ast.members) {
67174
+ for (const member of classInfo.ast?.members ?? []) {
65066
67175
  if (member.type !== "Methods") continue;
65067
67176
  for (const methodStmt of member.body) {
65068
67177
  if (methodStmt.type === "Function" && methodStmt.name === methodName) {
@@ -65119,6 +67228,36 @@ function collectClassProperties(classInfo) {
65119
67228
  }
65120
67229
  return { propertyNames, propertyDefaults };
65121
67230
  }
67231
+ function classPublicProperties(interp, className) {
67232
+ let info = interp.ctx.getClassInfo(className);
67233
+ if (!info) return void 0;
67234
+ const out = [];
67235
+ const seen = /* @__PURE__ */ new Set();
67236
+ while (info) {
67237
+ const ast = info.ast;
67238
+ if (ast) {
67239
+ for (const member of ast.members) {
67240
+ if (member.type !== "Properties") continue;
67241
+ const hidden = member.attributes.some((a) => {
67242
+ const n = a.name.toLowerCase();
67243
+ if (n !== "access" && n !== "getaccess") return false;
67244
+ const val = (a.value ?? "").toLowerCase();
67245
+ return val === "private" || val === "protected";
67246
+ });
67247
+ if (hidden) continue;
67248
+ for (const pn of member.names) {
67249
+ if (!seen.has(pn)) {
67250
+ seen.add(pn);
67251
+ out.push(pn);
67252
+ }
67253
+ }
67254
+ }
67255
+ }
67256
+ if (!info.superClass || info.superClass === "handle") break;
67257
+ info = interp.ctx.getClassInfo(info.superClass);
67258
+ }
67259
+ return out;
67260
+ }
65122
67261
  function isHandleClass(classInfo) {
65123
67262
  let parentName = classInfo.superClass;
65124
67263
  while (parentName) {
@@ -65237,6 +67376,10 @@ var Interpreter = class {
65237
67376
  workspaceEnv;
65238
67377
  /** @internal The caller's environment — for evalin/assignin('caller', ...) */
65239
67378
  callerEnv;
67379
+ /** @internal Call-site variable names for the next user-function call, set
67380
+ * by `evalFuncCall` and consumed by `callUserFunction` to support
67381
+ * `inputname`. One-shot: cleared as soon as it is consumed. */
67382
+ pendingInputNames;
65240
67383
  /** @internal Stack of [base, dimIndex, numIndices] for resolving `end` keyword in indexing. */
65241
67384
  endContextStack = [];
65242
67385
  /** @internal Number of enclosing `for` / `while` loop bodies the
@@ -65498,6 +67641,37 @@ function extractClassInfo(classDef, qualifiedName, fileName, source) {
65498
67641
  externalMethodFiles: /* @__PURE__ */ new Map()
65499
67642
  };
65500
67643
  }
67644
+ function makeOldStyleClassInfo(qualifiedName, baseName, constructorFile, methodFiles) {
67645
+ const externalMethodFiles = /* @__PURE__ */ new Map();
67646
+ externalMethodFiles.set(baseName, {
67647
+ fileName: constructorFile.fileName,
67648
+ source: constructorFile.source
67649
+ });
67650
+ const methodNames = /* @__PURE__ */ new Set();
67651
+ for (const mf of methodFiles) {
67652
+ externalMethodFiles.set(mf.name, {
67653
+ fileName: mf.fileName,
67654
+ source: mf.source
67655
+ });
67656
+ methodNames.add(mf.name);
67657
+ }
67658
+ return {
67659
+ name: baseName,
67660
+ qualifiedName,
67661
+ fileName: constructorFile.fileName,
67662
+ source: constructorFile.source,
67663
+ superClass: null,
67664
+ propertyNames: [],
67665
+ propertyDefaults: /* @__PURE__ */ new Map(),
67666
+ methodNames,
67667
+ staticMethodNames: /* @__PURE__ */ new Set(),
67668
+ constructorName: baseName,
67669
+ inferiorClasses: [],
67670
+ ast: null,
67671
+ isOldStyle: true,
67672
+ externalMethodFiles
67673
+ };
67674
+ }
65501
67675
 
65502
67676
  // src/numbl-core/lowering/loweringContext.ts
65503
67677
  function createWorkspaceRegistry() {
@@ -65690,8 +67864,41 @@ var LoweringContext = class _LoweringContext {
65690
67864
  }
65691
67865
  }
65692
67866
  }
67867
+ const startsWithClassdef = (source) => {
67868
+ let t = source.trimStart();
67869
+ while (t.startsWith("%")) {
67870
+ const nl = t.indexOf("\n");
67871
+ if (nl < 0) return false;
67872
+ t = t.slice(nl + 1).trimStart();
67873
+ }
67874
+ return t.startsWith("classdef");
67875
+ };
65693
67876
  for (const [className, group] of classFolderGroups) {
65694
- if (!group.classDefFile) {
67877
+ const hasRealClassdef = !!group.classDefFile && startsWithClassdef(group.classDefFile.source);
67878
+ if (!hasRealClassdef) {
67879
+ const dotIdx = className.lastIndexOf(".");
67880
+ const baseName = dotIdx >= 0 ? className.slice(dotIdx + 1) : className;
67881
+ const methodBase = (f) => f.name.split("/").pop().replace(/\.m$/, "");
67882
+ const ctorFile = group.classDefFile ?? group.methodFiles.find((f) => methodBase(f) === baseName);
67883
+ if (!ctorFile) {
67884
+ continue;
67885
+ }
67886
+ const methodFiles = group.methodFiles.filter((f) => f !== ctorFile).map((f) => ({
67887
+ name: methodBase(f),
67888
+ fileName: f.name,
67889
+ source: f.source
67890
+ }));
67891
+ if (!this.registry.classesByName.has(className)) {
67892
+ this.registry.classesByName.set(
67893
+ className,
67894
+ makeOldStyleClassInfo(
67895
+ className,
67896
+ baseName,
67897
+ { fileName: ctorFile.name, source: ctorFile.source },
67898
+ methodFiles
67899
+ )
67900
+ );
67901
+ }
65695
67902
  continue;
65696
67903
  }
65697
67904
  this.registerWorkspaceClass(className, group.classDefFile);
@@ -65927,7 +68134,7 @@ var LoweringContext = class _LoweringContext {
65927
68134
  if (!info) return null;
65928
68135
  if (info.ctx) return info.ctx;
65929
68136
  const ctx = new _LoweringContext(info.source, info.fileName);
65930
- for (const member of info.ast.members) {
68137
+ for (const member of info.ast?.members ?? []) {
65931
68138
  if (member.type !== "Methods") continue;
65932
68139
  for (const methodStmt of member.body) {
65933
68140
  if (methodStmt.type !== "Function") continue;
@@ -66086,6 +68293,7 @@ var LoweringContext = class _LoweringContext {
66086
68293
  for (const m of info.methodNames) instanceMethods.add(m);
66087
68294
  for (const m of info.staticMethodNames) staticMethods.add(m);
66088
68295
  for (const m of info.externalMethodFiles.keys()) {
68296
+ if (info.isOldStyle && m === info.constructorName) continue;
66089
68297
  if (!info.staticMethodNames.has(m)) {
66090
68298
  instanceMethods.add(m);
66091
68299
  }
@@ -66129,10 +68337,43 @@ var LoweringContext = class _LoweringContext {
66129
68337
  }
66130
68338
  }
66131
68339
  const fileImports = /* @__PURE__ */ new Map();
68340
+ const gatherImports = (body, out) => {
68341
+ for (const stmt of body) {
68342
+ switch (stmt.type) {
68343
+ case "Import":
68344
+ out.push(stmt);
68345
+ break;
68346
+ case "Function":
68347
+ case "While":
68348
+ case "For":
68349
+ gatherImports(stmt.body, out);
68350
+ break;
68351
+ case "If":
68352
+ gatherImports(stmt.thenBody, out);
68353
+ for (const b of stmt.elseifBlocks) gatherImports(b.body, out);
68354
+ if (stmt.elseBody) gatherImports(stmt.elseBody, out);
68355
+ break;
68356
+ case "Switch":
68357
+ for (const c of stmt.cases) gatherImports(c.body, out);
68358
+ if (stmt.otherwise) gatherImports(stmt.otherwise, out);
68359
+ break;
68360
+ case "TryCatch":
68361
+ gatherImports(stmt.tryBody, out);
68362
+ gatherImports(stmt.catchBody, out);
68363
+ break;
68364
+ case "ClassDef":
68365
+ for (const member of stmt.members) {
68366
+ if (member.type === "Methods") gatherImports(member.body, out);
68367
+ }
68368
+ break;
68369
+ }
68370
+ }
68371
+ };
66132
68372
  const collectImportsFromBody = (body, fileName) => {
66133
68373
  const entries = [];
66134
- for (const stmt of body) {
66135
- if (stmt.type !== "Import") continue;
68374
+ const importStmts = [];
68375
+ gatherImports(body, importStmts);
68376
+ for (const stmt of importStmts) {
66136
68377
  if (stmt.wildcard) {
66137
68378
  entries.push({ wildcard: true, namespace: stmt.path.join(".") });
66138
68379
  } else {
@@ -66207,6 +68448,10 @@ var LoweringContext = class _LoweringContext {
66207
68448
 
66208
68449
  // src/numbl-core/stdlib-bundle.ts
66209
68450
  var stdlibFiles = [
68451
+ {
68452
+ name: "TriRep.m",
68453
+ source: "classdef TriRep < triangulation\n % (Not recommended) Triangulation representation. Provided for legacy\n % code; use triangulation instead. TriRep inherits freeBoundary,\n % vertexAttachments, etc. from triangulation and exposes the legacy\n % property names X (vertex coordinates) and Triangulation (connectivity).\n %\n % TR = TriRep(tri, x, y)\n % TR = TriRep(tri, x, y, z)\n % TR = TriRep(tri, P)\n properties\n X\n Triangulation\n end\n methods\n function obj = TriRep(tri, varargin)\n obj = obj@triangulation(tri, varargin{:});\n obj.X = obj.Points;\n obj.Triangulation = obj.ConnectivityList;\n end\n end\nend\n"
68454
+ },
66210
68455
  {
66211
68456
  name: "addOptional.m",
66212
68457
  source: "function addOptional(obj, name, default, validator)\n if nargin < 4\n obj.addOptional(name, default);\n else\n obj.addOptional(name, default, validator);\n end\nend\n"
@@ -66234,6 +68479,10 @@ var stdlibFiles = [
66234
68479
  {
66235
68480
  name: "readmatrix.m",
66236
68481
  source: "function A = readmatrix(filename, varargin)\n % Parse name-value pairs\n delimiter = '';\n numHeaderLines = -1; % -1 means auto-detect\n\n i = 1;\n while i <= length(varargin)\n if ischar(varargin{i}) || isstring(varargin{i})\n key = lower(char(varargin{i}));\n if strcmp(key, 'delimiter')\n delimiter = char(varargin{i+1});\n i = i + 2;\n elseif strcmp(key, 'numheaderlines')\n numHeaderLines = varargin{i+1};\n i = i + 2;\n else\n % Skip unknown name-value pairs\n i = i + 2;\n end\n else\n i = i + 1;\n end\n end\n\n % Auto-detect delimiter from extension if not specified\n if isempty(delimiter)\n [~, ~, ext] = fileparts(filename);\n if strcmp(ext, '.csv')\n delimiter = ',';\n else\n delimiter = ''; % will split on whitespace\n end\n end\n\n % Read the entire file\n txt = fileread(filename);\n\n % Split into lines\n lines = strsplit(txt, sprintf('\\n'));\n\n % Remove trailing empty line (from trailing newline)\n if ~isempty(lines) && strcmp(strtrim(char(lines{end})), '')\n lines = lines(1:end-1);\n end\n\n if isempty(lines)\n A = [];\n return;\n end\n\n % Auto-detect header lines: skip lines that can't be fully parsed as numbers\n if numHeaderLines < 0\n numHeaderLines = 0;\n for k = 1:length(lines)\n line = strtrim(char(lines{k}));\n if strcmp(line, '')\n numHeaderLines = numHeaderLines + 1;\n continue;\n end\n if ~isempty(delimiter)\n parts = strsplit(line, delimiter);\n else\n parts = strsplit(line);\n end\n allNumeric = true;\n for j = 1:length(parts)\n val = str2double(strtrim(char(parts{j})));\n if isnan(val)\n token = strtrim(char(parts{j}));\n % Allow NaN, Inf, -Inf as valid numeric tokens\n if ~strcmpi(token, 'nan') && ~strcmpi(token, 'inf') && ~strcmpi(token, '-inf')\n allNumeric = false;\n break;\n end\n end\n end\n if allNumeric\n break;\n else\n numHeaderLines = numHeaderLines + 1;\n end\n end\n end\n\n % Parse data lines\n dataLines = lines(numHeaderLines+1:end);\n nRows = length(dataLines);\n if nRows == 0\n A = [];\n return;\n end\n\n % First pass: determine number of columns from first data line\n firstLine = strtrim(char(dataLines{1}));\n if ~isempty(delimiter)\n parts = strsplit(firstLine, delimiter);\n else\n parts = strsplit(firstLine);\n end\n nCols = length(parts);\n\n A = zeros(nRows, nCols);\n for r = 1:nRows\n line = strtrim(char(dataLines{r}));\n if strcmp(line, '')\n A(r, :) = NaN;\n continue;\n end\n if ~isempty(delimiter)\n parts = strsplit(line, delimiter);\n else\n parts = strsplit(line);\n end\n for c = 1:min(length(parts), nCols)\n val = str2double(strtrim(char(parts{c})));\n if isnan(val)\n token = strtrim(char(parts{c}));\n if strcmpi(token, 'nan')\n A(r, c) = NaN;\n elseif strcmpi(token, 'inf')\n A(r, c) = Inf;\n elseif strcmpi(token, '-inf')\n A(r, c) = -Inf;\n else\n A(r, c) = NaN; % non-numeric data becomes NaN\n end\n else\n A(r, c) = val;\n end\n end\n % Fill missing columns with NaN\n if length(parts) < nCols\n A(r, length(parts)+1:nCols) = NaN;\n end\n end\nend\n"
68482
+ },
68483
+ {
68484
+ name: "triangulation.m",
68485
+ source: "classdef triangulation\n % Triangulation representation providing topological queries over a\n % triangle (or tetrahedron) mesh. Mirrors a subset of MATLAB's\n % triangulation class.\n %\n % TR = triangulation(tri, P) % P is an n-by-d coordinate matrix\n % TR = triangulation(tri, x, y) % 2-D vertex coordinate columns\n % TR = triangulation(tri, x, y, z) % 3-D vertex coordinate columns\n properties\n Points\n ConnectivityList\n end\n methods\n function obj = triangulation(tri, varargin)\n if nargin == 0\n return;\n end\n obj.ConnectivityList = tri;\n if numel(varargin) == 1\n obj.Points = varargin{1};\n elseif numel(varargin) >= 2\n obj.Points = [varargin{:}];\n else\n error('triangulation: vertex coordinates are required');\n end\n end\n\n function [F, P] = freeBoundary(obj)\n % Free boundary facets: the edges referenced by exactly one\n % triangle, ordered into connected, consistently oriented loops.\n tri = obj.ConnectivityList;\n E = [tri(:, [1 2]); tri(:, [2 3]); tri(:, [3 1])];\n Es = sort(E, 2);\n [~, ~, ic] = unique(Es, 'rows');\n counts = accumarray(ic, 1);\n isB = counts(ic) == 1;\n bedges = E(isB, :);\n\n n = size(bedges, 1);\n F = zeros(n, 2);\n used = false(n, 1);\n pos = 1;\n while pos <= n\n startRow = find(~used, 1);\n if isempty(startRow)\n break;\n end\n cur = startRow;\n loopStart = bedges(cur, 1);\n while true\n F(pos, :) = bedges(cur, :);\n used(cur) = true;\n pos = pos + 1;\n nextv = bedges(cur, 2);\n if nextv == loopStart\n break;\n end\n cand = find(~used & bedges(:, 1) == nextv, 1);\n if isempty(cand)\n break;\n end\n cur = cand;\n end\n end\n\n if nargout > 1\n vid = unique(F(:));\n P = obj.Points(vid, :);\n remap = zeros(max(vid), 1);\n remap(vid) = 1:numel(vid);\n F = remap(F);\n end\n end\n\n function V = vertexAttachments(obj, id)\n % IDs of the triangles attached to each vertex, returned as a\n % cell array with one row vector of triangle IDs per vertex.\n tri = obj.ConnectivityList;\n nv = size(obj.Points, 1);\n if nargin < 2\n id = (1:nv)';\n end\n id = id(:);\n V = cell(numel(id), 1);\n for k = 1:numel(id)\n V{k} = find(any(tri == id(k), 2))';\n end\n end\n end\nend\n"
66237
68486
  }
66238
68487
  ];
66239
68488
  var shimFiles = [
@@ -75877,6 +78126,30 @@ var sin = defineUnaryRealMath({
75877
78126
  complex: { cFnComplex: "mtoc2_csin", jsFnComplex: cSin }
75878
78127
  });
75879
78128
 
78129
+ // src/numbl-core/jit/builtins/defs/math/rand.ts
78130
+ var rand = {
78131
+ name: "rand",
78132
+ transfer(argTypes, nargout) {
78133
+ if (nargout > 1) {
78134
+ throw new UnsupportedConstruct(
78135
+ `'rand' does not support multi-output (nargout=${nargout})`
78136
+ );
78137
+ }
78138
+ if (argTypes.length !== 0) {
78139
+ throw new UnsupportedConstruct(
78140
+ `JS-JIT 'rand' supports only the scalar form rand() so far (got ${argTypes.length} arg(s)); matrix/seed forms run in the interpreter`
78141
+ );
78142
+ }
78143
+ return [scalarDouble("nonneg")];
78144
+ },
78145
+ emitJs() {
78146
+ return "$rand()";
78147
+ },
78148
+ call() {
78149
+ return [rngRandom()];
78150
+ }
78151
+ };
78152
+
75880
78153
  // src/numbl-core/jit/builtins/defs/math/tan.ts
75881
78154
  var tan = defineUnaryRealMath({
75882
78155
  name: "tan",
@@ -81260,6 +83533,7 @@ for (const b of [
81260
83533
  stdBuiltin,
81261
83534
  min,
81262
83535
  max,
83536
+ rand,
81263
83537
  any,
81264
83538
  all,
81265
83539
  zeros,
@@ -87099,12 +89373,13 @@ function emitJsProgram(prog, opts = {}) {
87099
89373
  const wrapperLines = [];
87100
89374
  if (opts.exposeSpec !== void 0) {
87101
89375
  wrapperLines.push(
87102
- `return function ($h) { globalThis.$write = $h.write; globalThis.$plotDispatch = $h.plotDispatch; return ${opts.exposeSpec}; };`
89376
+ `return function ($h) { globalThis.$write = $h.write; globalThis.$plotDispatch = $h.plotDispatch; globalThis.$rand = $h.rand; return ${opts.exposeSpec}; };`
87103
89377
  );
87104
89378
  } else {
87105
89379
  wrapperLines.push("function run($h) {");
87106
89380
  wrapperLines.push(" globalThis.$write = $h.write;");
87107
89381
  wrapperLines.push(" globalThis.$plotDispatch = $h.plotDispatch;");
89382
+ wrapperLines.push(" globalThis.$rand = $h.rand;");
87108
89383
  const locals = collectAssignedLocals(prog.topLevelStmts);
87109
89384
  if (locals.length > 0) {
87110
89385
  wrapperLines.push(` let ${locals.join(", ")};`);
@@ -91810,6 +94085,7 @@ var Workspace = class _Workspace {
91810
94085
  }
91811
94086
  };
91812
94087
  for (const [name, info] of this.ctx.registry.classesByName) {
94088
+ if (info.isOldStyle) continue;
91813
94089
  registerOrDefer(
91814
94090
  name,
91815
94091
  () => registerClassDef(
@@ -91823,7 +94099,7 @@ var Workspace = class _Workspace {
91823
94099
  if (this.classes.has(name) || this.failedClassValidations.has(name)) {
91824
94100
  throw new UnsupportedConstruct(
91825
94101
  `class '${name}' is defined both locally and as a workspace class`,
91826
- info.ast.span
94102
+ info.ast?.span
91827
94103
  );
91828
94104
  }
91829
94105
  registerOrDefer(name, () => registerClassDef(info.ast, info.fileName));
@@ -91917,7 +94193,7 @@ var Workspace = class _Workspace {
91917
94193
  if (!ast) {
91918
94194
  throw new UnsupportedConstruct(
91919
94195
  `internal: external method file '${mf.fileName}' for '${info.qualifiedName}.${methodName}' was not parsed`,
91920
- info.ast.span
94196
+ info.ast?.span
91921
94197
  );
91922
94198
  }
91923
94199
  let primary = null;
@@ -91933,7 +94209,7 @@ var Workspace = class _Workspace {
91933
94209
  if (!primary) {
91934
94210
  throw new UnsupportedConstruct(
91935
94211
  `external method file '${mf.fileName}' has no function`,
91936
- info.ast.span
94212
+ info.ast?.span
91937
94213
  );
91938
94214
  }
91939
94215
  out.set(methodName, primary);
@@ -92298,6 +94574,7 @@ function getOrCreateSession(interp) {
92298
94574
  function buildHostHelpers(rt) {
92299
94575
  return {
92300
94576
  write: (s) => rt.output(s),
94577
+ rand: () => rngRandom(),
92301
94578
  plotDispatch: (name, args) => {
92302
94579
  const runtimeArgs = args.map((a) => jitToNumbl(a));
92303
94580
  const handled = dispatchPlotBuiltin(
@@ -92390,6 +94667,11 @@ var jitCallExecutor = {
92390
94667
  return { specFn };
92391
94668
  } catch (e) {
92392
94669
  if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
94670
+ recordJitDecline({
94671
+ message: e.message,
94672
+ kind: e.constructor.name,
94673
+ where: "jit-call"
94674
+ });
92393
94675
  return null;
92394
94676
  }
92395
94677
  throw e;
@@ -92516,6 +94798,11 @@ var jitLoopExecutor = {
92516
94798
  return { specFn };
92517
94799
  } catch (e) {
92518
94800
  if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
94801
+ recordJitDecline({
94802
+ message: e.message,
94803
+ kind: e.constructor.name,
94804
+ where: "jit-loop"
94805
+ });
92519
94806
  return null;
92520
94807
  }
92521
94808
  throw e;
@@ -92656,6 +94943,11 @@ var jitTopLevelExecutor = {
92656
94943
  return { specFn, nargout };
92657
94944
  } catch (e) {
92658
94945
  if (e instanceof UnsupportedConstruct || e instanceof TypeError2) {
94946
+ recordJitDecline({
94947
+ message: e.message,
94948
+ kind: e.constructor.name,
94949
+ where: "jit-top-level"
94950
+ });
92659
94951
  return null;
92660
94952
  }
92661
94953
  throw e;
@@ -93330,7 +95622,8 @@ import {
93330
95622
  rmSync,
93331
95623
  rmdirSync,
93332
95624
  renameSync,
93333
- chmodSync
95625
+ chmodSync,
95626
+ realpathSync
93334
95627
  } from "fs";
93335
95628
  import { unzipSync } from "fflate";
93336
95629
  import { execFileSync } from "child_process";
@@ -93722,6 +96015,51 @@ var NodeFileIOAdapter = class {
93722
96015
  return false;
93723
96016
  }
93724
96017
  }
96018
+ copyfile(source, destination, force) {
96019
+ try {
96020
+ const src = expandTilde(source);
96021
+ const srcStat = statSync2(src);
96022
+ let dst = expandTilde(destination);
96023
+ try {
96024
+ if (statSync2(dst).isDirectory()) dst = join3(dst, basename(src));
96025
+ } catch {
96026
+ }
96027
+ if (srcStat.isDirectory()) {
96028
+ copyDirRecursive(src, dst);
96029
+ return true;
96030
+ }
96031
+ const parent = dirname(dst);
96032
+ if (parent && parent !== "." && parent !== "/") {
96033
+ mkdirSync(parent, { recursive: true });
96034
+ }
96035
+ if (force) {
96036
+ try {
96037
+ chmodSync(dst, 438);
96038
+ } catch {
96039
+ }
96040
+ }
96041
+ writeFileSync(dst, readFileSync3(src));
96042
+ return true;
96043
+ } catch {
96044
+ return false;
96045
+ }
96046
+ }
96047
+ fileattrib(path) {
96048
+ try {
96049
+ const p2 = expandTilde(path);
96050
+ const st2 = statSync2(p2);
96051
+ const mode = st2.mode;
96052
+ return {
96053
+ Name: realpathSync(p2),
96054
+ directory: st2.isDirectory(),
96055
+ UserRead: (mode & 256) !== 0,
96056
+ UserWrite: (mode & 128) !== 0,
96057
+ UserExecute: (mode & 64) !== 0
96058
+ };
96059
+ } catch {
96060
+ return null;
96061
+ }
96062
+ }
93725
96063
  deleteFile(pattern) {
93726
96064
  const p2 = expandTilde(pattern);
93727
96065
  if (p2.includes("*") || p2.includes("?")) {
@@ -96365,6 +98703,7 @@ async function load() {
96365
98703
  const { loadQhull } = await import("qhull-wasm");
96366
98704
  const qhull = await loadQhull();
96367
98705
  setDelaunayBackend((points, dim) => qhull.delaunay(points, dim).facets);
98706
+ setConvexHullBackend((points, dim) => qhull.convexHull(points, dim).facets);
96368
98707
  }
96369
98708
 
96370
98709
  // src/cli.ts
@@ -96504,6 +98843,7 @@ function findTestFiles(dir) {
96504
98843
  }
96505
98844
  async function runTests(dir, optimization, fastMath) {
96506
98845
  loadNativeAddon(fastMath ?? false);
98846
+ await loadQhullBackend();
96507
98847
  const absDir = resolve2(process.cwd(), dir);
96508
98848
  const testFiles = findTestFiles(absDir);
96509
98849
  if (testFiles.length === 0) {
@@ -96599,6 +98939,11 @@ Options (for build-site):
96599
98939
  --title <text> Site title (default: from numbl-project.json or dir name)
96600
98940
  --entry <file> Default file to open, e.g. main.m (default: README.md or
96601
98941
  the first script)
98942
+ --repo-url <url> Source repository URL, shown as a link in the deployed
98943
+ site (default: from numbl-project.json)
98944
+ --max-initial-output-panel-height <px>
98945
+ Cap the initial height (px) of the output panel so the
98946
+ figure panel below it starts larger (desktop layout)
96602
98947
 
96603
98948
  Options (for parse):
96604
98949
  --dump-ast <file> Write the AST as indented JSON to <file> (default: stdout)
@@ -97128,6 +99473,9 @@ async function cmdBuildAddon(args) {
97128
99473
  }
97129
99474
  var SITE_DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([".git", ".github", "node_modules"]);
97130
99475
  var SITE_MAX_FILE_BYTES = 25 * 1024 * 1024;
99476
+ function isAgentInstructionFile(name) {
99477
+ return name === "GEMINI.md" || /^(CLAUDE|AGENT)[^/]*\.md$/i.test(name);
99478
+ }
97131
99479
  function readNumblIgnore(projectDir) {
97132
99480
  const p2 = join7(projectDir, ".numblignore");
97133
99481
  if (!existsSync3(p2)) return [];
@@ -97176,6 +99524,7 @@ function collectSiteFiles(projectDir, outDirAbs, ignore) {
97176
99524
  walk2(full);
97177
99525
  } else if (e.isFile()) {
97178
99526
  if (e.name === ".numblignore" || e.name === ".DS_Store") continue;
99527
+ if (isAgentInstructionFile(e.name)) continue;
97179
99528
  const st2 = statSync3(full);
97180
99529
  if (st2.size > SITE_MAX_FILE_BYTES) {
97181
99530
  console.error(
@@ -97196,6 +99545,8 @@ async function cmdBuildSite(args) {
97196
99545
  let base;
97197
99546
  let title;
97198
99547
  let entry;
99548
+ let repoUrl;
99549
+ let maxInitialOutputPanelHeight;
97199
99550
  const positional = [];
97200
99551
  for (let i = 0; i < args.length; i++) {
97201
99552
  const a = args[i];
@@ -97203,7 +99554,17 @@ async function cmdBuildSite(args) {
97203
99554
  else if (a === "--base") base = args[++i];
97204
99555
  else if (a === "--title") title = args[++i];
97205
99556
  else if (a === "--entry") entry = args[++i];
97206
- else if (a.startsWith("-")) {
99557
+ else if (a === "--repo-url") repoUrl = args[++i];
99558
+ else if (a === "--max-initial-output-panel-height") {
99559
+ const n = Number(args[++i]);
99560
+ if (!Number.isFinite(n) || n <= 0) {
99561
+ console.error(
99562
+ "Error: --max-initial-output-panel-height must be a positive number"
99563
+ );
99564
+ process.exit(1);
99565
+ }
99566
+ maxInitialOutputPanelHeight = n;
99567
+ } else if (a.startsWith("-")) {
97207
99568
  console.error(`Unknown option: ${a}`);
97208
99569
  process.exit(1);
97209
99570
  } else positional.push(a);
@@ -97255,6 +99616,9 @@ async function cmdBuildSite(args) {
97255
99616
  }
97256
99617
  if (title !== void 0) manifest.title = title;
97257
99618
  if (entry !== void 0) manifest.entry = entry;
99619
+ if (repoUrl !== void 0) manifest.repository = repoUrl;
99620
+ if (maxInitialOutputPanelHeight !== void 0)
99621
+ manifest.maxInitialOutputPanelHeight = maxInitialOutputPanelHeight;
97258
99622
  if (!manifest.title) manifest.title = basename2(projectDirAbs);
97259
99623
  const zipEntries = {};
97260
99624
  for (const f of collected) {
@@ -97275,14 +99639,22 @@ async function cmdBuildSite(args) {
97275
99639
  "warning: project bundle exceeds 50 MB; GitHub Pages may be slow or reject it"
97276
99640
  );
97277
99641
  }
99642
+ const buildId = createHash2("sha256").update(zip).digest("hex").slice(0, 16);
97278
99643
  const indexPath = join7(outDirAbs, "index.html");
99644
+ let injectJs = `window.__NUMBL_BUILD_ID__=${JSON.stringify(buildId)};`;
97279
99645
  if (normBase) {
97280
- let indexHtml = readFileSync6(indexPath, "utf-8");
97281
- const inject = `<script>window.__NUMBL_BASE__=${JSON.stringify(normBase)};</script>`;
97282
- indexHtml = indexHtml.includes("<!-- numbl:base -->") ? indexHtml.replace("<!-- numbl:base -->", inject) : indexHtml.replace("</head>", ` ${inject}
97283
- </head>`);
97284
- writeFileSync4(indexPath, indexHtml);
99646
+ injectJs += `window.__NUMBL_BASE__=${JSON.stringify(normBase)};`;
97285
99647
  }
99648
+ const inject = `<script>${injectJs}</script>`;
99649
+ let indexHtml = readFileSync6(indexPath, "utf-8");
99650
+ indexHtml = indexHtml.includes("<!-- numbl:base -->") ? indexHtml.replace("<!-- numbl:base -->", inject) : indexHtml.replace("</head>", ` ${inject}
99651
+ </head>`);
99652
+ const escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
99653
+ indexHtml = indexHtml.replace(
99654
+ /<title>.*?<\/title>/,
99655
+ `<title>${escapeHtml(manifest.title)}</title>`
99656
+ );
99657
+ writeFileSync4(indexPath, indexHtml);
97286
99658
  const redirectTarget = normBase ?? "/";
97287
99659
  writeFileSync4(
97288
99660
  join7(outDirAbs, "404.html"),