numbl 0.4.6 → 0.4.7

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 (50) hide show
  1. package/dist-cli/cli.js +2363 -220
  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.js +2349 -216
  23. package/dist-lib/numbl-core/interpreter/builtins/gallery.d.ts +7 -0
  24. package/dist-lib/numbl-core/interpreter/builtins/geometry.d.ts +3 -3
  25. package/dist-lib/numbl-core/interpreter/builtins/graph.d.ts +29 -0
  26. package/dist-lib/numbl-core/interpreter/builtins/index.d.ts +2 -0
  27. package/dist-lib/numbl-core/interpreter/interpreter.d.ts +5 -0
  28. package/dist-lib/numbl-core/interpreter/interpreterExec.d.ts +14 -1
  29. package/dist-lib/numbl-core/interpreter/interpreterFunctions.d.ts +6 -0
  30. package/dist-lib/numbl-core/interpreter/interpreterSpecialBuiltins.d.ts +4 -0
  31. package/dist-lib/numbl-core/interpreter/types.d.ts +13 -0
  32. package/dist-lib/numbl-core/lowering/classInfo.d.ts +19 -2
  33. package/dist-lib/numbl-core/native/geometry-bridge.d.ts +10 -0
  34. package/dist-lib/numbl-core/parser/ArgumentsParser.d.ts +6 -1
  35. package/dist-lib/numbl-core/runtime/constructors.d.ts +2 -1
  36. package/dist-lib/numbl-core/runtime/runtime.d.ts +2 -1
  37. package/dist-lib/numbl-core/runtime/runtimeMemberAccess.d.ts +1 -1
  38. package/dist-lib/numbl-core/version.d.ts +1 -1
  39. package/dist-plot-viewer/assets/hdf5_hl-C9YUKPMe.js +24296 -0
  40. package/dist-plot-viewer/assets/index-YeXWXIxH.js +4504 -0
  41. package/dist-plot-viewer/index.html +1 -1
  42. package/dist-site-viewer/assets/hdf5_hl-C9YUKPMe.js +24296 -0
  43. package/dist-site-viewer/assets/index-RucHpf4b.js +4826 -0
  44. package/dist-site-viewer/assets/numbl-worker-IO39kohI.js +5228 -0
  45. package/dist-site-viewer/index.html +1 -1
  46. package/native/lapack_svd.cpp +50 -0
  47. package/package.json +21 -2
  48. package/dist-plot-viewer/assets/index-D4grNz8m.js +0 -4504
  49. package/dist-site-viewer/assets/index-B2mCFC55.js +0 -4826
  50. package/dist-site-viewer/assets/numbl-worker-DWZE29ck.js +0 -5116
package/dist-lib/lib.js CHANGED
@@ -1141,6 +1141,9 @@ var RTV = {
1141
1141
  }
1142
1142
  return new RuntimeClassInstance(className, fields, isHandleClass2);
1143
1143
  },
1144
+ classInstanceArray(className, elements, shape) {
1145
+ return new RuntimeClassInstanceArray(className, elements, shape);
1146
+ },
1144
1147
  complex(re, im) {
1145
1148
  return new RuntimeComplexNumber(re, im);
1146
1149
  },
@@ -18426,6 +18429,24 @@ function lu(data, m, n) {
18426
18429
  }
18427
18430
  function svd(data, m, n, econ, computeUV) {
18428
18431
  const k = Math.min(m, n);
18432
+ let finite = true;
18433
+ for (let i = 0; i < data.length; i++) {
18434
+ if (!Number.isFinite(data[i])) {
18435
+ finite = false;
18436
+ break;
18437
+ }
18438
+ }
18439
+ if (!finite) {
18440
+ const sNan = allocFloat64Array(k).fill(NaN);
18441
+ if (!computeUV) return { S: sNan };
18442
+ const uCols2 = econ ? k : m;
18443
+ const vCols2 = econ ? k : n;
18444
+ return {
18445
+ U: allocFloat64Array(m * uCols2).fill(NaN),
18446
+ S: sNan,
18447
+ V: allocFloat64Array(n * vCols2).fill(NaN)
18448
+ };
18449
+ }
18429
18450
  const a = allocFloat64Array(data);
18430
18451
  const s = allocFloat64Array(k);
18431
18452
  const JOBU_A2 = 0;
@@ -22299,6 +22320,24 @@ function sprintfFormat(fmt, args) {
22299
22320
  case "t":
22300
22321
  result += " ";
22301
22322
  break;
22323
+ case "r":
22324
+ result += "\r";
22325
+ break;
22326
+ case "a":
22327
+ result += "\x07";
22328
+ break;
22329
+ case "b":
22330
+ result += "\b";
22331
+ break;
22332
+ case "f":
22333
+ result += "\f";
22334
+ break;
22335
+ case "v":
22336
+ result += "\v";
22337
+ break;
22338
+ case "0":
22339
+ result += "\0";
22340
+ break;
22302
22341
  case "\\":
22303
22342
  result += "\\";
22304
22343
  break;
@@ -24235,8 +24274,8 @@ function indexIntoScalar(base, indices) {
24235
24274
  const idx = indices[0];
24236
24275
  if (isRuntimeTensor(idx)) {
24237
24276
  const is0x0 = idx.shape.length === 2 && idx.shape[0] === 0 && idx.shape[1] === 0;
24238
- const outShape = is0x0 ? [0, 0] : [...idx.shape];
24239
- return RTV.tensor(allocFloat64Array(0), outShape);
24277
+ const outShape2 = is0x0 ? [0, 0] : [...idx.shape];
24278
+ return RTV.tensor(allocFloat64Array(0), outShape2);
24240
24279
  }
24241
24280
  }
24242
24281
  if (indices.length === 1) {
@@ -24266,22 +24305,46 @@ function indexIntoScalar(base, indices) {
24266
24305
  if (k !== 1) throw new RuntimeError("Index exceeds array bounds");
24267
24306
  }
24268
24307
  const n = idx.data.length;
24269
- const scalarRe = isRuntimeNumber(base) ? base : base.re;
24270
- const data = allocFloat64Array(n);
24271
- data.fill(scalarRe);
24308
+ const scalarRe2 = isRuntimeNumber(base) ? base : base.re;
24309
+ const data2 = allocFloat64Array(n);
24310
+ data2.fill(scalarRe2);
24272
24311
  if (isRuntimeComplexNumber(base) && base.im !== 0) {
24273
24312
  const im = allocFloat64Array(n);
24274
24313
  im.fill(base.im);
24275
- return RTV.tensor(data, [...idx.shape], im);
24314
+ return RTV.tensor(data2, [...idx.shape], im);
24276
24315
  }
24277
- return RTV.tensor(data, [...idx.shape]);
24316
+ return RTV.tensor(data2, [...idx.shape]);
24278
24317
  }
24318
+ const dimLengths = [];
24319
+ let anyTensor = false;
24279
24320
  for (const idx of indices) {
24280
- if (isColonIndex(idx)) continue;
24281
- const i = toNumber(idx);
24282
- if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
24321
+ if (isColonIndex(idx)) {
24322
+ dimLengths.push(1);
24323
+ } else if (isRuntimeTensor(idx)) {
24324
+ anyTensor = true;
24325
+ for (let i = 0; i < idx.data.length; i++) {
24326
+ if (Math.round(idx.data[i]) !== 1)
24327
+ throw new RuntimeError("Index exceeds array bounds");
24328
+ }
24329
+ dimLengths.push(idx.data.length);
24330
+ } else {
24331
+ if (Math.round(toNumber(idx)) !== 1)
24332
+ throw new RuntimeError("Index exceeds array bounds");
24333
+ dimLengths.push(1);
24334
+ }
24283
24335
  }
24284
- return base;
24336
+ if (!anyTensor) return base;
24337
+ const total = dimLengths.reduce((a, b) => a * b, 1);
24338
+ const scalarRe = isRuntimeNumber(base) ? base : base.re;
24339
+ const data = allocFloat64Array(total);
24340
+ data.fill(scalarRe);
24341
+ const outShape = dimLengths.length >= 2 ? dimLengths : [dimLengths[0], 1];
24342
+ if (isRuntimeComplexNumber(base) && base.im !== 0) {
24343
+ const im = allocFloat64Array(total);
24344
+ im.fill(base.im);
24345
+ return RTV.tensor(data, outShape, im);
24346
+ }
24347
+ return RTV.tensor(data, outShape);
24285
24348
  }
24286
24349
  function indexIntoTensor(base, indices) {
24287
24350
  if (indices.length === 1) {
@@ -24316,11 +24379,13 @@ function indexIntoTensor(base, indices) {
24316
24379
  function indexIntoTensor1D(base, idx) {
24317
24380
  if (isColonIndex(idx)) {
24318
24381
  const imag2 = base.imag ? allocFloat64Array(base.imag) : void 0;
24319
- return RTV.tensor(
24382
+ const result = RTV.tensor(
24320
24383
  allocFloat64Array(base.data),
24321
24384
  [base.data.length, 1],
24322
24385
  imag2
24323
24386
  );
24387
+ if (base._isLogical === true) result._isLogical = true;
24388
+ return result;
24324
24389
  }
24325
24390
  if (isRuntimeLogical(idx)) {
24326
24391
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
@@ -24518,6 +24583,17 @@ function indexIntoChar(base, indices) {
24518
24583
  if (indices.length === 1) {
24519
24584
  const idx = indices[0];
24520
24585
  if (isColonIndex(idx)) return base;
24586
+ if (isRuntimeTensor(idx) && idx._isLogical) {
24587
+ let result = "";
24588
+ for (let k = 0; k < idx.data.length; k++) {
24589
+ if (idx.data[k] !== 0) {
24590
+ if (k >= base.value.length)
24591
+ throw new RuntimeError("Index exceeds char array length");
24592
+ result += base.value[k];
24593
+ }
24594
+ }
24595
+ return RTV.char(result);
24596
+ }
24521
24597
  if (isRuntimeTensor(idx)) {
24522
24598
  let result = "";
24523
24599
  for (let k = 0; k < idx.data.length; k++) {
@@ -24536,22 +24612,19 @@ function indexIntoChar(base, indices) {
24536
24612
  if (indices.length === 2) {
24537
24613
  const rowIdx = indices[0];
24538
24614
  const colIdx = indices[1];
24539
- let rows;
24540
- if (isColonIndex(rowIdx)) {
24541
- rows = Array.from({ length: nRows }, (_, i) => i);
24542
- } else if (isRuntimeTensor(rowIdx)) {
24543
- rows = Array.from(rowIdx.data, (v) => Math.round(v) - 1);
24544
- } else {
24545
- rows = [Math.round(toNumber(rowIdx)) - 1];
24546
- }
24547
- let cols;
24548
- if (isColonIndex(colIdx)) {
24549
- cols = Array.from({ length: nCols }, (_, i) => i);
24550
- } else if (isRuntimeTensor(colIdx)) {
24551
- cols = Array.from(colIdx.data, (v) => Math.round(v) - 1);
24552
- } else {
24553
- cols = [Math.round(toNumber(colIdx)) - 1];
24554
- }
24615
+ const toPositions = (idx) => {
24616
+ if (isRuntimeTensor(idx) && idx._isLogical) {
24617
+ const out = [];
24618
+ for (let i = 0; i < idx.data.length; i++)
24619
+ if (idx.data[i] !== 0) out.push(i);
24620
+ return out;
24621
+ }
24622
+ if (isRuntimeTensor(idx))
24623
+ return Array.from(idx.data, (v) => Math.round(v) - 1);
24624
+ return [Math.round(toNumber(idx)) - 1];
24625
+ };
24626
+ const rows = isColonIndex(rowIdx) ? Array.from({ length: nRows }, (_, i) => i) : toPositions(rowIdx);
24627
+ const cols = isColonIndex(colIdx) ? Array.from({ length: nCols }, (_, i) => i) : toPositions(colIdx);
24555
24628
  let result = "";
24556
24629
  for (const r of rows) {
24557
24630
  for (const c of cols) {
@@ -24577,11 +24650,11 @@ function indexIntoLogical(base, indices) {
24577
24650
  const vi = Math.round(idx.data[i2]);
24578
24651
  if (vi !== 1) throw new RuntimeError("Index exceeds array bounds");
24579
24652
  }
24580
- const data = allocFloat64Array(idx.data.length);
24581
- data.fill(base ? 1 : 0);
24582
- const result = RTV.tensor(data, [1, idx.data.length]);
24583
- result._isLogical = true;
24584
- return result;
24653
+ const data2 = allocFloat64Array(idx.data.length);
24654
+ data2.fill(base ? 1 : 0);
24655
+ const result2 = RTV.tensor(data2, [...idx.shape]);
24656
+ result2._isLogical = true;
24657
+ return result2;
24585
24658
  }
24586
24659
  if (isRuntimeLogical(idx)) {
24587
24660
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
@@ -24591,16 +24664,35 @@ function indexIntoLogical(base, indices) {
24591
24664
  if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
24592
24665
  return base;
24593
24666
  }
24667
+ const dimLengths = [];
24668
+ let anyTensor = false;
24594
24669
  for (const idx of indices) {
24595
- if (isColonIndex(idx)) continue;
24596
- if (isRuntimeLogical(idx)) {
24670
+ if (isColonIndex(idx)) {
24671
+ dimLengths.push(1);
24672
+ } else if (isRuntimeLogical(idx)) {
24597
24673
  if (!idx) return RTV.tensor(allocFloat64Array(0), [0, 0]);
24598
- continue;
24674
+ dimLengths.push(1);
24675
+ } else if (isRuntimeTensor(idx)) {
24676
+ anyTensor = true;
24677
+ for (let i = 0; i < idx.data.length; i++) {
24678
+ if (Math.round(idx.data[i]) !== 1)
24679
+ throw new RuntimeError("Index exceeds array bounds");
24680
+ }
24681
+ dimLengths.push(idx.data.length);
24682
+ } else {
24683
+ if (Math.round(toNumber(idx)) !== 1)
24684
+ throw new RuntimeError("Index exceeds array bounds");
24685
+ dimLengths.push(1);
24599
24686
  }
24600
- const i = Math.round(toNumber(idx));
24601
- if (i !== 1) throw new RuntimeError("Index exceeds array bounds");
24602
24687
  }
24603
- return base;
24688
+ if (!anyTensor) return base;
24689
+ const total = dimLengths.reduce((a, b) => a * b, 1);
24690
+ const data = allocFloat64Array(total);
24691
+ data.fill(base ? 1 : 0);
24692
+ const outShape = dimLengths.length >= 2 ? dimLengths : [dimLengths[0], 1];
24693
+ const result = RTV.tensor(data, outShape);
24694
+ result._isLogical = true;
24695
+ return result;
24604
24696
  }
24605
24697
  function indexIntoTensorWithTensor(base, idx) {
24606
24698
  const baseLogical = base._isLogical === true;
@@ -24680,11 +24772,15 @@ function indexIntoRTValue(base, indices) {
24680
24772
  return indexIntoLogical(base, indices);
24681
24773
  }
24682
24774
  if (isRuntimeStruct(base) || isRuntimeClassInstance(base)) {
24683
- if (indices.length === 1) {
24684
- const i = Math.round(toNumber(indices[0]));
24685
- if (i !== 1) throw new RuntimeError("Index exceeds struct dimensions");
24686
- return base;
24687
- }
24775
+ const allOnes = indices.length >= 1 && indices.every((idx) => {
24776
+ if (isRuntimeNumber(idx)) return Math.round(idx) === 1;
24777
+ if (isRuntimeLogical(idx)) return idx === true;
24778
+ if (isRuntimeTensor(idx))
24779
+ return idx.data.length === 1 && Math.round(idx.data[0]) === 1;
24780
+ return false;
24781
+ });
24782
+ if (allOnes) return base;
24783
+ throw new RuntimeError("Index exceeds struct dimensions");
24688
24784
  }
24689
24785
  if (isRuntimeSparseMatrix(base)) {
24690
24786
  return indexIntoSparse(base, indices);
@@ -24734,7 +24830,13 @@ function storeIntoTensor(base, indices, rhs, _rt) {
24734
24830
  }
24735
24831
  if (isShared(base)) {
24736
24832
  const cowImag = base.imag ? allocFloat64Array(base.imag) : void 0;
24737
- base = RTV.tensor(allocFloat64Array(base.data), [...base.shape], cowImag);
24833
+ const copy = RTV.tensor(
24834
+ allocFloat64Array(base.data),
24835
+ [...base.shape],
24836
+ cowImag
24837
+ );
24838
+ copy._isLogical = base._isLogical;
24839
+ base = copy;
24738
24840
  }
24739
24841
  if (indices.length === 1) {
24740
24842
  return storeIntoTensor1D(base, indices[0], rhs);
@@ -24762,7 +24864,9 @@ function deleteTensorElements(base, idx) {
24762
24864
  toDelete.add(Math.round(idx.data[i]) - 1);
24763
24865
  }
24764
24866
  } else if (isColonIndex(idx)) {
24765
- return RTV.tensor(allocFloat64Array(0), [0, 0]);
24867
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
24868
+ empty._isLogical = base._isLogical;
24869
+ return empty;
24766
24870
  }
24767
24871
  if (toDelete.size === 0) return base;
24768
24872
  const newData = [];
@@ -24777,7 +24881,9 @@ function deleteTensorElements(base, idx) {
24777
24881
  const baseIsColVec = base.shape.length >= 2 && base.shape[1] === 1 && base.shape[0] !== 1;
24778
24882
  const outShape = baseIsColVec ? [newData.length, 1] : [1, newData.length];
24779
24883
  const imOut = hasImag && newIm.some((x) => x !== 0) ? allocFloat64Array(newIm) : void 0;
24780
- return RTV.tensor(allocFloat64Array(newData), outShape, imOut);
24884
+ const result = RTV.tensor(allocFloat64Array(newData), outShape, imOut);
24885
+ result._isLogical = base._isLogical;
24886
+ return result;
24781
24887
  }
24782
24888
  function collectDelIndices(idx, dimLen) {
24783
24889
  const s = /* @__PURE__ */ new Set();
@@ -24817,7 +24923,9 @@ function deleteTensorRowsOrCols(base, indices) {
24817
24923
  if (newIm && base.imag) newIm[dstIdx] = base.imag[srcIdx];
24818
24924
  }
24819
24925
  }
24820
- return RTV.tensor(newData, [newNrows, ncols], newIm);
24926
+ const result = RTV.tensor(newData, [newNrows, ncols], newIm);
24927
+ result._isLogical = base._isLogical;
24928
+ return result;
24821
24929
  }
24822
24930
  if (isColonIndex(indices[0])) {
24823
24931
  const delCols = collectDelIndices(indices[1], ncols);
@@ -24836,7 +24944,9 @@ function deleteTensorRowsOrCols(base, indices) {
24836
24944
  if (newIm && base.imag) newIm[dstIdx] = base.imag[srcIdx];
24837
24945
  }
24838
24946
  }
24839
- return RTV.tensor(newData, [nrows, newNcols], newIm);
24947
+ const result = RTV.tensor(newData, [nrows, newNcols], newIm);
24948
+ result._isLogical = base._isLogical;
24949
+ return result;
24840
24950
  }
24841
24951
  throw new RuntimeError("Cannot delete from both row and column dimensions");
24842
24952
  }
@@ -24906,6 +25016,9 @@ function storeIntoTensor1D(base, idx, rhs) {
24906
25016
  }
24907
25017
  function storeIntoTensorByVector(base, idx, rhs) {
24908
25018
  if (idx._isLogical) {
25019
+ if (isRuntimeTensor(rhs) && rhs.data.length === 1) {
25020
+ rhs = rhs.imag && rhs.imag[0] !== 0 ? RTV.complex(rhs.data[0], rhs.imag[0]) : RTV.num(rhs.data[0]);
25021
+ }
24909
25022
  const { re: rhsRe, im: rhsIm } = isRuntimeTensor(rhs) ? { re: null, im: null } : toReIm(rhs);
24910
25023
  let maxTruthy = -1;
24911
25024
  for (let i = 0; i < idx.data.length; i++) {
@@ -25155,7 +25268,8 @@ function storeIntoTensorND(base, indices, rhs) {
25155
25268
  const dimIndices = indices.map((idx, dim) => {
25156
25269
  const dimSize = dim < shape.length ? shape[dim] : 1;
25157
25270
  if (isColonIndex(idx)) {
25158
- if (dimSize === 0 && rhsShape) {
25271
+ const dimSizeUnknown = dimSize === 0 || dim >= shape.length;
25272
+ if (dimSizeUnknown && rhsShape) {
25159
25273
  const rDim = rhsDimCursor < rhsShape.length ? rhsShape[rhsDimCursor] : 1;
25160
25274
  rhsDimCursor++;
25161
25275
  return Array.from({ length: rDim }, (_, i) => i);
@@ -25472,6 +25586,9 @@ function horzcat(...values) {
25472
25586
  if (values.some((v) => isRuntimeSparseMatrix(v))) {
25473
25587
  return sparseCatAlongDim(values, 1);
25474
25588
  }
25589
+ if (values.some((v) => isRuntimeCell(v))) {
25590
+ return cellCatAlongDim(values, 1);
25591
+ }
25475
25592
  if (values.some((v) => isRuntimeChar(v))) {
25476
25593
  let result = "";
25477
25594
  for (const v of values) {
@@ -25492,9 +25609,6 @@ function horzcat(...values) {
25492
25609
  }
25493
25610
  return RTV.string(result);
25494
25611
  }
25495
- if (values.some((v) => isRuntimeCell(v))) {
25496
- return cellCatAlongDim(values, 1);
25497
- }
25498
25612
  if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
25499
25613
  return structCat(values);
25500
25614
  }
@@ -25506,12 +25620,12 @@ function vertcat(...values) {
25506
25620
  if (values.some((v) => isRuntimeSparseMatrix(v))) {
25507
25621
  return sparseCatAlongDim(values, 0);
25508
25622
  }
25509
- if (values.some((v) => isRuntimeChar(v))) {
25510
- return vertcatChars(values);
25511
- }
25512
25623
  if (values.some((v) => isRuntimeCell(v))) {
25513
25624
  return cellCatAlongDim(values, 0);
25514
25625
  }
25626
+ if (values.some((v) => isRuntimeChar(v))) {
25627
+ return vertcatChars(values);
25628
+ }
25515
25629
  if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
25516
25630
  return structCat(values);
25517
25631
  }
@@ -25857,11 +25971,22 @@ function catAlongDim(values, dimIdx) {
25857
25971
  if (allLogical) result._isLogical = true;
25858
25972
  return result;
25859
25973
  }
25974
+ function isEmptyNonCellOperand(v) {
25975
+ if (isRuntimeChar(v)) return v.value.length === 0;
25976
+ if (isRuntimeTensor(v)) return v.data.length === 0;
25977
+ return false;
25978
+ }
25860
25979
  function cellCatAlongDim(values, dimIdx) {
25861
- let cells = values.map((v) => {
25862
- if (isRuntimeCell(v)) return v;
25863
- return RTV.cell([v], [1, 1]);
25864
- });
25980
+ let cells = [];
25981
+ for (const v of values) {
25982
+ if (isRuntimeCell(v)) {
25983
+ cells.push(v);
25984
+ } else if (isEmptyNonCellOperand(v)) {
25985
+ continue;
25986
+ } else {
25987
+ cells.push(RTV.cell([v], [1, 1]));
25988
+ }
25989
+ }
25865
25990
  cells = cells.filter((c) => c.data.length > 0);
25866
25991
  if (cells.length === 0) return RTV.cell([], [0, 0]);
25867
25992
  if (cells.length === 1) return cells[0];
@@ -29760,7 +29885,7 @@ var ExpressionParser = class extends ParserBase {
29760
29885
  }
29761
29886
  } else if (expr.type === "Ident" && this.peekToken() === 41 /* At */ && this.peekTokenAt(1) === 4 /* Ident */) {
29762
29887
  this.pos++;
29763
- const className = this.expectIdent();
29888
+ const className = this.parseQualifiedName();
29764
29889
  if (!this.consume(50 /* LParen */)) {
29765
29890
  throw this.error("expected '(' after superclass name in super call");
29766
29891
  }
@@ -30361,7 +30486,10 @@ var ControlFlowParser = class extends CommandParser {
30361
30486
  let catchBody = [];
30362
30487
  if (this.consume(27 /* Catch */)) {
30363
30488
  if (this.peekToken() === 4 /* Ident */) {
30364
- catchVar = this.expectIdent();
30489
+ const after = this.peekTokenAt(1);
30490
+ if (after === void 0 || after === 68 /* Newline */ || after === 49 /* Semicolon */ || after === 47 /* Comma */ || after === 15 /* End */) {
30491
+ catchVar = this.expectIdent();
30492
+ }
30365
30493
  }
30366
30494
  catchBody = this.parseBlock((t) => t === 15 /* End */);
30367
30495
  }
@@ -30486,8 +30614,12 @@ var ArgumentsParser = class extends ControlFlowParser {
30486
30614
  dimensions = this.parseArgDimensions();
30487
30615
  }
30488
30616
  let className = null;
30489
- if (this.peekToken() === 4 /* Ident */ && this.peekToken() !== 68 /* Newline */ && this.peekToken() !== 49 /* Semicolon */) {
30617
+ if (this.peekToken() === 4 /* Ident */) {
30490
30618
  className = this.next().lexeme;
30619
+ while (this.peekToken() === 45 /* Dot */) {
30620
+ this.consume(45 /* Dot */);
30621
+ className += "." + this.expectIdent();
30622
+ }
30491
30623
  }
30492
30624
  let validators = [];
30493
30625
  if (this.peekToken() === 54 /* LBrace */) {
@@ -30523,18 +30655,28 @@ var ArgumentsParser = class extends ControlFlowParser {
30523
30655
  return dims;
30524
30656
  }
30525
30657
  /**
30526
- * Parse validator list: {mustBeNumeric, mustBePositive}
30658
+ * Parse validator list: {mustBeNumeric, mustBePositive}. Validators may be
30659
+ * full function calls with arguments, e.g.
30660
+ * `{mustBeMember(x,["a","b"]), mustBeInRange(x,0,7)}`. The validator bodies
30661
+ * are not enforced at runtime, so we capture the top-level validator name
30662
+ * tokens and skip past any nested `(...)`, `[...]`, `{...}` so parsing
30663
+ * resumes correctly at the default value (`= expr`) or end of line.
30527
30664
  */
30528
30665
  parseArgValidators() {
30529
30666
  this.consume(54 /* LBrace */);
30530
30667
  const validators = [];
30531
- while (this.peekToken() !== 55 /* RBrace */ && this.peekToken() !== void 0) {
30532
- if (this.consume(47 /* Comma */)) continue;
30533
- if (this.peekToken() === 4 /* Ident */) {
30534
- validators.push(this.next().lexeme);
30535
- } else {
30536
- break;
30668
+ let depth = 0;
30669
+ while (this.peekToken() !== void 0) {
30670
+ const tok = this.peekToken();
30671
+ if (depth === 0 && tok === 55 /* RBrace */) break;
30672
+ if (tok === 50 /* LParen */ || tok === 52 /* LBracket */ || tok === 54 /* LBrace */) {
30673
+ depth++;
30674
+ } else if (tok === 51 /* RParen */ || tok === 53 /* RBracket */ || tok === 55 /* RBrace */) {
30675
+ depth--;
30676
+ } else if (depth === 0 && tok === 4 /* Ident */) {
30677
+ validators.push(this.peek().lexeme);
30537
30678
  }
30679
+ this.next();
30538
30680
  }
30539
30681
  this.consume(55 /* RBrace */);
30540
30682
  return validators;
@@ -30565,7 +30707,8 @@ var FunctionParser = class extends ArgumentsParser {
30565
30707
  if (this.consume(52 /* LBracket */)) {
30566
30708
  if (this.peekToken() !== 53 /* RBracket */) {
30567
30709
  outputs.push(this.expectIdentOrTilde());
30568
- while (this.consume(47 /* Comma */)) {
30710
+ while (this.peekToken() === 47 /* Comma */ || this.peekToken() === 4 /* Ident */ || this.peekToken() === 40 /* Tilde */) {
30711
+ this.consume(47 /* Comma */);
30569
30712
  outputs.push(this.expectIdentOrTilde());
30570
30713
  }
30571
30714
  }
@@ -30673,6 +30816,14 @@ var FunctionParser = class extends ArgumentsParser {
30673
30816
  };
30674
30817
 
30675
30818
  // src/numbl-core/parser/ClassParser.ts
30819
+ var HANDLE_BASE_CLASSES = /* @__PURE__ */ new Set([
30820
+ "handle",
30821
+ "dynamicprops",
30822
+ "matlab.mixin.Copyable",
30823
+ "matlab.mixin.SetGet",
30824
+ "matlab.mixin.SetGetExactNames",
30825
+ "hgsetget"
30826
+ ]);
30676
30827
  var ClassParser = class extends FunctionParser {
30677
30828
  // ── Imports & ClassDef ───────────────────────────────────────────────
30678
30829
  parseImport() {
@@ -30707,7 +30858,11 @@ var ClassParser = class extends FunctionParser {
30707
30858
  const name = this.parseQualifiedName();
30708
30859
  let superClass = null;
30709
30860
  if (this.consume(43 /* Less */)) {
30710
- superClass = this.parseQualifiedName();
30861
+ const supers = [this.parseQualifiedName()];
30862
+ while (this.consume(38 /* And */)) {
30863
+ supers.push(this.parseQualifiedName());
30864
+ }
30865
+ superClass = supers.some((s) => HANDLE_BASE_CLASSES.has(s)) ? "handle" : supers[0];
30711
30866
  }
30712
30867
  const members = [];
30713
30868
  while (true) {
@@ -31582,7 +31737,7 @@ function insertFunctionEndTokens(tokens) {
31582
31737
  }
31583
31738
  if (BLOCK_OPENERS.has(tok.token)) {
31584
31739
  depth++;
31585
- } else if (tok.token === 15 /* End */ && groupDepth === 0) {
31740
+ } else if (tok.token === 15 /* End */ && groupDepth === 0 && tokens[i - 1]?.token !== 45 /* Dot */) {
31586
31741
  depth--;
31587
31742
  if (depth === 0 && inFunction) {
31588
31743
  inFunction = false;
@@ -34294,6 +34449,8 @@ defineBuiltin({
34294
34449
  if (isRuntimeComplexNumber(v)) return v.im === 0;
34295
34450
  if (isRuntimeTensor(v)) return imagAllZero(v.imag);
34296
34451
  if (isRuntimeSparseMatrix(v)) return !v.pi || imagAllZero(v.pi);
34452
+ if (isRuntimeCell(v) || isRuntimeStruct(v) || isRuntimeStructArray(v) || isRuntimeString(v) || isRuntimeFunction(v))
34453
+ return false;
34297
34454
  return true;
34298
34455
  }
34299
34456
  }
@@ -34765,10 +34922,79 @@ defineBuiltin({
34765
34922
  name: "ischar",
34766
34923
  cases: [anyToLogicalCase((args) => isRuntimeChar(args[0]))]
34767
34924
  });
34925
+ defineBuiltin({
34926
+ name: "isstr",
34927
+ cases: [anyToLogicalCase((args) => isRuntimeChar(args[0]))]
34928
+ });
34768
34929
  defineBuiltin({
34769
34930
  name: "isstring",
34770
34931
  cases: [anyToLogicalCase((args) => isRuntimeString(args[0]))]
34771
34932
  });
34933
+ var MATLAB_KEYWORDS = /* @__PURE__ */ new Set([
34934
+ "break",
34935
+ "case",
34936
+ "catch",
34937
+ "classdef",
34938
+ "continue",
34939
+ "else",
34940
+ "elseif",
34941
+ "end",
34942
+ "for",
34943
+ "function",
34944
+ "global",
34945
+ "if",
34946
+ "otherwise",
34947
+ "parfor",
34948
+ "persistent",
34949
+ "return",
34950
+ "spmd",
34951
+ "switch",
34952
+ "try",
34953
+ "while"
34954
+ ]);
34955
+ function singleRowText(v) {
34956
+ if (isRuntimeChar(v)) {
34957
+ if (v.shape && v.shape.length >= 1 && v.shape[0] > 1) return null;
34958
+ return v.value;
34959
+ }
34960
+ if (isRuntimeString(v)) return v;
34961
+ return null;
34962
+ }
34963
+ defineBuiltin({
34964
+ name: "isvarname",
34965
+ cases: [
34966
+ anyToLogicalCase((args) => {
34967
+ const s = singleRowText(args[0]);
34968
+ if (s === null || s.length === 0 || s.length > 63) return false;
34969
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(s)) return false;
34970
+ return !MATLAB_KEYWORDS.has(s);
34971
+ })
34972
+ ]
34973
+ });
34974
+ defineBuiltin({
34975
+ name: "iskeyword",
34976
+ cases: [
34977
+ {
34978
+ // iskeyword() with no args returns the list of keywords (column cell).
34979
+ match: (argTypes) => {
34980
+ if (argTypes.length === 0) return [{ kind: "cell" }];
34981
+ if (argTypes.length === 1) return [{ kind: "boolean" }];
34982
+ return null;
34983
+ },
34984
+ apply: (args) => {
34985
+ if (args.length === 0) {
34986
+ const names = [...MATLAB_KEYWORDS].sort();
34987
+ return RTV.cell(
34988
+ names.map((n) => RTV.char(n)),
34989
+ [names.length, 1]
34990
+ );
34991
+ }
34992
+ const s = singleRowText(args[0]);
34993
+ return RTV.logical(s !== null && MATLAB_KEYWORDS.has(s));
34994
+ }
34995
+ }
34996
+ ]
34997
+ });
34772
34998
  defineBuiltin({
34773
34999
  name: "iscell",
34774
35000
  cases: [anyToLogicalCase((args) => isRuntimeCell(args[0]))]
@@ -34785,6 +35011,82 @@ defineBuiltin({
34785
35011
  name: "issparse",
34786
35012
  cases: [anyToLogicalCase((args) => isRuntimeSparseMatrix(args[0]))]
34787
35013
  });
35014
+ defineBuiltin({
35015
+ name: "isobject",
35016
+ cases: [
35017
+ anyToLogicalCase(
35018
+ (args) => isRuntimeClassInstance(args[0]) || isRuntimeClassInstanceArray(args[0])
35019
+ )
35020
+ ]
35021
+ });
35022
+ defineBuiltin({
35023
+ name: "isprop",
35024
+ help: {
35025
+ signatures: ["tf = isprop(obj, PropertyName)"],
35026
+ 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."
35027
+ },
35028
+ cases: [
35029
+ {
35030
+ match: (argTypes) => {
35031
+ if (argTypes.length !== 2) return null;
35032
+ return [{ kind: "boolean" }];
35033
+ },
35034
+ apply: (args) => {
35035
+ const v = args[0];
35036
+ const nameArg = args[1];
35037
+ let propName = null;
35038
+ if (isRuntimeChar(nameArg)) propName = nameArg.value;
35039
+ else if (isRuntimeString(nameArg)) propName = nameArg;
35040
+ let answer = false;
35041
+ if (propName !== null) {
35042
+ if (isRuntimeClassInstance(v)) answer = v.fields.has(propName);
35043
+ else if (isRuntimeClassInstanceArray(v))
35044
+ answer = v.elements.length > 0 && v.elements[0].fields.has(propName);
35045
+ }
35046
+ const shape = getShape(v);
35047
+ const n = shape.reduce((a, b) => a * b, 1);
35048
+ if (n === 1) return RTV.logical(answer);
35049
+ const data = allocFloat64Array(n);
35050
+ if (answer) data.fill(1);
35051
+ return new RuntimeTensor(data, shape, void 0, true);
35052
+ }
35053
+ }
35054
+ ]
35055
+ });
35056
+ defineBuiltin({
35057
+ name: "addprop",
35058
+ help: {
35059
+ signatures: ["p = addprop(obj, PropertyName)"],
35060
+ 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."
35061
+ },
35062
+ cases: [
35063
+ {
35064
+ match: (argTypes) => {
35065
+ if (argTypes.length !== 2) return null;
35066
+ return [{ kind: "unknown" }];
35067
+ },
35068
+ apply: (args) => {
35069
+ const obj = args[0];
35070
+ if (!isRuntimeClassInstance(obj))
35071
+ throw new RuntimeError(
35072
+ "addprop: first argument must be a dynamicprops object"
35073
+ );
35074
+ const name = toString(args[1]);
35075
+ if (!obj.fields.has(name)) {
35076
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
35077
+ incref(empty);
35078
+ obj.fields.set(name, empty);
35079
+ }
35080
+ return RTV.classInstance(
35081
+ "meta.DynamicProperty",
35082
+ ["Name"],
35083
+ true,
35084
+ /* @__PURE__ */ new Map([["Name", RTV.char(name)]])
35085
+ );
35086
+ }
35087
+ }
35088
+ ]
35089
+ });
34788
35090
  defineBuiltin({
34789
35091
  name: "isscalar",
34790
35092
  cases: [
@@ -34982,6 +35284,30 @@ function mkChar(value) {
34982
35284
  defineBuiltin({
34983
35285
  name: "class",
34984
35286
  cases: [
35287
+ // Old-style (pre-classdef) constructor form: class(structData, 'ClassName')
35288
+ // builds a value-type instance whose fields are the struct's fields. The
35289
+ // optional class(s,'Name',parent,...) inheritance form is not supported
35290
+ // (returns null → "unsupported argument types").
35291
+ {
35292
+ match: (argTypes) => {
35293
+ if (argTypes.length !== 2) return null;
35294
+ if (argTypes[0].kind !== "struct") return null;
35295
+ const k = argTypes[1].kind;
35296
+ if (k !== "char" && k !== "string") return null;
35297
+ return [{ kind: "unknown" }];
35298
+ },
35299
+ apply: (args) => {
35300
+ const s = args[0];
35301
+ if (!isRuntimeStruct(s))
35302
+ throw new RuntimeError(
35303
+ "class: first argument must be a scalar struct"
35304
+ );
35305
+ const nameVal = args[1];
35306
+ const className = isRuntimeChar(nameVal) ? nameVal.value : isRuntimeString(nameVal) ? nameVal : String(nameVal);
35307
+ const fieldNames = [...s.fields.keys()];
35308
+ return RTV.classInstance(className, fieldNames, false, s.fields);
35309
+ }
35310
+ },
34985
35311
  {
34986
35312
  match: (argTypes) => {
34987
35313
  if (argTypes.length !== 1) return null;
@@ -35019,6 +35345,26 @@ defineBuiltin({
35019
35345
  }
35020
35346
  ]
35021
35347
  });
35348
+ for (const name of ["superiorto", "inferiorto"]) {
35349
+ defineBuiltin({
35350
+ name,
35351
+ help: {
35352
+ signatures: [`${name}('Class1', 'Class2', ...)`],
35353
+ 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."
35354
+ },
35355
+ cases: [
35356
+ {
35357
+ match: (argTypes) => {
35358
+ for (const t of argTypes) {
35359
+ if (t.kind !== "char" && t.kind !== "string") return null;
35360
+ }
35361
+ return [];
35362
+ },
35363
+ apply: () => void 0
35364
+ }
35365
+ ]
35366
+ });
35367
+ }
35022
35368
  function fieldnamesApply(args) {
35023
35369
  if (args.length !== 1)
35024
35370
  throw new RuntimeError("fieldnames requires 1 argument");
@@ -35134,6 +35480,11 @@ function validateDim(x) {
35134
35480
  throw new RuntimeError("Size inputs must be nonnegative integers.");
35135
35481
  return Math.max(0, x);
35136
35482
  }
35483
+ function toScalarDim(v) {
35484
+ if (isRuntimeTensor(v) && v.data.length !== 1)
35485
+ throw new RuntimeError("Size inputs must be scalar.");
35486
+ return validateDim(toNumber(v));
35487
+ }
35137
35488
  function parseShapeArgs(args) {
35138
35489
  if (args.length === 1 && isRuntimeTensor(args[0])) {
35139
35490
  const t = args[0];
@@ -35141,7 +35492,7 @@ function parseShapeArgs(args) {
35141
35492
  for (let i = 0; i < t.data.length; i++) shape.push(validateDim(t.data[i]));
35142
35493
  return shape;
35143
35494
  }
35144
- return args.map((a) => validateDim(toNumber(a)));
35495
+ return args.map(toScalarDim);
35145
35496
  }
35146
35497
  function arrayConstructorCases(fillFn, scalarValue, opts) {
35147
35498
  const cases = [
@@ -35182,12 +35533,14 @@ function arrayConstructorCases(fillFn, scalarValue, opts) {
35182
35533
  return fillFn(shape);
35183
35534
  }
35184
35535
  },
35185
- // Multiple scalar args
35536
+ // Multiple scalar args. A 1×1 array counts as a scalar dimension, so
35537
+ // accept `tensor` here too (apply-time validates each is scalar).
35186
35538
  {
35187
35539
  match: (argTypes) => {
35188
35540
  if (argTypes.length <= 1) return null;
35189
35541
  for (const a of argTypes) {
35190
- if (a.kind !== "number" && a.kind !== "boolean") return null;
35542
+ if (a.kind !== "number" && a.kind !== "boolean" && a.kind !== "tensor")
35543
+ return null;
35191
35544
  }
35192
35545
  const shape = argTypes.map(
35193
35546
  (a) => a.kind === "number" && typeof a.exact === "number" ? a.exact : -1
@@ -35852,7 +36205,6 @@ function anyAllApply(name, mode) {
35852
36205
  if (isRuntimeComplexNumber(v)) return RTV.logical(v.re !== 0 || v.im !== 0);
35853
36206
  if (isRuntimeTensor(v)) {
35854
36207
  if (args.length === 1) {
35855
- if (v.data.length === 0) return RTV.logical(mode === "all");
35856
36208
  const d = firstReduceDim(v.shape);
35857
36209
  if (d === 0) return RTV.logical(scanLogical(v.data, v.imag, mode));
35858
36210
  return logicalAlongDim(v, d, mode);
@@ -36122,7 +36474,12 @@ function applyTextFn(v, fn) {
36122
36474
  }
36123
36475
  return RTV.cell(out, [...v.shape]);
36124
36476
  }
36125
- if (isRuntimeChar(v)) return RTV.char(fn(v.value));
36477
+ if (isRuntimeChar(v)) {
36478
+ if (v.shape && (v.shape[0] ?? 1) > 1) {
36479
+ return charRowsToMatrix(valueToCharRows(v).map(fn));
36480
+ }
36481
+ return RTV.char(fn(v.value));
36482
+ }
36126
36483
  if (isRuntimeString(v)) return RTV.string(fn(toString(v)));
36127
36484
  return v;
36128
36485
  }
@@ -36274,7 +36631,7 @@ registerIBuiltin({
36274
36631
  const str = toString(args[0]);
36275
36632
  const pat = toString(args[1]);
36276
36633
  const rep = toString(args[2]);
36277
- let flags = "g";
36634
+ let flags = "gs";
36278
36635
  for (let i = 3; i < args.length; i++) {
36279
36636
  const opt = toString(args[i]).toLowerCase();
36280
36637
  if (opt === "ignorecase") flags += "i";
@@ -36638,6 +36995,38 @@ registerIBuiltin({
36638
36995
  };
36639
36996
  }
36640
36997
  });
36998
+ registerIBuiltin({
36999
+ name: "isspace",
37000
+ help: {
37001
+ signatures: ["TF = isspace(str)"],
37002
+ description: "Return a logical array the same size as str, true where the character is whitespace. Numeric input is treated as Unicode code points."
37003
+ },
37004
+ resolve: (argTypes) => {
37005
+ if (argTypes.length !== 1) return null;
37006
+ const k = argTypes[0].kind;
37007
+ if (k !== "char" && k !== "string" && k !== "number" && k !== "boolean" && k !== "tensor") {
37008
+ return null;
37009
+ }
37010
+ const pred = (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
37011
+ return {
37012
+ outputTypes: [{ kind: "tensor", isComplex: false, isLogical: true }],
37013
+ apply: (args) => {
37014
+ const v = args[0];
37015
+ if (isRuntimeChar(v)) {
37016
+ return logicalRowFromString(
37017
+ v.value,
37018
+ pred,
37019
+ v.shape ? [...v.shape] : void 0
37020
+ );
37021
+ }
37022
+ if (isRuntimeString(v)) return logicalRowFromString(toString(v), pred);
37023
+ if (isRuntimeTensor(v))
37024
+ return logicalFromNumericTensor(v.data, v.shape, pred);
37025
+ return logicalFromNumericTensor([toNumber(v)], [1, 1], pred);
37026
+ }
37027
+ };
37028
+ }
37029
+ });
36641
37030
  registerIBuiltin({
36642
37031
  name: "contains",
36643
37032
  resolve: (argTypes) => {
@@ -37085,12 +37474,102 @@ registerIBuiltin({
37085
37474
  };
37086
37475
  }
37087
37476
  });
37477
+ function collectRows(v) {
37478
+ if (isRuntimeCell(v)) {
37479
+ const rows = [];
37480
+ for (const el of v.data) rows.push(...valueToCharRows(el));
37481
+ return rows;
37482
+ }
37483
+ return valueToCharRows(v);
37484
+ }
37485
+ registerIBuiltin({
37486
+ name: "strmatch",
37487
+ help: {
37488
+ signatures: [
37489
+ "x = strmatch(str, strarray)",
37490
+ "x = strmatch(str, strarray, 'exact')"
37491
+ ],
37492
+ 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."
37493
+ },
37494
+ resolve: (argTypes) => {
37495
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
37496
+ if (!isTextType(argTypes[0])) return null;
37497
+ const sa = argTypes[1];
37498
+ if (sa.kind !== "char" && sa.kind !== "string" && sa.kind !== "cell")
37499
+ return null;
37500
+ return {
37501
+ outputTypes: [{ kind: "tensor", isComplex: false }],
37502
+ apply: (args) => {
37503
+ const str = toString(args[0]);
37504
+ let rows = collectRows(args[1]);
37505
+ const n = rows.length === 0 ? 0 : Math.max(...rows.map((r) => r.length));
37506
+ rows = rows.map((r) => r.padEnd(n, " "));
37507
+ const exactMatch = args.length === 3;
37508
+ let s = str;
37509
+ let len = s.length;
37510
+ if (len > n) {
37511
+ return RTV.tensor(allocFloat64Array(0), [0, 1]);
37512
+ }
37513
+ if (exactMatch && len < n) {
37514
+ const useNull = rows.some((r) => r.charCodeAt(n - 1) === 0);
37515
+ s = s.padEnd(n, useNull ? "\0" : " ");
37516
+ len = n;
37517
+ }
37518
+ const matches2 = [];
37519
+ for (let r = 0; r < rows.length; r++) {
37520
+ let ok = true;
37521
+ for (let i = 0; i < len; i++) {
37522
+ if (rows[r][i] !== s[i]) {
37523
+ ok = false;
37524
+ break;
37525
+ }
37526
+ }
37527
+ if (ok) matches2.push(r + 1);
37528
+ }
37529
+ return RTV.tensor(allocFloat64Array(matches2), [matches2.length, 1]);
37530
+ }
37531
+ };
37532
+ }
37533
+ });
37534
+ function valueToCharRows(v) {
37535
+ if (isRuntimeChar(v)) {
37536
+ const cols = v.shape ? v.shape[1] ?? v.value.length : v.value.length;
37537
+ const numRows = v.shape ? v.shape[0] ?? 1 : 1;
37538
+ if (numRows <= 1) return [v.value];
37539
+ const out = [];
37540
+ for (let r = 0; r < numRows; r++)
37541
+ out.push(v.value.slice(r * cols, (r + 1) * cols));
37542
+ return out;
37543
+ }
37544
+ if (isRuntimeString(v)) return [v];
37545
+ if (isRuntimeNumber(v)) return [String.fromCharCode(Math.round(v))];
37546
+ if (isRuntimeTensor(v)) {
37547
+ const rows = v.shape.length >= 2 ? v.shape[0] ?? 1 : 1;
37548
+ const cols = v.shape.length >= 2 ? v.shape[1] ?? 0 : v.data.length;
37549
+ const out = [];
37550
+ for (let r = 0; r < rows; r++) {
37551
+ let s = "";
37552
+ for (let c = 0; c < cols; c++)
37553
+ s += String.fromCharCode(Math.round(v.data[c * rows + r]));
37554
+ out.push(s);
37555
+ }
37556
+ return out;
37557
+ }
37558
+ throw new RuntimeError("char: unsupported cell element type");
37559
+ }
37560
+ function charRowsToMatrix(rows) {
37561
+ if (rows.length === 0) return RTV.char("");
37562
+ const width = Math.max(...rows.map((r) => r.length));
37563
+ const padded = rows.map((r) => r.padEnd(width, " "));
37564
+ if (rows.length === 1) return RTV.char(padded[0]);
37565
+ return new RuntimeChar(padded.join(""), [rows.length, width]);
37566
+ }
37088
37567
  registerIBuiltin({
37089
37568
  name: "char",
37090
37569
  resolve: (argTypes) => {
37091
37570
  if (argTypes.length !== 1) return null;
37092
37571
  const a = argTypes[0];
37093
- if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor")
37572
+ if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor" && a.kind !== "cell")
37094
37573
  return null;
37095
37574
  return {
37096
37575
  outputTypes: [{ kind: "char" }],
@@ -37107,6 +37586,11 @@ registerIBuiltin({
37107
37586
  }
37108
37587
  return RTV.char(chars.join(""));
37109
37588
  }
37589
+ if (isRuntimeCell(v)) {
37590
+ const rows = [];
37591
+ for (const el of v.data) rows.push(...valueToCharRows(el));
37592
+ return charRowsToMatrix(rows);
37593
+ }
37110
37594
  throw new RuntimeError("char: unsupported arguments");
37111
37595
  }
37112
37596
  };
@@ -37174,6 +37658,8 @@ registerIBuiltin({
37174
37658
  if (!isTextType(argTypes[0]) || !isTextType(argTypes[1])) return null;
37175
37659
  const outputTypes = [{ kind: "number" }];
37176
37660
  if (nargout >= 2) outputTypes.push({ kind: "number" });
37661
+ if (nargout >= 3) outputTypes.push({ kind: "char" });
37662
+ if (nargout >= 4) outputTypes.push({ kind: "number" });
37177
37663
  return {
37178
37664
  outputTypes,
37179
37665
  apply: (args, nout) => {
@@ -37183,6 +37669,7 @@ registerIBuiltin({
37183
37669
  const results = [];
37184
37670
  let strPos = 0;
37185
37671
  let fmtPos = 0;
37672
+ let matchFailure = false;
37186
37673
  while (fmtPos < fmt.length && strPos < str.length && results.length < maxCount) {
37187
37674
  if (fmt[fmtPos] === "%") {
37188
37675
  fmtPos++;
@@ -37194,22 +37681,34 @@ registerIBuiltin({
37194
37681
  }
37195
37682
  if (spec === "d" || spec === "i") {
37196
37683
  const m = str.slice(strPos).match(/^[+-]?\d+/);
37197
- if (!m) break;
37684
+ if (!m) {
37685
+ matchFailure = true;
37686
+ break;
37687
+ }
37198
37688
  results.push(parseInt(m[0], 10));
37199
37689
  strPos += m[0].length;
37200
37690
  } else if (spec === "f" || spec === "e" || spec === "g") {
37201
37691
  const m = str.slice(strPos).match(/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?/);
37202
- if (!m) break;
37692
+ if (!m) {
37693
+ matchFailure = true;
37694
+ break;
37695
+ }
37203
37696
  results.push(parseFloat(m[0]));
37204
37697
  strPos += m[0].length;
37205
37698
  } else if (spec === "x") {
37206
37699
  const m = str.slice(strPos).match(/^[+-]?[0-9a-fA-F]+/);
37207
- if (!m) break;
37700
+ if (!m) {
37701
+ matchFailure = true;
37702
+ break;
37703
+ }
37208
37704
  results.push(parseInt(m[0], 16));
37209
37705
  strPos += m[0].length;
37210
37706
  } else if (spec === "o") {
37211
37707
  const m = str.slice(strPos).match(/^[+-]?[0-7]+/);
37212
- if (!m) break;
37708
+ if (!m) {
37709
+ matchFailure = true;
37710
+ break;
37711
+ }
37213
37712
  results.push(parseInt(m[0], 8));
37214
37713
  strPos += m[0].length;
37215
37714
  } else if (spec === "c") {
@@ -37217,7 +37716,10 @@ registerIBuiltin({
37217
37716
  strPos++;
37218
37717
  } else if (spec === "s") {
37219
37718
  const m = str.slice(strPos).match(/^\S+/);
37220
- if (!m) break;
37719
+ if (!m) {
37720
+ matchFailure = true;
37721
+ break;
37722
+ }
37221
37723
  for (let ci = 0; ci < m[0].length && results.length < maxCount; ci++) {
37222
37724
  results.push(m[0].charCodeAt(ci));
37223
37725
  }
@@ -37227,7 +37729,10 @@ registerIBuiltin({
37227
37729
  fmtPos++;
37228
37730
  while (strPos < str.length && /\s/.test(str[strPos])) strPos++;
37229
37731
  } else {
37230
- if (str[strPos] !== fmt[fmtPos]) break;
37732
+ if (str[strPos] !== fmt[fmtPos]) {
37733
+ matchFailure = true;
37734
+ break;
37735
+ }
37231
37736
  strPos++;
37232
37737
  fmtPos++;
37233
37738
  }
@@ -37237,7 +37742,16 @@ registerIBuiltin({
37237
37742
  }
37238
37743
  const vals = results.length === 1 ? RTV.num(results[0]) : RTV.tensor(allocFloat64Array(results), [results.length, 1]);
37239
37744
  if (nout >= 2) {
37240
- return [vals, RTV.num(results.length)];
37745
+ const out = [vals, RTV.num(results.length)];
37746
+ if (nout >= 3) {
37747
+ out.push(
37748
+ RTV.char(matchFailure ? "Matching failure in format." : "")
37749
+ );
37750
+ }
37751
+ if (nout >= 4) {
37752
+ out.push(RTV.num(strPos + 1));
37753
+ }
37754
+ return out;
37241
37755
  }
37242
37756
  return vals;
37243
37757
  }
@@ -38648,8 +39162,34 @@ function normImplTensor(v, args) {
38648
39162
  return RTV.num(maxRowSum);
38649
39163
  }
38650
39164
  if (p2 === 2) {
39165
+ let anyNaN = false;
39166
+ let anyInf = false;
39167
+ for (let i = 0; i < v.data.length; i++) {
39168
+ const re = v.data[i];
39169
+ const im = imag2 ? imag2[i] : 0;
39170
+ if (Number.isNaN(re) || Number.isNaN(im)) anyNaN = true;
39171
+ else if (!isFinite(re) || !isFinite(im)) anyInf = true;
39172
+ }
39173
+ if (anyNaN) return RTV.num(NaN);
39174
+ if (anyInf) return RTV.num(Infinity);
38651
39175
  const bridge = getEffectiveBridge("norm", "svd");
38652
39176
  if (bridge && bridge.svd) {
39177
+ if (imag2) {
39178
+ const R = allocFloat64Array(4 * rows * cols);
39179
+ const tm = 2 * rows;
39180
+ for (let j = 0; j < cols; j++) {
39181
+ for (let i = 0; i < rows; i++) {
39182
+ const a = v.data[j * rows + i];
39183
+ const b = imag2[j * rows + i];
39184
+ R[j * tm + i] = a;
39185
+ R[j * tm + (rows + i)] = b;
39186
+ R[(cols + j) * tm + i] = -b;
39187
+ R[(cols + j) * tm + (rows + i)] = a;
39188
+ }
39189
+ }
39190
+ const result2 = bridge.svd(R, tm, 2 * cols, false, false);
39191
+ return RTV.num(result2.S[0]);
39192
+ }
38653
39193
  const f64 = v.data instanceof Float64Array ? v.data : allocFloat64Array(v.data);
38654
39194
  const result = bridge.svd(f64, rows, cols, false, false);
38655
39195
  return RTV.num(result.S[0]);
@@ -39409,6 +39949,100 @@ function svdApply(args, nargout) {
39409
39949
  [k, 1]
39410
39950
  );
39411
39951
  }
39952
+ function nullApply(args) {
39953
+ let A = args[0];
39954
+ if (isRuntimeNumber(A) || isRuntimeComplexNumber(A)) {
39955
+ A = isRuntimeNumber(A) ? RTV.tensor(allocFloat64Array([A]), [1, 1]) : RTV.tensor(
39956
+ allocFloat64Array([A.re]),
39957
+ [1, 1],
39958
+ allocFloat64Array([A.im])
39959
+ );
39960
+ }
39961
+ if (!isRuntimeTensor(A))
39962
+ throw new RuntimeError("null: argument must be a numeric matrix");
39963
+ const [m, n] = tensorSize2D(A);
39964
+ const [, Sdiag, V] = svdApply([A], 3);
39965
+ const k = Math.min(m, n);
39966
+ const s = [];
39967
+ for (let i = 0; i < k; i++) s.push(Sdiag.data[colMajorIndex(i, i, m)]);
39968
+ const maxS = s.length > 0 ? Math.max(...s) : 0;
39969
+ const tol = args.length > 1 && args[1] !== void 0 ? toNumber(args[1]) : Math.max(m, n) * epsOf(maxS);
39970
+ let r = 0;
39971
+ for (const sv of s) if (sv > tol) r++;
39972
+ const cols = n - r;
39973
+ const out = allocFloat64Array(n * cols);
39974
+ const outImag = V.imag ? allocFloat64Array(n * cols) : void 0;
39975
+ for (let c = 0; c < cols; c++) {
39976
+ for (let i = 0; i < n; i++) {
39977
+ const src = colMajorIndex(i, r + c, n);
39978
+ out[colMajorIndex(i, c, n)] = V.data[src];
39979
+ if (outImag) outImag[colMajorIndex(i, c, n)] = V.imag[src];
39980
+ }
39981
+ }
39982
+ return RTV.tensor(out, [n, cols], outImag);
39983
+ }
39984
+ registerIBuiltin({
39985
+ name: "null",
39986
+ resolve: (argTypes, nargout) => {
39987
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2) return null;
39988
+ if (!isNumericJitType(argTypes[0])) return null;
39989
+ const isComplex = argTypes[0].kind === "tensor" && argTypes[0].isComplex;
39990
+ return {
39991
+ outputTypes: [tensorType(isComplex || void 0)],
39992
+ apply: (args) => nullApply(args)
39993
+ };
39994
+ }
39995
+ });
39996
+ function computeBandwidth(A) {
39997
+ let lower = 0;
39998
+ let upper = 0;
39999
+ const note = (i, j) => {
40000
+ if (i > j) lower = Math.max(lower, i - j);
40001
+ else if (j > i) upper = Math.max(upper, j - i);
40002
+ };
40003
+ if (isRuntimeNumber(A) || isRuntimeComplexNumber(A)) return [0, 0];
40004
+ if (isRuntimeSparseMatrix(A)) {
40005
+ for (let j = 0; j < A.n; j++) {
40006
+ for (let k = A.jc[j]; k < A.jc[j + 1]; k++) {
40007
+ if (A.pr[k] === 0 && (!A.pi || A.pi[k] === 0)) continue;
40008
+ note(A.ir[k], j);
40009
+ }
40010
+ }
40011
+ return [lower, upper];
40012
+ }
40013
+ if (isRuntimeTensor(A)) {
40014
+ const [m, n] = tensorSize2D(A);
40015
+ for (let j = 0; j < n; j++) {
40016
+ for (let i = 0; i < m; i++) {
40017
+ const idx = colMajorIndex(i, j, m);
40018
+ if (A.data[idx] === 0 && (!A.imag || A.imag[idx] === 0)) continue;
40019
+ note(i, j);
40020
+ }
40021
+ }
40022
+ return [lower, upper];
40023
+ }
40024
+ throw new RuntimeError("bandwidth: first argument must be a numeric matrix");
40025
+ }
40026
+ function bandwidthApply(args, nargout) {
40027
+ const [lower, upper] = computeBandwidth(args[0]);
40028
+ if (args.length >= 2 && args[1] !== void 0) {
40029
+ const type = parseStringArgLower(args[1]);
40030
+ if (type === "lower") return RTV.num(lower);
40031
+ if (type === "upper") return RTV.num(upper);
40032
+ throw new RuntimeError("bandwidth: TYPE must be 'lower' or 'upper'");
40033
+ }
40034
+ if (nargout >= 2) return [RTV.num(lower), RTV.num(upper)];
40035
+ return RTV.num(lower);
40036
+ }
40037
+ registerIBuiltin({
40038
+ name: "bandwidth",
40039
+ resolve: (argTypes, nargout) => {
40040
+ if (nargout > 2 || argTypes.length < 1 || argTypes.length > 2) return null;
40041
+ if (!isNumericJitType(argTypes[0])) return null;
40042
+ const outs = nargout >= 2 ? [NUM, NUM] : [NUM];
40043
+ return { outputTypes: outs, apply: (args, n) => bandwidthApply(args, n) };
40044
+ }
40045
+ });
39412
40046
  function computeATA(A_data, m, n) {
39413
40047
  const result = allocFloat64Array(n * n);
39414
40048
  for (let j = 0; j < n; j++)
@@ -41279,6 +41913,33 @@ function validateSizeArg(x) {
41279
41913
  }
41280
41914
  return x < 0 ? 0 : x;
41281
41915
  }
41916
+ function parseReshapeDims(args, total) {
41917
+ let rawDims;
41918
+ if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
41919
+ rawDims = Array.from(args[1].data).map((x) => validateSizeArg(x));
41920
+ } else {
41921
+ rawDims = args.slice(1).map((a) => {
41922
+ if (isRuntimeTensor(a) && a.data.length === 0) return null;
41923
+ return validateSizeArg(toNumber(a));
41924
+ });
41925
+ }
41926
+ const autoCount = rawDims.filter((d) => d === null).length;
41927
+ if (autoCount > 1)
41928
+ throw new RuntimeError("reshape: only one dimension size can be []");
41929
+ let shape;
41930
+ if (autoCount === 1) {
41931
+ const known = rawDims.filter((d) => d !== null);
41932
+ const knownProduct = known.reduce((a, b) => a * b, 1);
41933
+ if (knownProduct === 0 || total % knownProduct !== 0)
41934
+ throw new RuntimeError("reshape: number of elements must not change");
41935
+ shape = rawDims.map((d) => d === null ? total / knownProduct : d);
41936
+ } else {
41937
+ shape = rawDims;
41938
+ }
41939
+ if (numel(shape) !== total)
41940
+ throw new RuntimeError("reshape: number of elements must not change");
41941
+ return shape;
41942
+ }
41282
41943
  defineBuiltin({
41283
41944
  name: "reshape",
41284
41945
  cases: [
@@ -41290,31 +41951,31 @@ defineBuiltin({
41290
41951
  const v = args[0];
41291
41952
  if (isRuntimeSparseMatrix(v)) {
41292
41953
  const totalEl = v.m * v.n;
41293
- let rawDims2;
41954
+ let rawDims;
41294
41955
  if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
41295
- rawDims2 = Array.from(args[1].data).map((x) => Math.round(x));
41956
+ rawDims = Array.from(args[1].data).map((x) => Math.round(x));
41296
41957
  } else {
41297
- rawDims2 = args.slice(1).map((a) => {
41958
+ rawDims = args.slice(1).map((a) => {
41298
41959
  if (isRuntimeTensor(a) && a.data.length === 0) return null;
41299
41960
  return Math.round(toNumber(a));
41300
41961
  });
41301
41962
  }
41302
- const autoCount2 = rawDims2.filter((d) => d === null).length;
41303
- if (autoCount2 > 1)
41963
+ const autoCount = rawDims.filter((d) => d === null).length;
41964
+ if (autoCount > 1)
41304
41965
  throw new RuntimeError(
41305
41966
  "reshape: only one dimension size can be []"
41306
41967
  );
41307
41968
  let shape2;
41308
- if (autoCount2 === 1) {
41309
- const known = rawDims2.filter((d) => d !== null);
41969
+ if (autoCount === 1) {
41970
+ const known = rawDims.filter((d) => d !== null);
41310
41971
  const knownProduct = known.reduce((a, b) => a * b, 1);
41311
41972
  if (totalEl % knownProduct !== 0)
41312
41973
  throw new RuntimeError(
41313
41974
  "reshape: number of elements must not change"
41314
41975
  );
41315
- shape2 = rawDims2.map((d) => d === null ? totalEl / knownProduct : d);
41976
+ shape2 = rawDims.map((d) => d === null ? totalEl / knownProduct : d);
41316
41977
  } else {
41317
- shape2 = rawDims2;
41978
+ shape2 = rawDims;
41318
41979
  }
41319
41980
  if (shape2.length !== 2)
41320
41981
  throw new RuntimeError("reshape: sparse matrices must be 2-D");
@@ -41352,40 +42013,32 @@ defineBuiltin({
41352
42013
  jc[newN] = ti;
41353
42014
  return RTV.sparseMatrix(newM, newN, ir, jc, pr, pi2);
41354
42015
  }
42016
+ if (isRuntimeChar(v)) {
42017
+ const srcRows = v.shape ? v.shape[0] : 1;
42018
+ const srcCols = v.shape ? v.shape[1] ?? 0 : v.value.length;
42019
+ const total = v.value.length;
42020
+ const reqShape = parseReshapeDims(args, total);
42021
+ const s = [...reqShape];
42022
+ while (s.length > 2 && s[s.length - 1] === 1) s.pop();
42023
+ if (s.length > 2)
42024
+ throw new RuntimeError("reshape: char arrays must be 2-D");
42025
+ const nr = s[0];
42026
+ const nc = s.length >= 2 ? s[1] : 1;
42027
+ const linear = new Array(total);
42028
+ for (let j = 0; j < srcCols; j++)
42029
+ for (let i = 0; i < srcRows; i++)
42030
+ linear[j * srcRows + i] = v.value[i * srcCols + j];
42031
+ const chars = new Array(total);
42032
+ for (let j2 = 0; j2 < nc; j2++)
42033
+ for (let i2 = 0; i2 < nr; i2++)
42034
+ chars[i2 * nc + j2] = linear[j2 * nr + i2];
42035
+ return new RuntimeChar(chars.join(""), [nr, nc]);
42036
+ }
41355
42037
  if (!isRuntimeTensor(v) && !isRuntimeNumber(v) && !isRuntimeComplexNumber(v))
41356
42038
  throw new RuntimeError("reshape: first argument must be numeric");
41357
42039
  const data = isRuntimeTensor(v) ? v.data : isRuntimeComplexNumber(v) ? allocFloat64Array([v.re]) : allocFloat64Array([v]);
41358
42040
  const imag2 = isRuntimeTensor(v) ? v.imag : isRuntimeComplexNumber(v) ? allocFloat64Array([v.im]) : void 0;
41359
- let rawDims;
41360
- if (args.length === 2 && isRuntimeTensor(args[1]) && args[1].data.length > 1) {
41361
- rawDims = Array.from(args[1].data).map((x) => validateSizeArg(x));
41362
- } else {
41363
- rawDims = args.slice(1).map((a) => {
41364
- if (isRuntimeTensor(a) && a.data.length === 0) return null;
41365
- return validateSizeArg(toNumber(a));
41366
- });
41367
- }
41368
- const autoCount = rawDims.filter((d) => d === null).length;
41369
- if (autoCount > 1)
41370
- throw new RuntimeError("reshape: only one dimension size can be []");
41371
- let shape;
41372
- if (autoCount === 1) {
41373
- const known = rawDims.filter((d) => d !== null);
41374
- const knownProduct = known.reduce((a, b) => a * b, 1);
41375
- if (data.length % knownProduct !== 0)
41376
- throw new RuntimeError(
41377
- "reshape: number of elements must not change"
41378
- );
41379
- shape = rawDims.map(
41380
- (d) => d === null ? data.length / knownProduct : d
41381
- );
41382
- } else {
41383
- shape = rawDims;
41384
- }
41385
- const n = numel(shape);
41386
- if (n !== data.length) {
41387
- throw new RuntimeError("reshape: number of elements must not change");
41388
- }
42041
+ const shape = parseReshapeDims(args, data.length);
41389
42042
  if (isRuntimeTensor(v)) {
41390
42043
  const dataCopy = allocFloat64Array(data);
41391
42044
  const imagCopy = imag2 ? allocFloat64Array(imag2) : void 0;
@@ -41797,6 +42450,35 @@ defineBuiltin({
41797
42450
  }
41798
42451
  ]
41799
42452
  });
42453
+ function repmatObjects(v, repArgs) {
42454
+ const srcElements = isRuntimeClassInstanceArray(v) ? v.elements : [v];
42455
+ const [rows, cols] = isRuntimeClassInstanceArray(v) ? v.shape : [1, 1];
42456
+ const className = v.className;
42457
+ let reps;
42458
+ if (repArgs.length === 1) {
42459
+ const arg1 = repArgs[0];
42460
+ if (isRuntimeTensor(arg1)) {
42461
+ reps = Array.from(arg1.data).map((x) => validateSizeArg(x));
42462
+ } else {
42463
+ const n = validateSizeArg(toNumber(arg1));
42464
+ reps = [n, n];
42465
+ }
42466
+ } else {
42467
+ reps = repArgs.map((a) => validateSizeArg(toNumber(a)));
42468
+ }
42469
+ const rRep = reps[0] ?? 1;
42470
+ const cRep = reps.length >= 2 ? reps[1] : reps[0] ?? 1;
42471
+ const newRows = rows * rRep;
42472
+ const newCols = cols * cRep;
42473
+ const out = new Array(Math.max(0, newRows * newCols));
42474
+ for (let J = 0; J < newCols; J++) {
42475
+ for (let I = 0; I < newRows; I++) {
42476
+ const src = srcElements[I % rows + J % cols * rows];
42477
+ out[I + J * newRows] = copyClassInstance(src);
42478
+ }
42479
+ }
42480
+ return out.length === 1 ? out[0] : RTV.classInstanceArray(className, out, [newRows, newCols]);
42481
+ }
41800
42482
  defineBuiltin({
41801
42483
  name: "repmat",
41802
42484
  cases: [
@@ -41821,6 +42503,8 @@ defineBuiltin({
41821
42503
  if (args.length < 2)
41822
42504
  throw new RuntimeError("repmat requires at least 2 arguments");
41823
42505
  let v = args[0];
42506
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
42507
+ return repmatObjects(v, args.slice(1));
41824
42508
  if (isRuntimeSparseMatrix(v)) v = sparseToDense(v);
41825
42509
  let reps;
41826
42510
  if (args.length === 2) {
@@ -41985,6 +42669,62 @@ defineBuiltin({
41985
42669
  }
41986
42670
  ]
41987
42671
  });
42672
+ function copyClassInstance(inst) {
42673
+ if (inst.isHandleClass) return inst;
42674
+ return new RuntimeClassInstance(
42675
+ inst.className,
42676
+ new Map(inst.fields),
42677
+ inst.isHandleClass,
42678
+ inst._builtinData
42679
+ );
42680
+ }
42681
+ function repelemObjects(v, repArgs) {
42682
+ const srcElements = isRuntimeClassInstanceArray(v) ? v.elements : [v];
42683
+ const [rows, cols] = isRuntimeClassInstanceArray(v) ? v.shape : [1, 1];
42684
+ const className = v.className;
42685
+ const wrap = (elements, shape) => elements.length === 1 ? elements[0] : RTV.classInstanceArray(className, elements, shape);
42686
+ if (repArgs.length === 1) {
42687
+ const repArg = repArgs[0];
42688
+ if (isRuntimeTensor(repArg) && repArg.data.length > 1) {
42689
+ const counts = repArg.data;
42690
+ if (counts.length !== srcElements.length)
42691
+ throw new RuntimeError(
42692
+ `repelem: counts vector length (${counts.length}) must match the number of elements (${srcElements.length})`
42693
+ );
42694
+ const out3 = [];
42695
+ for (let i = 0; i < srcElements.length; i++) {
42696
+ const c = Math.max(0, Math.round(counts[i]));
42697
+ for (let j = 0; j < c; j++) out3.push(copyClassInstance(srcElements[i]));
42698
+ }
42699
+ const isCol2 = cols === 1 && rows !== 1;
42700
+ return wrap(out3, isCol2 ? [out3.length, 1] : [1, out3.length]);
42701
+ }
42702
+ const n = Math.max(0, Math.round(toNumber(repArg)));
42703
+ const out2 = [];
42704
+ for (const el of srcElements)
42705
+ for (let j = 0; j < n; j++) out2.push(copyClassInstance(el));
42706
+ const isCol = cols === 1 && rows !== 1;
42707
+ return wrap(out2, isCol ? [out2.length, 1] : [1, out2.length]);
42708
+ }
42709
+ const rRep = Math.max(0, Math.round(toNumber(repArgs[0])));
42710
+ const cRep = Math.max(0, Math.round(toNumber(repArgs[1])));
42711
+ const newRows = rows * rRep;
42712
+ const newCols = cols * cRep;
42713
+ const out = new Array(newRows * newCols);
42714
+ for (let c = 0; c < cols; c++) {
42715
+ for (let r = 0; r < rows; r++) {
42716
+ const src = srcElements[c * rows + r];
42717
+ for (let dc = 0; dc < cRep; dc++) {
42718
+ for (let dr = 0; dr < rRep; dr++) {
42719
+ const dstRow = r * rRep + dr;
42720
+ const dstCol = c * cRep + dc;
42721
+ out[dstCol * newRows + dstRow] = copyClassInstance(src);
42722
+ }
42723
+ }
42724
+ }
42725
+ }
42726
+ return wrap(out, [newRows, newCols]);
42727
+ }
41988
42728
  defineBuiltin({
41989
42729
  name: "repelem",
41990
42730
  cases: [
@@ -41994,6 +42734,8 @@ defineBuiltin({
41994
42734
  if (args.length < 2)
41995
42735
  throw new RuntimeError("repelem requires at least 2 arguments");
41996
42736
  const v = args[0];
42737
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
42738
+ return repelemObjects(v, args.slice(1));
41997
42739
  if (args.length === 2) {
41998
42740
  const repArg = args[1];
41999
42741
  if (isRuntimeTensor(repArg) && repArg.data.length > 1) {
@@ -42116,7 +42858,15 @@ defineBuiltin({
42116
42858
  }
42117
42859
  return RTV.tensor(dataCopy, newShape, imagCopy);
42118
42860
  }
42119
- throw new RuntimeError("squeeze: argument must be numeric");
42861
+ if (isRuntimeCell(v)) {
42862
+ const shape = [...v.shape];
42863
+ while (shape.length > 2 && shape[shape.length - 1] === 1) shape.pop();
42864
+ if (shape.length <= 2) return RTV.cell(v.data, shape);
42865
+ const newShape = shape.filter((d) => d !== 1);
42866
+ while (newShape.length < 2) newShape.push(1);
42867
+ return RTV.cell(v.data, newShape);
42868
+ }
42869
+ return v;
42120
42870
  }
42121
42871
  }
42122
42872
  ]
@@ -42786,6 +43536,8 @@ defineBuiltin({
42786
43536
  ]
42787
43537
  });
42788
43538
  function bitwiseOp(a, b, op, name) {
43539
+ if (typeof a === "boolean") a = a ? 1 : 0;
43540
+ if (typeof b === "boolean") b = b ? 1 : 0;
42789
43541
  if (isRuntimeNumber(a) && isRuntimeNumber(b)) {
42790
43542
  return RTV.num(op(Math.round(a), Math.round(b)));
42791
43543
  }
@@ -45053,6 +45805,56 @@ defineBuiltin({
45053
45805
  }
45054
45806
  ]
45055
45807
  });
45808
+ function froundArray(src) {
45809
+ const out = allocFloat64Array(src.length);
45810
+ for (let i = 0; i < src.length; i++) out[i] = Math.fround(src[i]);
45811
+ return out;
45812
+ }
45813
+ function singleApply(v) {
45814
+ if (isRuntimeChar(v)) {
45815
+ if (v.value.length === 0) return RTV.tensor(allocFloat64Array(0), [0, 0]);
45816
+ if (v.value.length === 1)
45817
+ return RTV.num(Math.fround(v.value.charCodeAt(0)));
45818
+ return RTV.row(Array.from(v.value).map((c) => Math.fround(c.charCodeAt(0))));
45819
+ }
45820
+ if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
45821
+ if (isRuntimeNumber(v)) return RTV.num(Math.fround(v));
45822
+ if (isRuntimeComplexNumber(v))
45823
+ return RTV.complex(Math.fround(v.re), Math.fround(v.im));
45824
+ if (isRuntimeTensor(v)) {
45825
+ const imag2 = v.imag ? froundArray(v.imag) : void 0;
45826
+ return RTV.tensor(froundArray(v.data), v.shape.slice(), imag2);
45827
+ }
45828
+ if (isRuntimeClassInstance(v) && v._builtinData !== void 0)
45829
+ return singleApply(v._builtinData);
45830
+ return RTV.num(Math.fround(toNumber(v)));
45831
+ }
45832
+ defineBuiltin({
45833
+ name: "single",
45834
+ cases: [
45835
+ {
45836
+ match: (argTypes) => {
45837
+ if (argTypes.length !== 1) return null;
45838
+ const a = argTypes[0];
45839
+ if (a.kind === "number" || a.kind === "boolean" || a.kind === "char" || a.kind === "class_instance")
45840
+ return [{ kind: "number" }];
45841
+ if (a.kind === "complex_or_number")
45842
+ return [{ kind: "complex_or_number" }];
45843
+ if (a.kind === "tensor")
45844
+ return [
45845
+ {
45846
+ kind: "tensor",
45847
+ isComplex: a.isComplex,
45848
+ shape: a.shape,
45849
+ ndim: a.ndim
45850
+ }
45851
+ ];
45852
+ return null;
45853
+ },
45854
+ apply: (args) => singleApply(args[0])
45855
+ }
45856
+ ]
45857
+ });
45056
45858
  var INT_RANGES = [
45057
45859
  { name: "int8", min: -128, max: 127 },
45058
45860
  { name: "int16", min: -32768, max: 32767 },
@@ -45607,11 +46409,20 @@ defineBuiltin({
45607
46409
  },
45608
46410
  apply: (args) => {
45609
46411
  const v = args[0];
45610
- if (isRuntimeStructArray(v))
45611
- return RTV.logical(v.fieldNames.includes(toString(args[1])));
45612
- if (!isRuntimeStruct(v) && !isRuntimeClassInstance(v))
45613
- return RTV.logical(false);
45614
- return RTV.logical(v.fields.has(toString(args[1])));
46412
+ const hasField = (name) => {
46413
+ if (isRuntimeStructArray(v)) return v.fieldNames.includes(name);
46414
+ if (!isRuntimeStruct(v) && !isRuntimeClassInstance(v)) return false;
46415
+ return v.fields.has(name);
46416
+ };
46417
+ if (isRuntimeCell(args[1])) {
46418
+ const cell = args[1];
46419
+ const result = allocFloat64Array(cell.data.length);
46420
+ for (let i = 0; i < cell.data.length; i++) {
46421
+ result[i] = hasField(toString(cell.data[i])) ? 1 : 0;
46422
+ }
46423
+ return new RuntimeTensor(result, cell.shape.slice(), void 0, true);
46424
+ }
46425
+ return RTV.logical(hasField(toString(args[1])));
45615
46426
  }
45616
46427
  }
45617
46428
  ]
@@ -45728,7 +46539,7 @@ function regexpImpl(caseSensitive, name, args, nargout) {
45728
46539
  if (outModes.length === 0) {
45729
46540
  outModes.push("start", "end", "tokenextents", "match", "tokens", "names");
45730
46541
  }
45731
- const flags = caseSensitive ? "g" : "gi";
46542
+ const flags = caseSensitive ? "gs" : "gis";
45732
46543
  const re = new RegExp(pat, flags);
45733
46544
  const starts = [];
45734
46545
  const ends = [];
@@ -46448,6 +47259,13 @@ registerIBuiltin({
46448
47259
  if (args.length === 1) return RTV.cell([A], [1, 1]);
46449
47260
  A = RTV.tensor(allocFloat64Array([A ? 1 : 0]), [1, 1]);
46450
47261
  }
47262
+ if (isRuntimeStruct(A)) {
47263
+ return RTV.cell([A], [1, 1]);
47264
+ }
47265
+ if (isRuntimeStructArray(A)) {
47266
+ const elems = A.elements;
47267
+ return RTV.cell([...elems], [1, elems.length]);
47268
+ }
46451
47269
  if (!isRuntimeTensor(A))
46452
47270
  throw new RuntimeError(
46453
47271
  "num2cell: first argument must be a numeric array"
@@ -46571,6 +47389,69 @@ registerIBuiltin({
46571
47389
  };
46572
47390
  }
46573
47391
  });
47392
+ registerIBuiltin({
47393
+ name: "getfield",
47394
+ resolve: (argTypes) => {
47395
+ if (argTypes.length < 2) return null;
47396
+ return {
47397
+ outputTypes: [{ kind: "unknown" }],
47398
+ apply: (args) => {
47399
+ let v = args[0];
47400
+ for (let i = 1; i < args.length; i++) {
47401
+ if (isRuntimeCell(args[i]))
47402
+ throw new RuntimeError(
47403
+ "getfield: index ({}) subscripts are not supported"
47404
+ );
47405
+ if (!isRuntimeStruct(v))
47406
+ throw new RuntimeError("getfield: argument must be a structure");
47407
+ const name = toString(args[i]);
47408
+ if (!v.fields.has(name))
47409
+ throw new RuntimeError(
47410
+ `Reference to non-existent field '${name}'.`
47411
+ );
47412
+ v = v.fields.get(name);
47413
+ }
47414
+ return v;
47415
+ }
47416
+ };
47417
+ }
47418
+ });
47419
+ registerIBuiltin({
47420
+ name: "setfield",
47421
+ resolve: (argTypes) => {
47422
+ if (argTypes.length < 3) return null;
47423
+ return {
47424
+ outputTypes: [{ kind: "struct", fields: {} }],
47425
+ apply: (args) => {
47426
+ const value = args[args.length - 1];
47427
+ const fieldArgs = args.slice(1, args.length - 1);
47428
+ const setChain = (s, depth) => {
47429
+ const fa = fieldArgs[depth];
47430
+ if (isRuntimeCell(fa))
47431
+ throw new RuntimeError(
47432
+ "setfield: index ({}) subscripts are not supported"
47433
+ );
47434
+ const name = toString(fa);
47435
+ const base = isRuntimeStruct(s) ? s.fields : /* @__PURE__ */ new Map();
47436
+ if (!isRuntimeStruct(s) && !(isRuntimeTensor(s) && s.data.length === 0))
47437
+ throw new RuntimeError("setfield: argument must be a structure");
47438
+ const newFields = new Map(base);
47439
+ if (depth === fieldArgs.length - 1) {
47440
+ newFields.set(name, value);
47441
+ } else {
47442
+ const child = newFields.get(name);
47443
+ newFields.set(
47444
+ name,
47445
+ setChain(child ?? RTV.struct(/* @__PURE__ */ new Map()), depth + 1)
47446
+ );
47447
+ }
47448
+ return RTV.struct(newFields);
47449
+ };
47450
+ return setChain(args[0], 0);
47451
+ }
47452
+ };
47453
+ }
47454
+ });
46574
47455
  registerIBuiltin({
46575
47456
  name: "namedargs2cell",
46576
47457
  resolve: (argTypes) => {
@@ -46592,6 +47473,10 @@ registerIBuiltin({
46592
47473
  };
46593
47474
  }
46594
47475
  });
47476
+ function rmfieldNames(arg) {
47477
+ if (isRuntimeCell(arg)) return arg.data.map(toString);
47478
+ return [toString(arg)];
47479
+ }
46595
47480
  registerIBuiltin({
46596
47481
  name: "rmfield",
46597
47482
  resolve: (argTypes) => {
@@ -46600,25 +47485,26 @@ registerIBuiltin({
46600
47485
  outputTypes: [{ kind: "struct", fields: {} }],
46601
47486
  apply: (args) => {
46602
47487
  const v = args[0];
47488
+ const names = rmfieldNames(args[1]);
46603
47489
  if (isRuntimeStructArray(v)) {
46604
- const name2 = toString(args[1]);
46605
- if (!v.fieldNames.includes(name2))
46606
- throw new RuntimeError(`rmfield: field '${name2}' does not exist`);
46607
- const newFieldNames = v.fieldNames.filter((n) => n !== name2);
47490
+ for (const name of names)
47491
+ if (!v.fieldNames.includes(name))
47492
+ throw new RuntimeError(`rmfield: field '${name}' does not exist`);
47493
+ const newFieldNames = v.fieldNames.filter((n) => !names.includes(n));
46608
47494
  const newElements = v.elements.map((el) => {
46609
47495
  const newFields2 = new Map(el.fields);
46610
- newFields2.delete(name2);
47496
+ for (const name of names) newFields2.delete(name);
46611
47497
  return RTV.struct(newFields2);
46612
47498
  });
46613
47499
  return RTV.structArray(newFieldNames, newElements);
46614
47500
  }
46615
47501
  if (!isRuntimeStruct(v))
46616
47502
  throw new RuntimeError("rmfield: first argument must be a struct");
46617
- const name = toString(args[1]);
46618
- if (!v.fields.has(name))
46619
- throw new RuntimeError(`rmfield: field '${name}' does not exist`);
47503
+ for (const name of names)
47504
+ if (!v.fields.has(name))
47505
+ throw new RuntimeError(`rmfield: field '${name}' does not exist`);
46620
47506
  const newFields = new Map(v.fields);
46621
- newFields.delete(name);
47507
+ for (const name of names) newFields.delete(name);
46622
47508
  return RTV.struct(newFields);
46623
47509
  }
46624
47510
  };
@@ -47259,6 +48145,23 @@ registerIBuiltin({
47259
48145
  }
47260
48146
  })
47261
48147
  });
48148
+ registerIBuiltin({
48149
+ name: "spalloc",
48150
+ resolve: () => ({
48151
+ outputTypes: [{ kind: "unknown" }],
48152
+ apply: (args) => {
48153
+ const m = args.length >= 1 ? Math.round(toNumber(args[0])) : 0;
48154
+ const n = args.length >= 2 ? Math.round(toNumber(args[1])) : 0;
48155
+ return RTV.sparseMatrix(
48156
+ m,
48157
+ n,
48158
+ new Int32Array(0),
48159
+ new Int32Array(n + 1),
48160
+ allocFloat64Array(0)
48161
+ );
48162
+ }
48163
+ })
48164
+ });
47262
48165
  registerIBuiltin({
47263
48166
  name: "speye",
47264
48167
  resolve: () => ({
@@ -47544,6 +48447,598 @@ registerIBuiltin({
47544
48447
  }
47545
48448
  })
47546
48449
  });
48450
+ var SPPARMS_KEYS = [
48451
+ "spumoni",
48452
+ "thr_rel",
48453
+ "thr_abs",
48454
+ "exact_d",
48455
+ "supernd",
48456
+ "rreduce",
48457
+ "wh_frac",
48458
+ "autommd",
48459
+ "autoamd",
48460
+ "piv_tol",
48461
+ "bandden",
48462
+ "umfpack",
48463
+ "sym_tol",
48464
+ "ldl_tol",
48465
+ "usema57",
48466
+ "spqrtol",
48467
+ "sp_ctor",
48468
+ "reorder",
48469
+ "no_redo"
48470
+ ];
48471
+ var SPPARMS_DEFAULTS = [
48472
+ 0,
48473
+ 1.1,
48474
+ 1,
48475
+ 0,
48476
+ 3,
48477
+ 3,
48478
+ 0.5,
48479
+ 1,
48480
+ 1,
48481
+ 0.1,
48482
+ 0.5,
48483
+ 1,
48484
+ 1e-3,
48485
+ 0.01,
48486
+ 1,
48487
+ -2,
48488
+ 0,
48489
+ 0,
48490
+ 0
48491
+ ];
48492
+ var spparmsState = Float64Array.from(SPPARMS_DEFAULTS);
48493
+ function spparmsVector() {
48494
+ return RTV.tensor(allocFloat64Array(Array.from(spparmsState)), [
48495
+ spparmsState.length,
48496
+ 1
48497
+ ]);
48498
+ }
48499
+ registerIBuiltin({
48500
+ name: "spparms",
48501
+ resolve: (argTypes, nargout) => {
48502
+ if (argTypes.length > 2) return null;
48503
+ const tensorOut = { kind: "tensor", isComplex: false };
48504
+ const outputTypes = nargout >= 2 ? [{ kind: "char" }, tensorOut] : [tensorOut];
48505
+ return {
48506
+ outputTypes,
48507
+ apply: (args, n) => {
48508
+ if (args.length === 0) {
48509
+ if (n >= 2) {
48510
+ const width = Math.max(...SPPARMS_KEYS.map((k) => k.length));
48511
+ const padded = SPPARMS_KEYS.map((k) => k.padEnd(width)).join("");
48512
+ return [
48513
+ new RuntimeChar(padded, [SPPARMS_KEYS.length, width]),
48514
+ spparmsVector()
48515
+ ];
48516
+ }
48517
+ return spparmsVector();
48518
+ }
48519
+ const first = args[0];
48520
+ if (isRuntimeChar(first) || isRuntimeString(first)) {
48521
+ const key = parseStringArgLower(first);
48522
+ if (args.length >= 2) {
48523
+ const idx2 = SPPARMS_KEYS.indexOf(key);
48524
+ if (idx2 >= 0) spparmsState[idx2] = toNumber(args[1]);
48525
+ return RTV.num(0);
48526
+ }
48527
+ if (key === "default") {
48528
+ spparmsState = Float64Array.from(SPPARMS_DEFAULTS);
48529
+ return RTV.num(0);
48530
+ }
48531
+ if (key === "tight") {
48532
+ return RTV.num(0);
48533
+ }
48534
+ const idx = SPPARMS_KEYS.indexOf(key);
48535
+ if (idx >= 0) return RTV.num(spparmsState[idx]);
48536
+ throw new RuntimeError(`spparms: unknown parameter '${key}'`);
48537
+ }
48538
+ const vals = isRuntimeNumber(first) ? [first] : isRuntimeTensor(first) ? Array.from(first.data) : null;
48539
+ if (vals === null)
48540
+ throw new RuntimeError(
48541
+ "spparms: argument must be a parameter name or value vector"
48542
+ );
48543
+ const next = Float64Array.from(spparmsState);
48544
+ for (let i = 0; i < Math.min(vals.length, next.length); i++)
48545
+ next[i] = vals[i];
48546
+ spparmsState = next;
48547
+ return RTV.num(0);
48548
+ }
48549
+ };
48550
+ }
48551
+ });
48552
+
48553
+ // src/numbl-core/interpreter/builtins/graph.ts
48554
+ function isGraph(v) {
48555
+ return isRuntimeClassInstance(v) && v.className === "graph";
48556
+ }
48557
+ function requireGraph(v, fn) {
48558
+ if (!isGraph(v)) {
48559
+ throw new RuntimeError(`${fn}: first argument must be a graph`);
48560
+ }
48561
+ return v;
48562
+ }
48563
+ function graphN(g) {
48564
+ return toNumber(g.fields.get("_n") ?? 0);
48565
+ }
48566
+ function graphWeighted(g) {
48567
+ const w = g.fields.get("_weighted");
48568
+ return w === true;
48569
+ }
48570
+ function graphAdj(g) {
48571
+ const a = g.fields.get("_A");
48572
+ if (a === void 0 || !isRuntimeSparseMatrix(a)) {
48573
+ throw new RuntimeError("graph: corrupt internal adjacency");
48574
+ }
48575
+ return a;
48576
+ }
48577
+ function buildSymAdj(n, edges) {
48578
+ const triplets = [];
48579
+ for (const e of edges) {
48580
+ if (e.w === 0) continue;
48581
+ const u = e.u - 1;
48582
+ const v = e.v - 1;
48583
+ if (u === v) {
48584
+ triplets.push({ col: u, row: u, val: e.w });
48585
+ } else {
48586
+ triplets.push({ col: v, row: u, val: e.w });
48587
+ triplets.push({ col: u, row: v, val: e.w });
48588
+ }
48589
+ }
48590
+ triplets.sort((a, b) => a.col - b.col || a.row - b.row);
48591
+ const ir = [];
48592
+ const pr = [];
48593
+ const cols = [];
48594
+ let prevCol = -1;
48595
+ let prevRow = -1;
48596
+ for (const t of triplets) {
48597
+ if (t.col === prevCol && t.row === prevRow) {
48598
+ pr[pr.length - 1] += t.val;
48599
+ } else {
48600
+ ir.push(t.row);
48601
+ pr.push(t.val);
48602
+ cols.push(t.col);
48603
+ prevCol = t.col;
48604
+ prevRow = t.row;
48605
+ }
48606
+ }
48607
+ const jc = new Int32Array(n + 1);
48608
+ let ci = 0;
48609
+ for (let c = 0; c < n; c++) {
48610
+ jc[c] = ci;
48611
+ while (ci < cols.length && cols[ci] === c) ci++;
48612
+ }
48613
+ jc[n] = ci;
48614
+ return RTV.sparseMatrix(n, n, new Int32Array(ir), jc, allocFloat64Array(pr));
48615
+ }
48616
+ function makeGraph(n, edges, weighted) {
48617
+ const fields = /* @__PURE__ */ new Map();
48618
+ fields.set("_n", n);
48619
+ fields.set("_A", buildSymAdj(n, edges));
48620
+ fields.set("_weighted", weighted);
48621
+ return new RuntimeClassInstance("graph", fields, false);
48622
+ }
48623
+ function eachNeighbor(A, c, cb) {
48624
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) {
48625
+ const r = A.ir[k];
48626
+ if (r !== c) cb(r, A.pr[k]);
48627
+ }
48628
+ }
48629
+ function edgesFromAdj(A) {
48630
+ const edges = [];
48631
+ for (let c = 0; c < A.n; c++) {
48632
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) {
48633
+ const r = A.ir[k];
48634
+ if (r < c) edges.push({ u: r + 1, v: c + 1, w: A.pr[k] });
48635
+ else if (r === c) edges.push({ u: r + 1, v: r + 1, w: A.pr[k] });
48636
+ }
48637
+ }
48638
+ return edges;
48639
+ }
48640
+ function squareDim(A, fn) {
48641
+ if (isRuntimeNumber(A) || isRuntimeLogical(A)) return 1;
48642
+ if (isRuntimeSparseMatrix(A)) {
48643
+ if (A.m !== A.n) throw new RuntimeError(`${fn}: adjacency must be square`);
48644
+ return A.n;
48645
+ }
48646
+ if (isRuntimeTensor(A)) {
48647
+ const rows = A.shape[0] ?? 1;
48648
+ const cols = A.shape[1] ?? 1;
48649
+ if (rows !== cols)
48650
+ throw new RuntimeError(`${fn}: adjacency must be square`);
48651
+ return rows;
48652
+ }
48653
+ throw new RuntimeError(`${fn}: adjacency must be a numeric matrix`);
48654
+ }
48655
+ function takeEntry(r, c, triangle) {
48656
+ if (r === c) return true;
48657
+ if (triangle === "lower") return r > c;
48658
+ return r < c;
48659
+ }
48660
+ function adjToEdges(A, omitSelfLoops, triangle, fn) {
48661
+ const n = squareDim(A, fn);
48662
+ const weighted = !(isRuntimeTensor(A) && A._isLogical);
48663
+ const edges = [];
48664
+ const push = (r, c, val) => {
48665
+ if (val === 0) return;
48666
+ if (r === c) {
48667
+ if (!omitSelfLoops) edges.push({ u: r + 1, v: r + 1, w: val });
48668
+ } else if (takeEntry(r, c, triangle)) {
48669
+ edges.push({ u: Math.min(r, c) + 1, v: Math.max(r, c) + 1, w: val });
48670
+ }
48671
+ };
48672
+ if (isRuntimeSparseMatrix(A)) {
48673
+ for (let c = 0; c < A.n; c++) {
48674
+ for (let k = A.jc[c]; k < A.jc[c + 1]; k++) push(A.ir[k], c, A.pr[k]);
48675
+ }
48676
+ } else if (isRuntimeTensor(A)) {
48677
+ const m = A.shape[0] ?? 1;
48678
+ for (let c = 0; c < n; c++) {
48679
+ for (let r = 0; r < n; r++) {
48680
+ const val = A.data[c * m + r];
48681
+ if (val !== 0) push(r, c, val);
48682
+ }
48683
+ }
48684
+ } else if (isRuntimeNumber(A) || isRuntimeLogical(A)) {
48685
+ const val = isRuntimeNumber(A) ? A : A ? 1 : 0;
48686
+ if (val !== 0) push(0, 0, val);
48687
+ }
48688
+ return { n, edges, weighted };
48689
+ }
48690
+ function isNumericArg2(v) {
48691
+ return isRuntimeNumber(v) || isRuntimeLogical(v) || isRuntimeTensor(v) || isRuntimeSparseMatrix(v);
48692
+ }
48693
+ function toNumArray2(v) {
48694
+ if (isRuntimeNumber(v)) return [v];
48695
+ if (isRuntimeLogical(v)) return [v ? 1 : 0];
48696
+ if (isRuntimeTensor(v)) return Array.from(v.data);
48697
+ throw new RuntimeError("graph: node/weight arguments must be numeric");
48698
+ }
48699
+ function argString(v) {
48700
+ if (isRuntimeString(v)) return v;
48701
+ if (isRuntimeChar(v)) return v.value;
48702
+ return null;
48703
+ }
48704
+ registerIBuiltin({
48705
+ name: "graph",
48706
+ help: {
48707
+ signatures: [
48708
+ "G = graph(A)",
48709
+ "G = graph(A, 'omitselfloops')",
48710
+ "G = graph(A, 'upper' | 'lower')",
48711
+ "G = graph(s, t)",
48712
+ "G = graph(s, t, w)",
48713
+ "G = graph(s, t, w, num)"
48714
+ ],
48715
+ 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."
48716
+ },
48717
+ resolve: () => ({
48718
+ outputTypes: [
48719
+ {
48720
+ kind: "class_instance",
48721
+ className: "graph",
48722
+ isHandleClass: false,
48723
+ fields: {}
48724
+ }
48725
+ ],
48726
+ apply: (args) => {
48727
+ if (args.length === 0) return makeGraph(0, [], false);
48728
+ if (args.length >= 2 && isNumericArg2(args[1])) {
48729
+ const s = toNumArray2(args[0]);
48730
+ const t = toNumArray2(args[1]);
48731
+ if (s.length !== t.length) {
48732
+ throw new RuntimeError("graph: s and t must have the same length");
48733
+ }
48734
+ let weighted2 = false;
48735
+ let w = null;
48736
+ let num = null;
48737
+ let omitSelfLoops2 = false;
48738
+ if (args.length >= 3 && isNumericArg2(args[2])) {
48739
+ w = toNumArray2(args[2]);
48740
+ weighted2 = true;
48741
+ if (args.length >= 4 && isNumericArg2(args[3])) {
48742
+ num = Math.floor(toNumber(args[3]));
48743
+ }
48744
+ }
48745
+ for (let i = 2; i < args.length; i++) {
48746
+ const str = argString(args[i]);
48747
+ if (str && str.toLowerCase() === "omitselfloops")
48748
+ omitSelfLoops2 = true;
48749
+ }
48750
+ let n2 = num ?? 0;
48751
+ for (let i = 0; i < s.length; i++) n2 = Math.max(n2, s[i], t[i]);
48752
+ const edges2 = [];
48753
+ for (let i = 0; i < s.length; i++) {
48754
+ const u = Math.min(s[i], t[i]);
48755
+ const v = Math.max(s[i], t[i]);
48756
+ if (omitSelfLoops2 && u === v) continue;
48757
+ edges2.push({ u, v, w: w ? w[w.length === 1 ? 0 : i] : 1 });
48758
+ }
48759
+ return makeGraph(n2, edges2, weighted2);
48760
+ }
48761
+ let omitSelfLoops = false;
48762
+ let triangle = "sym";
48763
+ for (let i = 1; i < args.length; i++) {
48764
+ const str = argString(args[i]);
48765
+ if (!str) continue;
48766
+ const lc = str.toLowerCase();
48767
+ if (lc === "omitselfloops") omitSelfLoops = true;
48768
+ else if (lc === "upper") triangle = "upper";
48769
+ else if (lc === "lower") triangle = "lower";
48770
+ }
48771
+ const { n, edges, weighted } = adjToEdges(
48772
+ args[0],
48773
+ omitSelfLoops,
48774
+ triangle,
48775
+ "graph"
48776
+ );
48777
+ return makeGraph(n, edges, weighted);
48778
+ }
48779
+ })
48780
+ });
48781
+ registerIBuiltin({
48782
+ name: "conncomp",
48783
+ help: {
48784
+ signatures: ["bins = conncomp(G)", "[bins, binsizes] = conncomp(G)"],
48785
+ 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."
48786
+ },
48787
+ resolve: () => ({
48788
+ outputTypes: [{ kind: "unknown" }, { kind: "unknown" }],
48789
+ apply: (args, nargout) => {
48790
+ const g = requireGraph(args[0], "conncomp");
48791
+ const n = graphN(g);
48792
+ const A = graphAdj(g);
48793
+ const bins = new Float64Array(n);
48794
+ const sizes = [];
48795
+ let comp = 0;
48796
+ const stack = [];
48797
+ for (let start = 0; start < n; start++) {
48798
+ if (bins[start] !== 0) continue;
48799
+ comp++;
48800
+ let count = 0;
48801
+ bins[start] = comp;
48802
+ stack.length = 0;
48803
+ stack.push(start);
48804
+ while (stack.length > 0) {
48805
+ const node = stack.pop();
48806
+ count++;
48807
+ eachNeighbor(A, node, (r) => {
48808
+ if (bins[r] === 0) {
48809
+ bins[r] = comp;
48810
+ stack.push(r);
48811
+ }
48812
+ });
48813
+ }
48814
+ sizes.push(count);
48815
+ }
48816
+ const binsT = RTV.tensor(bins, [1, n]);
48817
+ if (nargout >= 2) {
48818
+ return [binsT, RTV.tensor(allocFloat64Array(sizes), [1, sizes.length])];
48819
+ }
48820
+ return binsT;
48821
+ }
48822
+ })
48823
+ });
48824
+ registerIBuiltin({
48825
+ name: "laplacian",
48826
+ help: {
48827
+ signatures: ["L = laplacian(G)"],
48828
+ 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."
48829
+ },
48830
+ resolve: () => ({
48831
+ outputTypes: [{ kind: "unknown" }],
48832
+ apply: (args) => {
48833
+ const g = requireGraph(args[0], "laplacian");
48834
+ const n = graphN(g);
48835
+ const A = graphAdj(g);
48836
+ const edges = [];
48837
+ for (let c = 0; c < n; c++) {
48838
+ let degree = 0;
48839
+ eachNeighbor(A, c, (r) => {
48840
+ degree++;
48841
+ if (r < c) edges.push({ u: r + 1, v: c + 1, w: -1 });
48842
+ });
48843
+ edges.push({ u: c + 1, v: c + 1, w: degree });
48844
+ }
48845
+ return buildSymAdj(n, edges);
48846
+ }
48847
+ })
48848
+ });
48849
+ registerIBuiltin({
48850
+ name: "addedge",
48851
+ help: {
48852
+ signatures: ["H = addedge(G, s, t)", "H = addedge(G, s, t, w)"],
48853
+ 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."
48854
+ },
48855
+ resolve: () => ({
48856
+ outputTypes: [
48857
+ {
48858
+ kind: "class_instance",
48859
+ className: "graph",
48860
+ isHandleClass: false,
48861
+ fields: {}
48862
+ }
48863
+ ],
48864
+ apply: (args) => {
48865
+ const g = requireGraph(args[0], "addedge");
48866
+ if (args.length < 3) {
48867
+ throw new RuntimeError("addedge: requires nodes s and t");
48868
+ }
48869
+ const weighted = graphWeighted(g);
48870
+ const s = toNumArray2(args[1]);
48871
+ const t = toNumArray2(args[2]);
48872
+ if (s.length !== t.length) {
48873
+ throw new RuntimeError("addedge: s and t must have the same length");
48874
+ }
48875
+ let w = null;
48876
+ if (args.length >= 4) {
48877
+ w = toNumArray2(args[3]);
48878
+ } else if (weighted) {
48879
+ throw new RuntimeError(
48880
+ "addedge: Must specify weights when adding an edge to a weighted graph."
48881
+ );
48882
+ }
48883
+ const edges = edgesFromAdj(graphAdj(g));
48884
+ let n = graphN(g);
48885
+ for (let i = 0; i < s.length; i++) {
48886
+ const u = Math.min(s[i], t[i]);
48887
+ const v = Math.max(s[i], t[i]);
48888
+ n = Math.max(n, u, v);
48889
+ edges.push({ u, v, w: w ? w[w.length === 1 ? 0 : i] : 1 });
48890
+ }
48891
+ return makeGraph(n, edges, weighted);
48892
+ }
48893
+ })
48894
+ });
48895
+ registerIBuiltin({
48896
+ name: "numnodes",
48897
+ help: {
48898
+ signatures: ["n = numnodes(G)"],
48899
+ description: "Number of nodes in graph G."
48900
+ },
48901
+ resolve: () => ({
48902
+ outputTypes: [{ kind: "number" }],
48903
+ apply: (args) => RTV.num(graphN(requireGraph(args[0], "numnodes")))
48904
+ })
48905
+ });
48906
+ registerIBuiltin({
48907
+ name: "numedges",
48908
+ help: {
48909
+ signatures: ["m = numedges(G)"],
48910
+ description: "Number of edges in graph G."
48911
+ },
48912
+ resolve: () => ({
48913
+ outputTypes: [{ kind: "number" }],
48914
+ apply: (args) => {
48915
+ const g = requireGraph(args[0], "numedges");
48916
+ return RTV.num(edgesFromAdj(graphAdj(g)).length);
48917
+ }
48918
+ })
48919
+ });
48920
+ registerIBuiltin({
48921
+ name: "degree",
48922
+ help: {
48923
+ signatures: ["d = degree(G)"],
48924
+ description: "Degree of each node in graph G, returned as a column vector of edge counts (self-loops excluded)."
48925
+ },
48926
+ resolve: () => ({
48927
+ outputTypes: [{ kind: "unknown" }],
48928
+ apply: (args) => {
48929
+ const g = requireGraph(args[0], "degree");
48930
+ const n = graphN(g);
48931
+ const A = graphAdj(g);
48932
+ const d = new Float64Array(n);
48933
+ for (let c = 0; c < n; c++) eachNeighbor(A, c, () => d[c] += 1);
48934
+ return RTV.tensor(d, [n, 1]);
48935
+ }
48936
+ })
48937
+ });
48938
+
48939
+ // src/numbl-core/interpreter/builtins/gallery.ts
48940
+ function toNumArray3(v, what) {
48941
+ if (isRuntimeNumber(v)) return [v];
48942
+ if (isRuntimeLogical(v)) return [v ? 1 : 0];
48943
+ if (isRuntimeTensor(v)) return Array.from(v.data);
48944
+ if (isRuntimeComplexNumber(v)) return [v.re];
48945
+ throw new RuntimeError(`gallery: ${what} must be numeric`);
48946
+ }
48947
+ function buildTridiag(sub2, diag2, sup) {
48948
+ const n = diag2.length;
48949
+ if (sub2.length !== n - 1 || sup.length !== n - 1) {
48950
+ throw new RuntimeError(
48951
+ "gallery: tridiag sub/superdiagonal must have length one less than the diagonal"
48952
+ );
48953
+ }
48954
+ const ir = [];
48955
+ const pr = [];
48956
+ const jc = new Int32Array(n + 1);
48957
+ for (let c = 0; c < n; c++) {
48958
+ jc[c] = ir.length;
48959
+ if (c >= 1 && sup[c - 1] !== 0) {
48960
+ ir.push(c - 1);
48961
+ pr.push(sup[c - 1]);
48962
+ }
48963
+ if (diag2[c] !== 0) {
48964
+ ir.push(c);
48965
+ pr.push(diag2[c]);
48966
+ }
48967
+ if (c <= n - 2 && sub2[c] !== 0) {
48968
+ ir.push(c + 1);
48969
+ pr.push(sub2[c]);
48970
+ }
48971
+ }
48972
+ jc[n] = ir.length;
48973
+ return RTV.sparseMatrix(n, n, new Int32Array(ir), jc, allocFloat64Array(pr));
48974
+ }
48975
+ function buildTridiagToeplitz(n, c, d, e) {
48976
+ const off = Math.max(0, n - 1);
48977
+ return buildTridiag(
48978
+ new Array(off).fill(c),
48979
+ new Array(n).fill(d),
48980
+ new Array(off).fill(e)
48981
+ );
48982
+ }
48983
+ function galleryTridiag(rest) {
48984
+ if (rest.length === 1) {
48985
+ return buildTridiagToeplitz(Math.round(toNumber(rest[0])), -1, 2, -1);
48986
+ }
48987
+ if (rest.length === 4) {
48988
+ return buildTridiagToeplitz(
48989
+ Math.round(toNumber(rest[0])),
48990
+ toNumber(rest[1]),
48991
+ toNumber(rest[2]),
48992
+ toNumber(rest[3])
48993
+ );
48994
+ }
48995
+ if (rest.length === 3) {
48996
+ const sub2 = toNumArray3(rest[0], "subdiagonal");
48997
+ const diag2 = toNumArray3(rest[1], "diagonal");
48998
+ const sup = toNumArray3(rest[2], "superdiagonal");
48999
+ if (sub2.length === 1 && diag2.length === 1 && sup.length === 1) {
49000
+ return buildTridiag([], diag2, []);
49001
+ }
49002
+ return buildTridiag(sub2, diag2, sup);
49003
+ }
49004
+ throw new RuntimeError("gallery: tridiag expects (n), (n,c,d,e), or (x,y,z)");
49005
+ }
49006
+ registerIBuiltin({
49007
+ name: "gallery",
49008
+ help: {
49009
+ signatures: [
49010
+ "A = gallery('tridiag', n)",
49011
+ "A = gallery('tridiag', n, c, d, e)",
49012
+ "A = gallery('tridiag', x, y, z)"
49013
+ ],
49014
+ 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."
49015
+ },
49016
+ resolve: () => ({
49017
+ outputTypes: [{ kind: "unknown" }],
49018
+ apply: (args) => {
49019
+ if (args.length < 1) {
49020
+ throw new RuntimeError("gallery: not enough input arguments");
49021
+ }
49022
+ const name = parseStringArgLower(args[0]);
49023
+ let rest = args.slice(1);
49024
+ if (rest.length >= 1) {
49025
+ const last = rest[rest.length - 1];
49026
+ if (isRuntimeString(last) || isRuntimeChar(last)) {
49027
+ const cn = parseStringArgLower(last);
49028
+ if (cn === "single" || cn === "double") rest = rest.slice(0, -1);
49029
+ }
49030
+ }
49031
+ switch (name) {
49032
+ case "tridiag":
49033
+ return galleryTridiag(rest);
49034
+ default:
49035
+ throw new RuntimeError(
49036
+ `gallery: matrix family '${name}' is not supported`
49037
+ );
49038
+ }
49039
+ }
49040
+ })
49041
+ });
47547
49042
 
47548
49043
  // src/numbl-core/interpreter/builtins/special-math.ts
47549
49044
  function binaryApply(a, b, fn) {
@@ -48027,12 +49522,19 @@ registerIBuiltin({
48027
49522
 
48028
49523
  // src/numbl-core/native/geometry-bridge.ts
48029
49524
  var backend = null;
49525
+ var hullBackend = null;
48030
49526
  function setDelaunayBackend(fn) {
48031
49527
  backend = fn;
48032
49528
  }
48033
49529
  function getDelaunayBackend() {
48034
49530
  return backend;
48035
49531
  }
49532
+ function setConvexHullBackend(fn) {
49533
+ hullBackend = fn;
49534
+ }
49535
+ function getConvexHullBackend() {
49536
+ return hullBackend;
49537
+ }
48036
49538
 
48037
49539
  // src/numbl-core/interpreter/builtins/geometry.ts
48038
49540
  function toFlatArray(v, name) {
@@ -48085,7 +49587,7 @@ function simplexVolume(points, cell, dim) {
48085
49587
  for (let k = 2; k <= dim; k++) fact *= k;
48086
49588
  return Math.abs(det) / fact;
48087
49589
  }
48088
- function triangulateToTensor(points, dim) {
49590
+ function triangulateToTensor(points, dim, orientCCW = false) {
48089
49591
  const backend2 = getDelaunayBackend();
48090
49592
  if (!backend2)
48091
49593
  throw new RuntimeError(
@@ -48105,6 +49607,16 @@ function triangulateToTensor(points, dim) {
48105
49607
  const cells = volTol > 0 ? raw.filter((cell) => simplexVolume(points, cell, dim) > volTol) : raw;
48106
49608
  const numCells = cells.length;
48107
49609
  const cols = dim + 1;
49610
+ if (orientCCW && dim === 2) {
49611
+ for (const cell of cells) {
49612
+ const [a, b, c] = cell;
49613
+ 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]);
49614
+ if (signedArea2 < 0) {
49615
+ cell[1] = c;
49616
+ cell[2] = b;
49617
+ }
49618
+ }
49619
+ }
48108
49620
  const out = allocFloat64Array(numCells * cols);
48109
49621
  for (let i = 0; i < numCells; i++) {
48110
49622
  const cell = cells[i];
@@ -48158,7 +49670,7 @@ defineBuiltin({
48158
49670
  throw new RuntimeError(
48159
49671
  `delaunay: need at least ${dim + 1} points for a ${dim}-D triangulation`
48160
49672
  );
48161
- return triangulateToTensor(points, dim);
49673
+ return triangulateToTensor(points, dim, true);
48162
49674
  }
48163
49675
  }
48164
49676
  ]
@@ -48201,6 +49713,203 @@ defineBuiltin({
48201
49713
  }
48202
49714
  ]
48203
49715
  });
49716
+ var CONVHULLN_MAX_DIM = 3;
49717
+ function convexHullFacets(points, dim, name) {
49718
+ const backend2 = getConvexHullBackend();
49719
+ if (!backend2)
49720
+ throw new RuntimeError(
49721
+ `${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.`
49722
+ );
49723
+ return backend2(points, dim);
49724
+ }
49725
+ function hullInteriorPoint(points, facets, dim) {
49726
+ const used = /* @__PURE__ */ new Set();
49727
+ for (const f of facets) for (const idx of f) used.add(idx);
49728
+ const c = new Array(dim).fill(0);
49729
+ for (const idx of used) {
49730
+ const p2 = points[idx];
49731
+ for (let k = 0; k < dim; k++) c[k] += p2[k];
49732
+ }
49733
+ const m = used.size || 1;
49734
+ for (let k = 0; k < dim; k++) c[k] /= m;
49735
+ return c;
49736
+ }
49737
+ function hullMeasure(points, facets, dim) {
49738
+ const c = hullInteriorPoint(points, facets, dim);
49739
+ let total = 0;
49740
+ if (dim === 2) {
49741
+ for (const f of facets) {
49742
+ const a = points[f[0]];
49743
+ const b = points[f[1]];
49744
+ const ax = a[0] - c[0], ay = a[1] - c[1];
49745
+ const bx = b[0] - c[0], by = b[1] - c[1];
49746
+ total += Math.abs(ax * by - ay * bx) / 2;
49747
+ }
49748
+ } else {
49749
+ for (const f of facets) {
49750
+ const a = points[f[0]];
49751
+ const b = points[f[1]];
49752
+ const d = points[f[2]];
49753
+ const ax = a[0] - c[0], ay = a[1] - c[1], az = a[2] - c[2];
49754
+ const bx = b[0] - c[0], by = b[1] - c[1], bz = b[2] - c[2];
49755
+ const dx = d[0] - c[0], dy = d[1] - c[1], dz = d[2] - c[2];
49756
+ const det = ax * (by * dz - bz * dy) - ay * (bx * dz - bz * dx) + az * (bx * dy - by * dx);
49757
+ total += Math.abs(det) / 6;
49758
+ }
49759
+ }
49760
+ return total;
49761
+ }
49762
+ function facetsToTensor(facets, cols) {
49763
+ const n = facets.length;
49764
+ const out = allocFloat64Array(n * cols);
49765
+ for (let i = 0; i < n; i++) {
49766
+ const f = facets[i];
49767
+ for (let j = 0; j < cols; j++) out[j * n + i] = f[j] + 1;
49768
+ }
49769
+ return RTV.tensor(out, [n, cols]);
49770
+ }
49771
+ function orderHull2D(facets, points) {
49772
+ const adj = /* @__PURE__ */ new Map();
49773
+ for (const [a, b] of facets) {
49774
+ (adj.get(a) ?? adj.set(a, []).get(a)).push(b);
49775
+ (adj.get(b) ?? adj.set(b, []).get(b)).push(a);
49776
+ }
49777
+ const start = facets[0][0];
49778
+ const loop = [start];
49779
+ let prev = -1;
49780
+ let cur = start;
49781
+ for (let guard = 0; guard <= facets.length; guard++) {
49782
+ const nbrs = adj.get(cur);
49783
+ const next = nbrs[0] === prev ? nbrs[1] : nbrs[0];
49784
+ if (next === void 0 || next === start) break;
49785
+ loop.push(next);
49786
+ prev = cur;
49787
+ cur = next;
49788
+ }
49789
+ let signed = 0;
49790
+ for (let i = 0; i < loop.length; i++) {
49791
+ const p2 = points[loop[i]];
49792
+ const q = points[loop[(i + 1) % loop.length]];
49793
+ signed += p2[0] * q[1] - q[0] * p2[1];
49794
+ }
49795
+ if (signed < 0) loop.reverse();
49796
+ loop.push(loop[0]);
49797
+ const out = allocFloat64Array(loop.length);
49798
+ for (let i = 0; i < loop.length; i++) out[i] = loop[i] + 1;
49799
+ return RTV.tensor(out, [loop.length, 1]);
49800
+ }
49801
+ defineBuiltin({
49802
+ name: "convhull",
49803
+ help: {
49804
+ signatures: [
49805
+ "k = convhull(P)",
49806
+ "k = convhull(x,y)",
49807
+ "k = convhull(x,y,z)",
49808
+ "k = convhull(___,'Simplify',tf)",
49809
+ "[k,av] = convhull(___)"
49810
+ ],
49811
+ 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."
49812
+ },
49813
+ cases: [
49814
+ {
49815
+ match: (argTypes, nargout) => {
49816
+ if (nargout > 2) return null;
49817
+ if (argTypes.length < 1 || argTypes.length > 5) return null;
49818
+ const ok = (t) => t.kind === "number" || t.kind === "boolean" || t.kind === "tensor" || t.kind === "string" || t.kind === "char";
49819
+ if (!argTypes.every(ok)) return null;
49820
+ const k = { kind: "tensor", isComplex: false };
49821
+ return nargout > 1 ? [k, { kind: "number" }] : [k];
49822
+ },
49823
+ apply: (args, nargout) => {
49824
+ let coordArgs = args;
49825
+ const tail = args[args.length - 2];
49826
+ const isSimplifyName = (v) => typeof v === "string" && v.toLowerCase() === "simplify" || isRuntimeChar(v) && v.value.toLowerCase() === "simplify";
49827
+ if (args.length >= 3 && isSimplifyName(tail)) {
49828
+ coordArgs = args.slice(0, args.length - 2);
49829
+ }
49830
+ let points;
49831
+ let dim;
49832
+ if (coordArgs.length === 1) {
49833
+ const P = coordArgs[0];
49834
+ if (!isRuntimeTensor(P))
49835
+ throw new RuntimeError(
49836
+ "convhull: P must be an N-by-2 or N-by-3 matrix"
49837
+ );
49838
+ const [, d] = tensorSize2D(P);
49839
+ if (d !== 2 && d !== 3)
49840
+ throw new RuntimeError("convhull: P must have 2 or 3 columns");
49841
+ points = matrixToPoints(P, "convhull");
49842
+ dim = d;
49843
+ } else if (coordArgs.length === 2 || coordArgs.length === 3) {
49844
+ const coords = coordArgs.map((a) => toFlatArray(a, "convhull"));
49845
+ const n = coords[0].length;
49846
+ if (coords.some((c) => c.length !== n))
49847
+ throw new RuntimeError(
49848
+ "convhull: coordinate vectors must have the same length"
49849
+ );
49850
+ points = new Array(n);
49851
+ for (let i = 0; i < n; i++) points[i] = coords.map((c) => c[i]);
49852
+ dim = coordArgs.length;
49853
+ } else {
49854
+ throw new RuntimeError("convhull: invalid arguments");
49855
+ }
49856
+ if (points.length < dim + 1)
49857
+ throw new RuntimeError(
49858
+ `convhull: need at least ${dim + 1} points for a ${dim}-D hull`
49859
+ );
49860
+ const facets = convexHullFacets(points, dim, "convhull");
49861
+ const k = dim === 2 ? orderHull2D(facets, points) : facetsToTensor(facets, 3);
49862
+ if (nargout > 1) return [k, hullMeasure(points, facets, dim)];
49863
+ return k;
49864
+ }
49865
+ }
49866
+ ]
49867
+ });
49868
+ defineBuiltin({
49869
+ name: "convhulln",
49870
+ help: {
49871
+ signatures: [
49872
+ "k = convhulln(P)",
49873
+ "k = convhulln(P,opts)",
49874
+ "[k,vol] = convhulln(___)"
49875
+ ],
49876
+ 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)."
49877
+ },
49878
+ cases: [
49879
+ {
49880
+ match: (argTypes, nargout) => {
49881
+ if (nargout > 2) return null;
49882
+ if (argTypes.length < 1 || argTypes.length > 2) return null;
49883
+ const x = argTypes[0];
49884
+ if (x.kind !== "tensor" && x.kind !== "number" && x.kind !== "boolean")
49885
+ return null;
49886
+ const k = { kind: "tensor", isComplex: false };
49887
+ return nargout > 1 ? [k, { kind: "number" }] : [k];
49888
+ },
49889
+ apply: (args, nargout) => {
49890
+ const P = args[0];
49891
+ if (!isRuntimeTensor(P))
49892
+ throw new RuntimeError("convhulln: P must be an m-by-n matrix");
49893
+ const [m, n] = tensorSize2D(P);
49894
+ if (n < 2)
49895
+ throw new RuntimeError("convhulln: P must have at least 2 columns");
49896
+ if (n > CONVHULLN_MAX_DIM)
49897
+ throw new RuntimeError(
49898
+ `convhulln: only 2-D and 3-D hulls are supported (got ${n}-D); higher dimensions are not yet validated`
49899
+ );
49900
+ if (m < n + 1)
49901
+ throw new RuntimeError(
49902
+ `convhulln: need at least ${n + 1} points for a ${n}-D hull`
49903
+ );
49904
+ const points = matrixToPoints(P, "convhulln");
49905
+ const facets = convexHullFacets(points, n, "convhulln");
49906
+ const k = facetsToTensor(facets, n);
49907
+ if (nargout > 1) return [k, hullMeasure(points, facets, n)];
49908
+ return k;
49909
+ }
49910
+ }
49911
+ ]
49912
+ });
48204
49913
  function getQueryValues(v, name) {
48205
49914
  if (typeof v === "number") return { data: [v], shape: null };
48206
49915
  if (typeof v === "boolean") return { data: [v ? 1 : 0], shape: null };
@@ -48698,6 +50407,13 @@ registerIBuiltin({
48698
50407
  apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
48699
50408
  })
48700
50409
  });
50410
+ registerIBuiltin({
50411
+ name: "waitbar",
50412
+ resolve: () => ({
50413
+ outputTypes: [{ kind: "unknown" }],
50414
+ apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
50415
+ })
50416
+ });
48701
50417
  registerIBuiltin({
48702
50418
  name: "get",
48703
50419
  resolve: () => ({
@@ -49836,6 +51552,10 @@ var H = {
49836
51552
  signatures: ["C = class(A)"],
49837
51553
  description: "Returns the class name of A as a character vector."
49838
51554
  },
51555
+ superclasses: {
51556
+ signatures: ["S = superclasses(ClassName)", "S = superclasses(obj)"],
51557
+ 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."
51558
+ },
49839
51559
  fieldnames: {
49840
51560
  signatures: ["F = fieldnames(S)"],
49841
51561
  description: "Returns cell array of field names of struct or object S."
@@ -50239,6 +51959,14 @@ var H = {
50239
51959
  signatures: ["B = pinv(A)", "B = pinv(A, TOL)"],
50240
51960
  description: "Moore-Penrose pseudoinverse."
50241
51961
  },
51962
+ null: {
51963
+ signatures: ["Z = null(A)", "Z = null(A, TOL)"],
51964
+ description: "Orthonormal basis for the null space of A (columns of Z), computed via the SVD. TOL overrides the singular-value tolerance."
51965
+ },
51966
+ bandwidth: {
51967
+ signatures: ["[lower, upper] = bandwidth(A)", "B = bandwidth(A, TYPE)"],
51968
+ description: "Lower and upper matrix bandwidth. TYPE is 'lower' or 'upper'; a single output without TYPE returns the lower bandwidth."
51969
+ },
50242
51970
  cond: {
50243
51971
  signatures: ["C = cond(A)", "C = cond(A, P)"],
50244
51972
  description: "Condition number of matrix."
@@ -50468,6 +52196,10 @@ var H = {
50468
52196
  signatures: ["Y = double(X)"],
50469
52197
  description: "Convert to double precision."
50470
52198
  },
52199
+ single: {
52200
+ signatures: ["Y = single(X)"],
52201
+ 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."
52202
+ },
50471
52203
  logical: {
50472
52204
  signatures: ["Y = logical(X)"],
50473
52205
  description: "Convert to logical (boolean) array."
@@ -50838,6 +52570,18 @@ var H = {
50838
52570
  ],
50839
52571
  description: "Extract or create sparse banded/diagonal matrices."
50840
52572
  },
52573
+ spparms: {
52574
+ signatures: [
52575
+ "spparms",
52576
+ "values = spparms",
52577
+ "[keys, values] = spparms",
52578
+ "value = spparms(KEY)",
52579
+ "spparms(KEY, VALUE)",
52580
+ "spparms('default')",
52581
+ "spparms(values)"
52582
+ ],
52583
+ 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."
52584
+ },
50841
52585
  // ── Linear algebra extras ─────────────────────────────────────────────
50842
52586
  qz: {
50843
52587
  signatures: [
@@ -52459,7 +54203,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
52459
54203
  return true;
52460
54204
  }
52461
54205
  case "close": {
52462
- if (args.length > 0 && toString(args[0]) === "all") {
54206
+ const a0 = args[0];
54207
+ const isText3 = a0 !== void 0 && (isRuntimeChar(a0) || isRuntimeString(a0));
54208
+ if (isText3 && toString(a0) === "all") {
52463
54209
  plotInstr(instructions, { type: "close_all" });
52464
54210
  } else {
52465
54211
  plotInstr(instructions, { type: "close" });
@@ -53502,8 +55248,8 @@ function registerSpecialBuiltins(rt) {
53502
55248
  if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
53503
55249
  const margs = args.map((a) => ensureRuntimeValue(a));
53504
55250
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
53505
- const state = toString(margs[0]);
53506
- if (state === "on" || state === "off") {
55251
+ const mode = toString(margs[0]);
55252
+ if (mode === "on" || mode === "off") {
53507
55253
  if (nargout === 0) return void 0;
53508
55254
  return RTV.struct(
53509
55255
  /* @__PURE__ */ new Map([
@@ -53512,6 +55258,14 @@ function registerSpecialBuiltins(rt) {
53512
55258
  ])
53513
55259
  );
53514
55260
  }
55261
+ if (mode === "query") {
55262
+ return RTV.struct(
55263
+ /* @__PURE__ */ new Map([
55264
+ ["state", RTV.char("on")],
55265
+ ["identifier", margs[1]]
55266
+ ])
55267
+ );
55268
+ }
53515
55269
  }
53516
55270
  let fmtIdx = 0;
53517
55271
  if (margs.length >= 2 && isRuntimeChar(margs[0]) && toString(margs[0]).includes(":")) {
@@ -55034,6 +56788,10 @@ function _eigsImpl(rt, nargout, args) {
55034
56788
  const margs = args.map((a) => ensureRuntimeValue(a));
55035
56789
  if (margs.length < 1)
55036
56790
  throw new RuntimeError("eigs requires at least 1 argument");
56791
+ for (let i = 0; i < margs.length; i++) {
56792
+ const mi = margs[i];
56793
+ if (isRuntimeSparseMatrix(mi)) margs[i] = sparseToDense(mi);
56794
+ }
55037
56795
  let afun = null;
55038
56796
  let A = null;
55039
56797
  let n;
@@ -56258,43 +58016,40 @@ function methodDispatch(rt, name, nargout, args) {
56258
58016
  }
56259
58017
  }
56260
58018
  }
56261
- try {
56262
- return callClassMethod(rt, firstRV.className, name, nargout, args);
56263
- } catch (e) {
56264
- if (e instanceof RuntimeError) {
56265
- const guardKey = `${firstRV.className}.subsref`;
56266
- if (!rt.activeAccessors.has(guardKey)) {
56267
- const subsrefFn = rt.cachedResolveClassMethod(
56268
- firstRV.className,
56269
- "subsref"
56270
- );
56271
- if (subsrefFn) {
56272
- const remaining = args.slice(1);
56273
- const sEntries = [
56274
- RTV.struct({
56275
- type: RTV.char("."),
56276
- subs: RTV.char(name)
56277
- }),
56278
- RTV.struct({
56279
- type: RTV.char("()"),
56280
- subs: RTV.cell(
56281
- remaining.map((a) => ensureRuntimeValue(a)),
56282
- [1, remaining.length]
56283
- )
56284
- })
56285
- ];
56286
- const S = RTV.structArray(["type", "subs"], sEntries);
56287
- rt.activeAccessors.add(guardKey);
56288
- try {
56289
- return subsrefFn(nargout, first, S);
56290
- } finally {
56291
- rt.activeAccessors.delete(guardKey);
56292
- }
58019
+ const methodExists = rt.cachedResolveClassMethod(firstRV.className, name) !== null;
58020
+ if (!methodExists) {
58021
+ const guardKey = `${firstRV.className}.subsref`;
58022
+ if (!rt.activeAccessors.has(guardKey)) {
58023
+ const subsrefFn = rt.cachedResolveClassMethod(
58024
+ firstRV.className,
58025
+ "subsref"
58026
+ );
58027
+ if (subsrefFn) {
58028
+ const remaining = args.slice(1);
58029
+ const sEntries = [
58030
+ RTV.struct({
58031
+ type: RTV.char("."),
58032
+ subs: RTV.char(name)
58033
+ }),
58034
+ RTV.struct({
58035
+ type: RTV.char("()"),
58036
+ subs: RTV.cell(
58037
+ remaining.map((a) => ensureRuntimeValue(a)),
58038
+ [1, remaining.length]
58039
+ )
58040
+ })
58041
+ ];
58042
+ const S = RTV.structArray(["type", "subs"], sEntries);
58043
+ rt.activeAccessors.add(guardKey);
58044
+ try {
58045
+ return subsrefFn(nargout, first, S);
58046
+ } finally {
58047
+ rt.activeAccessors.delete(guardKey);
56293
58048
  }
56294
58049
  }
56295
58050
  }
56296
- throw e;
56297
58051
  }
58052
+ return callClassMethod(rt, firstRV.className, name, nargout, args);
56298
58053
  }
56299
58054
  }
56300
58055
  const builtin = rt.builtins[name];
@@ -56351,11 +58106,13 @@ function arrayfunCellfunImpl(rt, name, nargout, args) {
56351
58106
  const getElem = (arr, i) => {
56352
58107
  if (isRuntimeCell(arr)) return arr.data[i];
56353
58108
  if (isRuntimeTensor(arr)) return arr.data[i];
58109
+ if (isRuntimeStructArray(arr)) return arr.elements[i];
56354
58110
  return arr;
56355
58111
  };
56356
58112
  const getLen = (arr) => {
56357
58113
  if (isRuntimeCell(arr)) return arr.data.length;
56358
58114
  if (isRuntimeTensor(arr)) return arr.data.length;
58115
+ if (isRuntimeStructArray(arr)) return arr.elements.length;
56359
58116
  return 1;
56360
58117
  };
56361
58118
  const collectArgs = (i) => {
@@ -56391,9 +58148,9 @@ function arrayfunCellfunImpl(rt, name, nargout, args) {
56391
58148
  if (allLogical && arrArg.data.length > 0) result._isLogical = true;
56392
58149
  return result;
56393
58150
  }
56394
- if (isRuntimeCell(arrArg) || extraInputs.length > 0 || nargout > 1) {
58151
+ if (isRuntimeCell(arrArg) || isRuntimeStructArray(arrArg) || extraInputs.length > 0 || nargout > 1) {
56395
58152
  const len = getLen(arrArg);
56396
- const shape = isRuntimeCell(arrArg) ? [...arrArg.shape] : isRuntimeTensor(arrArg) ? [...arrArg.shape] : [1, len];
58153
+ const shape = isRuntimeCell(arrArg) || isRuntimeTensor(arrArg) ? [...arrArg.shape] : [1, len];
56397
58154
  if (nargout > 1) {
56398
58155
  const allResults = Array.from(
56399
58156
  { length: nargout },
@@ -57228,6 +58985,28 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
57228
58985
  if (isRuntimeStruct(mv)) {
57229
58986
  return ensureRuntimeValue(rhs);
57230
58987
  }
58988
+ if (indices.length === 1 && isRuntimeClassInstance(ensureRuntimeValue(rhs)) && // Object-array growth uses a numeric position. A char/string index (e.g. a
58989
+ // key into a containers.Map whose value happens to be a class instance)
58990
+ // must fall through to the subsasgn dispatch below, not be coerced to a
58991
+ // number — `toNumber` on a multi-char value throws.
58992
+ !isRuntimeChar(ensureRuntimeValue(indices[0]))) {
58993
+ const k = Math.round(toNumber(ensureRuntimeValue(indices[0]))) - 1;
58994
+ const growsArray = isRuntimeClassInstanceArray(mv) || isRuntimeTensor(mv) && mv.data.length === 0 || isRuntimeClassInstance(mv) && k >= 1;
58995
+ if (growsArray) {
58996
+ if (k < 0) throw new RuntimeError("Index must be a positive integer");
58997
+ const rhsInst = ensureRuntimeValue(rhs);
58998
+ const existing = isRuntimeClassInstanceArray(mv) ? [...mv.elements] : isRuntimeClassInstance(mv) ? [mv] : [];
58999
+ const className = isRuntimeClassInstanceArray(mv) ? mv.className : isRuntimeClassInstance(mv) ? mv.className : rhsInst.className;
59000
+ while (existing.length < k) {
59001
+ existing.push(
59002
+ RTV.classInstance(className, [...rhsInst.fields.keys()], false)
59003
+ );
59004
+ }
59005
+ existing[k] = rhsInst;
59006
+ if (existing.length === 1) return existing[0];
59007
+ return RTV.classInstanceArray(className, existing, [1, existing.length]);
59008
+ }
59009
+ }
57231
59010
  if (isRuntimeClassInstance(mv)) {
57232
59011
  const guardKey = `${mv.className}.subsasgn`;
57233
59012
  if (!skipSubsasgn && !rt.activeAccessors.has(guardKey)) {
@@ -57385,12 +59164,17 @@ function getMember(rt, base, name) {
57385
59164
  `No property or method '${name}' for class '${mv.className}'`
57386
59165
  );
57387
59166
  }
59167
+ if (isRuntimeClassInstanceArray(mv)) {
59168
+ const values = mv.elements.map(
59169
+ (el) => ensureRuntimeValue(getMember(rt, el, name))
59170
+ );
59171
+ return horzcat(...values);
59172
+ }
57388
59173
  return getRTValueField(mv, name);
57389
59174
  }
57390
- function getMemberDynamic(base, nameExpr) {
57391
- const mv = ensureRuntimeValue(base);
59175
+ function getMemberDynamic(rt, base, nameExpr) {
57392
59176
  const name = toString(ensureRuntimeValue(nameExpr));
57393
- return getRTValueField(mv, name);
59177
+ return ensureRuntimeValue(getMember(rt, base, name));
57394
59178
  }
57395
59179
  function getMemberOrEmpty(base, name) {
57396
59180
  try {
@@ -57412,7 +59196,7 @@ function setMemberReturn(rt, base, name, rhs) {
57412
59196
  if (setter) {
57413
59197
  rt.activeAccessors.add(accessorKey);
57414
59198
  try {
57415
- const result = setter(1, base, rhs);
59199
+ const result = setter(0, base, rhs);
57416
59200
  return result !== void 0 ? result : base;
57417
59201
  } finally {
57418
59202
  rt.activeAccessors.delete(accessorKey);
@@ -57424,10 +59208,8 @@ function setMemberReturn(rt, base, name, rhs) {
57424
59208
  return setRTValueField(mv, name, rhsMv, rt);
57425
59209
  }
57426
59210
  function setMemberDynamicReturn(rt, base, nameExpr, rhs) {
57427
- const mv = ensureRuntimeValue(base);
57428
59211
  const name = toString(ensureRuntimeValue(nameExpr));
57429
- const rhsMv = ensureRuntimeValue(rhs);
57430
- return setRTValueField(mv, name, rhsMv, rt);
59212
+ return ensureRuntimeValue(setMemberReturn(rt, base, name, rhs));
57431
59213
  }
57432
59214
  function subsrefCall(rt, base, names) {
57433
59215
  const mv = ensureRuntimeValue(base);
@@ -58722,7 +60504,7 @@ function marchingCubes(dims, getVal, getCoord, iso, opts) {
58722
60504
  }
58723
60505
  return { vertices, faces, colors };
58724
60506
  }
58725
- function isNumericArg2(v) {
60507
+ function isNumericArg3(v) {
58726
60508
  return typeof v === "number" || typeof v === "boolean" || isRuntimeTensor(v);
58727
60509
  }
58728
60510
  function dims3(v) {
@@ -58739,7 +60521,7 @@ function isosurfaceFromArgs(args) {
58739
60521
  if (isRuntimeChar(a) || isRuntimeString(a)) {
58740
60522
  const s = toString(a).toLowerCase();
58741
60523
  if (s === "noshare") share = false;
58742
- } else if (isNumericArg2(a)) {
60524
+ } else if (isNumericArg3(a)) {
58743
60525
  nums.push(a);
58744
60526
  }
58745
60527
  }
@@ -60076,7 +61858,7 @@ var Runtime = class _Runtime {
60076
61858
  return getMember(this, base, name);
60077
61859
  }
60078
61860
  getMemberDynamic(base, nameExpr) {
60079
- return getMemberDynamic(base, nameExpr);
61861
+ return getMemberDynamic(this, base, nameExpr);
60080
61862
  }
60081
61863
  getMemberOrEmpty(base, name) {
60082
61864
  return getMemberOrEmpty(base, name);
@@ -60724,6 +62506,7 @@ function resolveFunctionImpl(name, argTypes, callSite, index2) {
60724
62506
  for (const argType of argTypes) {
60725
62507
  if (argType?.kind === "ClassInstance") {
60726
62508
  const className = argType.className;
62509
+ if (index2.classConstructors.get(className) === name) continue;
60727
62510
  if (!candidates.includes(className) && (index2.classInstanceMethods.get(className)?.has(name) || index2.classStaticMethods.get(className)?.has(name))) {
60728
62511
  candidates.push(className);
60729
62512
  }
@@ -60832,12 +62615,25 @@ var Environment = class _Environment {
60832
62615
  }
60833
62616
  /** Function ID for persistent variable storage */
60834
62617
  persistentFuncId;
62618
+ /** Call-site variable names of this frame's arguments, for `inputname`.
62619
+ * Entry i is the name of the variable passed as argument i+1, or '' if
62620
+ * that argument was not a plain variable. Undefined when the call did
62621
+ * not originate from an interpreted call expression (e.g. feval, JIT).
62622
+ * Read directly off the executing frame — the interpreter has only
62623
+ * function-level scoping, so `this.env` is the frame while a body runs. */
62624
+ inputArgNames;
60835
62625
  /** Back-reference to the runtime (needed for global/persistent access) */
60836
62626
  rt = null;
60837
62627
  /** Set when a `@nestedFn` handle has been created that captures this env
60838
62628
  * (or an ancestor). Tells the function-exit cleanup that clearing this
60839
62629
  * env would strand the handle's closure, so locals must be left alive. */
60840
62630
  nestedHandleCreated = false;
62631
+ /** For a nested-function frame: the names of this function's own formal
62632
+ * input/output arguments. These are always local — a write must never be
62633
+ * redirected to a same-named variable in the parent, even before the
62634
+ * output has been assigned. (MATLAB scopes a nested function's formal
62635
+ * arguments to that function; only other variables are shared.) */
62636
+ nestedLocalNames;
60841
62637
  get(name) {
60842
62638
  if (this._globalNames !== void 0 && this._globalNames.has(name) && this.rt) {
60843
62639
  const v = this.rt.$g[name];
@@ -60856,7 +62652,7 @@ var Environment = class _Environment {
60856
62652
  if (old !== void 0) decref(this.rt, old);
60857
62653
  return;
60858
62654
  }
60859
- if (this.isNested && !this.vars.has(name) && this.parent) {
62655
+ if (this.isNested && !this.vars.has(name) && this.parent && !this.nestedLocalNames?.has(name)) {
60860
62656
  const owner = this.findOwner(name);
60861
62657
  if (owner) {
60862
62658
  owner.setLocal(name, value);
@@ -62051,6 +63847,7 @@ function makeRootContext(interp, registry3) {
62051
63847
  var interpreterExec_exports = {};
62052
63848
  __export(interpreterExec_exports, {
62053
63849
  assignLValue: () => assignLValue,
63850
+ computeInputNames: () => computeInputNames,
62054
63851
  evalAnonFunc: () => evalAnonFunc,
62055
63852
  evalArgs: () => evalArgs,
62056
63853
  evalBinary: () => evalBinary,
@@ -62353,6 +64150,11 @@ function execStmtInner(stmt) {
62353
64150
  case "Global": {
62354
64151
  for (const name of stmt.names) {
62355
64152
  this.env.globalNames.add(name);
64153
+ if (this.rt && !(name in this.rt.$g)) {
64154
+ const empty = RTV.tensor(allocFloat64Array(0), [0, 0]);
64155
+ incref(empty);
64156
+ this.rt.$g[name] = empty;
64157
+ }
62356
64158
  }
62357
64159
  return null;
62358
64160
  }
@@ -62464,6 +64266,10 @@ function evalExprNargout(expr, nargout) {
62464
64266
  if (isRuntimeStructArray(rv)) {
62465
64267
  return rv.elements.length;
62466
64268
  }
64269
+ if (isRuntimeClassInstanceArray(rv)) {
64270
+ if (ctx.numIndices === 1) return rv.elements.length;
64271
+ return ctx.dimIndex < rv.shape.length ? rv.shape[ctx.dimIndex] : 1;
64272
+ }
62467
64273
  if (isRuntimeSparseMatrix(rv)) {
62468
64274
  if (ctx.numIndices === 1) return rv.m * rv.n;
62469
64275
  return ctx.dimIndex === 0 ? rv.m : ctx.dimIndex === 1 ? rv.n : 1;
@@ -62624,6 +64430,23 @@ function evalExprNargout(expr, nargout) {
62624
64430
  throw new RuntimeError("Interpreter does not yet support meta.class");
62625
64431
  }
62626
64432
  }
64433
+ function computeInputNames(argExprs, callerEnv) {
64434
+ const names = [];
64435
+ let blanked = false;
64436
+ for (const a of argExprs) {
64437
+ if (blanked) {
64438
+ names.push("");
64439
+ } else if (a.type === "Ident" && callerEnv.has(a.name)) {
64440
+ names.push(a.name);
64441
+ } else if (a.type === "IndexCell" || a.type === "Member" || a.type === "MemberDynamic") {
64442
+ names.push("");
64443
+ blanked = true;
64444
+ } else {
64445
+ names.push("");
64446
+ }
64447
+ }
64448
+ return names;
64449
+ }
62627
64450
  function evalArgs(argExprs) {
62628
64451
  const args = [];
62629
64452
  for (const a of argExprs) {
@@ -62753,6 +64576,7 @@ function evalFuncCall(expr, nargout) {
62753
64576
  const c = getConstant(expr.name);
62754
64577
  if (c !== void 0) return c;
62755
64578
  }
64579
+ this.pendingInputNames = expr.args.length > 0 ? computeInputNames(expr.args, this.env) : void 0;
62756
64580
  return this.callFunction(expr.name, args, nargout);
62757
64581
  }
62758
64582
  function evalMember(expr, nargout) {
@@ -62932,7 +64756,14 @@ function makeFuncHandle(name) {
62932
64756
  );
62933
64757
  };
62934
64758
  fn.jsFnExpectsNargout = true;
62935
- const narg = getIBuiltinNargin(name);
64759
+ let narg = getIBuiltinNargin(name);
64760
+ if (narg === void 0) {
64761
+ try {
64762
+ narg = this.declaredNargin(name);
64763
+ } catch {
64764
+ narg = void 0;
64765
+ }
64766
+ }
62936
64767
  if (narg !== void 0) fn.nargin = narg;
62937
64768
  if (isNested) {
62938
64769
  fn.releaseExtra = () => capturedEnv.clearLocals();
@@ -63221,6 +65052,7 @@ __export(interpreterFunctions_exports, {
63221
65052
  callNestedFunction: () => callNestedFunction,
63222
65053
  callUserFunction: () => callUserFunction,
63223
65054
  collectClassProperties: () => collectClassProperties,
65055
+ declaredNargin: () => declaredNargin,
63224
65056
  evalInLocalScope: () => evalInLocalScope,
63225
65057
  findExternalMethod: () => findExternalMethod,
63226
65058
  findFunctionInClassFile: () => findFunctionInClassFile,
@@ -63523,6 +65355,30 @@ register("isa", (ctx, args) => {
63523
65355
  if (args.length !== 2) return FALL_THROUGH;
63524
65356
  return ctx.rt.isa(args[0], args[1]);
63525
65357
  });
65358
+ register("superclasses", (ctx, args) => {
65359
+ if (args.length !== 1) return FALL_THROUGH;
65360
+ const v = ensureRuntimeValue(args[0]);
65361
+ let className;
65362
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v)) {
65363
+ className = v.className;
65364
+ } else if (isRuntimeChar(v) || isRuntimeString(v)) {
65365
+ className = toString(v);
65366
+ }
65367
+ const names = [];
65368
+ if (className) {
65369
+ const seen = /* @__PURE__ */ new Set([className]);
65370
+ let current = ctx.rt.getClassParentName(className);
65371
+ while (current && !seen.has(current)) {
65372
+ names.push(current);
65373
+ seen.add(current);
65374
+ current = ctx.rt.getClassParentName(current);
65375
+ }
65376
+ }
65377
+ return RTV.cell(
65378
+ names.map((n) => RTV.string(n)),
65379
+ [names.length, 1]
65380
+ );
65381
+ });
63526
65382
  register("__inferred_type_str", (_ctx, args) => {
63527
65383
  if (args.length !== 1) return FALL_THROUGH;
63528
65384
  const rv = ensureRuntimeValue(args[0]);
@@ -63547,6 +65403,44 @@ register("nargout", (ctx, args) => {
63547
65403
  const v = ctx.env.get("$nargout");
63548
65404
  return v !== void 0 ? v : 0;
63549
65405
  });
65406
+ register("inputname", (ctx, args) => {
65407
+ if (args.length !== 1) return FALL_THROUGH;
65408
+ const narginVal = ctx.env.get("$nargin");
65409
+ if (typeof narginVal !== "number") {
65410
+ throw new RuntimeError(
65411
+ "You can only call 'inputname' from within a MATLAB function."
65412
+ );
65413
+ }
65414
+ const k = toNumber(ensureRuntimeValue(args[0]));
65415
+ if (!Number.isInteger(k) || k < 1) {
65416
+ throw new RuntimeError(
65417
+ "Argument number must be a positive integer scalar."
65418
+ );
65419
+ }
65420
+ if (k > narginVal) {
65421
+ throw new RuntimeError(
65422
+ "Argument number exceeds number of function input arguments."
65423
+ );
65424
+ }
65425
+ const names = ctx.env.inputArgNames;
65426
+ const name = names && k <= names.length ? names[k - 1] : "";
65427
+ return RTV.char(name);
65428
+ });
65429
+ register("properties", (ctx, args) => {
65430
+ if (args.length !== 1) return FALL_THROUGH;
65431
+ const v = ensureRuntimeValue(args[0]);
65432
+ let names;
65433
+ if (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v)) {
65434
+ names = ctx.classPublicProperties(v.className);
65435
+ } else if (isRuntimeChar(v) || isRuntimeString(v)) {
65436
+ names = ctx.classPublicProperties(toString(v));
65437
+ }
65438
+ if (!names) return RTV.cell([], [0, 1]);
65439
+ return RTV.cell(
65440
+ names.map((n) => RTV.string(n)),
65441
+ [names.length, 1]
65442
+ );
65443
+ });
63550
65444
  register("narginchk", (ctx, args) => {
63551
65445
  if (args.length !== 2) return FALL_THROUGH;
63552
65446
  const narginVal = ctx.env.get("$nargin");
@@ -63651,10 +65545,14 @@ function callFunction(name, args, nargout) {
63651
65545
  source: ""
63652
65546
  };
63653
65547
  return void 0;
63654
- }
65548
+ },
65549
+ classPublicProperties: (n) => classPublicProperties(this, n)
63655
65550
  };
63656
65551
  const result = specialHandler(ctx, args, nargout);
63657
- if (result !== FALL_THROUGH) return result;
65552
+ if (result !== FALL_THROUGH) {
65553
+ this.pendingInputNames = void 0;
65554
+ return result;
65555
+ }
63658
65556
  }
63659
65557
  const nested = this.env.getNestedFunction(name);
63660
65558
  if (nested) {
@@ -63674,6 +65572,75 @@ function callFunction(name, args, nargout) {
63674
65572
  }
63675
65573
  throw new RuntimeError(`Undefined function or variable '${name}'`);
63676
65574
  }
65575
+ function narginFromParams(params) {
65576
+ const hasVarargin = params.length > 0 && params[params.length - 1] === "varargin";
65577
+ return hasVarargin ? -params.length : params.length;
65578
+ }
65579
+ function paramsInFile(interp, fileName, funcName) {
65580
+ const ast = interp.ctx.getCachedAST(fileName);
65581
+ for (const stmt of ast.body) {
65582
+ if (stmt.type === "Function" && stmt.name === funcName) return stmt.params;
65583
+ }
65584
+ return void 0;
65585
+ }
65586
+ function declaredNargin(name) {
65587
+ const nested = this.env.getNestedFunction(name);
65588
+ if (nested) return narginFromParams(nested.fn.params);
65589
+ const callSite = {
65590
+ file: this.currentFile,
65591
+ ...this.currentClassName ? { className: this.currentClassName } : {},
65592
+ ...this.currentMethodName ? { methodName: this.currentMethodName } : {}
65593
+ };
65594
+ const target = resolveFunction(name, [], callSite, this.functionIndex);
65595
+ if (!target) return void 0;
65596
+ let params;
65597
+ switch (target.kind) {
65598
+ case "localFunction": {
65599
+ const { source } = target;
65600
+ if (source.from === "main") {
65601
+ params = this.mainLocalFunctions.get(target.name)?.params;
65602
+ } else if (source.from === "workspaceFile") {
65603
+ params = this.findFunctionInWorkspaceFile(source.wsName, target.name)?.params ?? void 0;
65604
+ } else if (source.from === "classFile") {
65605
+ params = this.findFunctionInClassFile(
65606
+ source.className,
65607
+ target.name,
65608
+ source.methodScope
65609
+ )?.params ?? void 0;
65610
+ } else if (source.from === "privateFile") {
65611
+ params = paramsInFile(this, source.callerFile, target.name);
65612
+ }
65613
+ break;
65614
+ }
65615
+ case "workspaceFunction": {
65616
+ const dotIdx = target.name.lastIndexOf(".");
65617
+ const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
65618
+ params = this.findFunctionInWorkspaceFile(target.name, primaryName)?.params ?? void 0;
65619
+ if (params === void 0) {
65620
+ const entry = this.ctx.registry.filesByFuncName.get(target.name);
65621
+ if (entry) {
65622
+ const ast = this.ctx.getCachedAST(entry.fileName);
65623
+ for (const stmt of ast.body) {
65624
+ if (stmt.type === "Function") {
65625
+ params = stmt.params;
65626
+ break;
65627
+ }
65628
+ }
65629
+ }
65630
+ }
65631
+ break;
65632
+ }
65633
+ case "privateFunction": {
65634
+ const entry = this.ctx.getPrivateFileEntry(
65635
+ target.callerFile,
65636
+ target.name
65637
+ );
65638
+ if (entry) params = paramsInFile(this, entry.fileName, target.name);
65639
+ break;
65640
+ }
65641
+ }
65642
+ return params !== void 0 ? narginFromParams(params) : void 0;
65643
+ }
63677
65644
  function interpretTarget(target, args, nargout) {
63678
65645
  switch (target.kind) {
63679
65646
  case "builtin": {
@@ -63842,18 +65809,12 @@ function interpretWorkspaceFunction(target, args, nargout) {
63842
65809
  if (entry) {
63843
65810
  const ast = this.ctx.getCachedAST(entry.fileName);
63844
65811
  return this.withFileContext(entry.fileName, void 0, void 0, () => {
63845
- const savedEnv = this.env;
63846
- this.env = new Environment();
63847
- try {
63848
- for (const stmt of ast.body) {
63849
- if (stmt.type === "Function") continue;
63850
- const signal = this.execStmt(stmt);
63851
- if (signal instanceof ReturnSignal) break;
63852
- }
63853
- return this.ans;
63854
- } finally {
63855
- this.env = savedEnv;
65812
+ for (const stmt of ast.body) {
65813
+ if (stmt.type === "Function") continue;
65814
+ const signal = this.execStmt(stmt);
65815
+ if (signal instanceof ReturnSignal) break;
63856
65816
  }
65817
+ return this.ans;
63857
65818
  });
63858
65819
  }
63859
65820
  throw new RuntimeError(`Workspace function '${target.name}' not found`);
@@ -63920,6 +65881,21 @@ function instantiateClass(className, args, nargout) {
63920
65881
  if (!classInfo) {
63921
65882
  return this.rt.callClassMethod(className, className, nargout, args);
63922
65883
  }
65884
+ if (classInfo.isOldStyle) {
65885
+ const ctorName = classInfo.constructorName ?? className;
65886
+ const ctorFn = this.findExternalMethod(classInfo, ctorName);
65887
+ if (!ctorFn)
65888
+ throw new RuntimeError(
65889
+ `Constructor for old-style class '${className}' not found`
65890
+ );
65891
+ const fileName = classInfo.externalMethodFiles.get(ctorName)?.fileName ?? classInfo.fileName;
65892
+ return this.withFileContext(
65893
+ fileName,
65894
+ className,
65895
+ ctorName,
65896
+ () => this.callUserFunction(ctorFn, args, nargout)
65897
+ );
65898
+ }
63923
65899
  const { propertyNames, propertyDefaults } = this.collectClassProperties(classInfo);
63924
65900
  const defaults = /* @__PURE__ */ new Map();
63925
65901
  for (const [propName, defaultExpr] of propertyDefaults) {
@@ -63943,7 +65919,7 @@ function instantiateClass(className, args, nargout) {
63943
65919
  function interpretConstructor(classInfo, args, nargout) {
63944
65920
  const constructorName = classInfo.constructorName;
63945
65921
  if (!constructorName) return args[0];
63946
- for (const member of classInfo.ast.members) {
65922
+ for (const member of classInfo.ast?.members ?? []) {
63947
65923
  if (member.type !== "Methods") continue;
63948
65924
  for (const methodStmt of member.body) {
63949
65925
  if (methodStmt.type === "Function" && methodStmt.name === constructorName) {
@@ -63985,6 +65961,8 @@ function callUserFunction(fn, args, nargout, narginOverride) {
63985
65961
  if (!hasVarargoutDecl && nargout > declaredRegularOutputs) {
63986
65962
  throw new RuntimeError("Too many output arguments.");
63987
65963
  }
65964
+ const callInputNames = this.pendingInputNames;
65965
+ this.pendingInputNames = void 0;
63988
65966
  const sharedArgs = args;
63989
65967
  if (narginOverride === void 0 && this.registry.size > 0) {
63990
65968
  const r = this.registry.dispatchCall(fn, sharedArgs, nargout, this);
@@ -64018,6 +65996,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
64018
65996
  }
64019
65997
  fnEnv.set("$nargin", narginOverride ?? args.length);
64020
65998
  fnEnv.set("$nargout", nargout);
65999
+ fnEnv.inputArgNames = callInputNames;
64021
66000
  for (const stmt of fn.body) {
64022
66001
  if (stmt.type === "Function") {
64023
66002
  fnEnv.nestedFunctions.set(stmt.name, {
@@ -64094,10 +66073,13 @@ function callUserFunction(fn, args, nargout, narginOverride) {
64094
66073
  }
64095
66074
  }
64096
66075
  function callNestedFunction(fn, parentEnv, args, nargout) {
66076
+ const callInputNames = this.pendingInputNames;
66077
+ this.pendingInputNames = void 0;
64097
66078
  const fnEnv = new Environment(parentEnv);
64098
66079
  fnEnv.isNested = true;
64099
66080
  fnEnv.rt = this.rt;
64100
66081
  fnEnv.persistentFuncId = `${this.currentFile}:${fn.name}`;
66082
+ fnEnv.nestedLocalNames = /* @__PURE__ */ new Set([...fn.params, ...fn.outputs]);
64101
66083
  const hasVarargin = fn.params.length > 0 && fn.params[fn.params.length - 1] === "varargin";
64102
66084
  const regularParams = hasVarargin ? fn.params.slice(0, -1) : fn.params;
64103
66085
  for (let i = 0; i < regularParams.length; i++) {
@@ -64111,6 +66093,15 @@ function callNestedFunction(fn, parentEnv, args, nargout) {
64111
66093
  }
64112
66094
  fnEnv.setLocal("$nargin", args.length);
64113
66095
  fnEnv.setLocal("$nargout", nargout);
66096
+ fnEnv.inputArgNames = callInputNames;
66097
+ for (const stmt of fn.body) {
66098
+ if (stmt.type === "Function") {
66099
+ fnEnv.nestedFunctions.set(stmt.name, {
66100
+ fn: funcDefFromStmt(stmt),
66101
+ env: fnEnv
66102
+ });
66103
+ }
66104
+ }
64114
66105
  const savedEnv = this.env;
64115
66106
  this.env = fnEnv;
64116
66107
  this.rt.pushCleanupScope();
@@ -64249,7 +66240,7 @@ function findMethodInClass(classInfo, methodName) {
64249
66240
  const cacheKey = `method:${classInfo.name}:${methodName}`;
64250
66241
  const cached = this.functionDefCache.get(cacheKey);
64251
66242
  if (cached) return cached;
64252
- for (const member of classInfo.ast.members) {
66243
+ for (const member of classInfo.ast?.members ?? []) {
64253
66244
  if (member.type !== "Methods") continue;
64254
66245
  for (const methodStmt of member.body) {
64255
66246
  if (methodStmt.type === "Function" && methodStmt.name === methodName) {
@@ -64306,6 +66297,36 @@ function collectClassProperties(classInfo) {
64306
66297
  }
64307
66298
  return { propertyNames, propertyDefaults };
64308
66299
  }
66300
+ function classPublicProperties(interp, className) {
66301
+ let info = interp.ctx.getClassInfo(className);
66302
+ if (!info) return void 0;
66303
+ const out = [];
66304
+ const seen = /* @__PURE__ */ new Set();
66305
+ while (info) {
66306
+ const ast = info.ast;
66307
+ if (ast) {
66308
+ for (const member of ast.members) {
66309
+ if (member.type !== "Properties") continue;
66310
+ const hidden = member.attributes.some((a) => {
66311
+ const n = a.name.toLowerCase();
66312
+ if (n !== "access" && n !== "getaccess") return false;
66313
+ const val = (a.value ?? "").toLowerCase();
66314
+ return val === "private" || val === "protected";
66315
+ });
66316
+ if (hidden) continue;
66317
+ for (const pn of member.names) {
66318
+ if (!seen.has(pn)) {
66319
+ seen.add(pn);
66320
+ out.push(pn);
66321
+ }
66322
+ }
66323
+ }
66324
+ }
66325
+ if (!info.superClass || info.superClass === "handle") break;
66326
+ info = interp.ctx.getClassInfo(info.superClass);
66327
+ }
66328
+ return out;
66329
+ }
64309
66330
  function isHandleClass(classInfo) {
64310
66331
  let parentName = classInfo.superClass;
64311
66332
  while (parentName) {
@@ -64424,6 +66445,10 @@ var Interpreter = class {
64424
66445
  workspaceEnv;
64425
66446
  /** @internal The caller's environment — for evalin/assignin('caller', ...) */
64426
66447
  callerEnv;
66448
+ /** @internal Call-site variable names for the next user-function call, set
66449
+ * by `evalFuncCall` and consumed by `callUserFunction` to support
66450
+ * `inputname`. One-shot: cleared as soon as it is consumed. */
66451
+ pendingInputNames;
64427
66452
  /** @internal Stack of [base, dimIndex, numIndices] for resolving `end` keyword in indexing. */
64428
66453
  endContextStack = [];
64429
66454
  /** @internal Number of enclosing `for` / `while` loop bodies the
@@ -64685,6 +66710,37 @@ function extractClassInfo(classDef, qualifiedName, fileName, source) {
64685
66710
  externalMethodFiles: /* @__PURE__ */ new Map()
64686
66711
  };
64687
66712
  }
66713
+ function makeOldStyleClassInfo(qualifiedName, baseName, constructorFile, methodFiles) {
66714
+ const externalMethodFiles = /* @__PURE__ */ new Map();
66715
+ externalMethodFiles.set(baseName, {
66716
+ fileName: constructorFile.fileName,
66717
+ source: constructorFile.source
66718
+ });
66719
+ const methodNames = /* @__PURE__ */ new Set();
66720
+ for (const mf of methodFiles) {
66721
+ externalMethodFiles.set(mf.name, {
66722
+ fileName: mf.fileName,
66723
+ source: mf.source
66724
+ });
66725
+ methodNames.add(mf.name);
66726
+ }
66727
+ return {
66728
+ name: baseName,
66729
+ qualifiedName,
66730
+ fileName: constructorFile.fileName,
66731
+ source: constructorFile.source,
66732
+ superClass: null,
66733
+ propertyNames: [],
66734
+ propertyDefaults: /* @__PURE__ */ new Map(),
66735
+ methodNames,
66736
+ staticMethodNames: /* @__PURE__ */ new Set(),
66737
+ constructorName: baseName,
66738
+ inferiorClasses: [],
66739
+ ast: null,
66740
+ isOldStyle: true,
66741
+ externalMethodFiles
66742
+ };
66743
+ }
64688
66744
 
64689
66745
  // src/numbl-core/lowering/loweringContext.ts
64690
66746
  function createWorkspaceRegistry() {
@@ -64877,8 +66933,41 @@ var LoweringContext = class _LoweringContext {
64877
66933
  }
64878
66934
  }
64879
66935
  }
66936
+ const startsWithClassdef = (source) => {
66937
+ let t = source.trimStart();
66938
+ while (t.startsWith("%")) {
66939
+ const nl = t.indexOf("\n");
66940
+ if (nl < 0) return false;
66941
+ t = t.slice(nl + 1).trimStart();
66942
+ }
66943
+ return t.startsWith("classdef");
66944
+ };
64880
66945
  for (const [className, group] of classFolderGroups) {
64881
- if (!group.classDefFile) {
66946
+ const hasRealClassdef = !!group.classDefFile && startsWithClassdef(group.classDefFile.source);
66947
+ if (!hasRealClassdef) {
66948
+ const dotIdx = className.lastIndexOf(".");
66949
+ const baseName = dotIdx >= 0 ? className.slice(dotIdx + 1) : className;
66950
+ const methodBase = (f) => f.name.split("/").pop().replace(/\.m$/, "");
66951
+ const ctorFile = group.classDefFile ?? group.methodFiles.find((f) => methodBase(f) === baseName);
66952
+ if (!ctorFile) {
66953
+ continue;
66954
+ }
66955
+ const methodFiles = group.methodFiles.filter((f) => f !== ctorFile).map((f) => ({
66956
+ name: methodBase(f),
66957
+ fileName: f.name,
66958
+ source: f.source
66959
+ }));
66960
+ if (!this.registry.classesByName.has(className)) {
66961
+ this.registry.classesByName.set(
66962
+ className,
66963
+ makeOldStyleClassInfo(
66964
+ className,
66965
+ baseName,
66966
+ { fileName: ctorFile.name, source: ctorFile.source },
66967
+ methodFiles
66968
+ )
66969
+ );
66970
+ }
64882
66971
  continue;
64883
66972
  }
64884
66973
  this.registerWorkspaceClass(className, group.classDefFile);
@@ -65114,7 +67203,7 @@ var LoweringContext = class _LoweringContext {
65114
67203
  if (!info) return null;
65115
67204
  if (info.ctx) return info.ctx;
65116
67205
  const ctx = new _LoweringContext(info.source, info.fileName);
65117
- for (const member of info.ast.members) {
67206
+ for (const member of info.ast?.members ?? []) {
65118
67207
  if (member.type !== "Methods") continue;
65119
67208
  for (const methodStmt of member.body) {
65120
67209
  if (methodStmt.type !== "Function") continue;
@@ -65273,6 +67362,7 @@ var LoweringContext = class _LoweringContext {
65273
67362
  for (const m of info.methodNames) instanceMethods.add(m);
65274
67363
  for (const m of info.staticMethodNames) staticMethods.add(m);
65275
67364
  for (const m of info.externalMethodFiles.keys()) {
67365
+ if (info.isOldStyle && m === info.constructorName) continue;
65276
67366
  if (!info.staticMethodNames.has(m)) {
65277
67367
  instanceMethods.add(m);
65278
67368
  }
@@ -65316,10 +67406,43 @@ var LoweringContext = class _LoweringContext {
65316
67406
  }
65317
67407
  }
65318
67408
  const fileImports = /* @__PURE__ */ new Map();
67409
+ const gatherImports = (body, out) => {
67410
+ for (const stmt of body) {
67411
+ switch (stmt.type) {
67412
+ case "Import":
67413
+ out.push(stmt);
67414
+ break;
67415
+ case "Function":
67416
+ case "While":
67417
+ case "For":
67418
+ gatherImports(stmt.body, out);
67419
+ break;
67420
+ case "If":
67421
+ gatherImports(stmt.thenBody, out);
67422
+ for (const b of stmt.elseifBlocks) gatherImports(b.body, out);
67423
+ if (stmt.elseBody) gatherImports(stmt.elseBody, out);
67424
+ break;
67425
+ case "Switch":
67426
+ for (const c of stmt.cases) gatherImports(c.body, out);
67427
+ if (stmt.otherwise) gatherImports(stmt.otherwise, out);
67428
+ break;
67429
+ case "TryCatch":
67430
+ gatherImports(stmt.tryBody, out);
67431
+ gatherImports(stmt.catchBody, out);
67432
+ break;
67433
+ case "ClassDef":
67434
+ for (const member of stmt.members) {
67435
+ if (member.type === "Methods") gatherImports(member.body, out);
67436
+ }
67437
+ break;
67438
+ }
67439
+ }
67440
+ };
65319
67441
  const collectImportsFromBody = (body, fileName) => {
65320
67442
  const entries = [];
65321
- for (const stmt of body) {
65322
- if (stmt.type !== "Import") continue;
67443
+ const importStmts = [];
67444
+ gatherImports(body, importStmts);
67445
+ for (const stmt of importStmts) {
65323
67446
  if (stmt.wildcard) {
65324
67447
  entries.push({ wildcard: true, namespace: stmt.path.join(".") });
65325
67448
  } else {
@@ -65394,6 +67517,10 @@ var LoweringContext = class _LoweringContext {
65394
67517
 
65395
67518
  // src/numbl-core/stdlib-bundle.ts
65396
67519
  var stdlibFiles = [
67520
+ {
67521
+ name: "TriRep.m",
67522
+ 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"
67523
+ },
65397
67524
  {
65398
67525
  name: "addOptional.m",
65399
67526
  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"
@@ -65421,6 +67548,10 @@ var stdlibFiles = [
65421
67548
  {
65422
67549
  name: "readmatrix.m",
65423
67550
  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"
67551
+ },
67552
+ {
67553
+ name: "triangulation.m",
67554
+ 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"
65424
67555
  }
65425
67556
  ];
65426
67557
  var shimFiles = [
@@ -87684,6 +89815,7 @@ var Workspace = class _Workspace {
87684
89815
  }
87685
89816
  };
87686
89817
  for (const [name, info] of this.ctx.registry.classesByName) {
89818
+ if (info.isOldStyle) continue;
87687
89819
  registerOrDefer(
87688
89820
  name,
87689
89821
  () => registerClassDef(
@@ -87697,7 +89829,7 @@ var Workspace = class _Workspace {
87697
89829
  if (this.classes.has(name) || this.failedClassValidations.has(name)) {
87698
89830
  throw new UnsupportedConstruct(
87699
89831
  `class '${name}' is defined both locally and as a workspace class`,
87700
- info.ast.span
89832
+ info.ast?.span
87701
89833
  );
87702
89834
  }
87703
89835
  registerOrDefer(name, () => registerClassDef(info.ast, info.fileName));
@@ -87791,7 +89923,7 @@ var Workspace = class _Workspace {
87791
89923
  if (!ast) {
87792
89924
  throw new UnsupportedConstruct(
87793
89925
  `internal: external method file '${mf.fileName}' for '${info.qualifiedName}.${methodName}' was not parsed`,
87794
- info.ast.span
89926
+ info.ast?.span
87795
89927
  );
87796
89928
  }
87797
89929
  let primary = null;
@@ -87807,7 +89939,7 @@ var Workspace = class _Workspace {
87807
89939
  if (!primary) {
87808
89940
  throw new UnsupportedConstruct(
87809
89941
  `external method file '${mf.fileName}' has no function`,
87810
- info.ast.span
89942
+ info.ast?.span
87811
89943
  );
87812
89944
  }
87813
89945
  out.set(methodName, primary);
@@ -89191,6 +91323,7 @@ async function load() {
89191
91323
  const { loadQhull } = await import("qhull-wasm");
89192
91324
  const qhull = await loadQhull();
89193
91325
  setDelaunayBackend((points, dim) => qhull.delaunay(points, dim).facets);
91326
+ setConvexHullBackend((points, dim) => qhull.convexHull(points, dim).facets);
89194
91327
  }
89195
91328
  export {
89196
91329
  RTV,