numbl 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/cli.js CHANGED
@@ -20298,6 +20298,7 @@ function extractTensorElement(base, i) {
20298
20298
  const im2 = base.imag[i];
20299
20299
  return im2 === 0 ? RTV.num(base.data[i]) : RTV.complex(base.data[i], im2);
20300
20300
  }
20301
+ if (base._isLogical === true) return base.data[i] !== 0;
20301
20302
  return RTV.num(base.data[i]);
20302
20303
  }
20303
20304
  function resolveIndex(idx, dimSize, boundsLimit = dimSize) {
@@ -20351,17 +20352,35 @@ function growTensor2D(base, newRows, newCols) {
20351
20352
  }
20352
20353
  function assignSlice(base, rowIndices, colIndices, rhs, curRows) {
20353
20354
  if (isRuntimeTensor(rhs)) {
20355
+ const nR = rowIndices.length;
20356
+ const nC = colIndices.length;
20354
20357
  const [rhsRows, rhsCols] = tensorSize2D(rhs);
20355
- if (rhsRows !== rowIndices.length || rhsCols !== colIndices.length) {
20358
+ const shapeMatches = rhsRows === nR && rhsCols === nC;
20359
+ const sliceIsVector = nR === 1 || nC === 1;
20360
+ const rhsIsVector = rhsRows === 1 || rhsCols === 1;
20361
+ const countMatches = rhs.data.length === nR * nC;
20362
+ if (!shapeMatches && !(sliceIsVector && rhsIsVector && countMatches)) {
20356
20363
  throw new RuntimeError("Subscripted assignment dimension mismatch");
20357
20364
  }
20358
20365
  if (rhs.imag || base.imag) ensureImag(base);
20359
- for (let ri = 0; ri < rowIndices.length; ri++) {
20360
- for (let ci = 0; ci < colIndices.length; ci++) {
20361
- const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20362
- const srcLi = colMajorIndex(ri, ci, rhsRows);
20363
- base.data[dstLi] = rhs.data[srcLi];
20364
- if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20366
+ if (shapeMatches) {
20367
+ for (let ri = 0; ri < nR; ri++) {
20368
+ for (let ci = 0; ci < nC; ci++) {
20369
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20370
+ const srcLi = colMajorIndex(ri, ci, rhsRows);
20371
+ base.data[dstLi] = rhs.data[srcLi];
20372
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20373
+ }
20374
+ }
20375
+ } else {
20376
+ let k = 0;
20377
+ for (let ci = 0; ci < nC; ci++) {
20378
+ for (let ri = 0; ri < nR; ri++) {
20379
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20380
+ base.data[dstLi] = rhs.data[k];
20381
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[k] : 0;
20382
+ k++;
20383
+ }
20365
20384
  }
20366
20385
  }
20367
20386
  } else {
@@ -20698,6 +20717,23 @@ function indexIntoScalar(base, indices) {
20698
20717
  return RTV.tensor(out, [1, count]);
20699
20718
  }
20700
20719
  }
20720
+ if (indices.length === 1 && isRuntimeTensor(indices[0])) {
20721
+ const idx = indices[0];
20722
+ for (let i = 0; i < idx.data.length; i++) {
20723
+ const k = Math.round(idx.data[i]);
20724
+ if (k !== 1) throw new RuntimeError("Index exceeds array bounds");
20725
+ }
20726
+ const n = idx.data.length;
20727
+ const scalarRe = isRuntimeNumber(base) ? base : base.re;
20728
+ const data = new FloatXArray(n);
20729
+ data.fill(scalarRe);
20730
+ if (isRuntimeComplexNumber(base) && base.im !== 0) {
20731
+ const im2 = new FloatXArray(n);
20732
+ im2.fill(base.im);
20733
+ return RTV.tensor(data, [...idx.shape], im2);
20734
+ }
20735
+ return RTV.tensor(data, [...idx.shape]);
20736
+ }
20701
20737
  for (const idx of indices) {
20702
20738
  if (isColonIndex(idx)) continue;
20703
20739
  const i = toNumber(idx);
@@ -20709,6 +20745,22 @@ function indexIntoTensor(base, indices) {
20709
20745
  if (indices.length === 1) {
20710
20746
  return indexIntoTensor1D(base, indices[0]);
20711
20747
  }
20748
+ if (base.shape.length > indices.length) {
20749
+ const collapsedShape = [];
20750
+ for (let d = 0; d < indices.length - 1; d++) {
20751
+ collapsedShape.push(base.shape[d]);
20752
+ }
20753
+ let tail = 1;
20754
+ for (let d = indices.length - 1; d < base.shape.length; d++) {
20755
+ tail *= base.shape[d];
20756
+ }
20757
+ collapsedShape.push(tail);
20758
+ const view = { ...base, shape: collapsedShape };
20759
+ if (indices.length === 2) {
20760
+ return indexIntoTensor2D(view, indices[0], indices[1]);
20761
+ }
20762
+ return indexIntoTensorND(view, indices);
20763
+ }
20712
20764
  if (indices.length === 2) {
20713
20765
  return indexIntoTensor2D(base, indices[0], indices[1]);
20714
20766
  }
@@ -20737,6 +20789,11 @@ function indexIntoTensor1D(base, idx) {
20737
20789
  }
20738
20790
  function indexIntoTensor2D(base, rowIdx, colIdx) {
20739
20791
  const [rows, cols] = tensorSize2D(base);
20792
+ const baseLogical = base._isLogical === true;
20793
+ const markLogical = (t) => {
20794
+ if (baseLogical && isRuntimeTensor(t)) t._isLogical = true;
20795
+ return t;
20796
+ };
20740
20797
  if (isRuntimeNumber(rowIdx) && isColonIndex(colIdx)) {
20741
20798
  const r = Math.round(rowIdx) - 1;
20742
20799
  if (r < 0 || r >= rows)
@@ -20747,7 +20804,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20747
20804
  resultData2[ci] = base.data[r + ci * rows];
20748
20805
  if (resultImag2 && base.imag) resultImag2[ci] = base.imag[r + ci * rows];
20749
20806
  }
20750
- return RTV.tensor(resultData2, [1, cols], resultImag2);
20807
+ return markLogical(RTV.tensor(resultData2, [1, cols], resultImag2));
20751
20808
  }
20752
20809
  if (isColonIndex(rowIdx) && isRuntimeNumber(colIdx)) {
20753
20810
  const c = Math.round(colIdx) - 1;
@@ -20759,7 +20816,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20759
20816
  const resultImag2 = base.imag ? new FloatXArray(rows) : void 0;
20760
20817
  if (resultImag2 && base.imag)
20761
20818
  for (let ri = 0; ri < rows; ri++) resultImag2[ri] = base.imag[offset + ri];
20762
- return RTV.tensor(resultData2, [rows, 1], resultImag2);
20819
+ return markLogical(RTV.tensor(resultData2, [rows, 1], resultImag2));
20763
20820
  }
20764
20821
  const rowIdxArr = resolveIndex(rowIdx, rows);
20765
20822
  const colIdxArr = resolveIndex(colIdx, cols);
@@ -20781,7 +20838,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20781
20838
  }
20782
20839
  }
20783
20840
  }
20784
- return RTV.tensor(resultData, [numR, numC], resultImag);
20841
+ return markLogical(RTV.tensor(resultData, [numR, numC], resultImag));
20785
20842
  }
20786
20843
  function indexIntoTensorND(base, indices) {
20787
20844
  const shape = base.shape;
@@ -20829,7 +20886,13 @@ function indexIntoTensorND(base, indices) {
20829
20886
  subs[d] = 0;
20830
20887
  }
20831
20888
  }
20832
- return RTV.tensor(resultData, resultShape, resultImag);
20889
+ const result = RTV.tensor(
20890
+ resultData,
20891
+ resultShape,
20892
+ resultImag
20893
+ );
20894
+ if (base._isLogical === true) result._isLogical = true;
20895
+ return result;
20833
20896
  }
20834
20897
  function indexIntoCell(base, indices) {
20835
20898
  if (indices.length === 1) {
@@ -20994,6 +21057,7 @@ function indexIntoLogical(base, indices) {
20994
21057
  return base;
20995
21058
  }
20996
21059
  function indexIntoTensorWithTensor(base, idx) {
21060
+ const baseLogical = base._isLogical === true;
20997
21061
  if (idx._isLogical) {
20998
21062
  const selected = [];
20999
21063
  const selectedIm = [];
@@ -21007,12 +21071,19 @@ function indexIntoTensorWithTensor(base, idx) {
21007
21071
  if (selected.length === 1) {
21008
21072
  if (hasImag2 && selectedIm[0] !== 0)
21009
21073
  return RTV.complex(selected[0], selectedIm[0]);
21074
+ if (baseLogical) return selected[0] !== 0;
21010
21075
  return RTV.num(selected[0]);
21011
21076
  }
21012
21077
  const imOut2 = hasImag2 && selectedIm.some((x) => x !== 0) ? new FloatXArray(selectedIm) : void 0;
21013
21078
  const isRow = base.shape.length === 2 && base.shape[0] === 1;
21014
21079
  const outShape2 = isRow ? [1, selected.length] : [selected.length, 1];
21015
- return RTV.tensor(new FloatXArray(selected), outShape2, imOut2);
21080
+ const result2 = RTV.tensor(
21081
+ new FloatXArray(selected),
21082
+ outShape2,
21083
+ imOut2
21084
+ );
21085
+ if (baseLogical) result2._isLogical = true;
21086
+ return result2;
21016
21087
  }
21017
21088
  const resultData = [];
21018
21089
  const hasImag = base.imag !== void 0;
@@ -21028,7 +21099,13 @@ function indexIntoTensorWithTensor(base, idx) {
21028
21099
  const baseIsVector = base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
21029
21100
  const outShape = idxIs0x0 ? [0, 0] : baseIsVector ? base.shape[0] === 1 ? [1, resultData.length] : [resultData.length, 1] : idx.shape;
21030
21101
  const imOut = hasImag && imIndices.some((x) => x !== 0) ? new FloatXArray(imIndices) : void 0;
21031
- return RTV.tensor(new FloatXArray(resultData), outShape, imOut);
21102
+ const result = RTV.tensor(
21103
+ new FloatXArray(resultData),
21104
+ outShape,
21105
+ imOut
21106
+ );
21107
+ if (baseLogical) result._isLogical = true;
21108
+ return result;
21032
21109
  }
21033
21110
  function indexIntoRTValue(base, indices) {
21034
21111
  if (isRuntimeNumber(base) || isRuntimeComplexNumber(base)) {
@@ -21378,10 +21455,18 @@ function storeIntoTensor2D(base, indices, rhs) {
21378
21455
  return storeIntoTensorColonCol(base, indices[0], rhs, rows, cols);
21379
21456
  }
21380
21457
  if (rowIsTensor || colIsTensor) {
21381
- const rowIndices = resolveIndex(indices[0], rows, 0);
21382
- const colIndices = resolveIndex(indices[1], cols, 0);
21383
- const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : rows;
21384
- const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : cols;
21458
+ const isEmpty = base.data.length === 0;
21459
+ let effectiveCols = cols;
21460
+ let effectiveRows = rows;
21461
+ if (isEmpty && isRuntimeTensor(rhs)) {
21462
+ const [rhsRowsG, rhsColsG] = tensorSize2D(rhs);
21463
+ if (rowIsColon && !colIsColon) effectiveRows = rhsRowsG;
21464
+ if (colIsColon && !rowIsColon) effectiveCols = rhsColsG;
21465
+ }
21466
+ const rowIndices = rowIsColon ? Array.from({ length: effectiveRows }, (_, i) => i) : resolveIndex(indices[0], rows, 0);
21467
+ const colIndices = colIsColon ? Array.from({ length: effectiveCols }, (_, i) => i) : resolveIndex(indices[1], cols, 0);
21468
+ const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : effectiveRows;
21469
+ const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : effectiveCols;
21385
21470
  base = growTensor2D(base, maxRow, maxCol);
21386
21471
  const [curRows] = tensorSize2D(base);
21387
21472
  assignSlice(base, rowIndices, colIndices, rhs, curRows);
@@ -21809,6 +21894,9 @@ function horzcat(...values) {
21809
21894
  if (values.some((v) => isRuntimeCell(v))) {
21810
21895
  return cellCatAlongDim(values, 1);
21811
21896
  }
21897
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21898
+ return structCat(values);
21899
+ }
21812
21900
  return catAlongDim(values, 1);
21813
21901
  }
21814
21902
  function vertcat(...values) {
@@ -21820,8 +21908,47 @@ function vertcat(...values) {
21820
21908
  if (values.some((v) => isRuntimeCell(v))) {
21821
21909
  return cellCatAlongDim(values, 0);
21822
21910
  }
21911
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21912
+ return structCat(values);
21913
+ }
21823
21914
  return catAlongDim(values, 0);
21824
21915
  }
21916
+ function structCat(values) {
21917
+ const elements = [];
21918
+ let fieldNames = null;
21919
+ for (const v of values) {
21920
+ if (isRuntimeStruct(v)) {
21921
+ const keys = Array.from(v.fields.keys());
21922
+ if (fieldNames === null) fieldNames = keys;
21923
+ else if (!arraysEqual(fieldNames, keys)) {
21924
+ throw new RuntimeError(
21925
+ "Cannot concatenate structs with different field names"
21926
+ );
21927
+ }
21928
+ elements.push(v);
21929
+ } else if (isRuntimeStructArray(v)) {
21930
+ if (fieldNames === null) fieldNames = [...v.fieldNames];
21931
+ else if (!arraysEqual(fieldNames, v.fieldNames)) {
21932
+ throw new RuntimeError(
21933
+ "Cannot concatenate struct arrays with different field names"
21934
+ );
21935
+ }
21936
+ for (const e of v.elements) elements.push(e);
21937
+ } else {
21938
+ if (isRuntimeTensor(v) && v.data.length === 0 && v.shape.every((d) => d === 0)) {
21939
+ continue;
21940
+ }
21941
+ throw new RuntimeError(`Cannot concatenate ${kstr(v)} into struct`);
21942
+ }
21943
+ }
21944
+ if (fieldNames === null) fieldNames = [];
21945
+ return RTV.structArray(fieldNames, elements);
21946
+ }
21947
+ function arraysEqual(a, b) {
21948
+ if (a.length !== b.length) return false;
21949
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
21950
+ return true;
21951
+ }
21825
21952
  function toSparseForCat(v) {
21826
21953
  if (isRuntimeSparseMatrix(v)) return v;
21827
21954
  if (isRuntimeNumber(v)) {
@@ -22316,6 +22443,258 @@ function coerceToTensor(v, name) {
22316
22443
  throw new RuntimeError(`${name}: argument must be numeric`);
22317
22444
  }
22318
22445
 
22446
+ // src/numbl-core/helpers/string.ts
22447
+ function numStr(n) {
22448
+ if (n === Infinity) return "Inf";
22449
+ if (n === -Infinity) return "-Inf";
22450
+ if (isNaN(n)) return "NaN";
22451
+ if (n === 0) return "0";
22452
+ const prec = 5;
22453
+ const exp = Math.floor(Math.log10(Math.abs(n)));
22454
+ let s;
22455
+ if (exp < -4 || exp >= prec) {
22456
+ s = n.toExponential(prec - 1);
22457
+ const ePos = s.indexOf("e");
22458
+ let mantissa = s.slice(0, ePos);
22459
+ const expPart0 = s.slice(ePos);
22460
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
22461
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
22462
+ s = mantissa + expPart;
22463
+ } else {
22464
+ if (Number.isInteger(n)) return String(n);
22465
+ s = n.toPrecision(prec);
22466
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
22467
+ }
22468
+ return s;
22469
+ }
22470
+ function num2strScalar(n) {
22471
+ if (n === Infinity) return "Inf";
22472
+ if (n === -Infinity) return "-Inf";
22473
+ if (isNaN(n)) return "NaN";
22474
+ if (n === 0) return "0";
22475
+ if (Number.isInteger(n)) return String(n);
22476
+ const prec = 5;
22477
+ const exp = Math.floor(Math.log10(Math.abs(n)));
22478
+ let s;
22479
+ if (exp < -4 || exp >= prec) {
22480
+ s = n.toExponential(prec - 1);
22481
+ const ePos = s.indexOf("e");
22482
+ let mantissa = s.slice(0, ePos);
22483
+ const expPart0 = s.slice(ePos);
22484
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
22485
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
22486
+ s = mantissa + expPart;
22487
+ } else {
22488
+ s = n.toPrecision(prec);
22489
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
22490
+ }
22491
+ return s;
22492
+ }
22493
+ function applyWidth(spec, str) {
22494
+ const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
22495
+ if (!m) return str;
22496
+ const explicitFlags = m[1] || "";
22497
+ const leftAlign = explicitFlags.includes("-");
22498
+ const afterPercent = spec.slice(1);
22499
+ const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
22500
+ const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
22501
+ const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
22502
+ if (width <= str.length) return str;
22503
+ const zeroPad = !leftAlign && zeroFlag;
22504
+ const padLen = width - str.length;
22505
+ if (leftAlign) return str + " ".repeat(padLen);
22506
+ if (zeroPad) {
22507
+ if (str[0] === "-" || str[0] === "+") {
22508
+ return str[0] + "0".repeat(padLen) + str.slice(1);
22509
+ }
22510
+ return "0".repeat(padLen) + str;
22511
+ }
22512
+ return " ".repeat(padLen) + str;
22513
+ }
22514
+ function sprintfFormat(fmt, args) {
22515
+ const flatArgs = [];
22516
+ for (const arg of args) {
22517
+ if (isRuntimeTensor(arg)) {
22518
+ for (let k = 0; k < arg.data.length; k++) {
22519
+ flatArgs.push(arg.data[k]);
22520
+ }
22521
+ } else {
22522
+ flatArgs.push(arg);
22523
+ }
22524
+ }
22525
+ let result = "";
22526
+ let argIdx = 0;
22527
+ do {
22528
+ const startArgIdx = argIdx;
22529
+ let outOfArgs = false;
22530
+ let i = 0;
22531
+ while (i < fmt.length && !outOfArgs) {
22532
+ if (fmt[i] === "%" && i + 1 < fmt.length) {
22533
+ i++;
22534
+ let spec = "%";
22535
+ while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
22536
+ if (fmt[i] === "*") {
22537
+ if (argIdx >= flatArgs.length) {
22538
+ outOfArgs = true;
22539
+ break;
22540
+ }
22541
+ spec += String(Math.round(toNumber(flatArgs[argIdx++])));
22542
+ i++;
22543
+ } else {
22544
+ spec += fmt[i];
22545
+ i++;
22546
+ }
22547
+ }
22548
+ if (outOfArgs) break;
22549
+ if (i < fmt.length) {
22550
+ const ch = fmt[i];
22551
+ i++;
22552
+ if (ch === "%") {
22553
+ result += "%";
22554
+ } else if (argIdx >= flatArgs.length) {
22555
+ outOfArgs = true;
22556
+ } else if (ch === "d" || ch === "i" || ch === "u") {
22557
+ const raw = toNumber(flatArgs[argIdx++]);
22558
+ const isInt = Number.isInteger(raw);
22559
+ const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
22560
+ if (!canPrintAsInt) {
22561
+ let eStr = raw.toExponential(6);
22562
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
22563
+ result += applyWidth(spec, eStr);
22564
+ } else {
22565
+ const n = raw;
22566
+ const flags = spec.slice(1);
22567
+ const hasPlus = flags.includes("+");
22568
+ const leftAlign = flags.includes("-");
22569
+ const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
22570
+ const width = widthMatch ? parseInt(widthMatch[1]) : 0;
22571
+ const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
22572
+ const s = String(Math.abs(n));
22573
+ const sign = n < 0 ? "-" : hasPlus ? "+" : "";
22574
+ if (width > 0) {
22575
+ const padChar = zeroPad ? "0" : " ";
22576
+ const padLen = Math.max(0, width - sign.length - s.length);
22577
+ const pad = padChar.repeat(padLen);
22578
+ result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
22579
+ } else {
22580
+ result += sign + s;
22581
+ }
22582
+ }
22583
+ } else if (ch === "f") {
22584
+ const n = toNumber(flatArgs[argIdx++]);
22585
+ if (!isFinite(n) || isNaN(n)) {
22586
+ result += applyWidth(spec, numStr(n));
22587
+ } else {
22588
+ const fFlags = spec.slice(1);
22589
+ const fHasPlus = fFlags.includes("+");
22590
+ const precMatch = spec.match(/\.(\d+)/);
22591
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
22592
+ const formatted = n.toFixed(prec);
22593
+ const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
22594
+ result += applyWidth(spec, fSign + formatted);
22595
+ }
22596
+ } else if (ch === "e" || ch === "E") {
22597
+ const n = toNumber(flatArgs[argIdx++]);
22598
+ if (!isFinite(n) || isNaN(n)) {
22599
+ result += applyWidth(spec, numStr(n));
22600
+ } else {
22601
+ const precMatch = spec.match(/\.(\d+)/);
22602
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
22603
+ let eStr = n.toExponential(prec);
22604
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
22605
+ if (ch === "E") eStr = eStr.toUpperCase();
22606
+ result += applyWidth(spec, eStr);
22607
+ }
22608
+ } else if (ch === "x" || ch === "X") {
22609
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
22610
+ let s = Math.abs(n).toString(16);
22611
+ if (ch === "X") s = s.toUpperCase();
22612
+ result += applyWidth(spec, s);
22613
+ } else if (ch === "o") {
22614
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
22615
+ result += applyWidth(spec, Math.abs(n).toString(8));
22616
+ } else if (ch === "g" || ch === "G") {
22617
+ const gVal = toNumber(flatArgs[argIdx++]);
22618
+ if (!isFinite(gVal) || isNaN(gVal)) {
22619
+ result += applyWidth(spec, numStr(gVal));
22620
+ } else {
22621
+ const precMatch = spec.match(/\.(\d+)/);
22622
+ const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
22623
+ let gStr;
22624
+ if (gVal === 0) {
22625
+ gStr = "0";
22626
+ } else {
22627
+ const exp = Math.floor(Math.log10(Math.abs(gVal)));
22628
+ if (exp < -4 || exp >= gPrec) {
22629
+ gStr = gVal.toExponential(gPrec - 1);
22630
+ const ePos = gStr.indexOf("e");
22631
+ let mantissa = gStr.slice(0, ePos);
22632
+ let expPart = gStr.slice(ePos);
22633
+ if (mantissa.includes(".")) {
22634
+ mantissa = mantissa.replace(/\.?0+$/, "");
22635
+ }
22636
+ expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
22637
+ gStr = mantissa + expPart;
22638
+ } else {
22639
+ gStr = gVal.toPrecision(gPrec);
22640
+ if (gStr.includes(".")) {
22641
+ gStr = gStr.replace(/\.?0+$/, "");
22642
+ }
22643
+ if (gStr.includes("e")) {
22644
+ gStr = String(parseFloat(gStr));
22645
+ }
22646
+ }
22647
+ }
22648
+ if (ch === "G") gStr = gStr.toUpperCase();
22649
+ result += applyWidth(spec, gStr);
22650
+ }
22651
+ } else if (ch === "s") {
22652
+ const sVal = toString(flatArgs[argIdx++]);
22653
+ const sFlags = spec.slice(1);
22654
+ const sLeftAlign = sFlags.includes("-");
22655
+ const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
22656
+ const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
22657
+ if (sWidth > sVal.length) {
22658
+ const sPad = " ".repeat(sWidth - sVal.length);
22659
+ result += sLeftAlign ? sVal + sPad : sPad + sVal;
22660
+ } else {
22661
+ result += sVal;
22662
+ }
22663
+ } else if (ch === "c") {
22664
+ result += String.fromCharCode(
22665
+ Math.round(toNumber(flatArgs[argIdx++]))
22666
+ );
22667
+ } else {
22668
+ result += spec + ch;
22669
+ argIdx++;
22670
+ }
22671
+ }
22672
+ } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
22673
+ i++;
22674
+ switch (fmt[i]) {
22675
+ case "n":
22676
+ result += "\n";
22677
+ break;
22678
+ case "t":
22679
+ result += " ";
22680
+ break;
22681
+ case "\\":
22682
+ result += "\\";
22683
+ break;
22684
+ default:
22685
+ result += "\\" + fmt[i];
22686
+ }
22687
+ i++;
22688
+ } else {
22689
+ result += fmt[i];
22690
+ i++;
22691
+ }
22692
+ }
22693
+ if (argIdx === startArgIdx) break;
22694
+ } while (argIdx < flatArgs.length);
22695
+ return result;
22696
+ }
22697
+
22319
22698
  // src/numbl-core/helpers/arithmetic.ts
22320
22699
  function toComplex(v) {
22321
22700
  if (isRuntimeComplexNumber(v)) return { re: v.re, im: v.im };
@@ -22543,7 +22922,26 @@ function tensorElemwiseComplex(at, bt, opCode, jsOp) {
22543
22922
  const isReal = resultIm.every((x) => x === 0);
22544
22923
  return RTV.tensor(resultRe, at.shape, isReal ? void 0 : resultIm);
22545
22924
  }
22925
+ function coerceToConcatString(v) {
22926
+ if (isRuntimeString(v)) return v;
22927
+ if (isRuntimeChar(v)) return v.value;
22928
+ if (isRuntimeLogical(v)) return v ? "true" : "false";
22929
+ if (isRuntimeNumber(v)) return num2strScalar(v);
22930
+ if (isRuntimeTensor(v) && v.data.length === 1 && !v.imag) {
22931
+ const x = v.data[0];
22932
+ if (v._isLogical === true) return x ? "true" : "false";
22933
+ return num2strScalar(x);
22934
+ }
22935
+ return null;
22936
+ }
22546
22937
  function mAdd(a, b) {
22938
+ if (isRuntimeString(a) || isRuntimeString(b)) {
22939
+ const aStr = coerceToConcatString(a);
22940
+ const bStr = coerceToConcatString(b);
22941
+ if (aStr !== null && bStr !== null) {
22942
+ return RTV.string(aStr + bStr);
22943
+ }
22944
+ }
22547
22945
  const m = matchSameShapeTensors(a, b);
22548
22946
  if (m) {
22549
22947
  const [at, bt] = m;
@@ -23695,7 +24093,7 @@ var token_config_default = {
23695
24093
  stripUnderscores: true,
23696
24094
  integerToken: "Integer",
23697
24095
  floatToken: "Float",
23698
- exponentChars: ["e", "E"],
24096
+ exponentChars: ["e", "E", "d", "D"],
23699
24097
  decimalPoint: ".",
23700
24098
  dotOperatorPrefixes: ["*", "/", "\\", "^"]
23701
24099
  },
@@ -24000,6 +24398,9 @@ function tokenizeDetailed(input) {
24000
24398
  if (numCfg.stripUnderscores) {
24001
24399
  lexeme = lexeme.replace(/_/g, "");
24002
24400
  }
24401
+ if (isFloat) {
24402
+ lexeme = lexeme.replace(/[dD]/g, "e");
24403
+ }
24003
24404
  const tok = isFloat ? Token[numCfg.floatToken] : Token[numCfg.integerToken];
24004
24405
  lineStart = false;
24005
24406
  out.push({ token: tok, lexeme, start, end: pos });
@@ -26991,6 +27392,29 @@ function methodDispatch(rt, name, nargout, args) {
26991
27392
  }
26992
27393
  return fieldVal;
26993
27394
  }
27395
+ {
27396
+ const accessorKey = `${firstRV.className}.get.${name}`;
27397
+ if (!rt.activeAccessors.has(accessorKey)) {
27398
+ const getter = rt.cachedResolveClassMethod(
27399
+ firstRV.className,
27400
+ `get.${name}`
27401
+ );
27402
+ if (getter) {
27403
+ rt.activeAccessors.add(accessorKey);
27404
+ let gotVal;
27405
+ try {
27406
+ gotVal = getter(1, first);
27407
+ } finally {
27408
+ rt.activeAccessors.delete(accessorKey);
27409
+ }
27410
+ const remaining = args.slice(1);
27411
+ if (remaining.length > 0) {
27412
+ return rt.index(gotVal, remaining, nargout);
27413
+ }
27414
+ return gotVal;
27415
+ }
27416
+ }
27417
+ }
26994
27418
  try {
26995
27419
  return callClassMethod(rt, firstRV.className, name, nargout, args);
26996
27420
  } catch (e) {
@@ -27945,6 +28369,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
27945
28369
  const im2 = t.imag[i];
27946
28370
  return im2 === 0 ? t.data[i] : RTV.complex(t.data[i], im2);
27947
28371
  }
28372
+ if (t._isLogical === true) return t.data[i] !== 0;
27948
28373
  return t.data[i];
27949
28374
  }
27950
28375
  } else if (nIdx === 2) {
@@ -27953,7 +28378,14 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
27953
28378
  if (typeof ri === "number" && typeof ci === "number") {
27954
28379
  const s = t.shape;
27955
28380
  const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
27956
- const cols = s.length === 0 ? 1 : s.length === 1 ? s[0] : s[1];
28381
+ let cols;
28382
+ if (s.length === 0) cols = 1;
28383
+ else if (s.length === 1) cols = s[0];
28384
+ else if (s.length === 2) cols = s[1];
28385
+ else {
28386
+ cols = 1;
28387
+ for (let k = 1; k < s.length; k++) cols *= s[k];
28388
+ }
27957
28389
  const r = Math.round(ri) - 1;
27958
28390
  const c = Math.round(ci) - 1;
27959
28391
  if (r < 0 || r >= rows || c < 0 || c >= cols)
@@ -27963,6 +28395,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
27963
28395
  const im2 = t.imag[lin];
27964
28396
  return im2 === 0 ? t.data[lin] : RTV.complex(t.data[lin], im2);
27965
28397
  }
28398
+ if (t._isLogical === true) return t.data[lin] !== 0;
27966
28399
  return t.data[lin];
27967
28400
  }
27968
28401
  } else if (nIdx >= 3) {
@@ -28353,14 +28786,20 @@ function multiOutputCellAssign(base, indices, results) {
28353
28786
  if (base === void 0 || base === null) base = RTV.cell([], [0, 0]);
28354
28787
  let mv = ensureRuntimeValue(base);
28355
28788
  if (!isRuntimeCell(mv)) mv = RTV.cell([], [0, 0]);
28356
- const idxMv = ensureRuntimeValue(indices);
28357
28789
  let idxArray;
28358
- if (isRuntimeTensor(idxMv)) {
28359
- idxArray = Array.from(idxMv.data);
28360
- } else if (isRuntimeNumber(idxMv)) {
28361
- idxArray = [idxMv];
28790
+ if (indices === COLON_SENTINEL) {
28791
+ const n = mv.data.length;
28792
+ idxArray = [];
28793
+ for (let i = 1; i <= n; i++) idxArray.push(i);
28362
28794
  } else {
28363
- idxArray = [1];
28795
+ const idxMv = ensureRuntimeValue(indices);
28796
+ if (isRuntimeTensor(idxMv)) {
28797
+ idxArray = Array.from(idxMv.data);
28798
+ } else if (isRuntimeNumber(idxMv)) {
28799
+ idxArray = [idxMv];
28800
+ } else {
28801
+ idxArray = [1];
28802
+ }
28364
28803
  }
28365
28804
  for (let i = 0; i < idxArray.length && i < results.length; i++) {
28366
28805
  const idx = idxArray[i];
@@ -28371,225 +28810,6 @@ function multiOutputCellAssign(base, indices, results) {
28371
28810
  return mv;
28372
28811
  }
28373
28812
 
28374
- // src/numbl-core/helpers/string.ts
28375
- function numStr(n) {
28376
- if (n === Infinity) return "Inf";
28377
- if (n === -Infinity) return "-Inf";
28378
- if (isNaN(n)) return "NaN";
28379
- if (n === 0) return "0";
28380
- const prec = 5;
28381
- const exp = Math.floor(Math.log10(Math.abs(n)));
28382
- let s;
28383
- if (exp < -4 || exp >= prec) {
28384
- s = n.toExponential(prec - 1);
28385
- const ePos = s.indexOf("e");
28386
- let mantissa = s.slice(0, ePos);
28387
- const expPart0 = s.slice(ePos);
28388
- if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
28389
- const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
28390
- s = mantissa + expPart;
28391
- } else {
28392
- if (Number.isInteger(n)) return String(n);
28393
- s = n.toPrecision(prec);
28394
- if (s.includes(".")) s = s.replace(/\.?0+$/, "");
28395
- }
28396
- return s;
28397
- }
28398
- function applyWidth(spec, str) {
28399
- const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
28400
- if (!m) return str;
28401
- const explicitFlags = m[1] || "";
28402
- const leftAlign = explicitFlags.includes("-");
28403
- const afterPercent = spec.slice(1);
28404
- const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
28405
- const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
28406
- const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
28407
- if (width <= str.length) return str;
28408
- const zeroPad = !leftAlign && zeroFlag;
28409
- const padLen = width - str.length;
28410
- if (leftAlign) return str + " ".repeat(padLen);
28411
- if (zeroPad) {
28412
- if (str[0] === "-" || str[0] === "+") {
28413
- return str[0] + "0".repeat(padLen) + str.slice(1);
28414
- }
28415
- return "0".repeat(padLen) + str;
28416
- }
28417
- return " ".repeat(padLen) + str;
28418
- }
28419
- function sprintfFormat(fmt, args) {
28420
- const flatArgs = [];
28421
- for (const arg of args) {
28422
- if (isRuntimeTensor(arg)) {
28423
- for (let k = 0; k < arg.data.length; k++) {
28424
- flatArgs.push(arg.data[k]);
28425
- }
28426
- } else {
28427
- flatArgs.push(arg);
28428
- }
28429
- }
28430
- let result = "";
28431
- let argIdx = 0;
28432
- do {
28433
- const startArgIdx = argIdx;
28434
- let outOfArgs = false;
28435
- let i = 0;
28436
- while (i < fmt.length && !outOfArgs) {
28437
- if (fmt[i] === "%" && i + 1 < fmt.length) {
28438
- i++;
28439
- let spec = "%";
28440
- while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
28441
- spec += fmt[i];
28442
- i++;
28443
- }
28444
- if (i < fmt.length) {
28445
- const ch = fmt[i];
28446
- i++;
28447
- if (ch === "%") {
28448
- result += "%";
28449
- } else if (argIdx >= flatArgs.length) {
28450
- outOfArgs = true;
28451
- } else if (ch === "d" || ch === "i" || ch === "u") {
28452
- const raw = toNumber(flatArgs[argIdx++]);
28453
- const isInt = Number.isInteger(raw);
28454
- const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
28455
- if (!canPrintAsInt) {
28456
- let eStr = raw.toExponential(6);
28457
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
28458
- result += applyWidth(spec, eStr);
28459
- } else {
28460
- const n = raw;
28461
- const flags = spec.slice(1);
28462
- const hasPlus = flags.includes("+");
28463
- const leftAlign = flags.includes("-");
28464
- const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
28465
- const width = widthMatch ? parseInt(widthMatch[1]) : 0;
28466
- const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
28467
- const s = String(Math.abs(n));
28468
- const sign = n < 0 ? "-" : hasPlus ? "+" : "";
28469
- if (width > 0) {
28470
- const padChar = zeroPad ? "0" : " ";
28471
- const padLen = Math.max(0, width - sign.length - s.length);
28472
- const pad = padChar.repeat(padLen);
28473
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
28474
- } else {
28475
- result += sign + s;
28476
- }
28477
- }
28478
- } else if (ch === "f") {
28479
- const n = toNumber(flatArgs[argIdx++]);
28480
- if (!isFinite(n) || isNaN(n)) {
28481
- result += applyWidth(spec, numStr(n));
28482
- } else {
28483
- const fFlags = spec.slice(1);
28484
- const fHasPlus = fFlags.includes("+");
28485
- const precMatch = spec.match(/\.(\d+)/);
28486
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
28487
- const formatted = n.toFixed(prec);
28488
- const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
28489
- result += applyWidth(spec, fSign + formatted);
28490
- }
28491
- } else if (ch === "e" || ch === "E") {
28492
- const n = toNumber(flatArgs[argIdx++]);
28493
- if (!isFinite(n) || isNaN(n)) {
28494
- result += applyWidth(spec, numStr(n));
28495
- } else {
28496
- const precMatch = spec.match(/\.(\d+)/);
28497
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
28498
- let eStr = n.toExponential(prec);
28499
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
28500
- if (ch === "E") eStr = eStr.toUpperCase();
28501
- result += applyWidth(spec, eStr);
28502
- }
28503
- } else if (ch === "x" || ch === "X") {
28504
- const n = Math.round(toNumber(flatArgs[argIdx++]));
28505
- let s = Math.abs(n).toString(16);
28506
- if (ch === "X") s = s.toUpperCase();
28507
- result += applyWidth(spec, s);
28508
- } else if (ch === "o") {
28509
- const n = Math.round(toNumber(flatArgs[argIdx++]));
28510
- result += applyWidth(spec, Math.abs(n).toString(8));
28511
- } else if (ch === "g" || ch === "G") {
28512
- const gVal = toNumber(flatArgs[argIdx++]);
28513
- if (!isFinite(gVal) || isNaN(gVal)) {
28514
- result += applyWidth(spec, numStr(gVal));
28515
- } else {
28516
- const precMatch = spec.match(/\.(\d+)/);
28517
- const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
28518
- let gStr;
28519
- if (gVal === 0) {
28520
- gStr = "0";
28521
- } else {
28522
- const exp = Math.floor(Math.log10(Math.abs(gVal)));
28523
- if (exp < -4 || exp >= gPrec) {
28524
- gStr = gVal.toExponential(gPrec - 1);
28525
- const ePos = gStr.indexOf("e");
28526
- let mantissa = gStr.slice(0, ePos);
28527
- let expPart = gStr.slice(ePos);
28528
- if (mantissa.includes(".")) {
28529
- mantissa = mantissa.replace(/\.?0+$/, "");
28530
- }
28531
- expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
28532
- gStr = mantissa + expPart;
28533
- } else {
28534
- gStr = gVal.toPrecision(gPrec);
28535
- if (gStr.includes(".")) {
28536
- gStr = gStr.replace(/\.?0+$/, "");
28537
- }
28538
- if (gStr.includes("e")) {
28539
- gStr = String(parseFloat(gStr));
28540
- }
28541
- }
28542
- }
28543
- if (ch === "G") gStr = gStr.toUpperCase();
28544
- result += applyWidth(spec, gStr);
28545
- }
28546
- } else if (ch === "s") {
28547
- const sVal = toString(flatArgs[argIdx++]);
28548
- const sFlags = spec.slice(1);
28549
- const sLeftAlign = sFlags.includes("-");
28550
- const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
28551
- const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
28552
- if (sWidth > sVal.length) {
28553
- const sPad = " ".repeat(sWidth - sVal.length);
28554
- result += sLeftAlign ? sVal + sPad : sPad + sVal;
28555
- } else {
28556
- result += sVal;
28557
- }
28558
- } else if (ch === "c") {
28559
- result += String.fromCharCode(
28560
- Math.round(toNumber(flatArgs[argIdx++]))
28561
- );
28562
- } else {
28563
- result += spec + ch;
28564
- argIdx++;
28565
- }
28566
- }
28567
- } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
28568
- i++;
28569
- switch (fmt[i]) {
28570
- case "n":
28571
- result += "\n";
28572
- break;
28573
- case "t":
28574
- result += " ";
28575
- break;
28576
- case "\\":
28577
- result += "\\";
28578
- break;
28579
- default:
28580
- result += "\\" + fmt[i];
28581
- }
28582
- i++;
28583
- } else {
28584
- result += fmt[i];
28585
- i++;
28586
- }
28587
- }
28588
- if (argIdx === startArgIdx) break;
28589
- } while (argIdx < flatArgs.length);
28590
- return result;
28591
- }
28592
-
28593
28813
  // src/numbl-core/interpreter/builtins/misc.ts
28594
28814
  registerIBuiltin({
28595
28815
  name: "substruct",
@@ -28789,7 +29009,7 @@ for (const name of ["clear", "clc", "clf"]) {
28789
29009
  name,
28790
29010
  resolve: () => ({
28791
29011
  outputTypes: [],
28792
- apply: () => 0
29012
+ apply: () => void 0
28793
29013
  })
28794
29014
  });
28795
29015
  }
@@ -28923,7 +29143,7 @@ registerIBuiltin({
28923
29143
  name: "set",
28924
29144
  resolve: () => ({
28925
29145
  outputTypes: [],
28926
- apply: () => 0
29146
+ apply: () => void 0
28927
29147
  })
28928
29148
  });
28929
29149
  for (const name of [
@@ -28945,7 +29165,7 @@ for (const name of [
28945
29165
  name,
28946
29166
  resolve: () => ({
28947
29167
  outputTypes: [],
28948
- apply: () => 0
29168
+ apply: () => void 0
28949
29169
  })
28950
29170
  });
28951
29171
  }
@@ -29341,6 +29561,24 @@ registerIBuiltin({
29341
29561
  }
29342
29562
  })
29343
29563
  });
29564
+ function getMexExt() {
29565
+ if (typeof process === "undefined") return "";
29566
+ const platform = process.platform;
29567
+ const cpuArch = process.arch;
29568
+ if (platform === "win32") return "mexw64";
29569
+ if (platform === "darwin")
29570
+ return cpuArch === "arm64" ? "mexmaca64" : "mexmaci64";
29571
+ return "mexa64";
29572
+ }
29573
+ defineBuiltin({
29574
+ name: "mexext",
29575
+ cases: [
29576
+ {
29577
+ match: (argTypes) => argTypes.length === 0 ? [{ kind: "char" }] : null,
29578
+ apply: () => RTV.char(getMexExt())
29579
+ }
29580
+ ]
29581
+ });
29344
29582
  var _platform = typeof process !== "undefined" ? process.platform : "linux";
29345
29583
  for (const [name, val] of [
29346
29584
  ["ismac", _platform === "darwin"],
@@ -31590,6 +31828,114 @@ function bisectEvent(eventIdx, step, events) {
31590
31828
  return [tOld + xFinal * h, denseOutputEval(yOld, Q, h, xFinal)];
31591
31829
  }
31592
31830
 
31831
+ // src/numbl-core/helpers/quadgk.ts
31832
+ var XK = [
31833
+ -0.9914553711208126,
31834
+ -0.9491079123427585,
31835
+ -0.8648644233597691,
31836
+ -0.7415311855993945,
31837
+ -0.586087235467691,
31838
+ -0.4058451513773972,
31839
+ -0.2077849550078985,
31840
+ 0,
31841
+ 0.2077849550078985,
31842
+ 0.4058451513773972,
31843
+ 0.586087235467691,
31844
+ 0.7415311855993945,
31845
+ 0.8648644233597691,
31846
+ 0.9491079123427585,
31847
+ 0.9914553711208126
31848
+ ];
31849
+ var WK = [
31850
+ 0.0229353220105292,
31851
+ 0.0630920926299786,
31852
+ 0.1047900103222502,
31853
+ 0.1406532597155259,
31854
+ 0.1690047266392679,
31855
+ 0.1903505780647854,
31856
+ 0.2044329400752989,
31857
+ 0.2094821410847278,
31858
+ 0.2044329400752989,
31859
+ 0.1903505780647854,
31860
+ 0.1690047266392679,
31861
+ 0.1406532597155259,
31862
+ 0.1047900103222502,
31863
+ 0.0630920926299786,
31864
+ 0.0229353220105292
31865
+ ];
31866
+ var WG = [
31867
+ 0.1294849661688697,
31868
+ 0.2797053914892767,
31869
+ 0.3818300505051189,
31870
+ 0.4179591836734694,
31871
+ 0.3818300505051189,
31872
+ 0.2797053914892767,
31873
+ 0.1294849661688697
31874
+ ];
31875
+ function kronrodNodes(lo, hi) {
31876
+ const m = (lo + hi) / 2;
31877
+ const h = (hi - lo) / 2;
31878
+ const out = new Array(15);
31879
+ for (let i = 0; i < 15; i++) out[i] = m + h * XK[i];
31880
+ return out;
31881
+ }
31882
+ function segmentEstimate(lo, hi, fv) {
31883
+ const h = (hi - lo) / 2;
31884
+ let K = 0;
31885
+ let G = 0;
31886
+ for (let i = 0; i < 15; i++) K += WK[i] * fv[i];
31887
+ for (let i = 0; i < 7; i++) G += WG[i] * fv[2 * i + 1];
31888
+ K *= h;
31889
+ G *= h;
31890
+ return { K, err: Math.abs(K - G) };
31891
+ }
31892
+ function quadgkAdaptive(integrand, a, b, opts = {}) {
31893
+ const relTol = opts.relTol ?? 1e-6;
31894
+ const absTol = opts.absTol ?? 1e-10;
31895
+ const maxIntervals = opts.maxIntervalCount ?? 650;
31896
+ if (a === b) return { value: 0, errbnd: 0, intervalsUsed: 0 };
31897
+ const sign = a < b ? 1 : -1;
31898
+ const lo0 = Math.min(a, b);
31899
+ const hi0 = Math.max(a, b);
31900
+ const segmentOn = (lo, hi) => {
31901
+ const pts = kronrodNodes(lo, hi);
31902
+ const fv = integrand(pts);
31903
+ if (fv.length !== 15) {
31904
+ throw new Error(
31905
+ `quadgk: integrand must return 15 values for 15 nodes, got ${fv.length}`
31906
+ );
31907
+ }
31908
+ return segmentEstimate(lo, hi, fv);
31909
+ };
31910
+ const initial = segmentOn(lo0, hi0);
31911
+ let totalK = initial.K;
31912
+ let totalErr = initial.err;
31913
+ const worklist = [{ lo: lo0, hi: hi0, ...initial }];
31914
+ const converged = () => totalErr <= Math.max(absTol, relTol * Math.abs(totalK));
31915
+ let iters = 0;
31916
+ while (!converged() && worklist.length < maxIntervals && iters < 1e4) {
31917
+ iters++;
31918
+ let worstIdx = 0;
31919
+ for (let i = 1; i < worklist.length; i++) {
31920
+ if (worklist[i].err > worklist[worstIdx].err) worstIdx = i;
31921
+ }
31922
+ const worst = worklist[worstIdx];
31923
+ if (worst.hi - worst.lo <= 1e-15 * (hi0 - lo0)) break;
31924
+ const mid = (worst.lo + worst.hi) / 2;
31925
+ const s1 = segmentOn(worst.lo, mid);
31926
+ const s2 = segmentOn(mid, worst.hi);
31927
+ totalK += s1.K + s2.K - worst.K;
31928
+ totalErr += s1.err + s2.err - worst.err;
31929
+ worklist[worstIdx] = { lo: worst.lo, hi: mid, ...s1 };
31930
+ worklist.push({ lo: mid, hi: worst.hi, ...s2 });
31931
+ }
31932
+ return {
31933
+ value: sign * totalK,
31934
+ errbnd: totalErr,
31935
+ intervalsUsed: worklist.length
31936
+ };
31937
+ }
31938
+
31593
31939
  // src/numbl-core/runtime/specialBuiltinNames.ts
31594
31940
  var SPECIAL_BUILTIN_NAMES = [
31595
31941
  "help",
@@ -31678,6 +32024,7 @@ var SPECIAL_BUILTIN_NAMES = [
31678
32024
  "webread",
31679
32025
  "delete",
31680
32026
  "rmdir",
32027
+ "movefile",
31681
32028
  "unzip",
31682
32029
  "dir",
31683
32030
  "warning",
@@ -31691,7 +32038,8 @@ var SPECIAL_BUILTIN_NAMES = [
31691
32038
  "ode45",
31692
32039
  "ode23",
31693
32040
  "deval",
31694
- "toc"
32041
+ "toc",
32042
+ "quadgk"
31695
32043
  ];
31696
32044
 
31697
32045
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -31705,42 +32053,58 @@ function registerSpecial(name, fn) {
31705
32053
  })
31706
32054
  });
31707
32055
  }
32056
+ function registerSpecialVoid(name, fn) {
32057
+ registerDynamicIBuiltin({
32058
+ name,
32059
+ resolve: () => ({
32060
+ outputTypes: [],
32061
+ apply: (args) => {
32062
+ fn(args);
32063
+ return void 0;
32064
+ }
32065
+ })
32066
+ });
32067
+ }
31708
32068
  function registerSpecialBuiltins(rt) {
31709
- registerSpecial("help", (_nargout, args) => {
32069
+ registerSpecial("help", (nargout, args) => {
32070
+ let text = "";
32071
+ const emit = (s) => {
32072
+ text += s;
32073
+ if (nargout === 0) rt.output(s);
32074
+ };
31710
32075
  if (args.length === 0) {
31711
32076
  const names = getAllBuiltinNames().sort();
31712
- rt.output("Available builtins:\n");
31713
- rt.output(" " + names.join(", ") + "\n");
31714
- rt.output("\nType 'help <name>' for help on a specific builtin.\n");
31715
- return 0;
31716
- }
31717
- const name = toString(args[0]);
31718
- const h = getIBuiltinHelp(name);
31719
- if (!h) {
31720
- const allNames = getAllBuiltinNames();
31721
- if (allNames.includes(name)) {
31722
- rt.output(`No help available for '${name}'.
32077
+ emit("Available builtins:\n");
32078
+ emit(" " + names.join(", ") + "\n");
32079
+ emit("\nType 'help <name>' for help on a specific builtin.\n");
32080
+ } else {
32081
+ const name = toString(args[0]);
32082
+ const h = getIBuiltinHelp(name);
32083
+ if (!h) {
32084
+ const allNames = getAllBuiltinNames();
32085
+ if (allNames.includes(name)) {
32086
+ emit(`No help available for '${name}'.
31723
32087
  `);
31724
- } else {
31725
- rt.output(`Unknown function '${name}'.
32088
+ } else {
32089
+ emit(`Unknown function '${name}'.
31726
32090
  `);
31727
- }
31728
- return 0;
31729
- }
31730
- rt.output(` ${h.signatures.join("\n ")}
32091
+ }
32092
+ } else {
32093
+ emit(` ${h.signatures.join("\n ")}
31731
32094
 
31732
32095
  `);
31733
- rt.output(`${h.description}
32096
+ emit(`${h.description}
31734
32097
  `);
31735
- return 0;
32098
+ }
32099
+ }
32100
+ return nargout >= 1 ? RTV.char(text) : void 0;
31736
32101
  });
31737
- registerSpecial("disp", (_nargout, args) => {
32102
+ registerSpecialVoid("disp", (args) => {
31738
32103
  if (args.length >= 1) {
31739
32104
  const mv = ensureRuntimeValue(args[0]);
31740
- if (isRuntimeTensor(mv) && mv.data.length === 0) return 0;
32105
+ if (isRuntimeTensor(mv) && mv.data.length === 0) return;
31741
32106
  rt.output(displayValue(mv) + "\n");
31742
32107
  }
31743
- return 0;
31744
32108
  });
31745
32109
  registerSpecial("toc", (nargout) => {
31746
32110
  const elapsed = (performance.now() - getTicTime()) / 1e3;
@@ -31750,12 +32114,13 @@ function registerSpecialBuiltins(rt) {
31750
32114
  }
31751
32115
  return RTV.num(elapsed);
31752
32116
  });
31753
- registerSpecial("warning", (_nargout, args) => {
31754
- if (args.length === 0) return RTV.num(0);
32117
+ registerSpecial("warning", (nargout, args) => {
32118
+ if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
31755
32119
  const margs = args.map((a) => ensureRuntimeValue(a));
31756
32120
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
31757
32121
  const state = toString(margs[0]);
31758
32122
  if (state === "on" || state === "off") {
32123
+ if (nargout === 0) return void 0;
31759
32124
  return RTV.struct(
31760
32125
  /* @__PURE__ */ new Map([
31761
32126
  ["state", RTV.char("on")],
@@ -31784,9 +32149,9 @@ function registerSpecialBuiltins(rt) {
31784
32149
  } else {
31785
32150
  rt.output("Warning: " + sprintfFormat(fmt, fmtArgs) + "\n");
31786
32151
  }
31787
- return RTV.num(0);
32152
+ return nargout >= 1 ? RTV.num(0) : void 0;
31788
32153
  });
31789
- registerSpecial("fprintf", (_nargout, args) => {
32154
+ registerSpecial("fprintf", (nargout, args) => {
31790
32155
  let output = "";
31791
32156
  if (args.length >= 1) {
31792
32157
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -31797,27 +32162,7 @@ function registerSpecialBuiltins(rt) {
31797
32162
  fmtIdx = 1;
31798
32163
  }
31799
32164
  const fmt = toString(margs[fmtIdx]);
31800
- const scalarArgs = [];
31801
- for (let i = fmtIdx + 1; i < margs.length; i++) {
31802
- const a = margs[i];
31803
- if (isRuntimeTensor(a)) {
31804
- for (let j = 0; j < a.data.length; j++)
31805
- scalarArgs.push(RTV.num(a.data[j]));
31806
- } else {
31807
- scalarArgs.push(a);
31808
- }
31809
- }
31810
- const specCount = (fmt.match(/%[^%]/g) || []).length;
31811
- if (specCount === 0 || scalarArgs.length === 0) {
31812
- output = sprintfFormat(fmt, scalarArgs);
31813
- } else {
31814
- let idx = 0;
31815
- while (idx < scalarArgs.length) {
31816
- const batch = scalarArgs.slice(idx, idx + specCount);
31817
- output += sprintfFormat(fmt, batch);
31818
- idx += specCount;
31819
- }
31820
- }
32165
+ output = sprintfFormat(fmt, margs.slice(fmtIdx + 1));
31821
32166
  if (fid === 1 || fid === 2) {
31822
32167
  rt.output(output);
31823
32168
  } else {
@@ -31828,7 +32173,7 @@ function registerSpecialBuiltins(rt) {
31828
32173
  rt.fileIO.fwrite(fid, output);
31829
32174
  }
31830
32175
  }
31831
- return output.length;
32176
+ return nargout >= 1 ? output.length : void 0;
31832
32177
  });
31833
32178
  registerSpecial("arrayfun", (nargout, args) => {
31834
32179
  return arrayfunImpl(rt, nargout, args);
@@ -31860,6 +32205,55 @@ function registerSpecialBuiltins(rt) {
31860
32205
  registerSpecial("bsxfun", (nargout, args) => {
31861
32206
  return bsxfunImpl(rt, nargout, args);
31862
32207
  });
32208
+ registerSpecial("quadgk", (nargout, args) => {
32209
+ if (args.length < 3)
32210
+ throw new RuntimeError(
32211
+ "quadgk: requires at least 3 arguments (fun, a, b)"
32212
+ );
32213
+ const fnArg = ensureRuntimeValue(args[0]);
32214
+ if (!isRuntimeFunction(fnArg))
32215
+ throw new RuntimeError("quadgk: first argument must be a function");
32216
+ const a = toNumber(ensureRuntimeValue(args[1]));
32217
+ const b = toNumber(ensureRuntimeValue(args[2]));
32218
+ let relTol;
32219
+ let absTol;
32220
+ let maxIntervalCount;
32221
+ for (let i = 3; i + 1 < args.length; i += 2) {
32222
+ const keyRv = ensureRuntimeValue(args[i]);
32223
+ const key = isRuntimeChar(keyRv) ? keyRv.value : isRuntimeString(keyRv) ? keyRv : "";
32224
+ const lowerKey = key.toLowerCase();
32225
+ const valRv = ensureRuntimeValue(args[i + 1]);
32226
+ if (lowerKey === "reltol") relTol = toNumber(valRv);
32227
+ else if (lowerKey === "abstol") absTol = toNumber(valRv);
32228
+ else if (lowerKey === "maxintervalcount")
32229
+ maxIntervalCount = toNumber(valRv);
32230
+ }
32231
+ const integrand = (pts) => {
32232
+ const vecData = new FloatXArray(pts);
32233
+ const vec = RTV.tensor(vecData, [1, pts.length]);
32234
+ const resultRaw = rt.index(fnArg, [vec], 1);
32235
+ const rv = ensureRuntimeValue(resultRaw);
32236
+ if (isRuntimeNumber(rv)) {
32237
+ return new Array(pts.length).fill(rv);
32238
+ }
32239
+ if (isRuntimeTensor(rv)) {
32240
+ if (rv.data.length !== pts.length) {
32241
+ throw new RuntimeError(
32242
+ `quadgk: integrand returned ${rv.data.length} values for ${pts.length} nodes`
32243
+ );
32244
+ }
32245
+ return Array.from(rv.data);
32246
+ }
32247
+ throw new RuntimeError("quadgk: integrand must return a numeric vector");
32248
+ };
32249
+ const result = quadgkAdaptive(integrand, a, b, {
32250
+ relTol,
32251
+ absTol,
32252
+ maxIntervalCount
32253
+ });
32254
+ if (nargout >= 2) return [result.value, result.errbnd];
32255
+ return result.value;
32256
+ });
31863
32257
  registerSpecial("subsref", (nargout, args) => {
31864
32258
  return subsrefBuiltin(rt, nargout, args);
31865
32259
  });
@@ -32482,7 +32876,7 @@ function registerSpecialBuiltins(rt) {
32482
32876
  return RTV.char(text);
32483
32877
  }
32484
32878
  });
32485
- registerSpecial("delete", (_nargout, args) => {
32879
+ registerSpecialVoid("delete", (args) => {
32486
32880
  const io = requireFileIO();
32487
32881
  if (!io.deleteFile)
32488
32882
  throw new RuntimeError("delete is not available in this environment");
@@ -32492,7 +32886,6 @@ function registerSpecialBuiltins(rt) {
32492
32886
  for (const arg of margs) {
32493
32887
  io.deleteFile(toString(arg));
32494
32888
  }
32495
- return 0;
32496
32889
  });
32497
32890
  registerSpecial("rmdir", (nargout, args) => {
32498
32891
  const io = requireFileIO();
@@ -32515,6 +32908,34 @@ function registerSpecialBuiltins(rt) {
32515
32908
  RTV.char("")
32516
32909
  ];
32517
32910
  });
32911
+ registerSpecial("movefile", (nargout, args) => {
32912
+ const io = requireFileIO();
32913
+ if (!io.movefile)
32914
+ throw new RuntimeError("movefile is not available in this environment");
32915
+ const margs = args.map((a) => ensureRuntimeValue(a));
32916
+ if (margs.length < 1)
32917
+ throw new RuntimeError("movefile requires at least 1 argument");
32918
+ const source = toString(margs[0]);
32919
+ const destination = margs.length >= 2 ? toString(margs[1]) : rt.system?.cwd() ?? ".";
32920
+ let force = false;
32921
+ if (margs.length >= 3) {
32922
+ const third = toString(margs[2]);
32923
+ if (third.toLowerCase() === "f") force = true;
32924
+ }
32925
+ const ok = io.movefile(source, destination, force);
32926
+ if (nargout === 0) {
32927
+ if (!ok)
32928
+ throw new RuntimeError(
32929
+ `movefile: cannot move '${source}' to '${destination}'`
32930
+ );
32931
+ return void 0;
32932
+ }
32933
+ return nargout <= 1 ? RTV.num(ok ? 1 : 0) : [
32934
+ RTV.num(ok ? 1 : 0),
32935
+ RTV.char(ok ? "" : `Cannot move '${source}' to '${destination}'`),
32936
+ RTV.char("")
32937
+ ];
32938
+ });
32518
32939
  registerSpecial("unzip", (nargout, args) => {
32519
32940
  const io = requireFileIO();
32520
32941
  if (!io.unzip)
@@ -32566,7 +32987,7 @@ function registerSpecialBuiltins(rt) {
32566
32987
  const cellData = extracted.map((f) => RTV.char(f));
32567
32988
  return RTV.cell(cellData, [1, cellData.length]);
32568
32989
  }
32569
- return 0;
32990
+ return void 0;
32570
32991
  });
32571
32992
  registerSpecial("dir", (nargout, args) => {
32572
32993
  const io = requireFileIO();
@@ -32663,7 +33084,7 @@ function registerSpecialBuiltins(rt) {
32663
33084
  const parts = margs.map((a) => toString(a));
32664
33085
  return RTV.char(parts.join("/"));
32665
33086
  });
32666
- registerSpecial("assignin", (_nargout, args) => {
33087
+ registerSpecialVoid("assignin", (args) => {
32667
33088
  if (args.length < 3)
32668
33089
  throw new RuntimeError("assignin requires 3 arguments");
32669
33090
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -32678,7 +33099,6 @@ function registerSpecialBuiltins(rt) {
32678
33099
  } else {
32679
33100
  rt.setWorkspaceVariable(varName, args[2]);
32680
33101
  }
32681
- return 0;
32682
33102
  });
32683
33103
  registerSpecial("evalin", (_nargout, args) => {
32684
33104
  if (args.length < 2)
@@ -32699,13 +33119,12 @@ function registerSpecialBuiltins(rt) {
32699
33119
  }
32700
33120
  return val;
32701
33121
  });
32702
- registerSpecial("drawnow", () => {
33122
+ registerSpecialVoid("drawnow", () => {
32703
33123
  rt.drawnow();
32704
- return 0;
32705
33124
  });
32706
- registerSpecial("pause", (_nargout, args) => {
33125
+ registerSpecial("pause", (nargout, args) => {
32707
33126
  rt.pause(args[0] ?? 0);
32708
- return 0;
33127
+ return nargout >= 1 ? RTV.char("on") : void 0;
32709
33128
  });
32710
33129
  registerSpecial("mfilename", (_nargout, args) => {
32711
33130
  const file = rt.$file ?? "";
@@ -32749,7 +33168,7 @@ function registerSpecialBuiltins(rt) {
32749
33168
  }
32750
33169
  }
32751
33170
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
32752
- return 0;
33171
+ return void 0;
32753
33172
  });
32754
33173
  registerSpecial("rmpath", (nargout, args) => {
32755
33174
  if (!rt.onPathChange) {
@@ -32769,11 +33188,11 @@ function registerSpecialBuiltins(rt) {
32769
33188
  }
32770
33189
  }
32771
33190
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
32772
- return 0;
33191
+ return void 0;
32773
33192
  });
32774
- registerSpecial("savepath", () => {
33193
+ registerSpecial("savepath", (nargout) => {
32775
33194
  rt.output("Warning: savepath is a no-op in numbl\n");
32776
- return 0;
33195
+ return nargout >= 1 ? RTV.num(0) : void 0;
32777
33196
  });
32778
33197
  registerSpecial("input", (_nargout, args) => {
32779
33198
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -32836,11 +33255,11 @@ function registerSpecialBuiltins(rt) {
32836
33255
  }
32837
33256
  return RTV.char(sys?.getEnv(toString(args[0])) ?? "");
32838
33257
  });
32839
- registerSpecial("setenv", (_nargout, args) => {
33258
+ registerSpecialVoid("setenv", (args) => {
32840
33259
  const sys = rt.system;
32841
33260
  if (args.length === 2) {
32842
33261
  sys?.setEnv(toString(args[0]), toString(args[1]));
32843
- return 0;
33262
+ return;
32844
33263
  }
32845
33264
  if (args.length === 1) {
32846
33265
  const d = args[0];
@@ -32848,20 +33267,26 @@ function registerSpecialBuiltins(rt) {
32848
33267
  for (const { key, value } of d.entries.values()) {
32849
33268
  sys?.setEnv(toString(key), toString(value));
32850
33269
  }
32851
- return 0;
33270
+ return;
32852
33271
  }
32853
33272
  sys?.setEnv(toString(d), "");
32854
- return 0;
33273
+ return;
32855
33274
  }
32856
33275
  throw new RuntimeError("setenv: invalid arguments");
32857
33276
  });
32858
33277
  registerSpecial("pwd", () => {
32859
33278
  return RTV.char(rt.system?.cwd() ?? "/");
32860
33279
  });
32861
- registerSpecial("cd", (_nargout, args) => {
33280
+ registerSpecial("cd", (nargout, args) => {
32862
33281
  const sys = rt.system;
32863
33282
  const curDir = sys?.cwd() ?? "/";
32864
- if (args.length === 0) return RTV.char(curDir);
33283
+ if (args.length === 0) {
33284
+ if (nargout === 0) {
33285
+ rt.output(curDir + "\n");
33286
+ return void 0;
33287
+ }
33288
+ return RTV.char(curDir);
33289
+ }
32865
33290
  const target = toString(args[0]);
32866
33291
  if (sys) {
32867
33292
  try {
@@ -32869,15 +33294,18 @@ function registerSpecialBuiltins(rt) {
32869
33294
  } catch {
32870
33295
  throw new RuntimeError(`Cannot change directory to '${target}'`);
32871
33296
  }
33297
+ if (rt.onCwdChange) {
33298
+ rt.onCwdChange(sys.cwd());
33299
+ }
32872
33300
  }
32873
- return RTV.char(curDir);
33301
+ return nargout >= 1 ? RTV.char(curDir) : void 0;
32874
33302
  });
32875
- registerSpecial("figure", (_nargout, args) => {
33303
+ registerSpecial("figure", (nargout, args) => {
32876
33304
  const handle = args.length > 0 ? args[0] : 1;
32877
33305
  plotInstr(rt.plotInstructions, { type: "set_figure_handle", handle });
32878
- return 0;
33306
+ return nargout >= 1 ? RTV.num(toNumber(ensureRuntimeValue(handle))) : void 0;
32879
33307
  });
32880
- registerSpecial("subplot", (_nargout, args) => {
33308
+ registerSpecial("subplot", (nargout, args) => {
32881
33309
  if (args.length >= 3) {
32882
33310
  plotInstr(rt.plotInstructions, {
32883
33311
  type: "set_subplot",
@@ -32886,43 +33314,41 @@ function registerSpecialBuiltins(rt) {
32886
33314
  index: args[2]
32887
33315
  });
32888
33316
  }
32889
- return 0;
33317
+ return nargout >= 1 ? RTV.num(0) : void 0;
32890
33318
  });
32891
- registerSpecial("title", (_nargout, args) => {
33319
+ registerSpecial("title", (nargout, args) => {
32892
33320
  if (args.length > 0) {
32893
33321
  plotInstr(rt.plotInstructions, { type: "set_title", text: args[0] });
32894
33322
  }
32895
- return 0;
33323
+ return nargout >= 1 ? RTV.num(0) : void 0;
32896
33324
  });
32897
- registerSpecial("xlabel", (_nargout, args) => {
33325
+ registerSpecial("xlabel", (nargout, args) => {
32898
33326
  if (args.length > 0) {
32899
33327
  plotInstr(rt.plotInstructions, { type: "set_xlabel", text: args[0] });
32900
33328
  }
32901
- return 0;
33329
+ return nargout >= 1 ? RTV.num(0) : void 0;
32902
33330
  });
32903
- registerSpecial("ylabel", (_nargout, args) => {
33331
+ registerSpecial("ylabel", (nargout, args) => {
32904
33332
  if (args.length > 0) {
32905
33333
  plotInstr(rt.plotInstructions, { type: "set_ylabel", text: args[0] });
32906
33334
  }
32907
- return 0;
33335
+ return nargout >= 1 ? RTV.num(0) : void 0;
32908
33336
  });
32909
- registerSpecial("hold", (_nargout, args) => {
33337
+ registerSpecialVoid("hold", (args) => {
32910
33338
  if (args.length > 0) {
32911
33339
  plotInstr(rt.plotInstructions, { type: "set_hold", value: args[0] });
32912
33340
  }
32913
- return 0;
32914
33341
  });
32915
- registerSpecial("grid", (_nargout, args) => {
33342
+ registerSpecialVoid("grid", (args) => {
32916
33343
  if (args.length > 0) {
32917
33344
  plotInstr(rt.plotInstructions, { type: "set_grid", value: args[0] });
32918
33345
  }
32919
- return 0;
32920
33346
  });
32921
- registerSpecial("legend", (_nargout, args) => {
33347
+ registerSpecial("legend", (nargout, args) => {
32922
33348
  legendCall(rt.plotInstructions, args);
32923
- return 0;
33349
+ return nargout >= 1 ? RTV.num(0) : void 0;
32924
33350
  });
32925
- registerSpecial("close", (_nargout, args) => {
33351
+ registerSpecial("close", (nargout, args) => {
32926
33352
  if (args.length > 0) {
32927
33353
  const val = toString(args[0]);
32928
33354
  if (val === "all") {
@@ -32933,26 +33359,25 @@ function registerSpecialBuiltins(rt) {
32933
33359
  } else {
32934
33360
  plotInstr(rt.plotInstructions, { type: "close" });
32935
33361
  }
32936
- return 0;
33362
+ return nargout >= 1 ? RTV.num(1) : void 0;
32937
33363
  });
32938
- registerSpecial("sgtitle", (_nargout, args) => {
33364
+ registerSpecial("sgtitle", (nargout, args) => {
32939
33365
  if (args.length > 0) {
32940
33366
  plotInstr(rt.plotInstructions, { type: "set_sgtitle", text: args[0] });
32941
33367
  }
32942
- return 0;
33368
+ return nargout >= 1 ? RTV.num(0) : void 0;
32943
33369
  });
32944
- registerSpecial("shading", (_nargout, args) => {
33370
+ registerSpecialVoid("shading", (args) => {
32945
33371
  if (args.length > 0) {
32946
33372
  plotInstr(rt.plotInstructions, {
32947
33373
  type: "set_shading",
32948
33374
  shading: args[0]
32949
33375
  });
32950
33376
  }
32951
- return 0;
32952
33377
  });
32953
- registerSpecial("clf", () => {
33378
+ registerSpecial("clf", (nargout) => {
32954
33379
  plotInstr(rt.plotInstructions, { type: "clf" });
32955
- return 0;
33380
+ return nargout >= 1 ? RTV.num(1) : void 0;
32956
33381
  });
32957
33382
  registerSpecial("ode45", (nargout, args) => {
32958
33383
  return _ode45Impl(rt, nargout, args, dormandPrince45);
@@ -33408,6 +33833,8 @@ var Runtime = class _Runtime {
33408
33833
  classMethodCache = /* @__PURE__ */ new Map();
33409
33834
  /** Callback for addpath/rmpath — mutates search paths and rebuilds function index. */
33410
33835
  onPathChange = null;
33836
+ /** Callback invoked after cd() to update the implicit cwd search path. */
33837
+ onCwdChange = null;
33411
33838
  /** Reference to the active search paths (set by executeCode). */
33412
33839
  searchPaths = [];
33413
33840
  // Workspace accessors: varName → { get, set } closures over script-level vars
@@ -33687,23 +34114,26 @@ var Runtime = class _Runtime {
33687
34114
  if (e instanceof RuntimeError) {
33688
34115
  return RTV.struct(
33689
34116
  /* @__PURE__ */ new Map([
33690
- ["message", RTV.string(e.message)],
33691
- ["identifier", RTV.string(e.identifier)]
34117
+ ["message", RTV.char(e.message)],
34118
+ ["identifier", RTV.char(e.identifier)],
34119
+ ["stack", buildStackField(e)]
33692
34120
  ])
33693
34121
  );
33694
34122
  }
33695
34123
  if (e instanceof Error) {
33696
34124
  return RTV.struct(
33697
34125
  /* @__PURE__ */ new Map([
33698
- ["message", RTV.string(e.message)],
33699
- ["identifier", RTV.string("")]
34126
+ ["message", RTV.char(e.message)],
34127
+ ["identifier", RTV.char("")],
34128
+ ["stack", emptyStackField()]
33700
34129
  ])
33701
34130
  );
33702
34131
  }
33703
34132
  return RTV.struct(
33704
34133
  /* @__PURE__ */ new Map([
33705
- ["message", RTV.string(String(e))],
33706
- ["identifier", RTV.string("")]
34134
+ ["message", RTV.char(String(e))],
34135
+ ["identifier", RTV.char("")],
34136
+ ["stack", emptyStackField()]
33707
34137
  ])
33708
34138
  );
33709
34139
  }
@@ -34535,6 +34965,63 @@ var rstr = (s) => {
34535
34965
  if (isRuntimeChar(s)) return s.value;
34536
34966
  throw new RuntimeError(`Expected string or char, got ${kstr(s)}`);
34537
34967
  };
34968
+ function emptyStackField() {
34969
+ return RTV.structArray(["file", "name", "line"], []);
34970
+ }
34971
+ function buildStackField(e) {
34972
+ const fieldNames = ["file", "name", "line"];
34973
+ const frames = e.callStack;
34974
+ if (!frames || frames.length === 0) {
34975
+ if (e.file || e.line !== null) {
34976
+ return RTV.structArray(fieldNames, [
34977
+ RTV.struct(
34978
+ /* @__PURE__ */ new Map([
34979
+ ["file", RTV.char(e.file ?? "")],
34980
+ ["name", RTV.char("")],
34981
+ ["line", e.line ?? 0]
34982
+ ])
34983
+ )
34984
+ ]);
34985
+ }
34986
+ return emptyStackField();
34987
+ }
34988
+ const elements = [];
34989
+ const N = frames.length;
34990
+ for (let i = N - 1; i >= 0; i--) {
34991
+ let frameFile;
34992
+ let frameLine;
34993
+ if (i === N - 1) {
34994
+ frameFile = e.file;
34995
+ frameLine = e.line ?? 0;
34996
+ } else {
34997
+ const callerFrame = frames[i + 1];
34998
+ frameFile = callerFrame.callerFile;
34999
+ frameLine = callerFrame.callerLine;
35000
+ }
35001
+ elements.push(
35002
+ RTV.struct(
35003
+ /* @__PURE__ */ new Map([
35004
+ ["file", RTV.char(frameFile ?? "")],
35005
+ ["name", RTV.char(frames[i].name)],
35006
+ ["line", frameLine]
35007
+ ])
35008
+ )
35009
+ );
35010
+ }
35011
+ const outermost = frames[0];
35012
+ if (outermost.callerFile && outermost.callerLine > 0) {
35013
+ elements.push(
35014
+ RTV.struct(
35015
+ /* @__PURE__ */ new Map([
35016
+ ["file", RTV.char(outermost.callerFile)],
35017
+ ["name", RTV.char("")],
35018
+ ["line", outermost.callerLine]
35019
+ ])
35020
+ )
35021
+ );
35022
+ }
35023
+ return RTV.structArray(fieldNames, elements);
35024
+ }
34538
35025
 
34539
35026
  // src/numbl-core/helpers/reduction-helpers.ts
34540
35027
  function squeezeTrailing(shape) {
@@ -35542,6 +36029,89 @@ defineBuiltin({
35542
36029
  }
35543
36030
  });
35544
36031
 
36032
+ // src/numbl-core/interpreter/builtins/logical.ts
36033
+ var LOGICAL_KINDS = /* @__PURE__ */ new Set([
36034
+ "number",
36035
+ "boolean",
36036
+ "tensor",
36037
+ "sparse_matrix"
36038
+ ]);
36039
+ function binaryLogicalMatch(argTypes) {
36040
+ if (argTypes.length !== 2) return null;
36041
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
36042
+ if (!LOGICAL_KINDS.has(argTypes[1].kind)) return null;
36043
+ if (argTypes[0].kind === "tensor" || argTypes[1].kind === "tensor") {
36044
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
36045
+ }
36046
+ if (argTypes[0].kind === "sparse_matrix" || argTypes[1].kind === "sparse_matrix") {
36047
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
36048
+ }
36049
+ return [{ kind: "boolean" }];
36050
+ }
36051
+ function isTensorLike(v) {
36052
+ return isRuntimeTensor(v) || isRuntimeSparseMatrix(v);
36053
+ }
36054
+ function applyBinaryLogical(args, op, scalarFn) {
36055
+ const a = args[0];
36056
+ const b = args[1];
36057
+ if (isTensorLike(a) || isTensorLike(b)) {
36058
+ return elementWiseLogicalOp(a, b, op);
36059
+ }
36060
+ return RTV.logical(scalarFn(toBool(a), toBool(b)));
36061
+ }
36062
+ var orCase = {
36063
+ match: binaryLogicalMatch,
36064
+ apply: (args) => applyBinaryLogical(
36065
+ args,
36066
+ (x, y) => x !== 0 || y !== 0 ? 1 : 0,
36067
+ (a, b) => a || b
36068
+ )
36069
+ };
36070
+ defineBuiltin({
36071
+ name: "or",
36072
+ help: {
36073
+ signatures: ["TF = or(A, B)"],
36074
+ description: "Logical OR. Functional form of A | B; returns a logical scalar or tensor."
36075
+ },
36076
+ cases: [orCase]
36077
+ });
36078
+ var andCase = {
36079
+ match: binaryLogicalMatch,
36080
+ apply: (args) => applyBinaryLogical(
36081
+ args,
36082
+ (x, y) => x !== 0 && y !== 0 ? 1 : 0,
36083
+ (a, b) => a && b
36084
+ )
36085
+ };
36086
+ defineBuiltin({
36087
+ name: "and",
36088
+ help: {
36089
+ signatures: ["TF = and(A, B)"],
36090
+ description: "Logical AND. Functional form of A & B; returns a logical scalar or tensor."
36091
+ },
36092
+ cases: [andCase]
36093
+ });
36094
+ defineBuiltin({
36095
+ name: "not",
36096
+ help: {
36097
+ signatures: ["TF = not(A)"],
36098
+ description: "Logical negation. Functional form of ~A; returns a logical scalar or tensor."
36099
+ },
36100
+ cases: [
36101
+ {
36102
+ match: (argTypes) => {
36103
+ if (argTypes.length !== 1) return null;
36104
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
36105
+ if (argTypes[0].kind === "tensor" || argTypes[0].kind === "sparse_matrix") {
36106
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
36107
+ }
36108
+ return [{ kind: "boolean" }];
36109
+ },
36110
+ apply: (args) => not(args[0])
36111
+ }
36112
+ ]
36113
+ });
36114
+
35545
36115
  // src/numbl-core/interpreter/builtins/utility.ts
35546
36116
  function sparseToDense2(S) {
35547
36117
  const data = new FloatXArray(S.m * S.n);
@@ -35650,7 +36220,7 @@ defineBuiltin({
35650
36220
  {
35651
36221
  match: (argTypes) => {
35652
36222
  if (argTypes.length < 1) return null;
35653
- return [{ kind: "number" }];
36223
+ return [];
35654
36224
  },
35655
36225
  apply: (args) => {
35656
36226
  const v = args[0];
@@ -35671,7 +36241,7 @@ defineBuiltin({
35671
36241
  const msg = args.length > 1 ? textValue(args[1]) ?? String(args[1]) : "Assertion failed";
35672
36242
  throw new Error(msg);
35673
36243
  }
35674
- return 0;
36244
+ return void 0;
35675
36245
  }
35676
36246
  }
35677
36247
  ]
@@ -35682,7 +36252,7 @@ defineBuiltin({
35682
36252
  {
35683
36253
  match: (argTypes) => {
35684
36254
  if (argTypes.length === 0) return null;
35685
- return [{ kind: "number" }];
36255
+ return [];
35686
36256
  },
35687
36257
  apply: (args) => {
35688
36258
  const first = textValue(args[0]) ?? String(args[0]);
@@ -36354,6 +36924,19 @@ defineBuiltin({
36354
36924
  name: "NaN",
36355
36925
  cases: arrayConstructorCases(nanFill, NaN)
36356
36926
  });
36927
+ function infFill(shape) {
36928
+ const data = new FloatXArray(numel(shape));
36929
+ data.fill(Infinity);
36930
+ return makeTensor(data, void 0, shape);
36931
+ }
36932
+ defineBuiltin({
36933
+ name: "inf",
36934
+ cases: arrayConstructorCases(infFill, Infinity)
36935
+ });
36936
+ defineBuiltin({
36937
+ name: "Inf",
36938
+ cases: arrayConstructorCases(infFill, Infinity)
36939
+ });
36357
36940
  defineBuiltin({
36358
36941
  name: "eye",
36359
36942
  cases: arrayConstructorCases(
@@ -37191,19 +37774,33 @@ function preserveTextType(t) {
37191
37774
  if (t.kind === "string") return { kind: "string" };
37192
37775
  return null;
37193
37776
  }
37777
+ function applyTextFn(v, fn) {
37778
+ if (isRuntimeCell(v)) {
37779
+ const out = new Array(v.data.length);
37780
+ for (let i = 0; i < v.data.length; i++) {
37781
+ out[i] = applyTextFn(v.data[i], fn);
37782
+ }
37783
+ return RTV.cell(out, [...v.shape]);
37784
+ }
37785
+ if (isRuntimeChar(v)) return RTV.char(fn(v.value));
37786
+ if (isRuntimeString(v)) return RTV.string(fn(toString(v)));
37787
+ return v;
37788
+ }
37194
37789
  function textPreserveResolve(fn) {
37195
37790
  return (argTypes) => {
37196
37791
  if (argTypes.length !== 1) return null;
37197
- const out = preserveTextType(argTypes[0]);
37792
+ const t = argTypes[0];
37793
+ if (t.kind === "cell") {
37794
+ return {
37795
+ outputTypes: [{ kind: "cell" }],
37796
+ apply: (args) => applyTextFn(args[0], fn)
37797
+ };
37798
+ }
37799
+ const out = preserveTextType(t);
37198
37800
  if (!out) return null;
37199
37801
  return {
37200
37802
  outputTypes: [out],
37201
- apply: (args) => {
37202
- const v = args[0];
37203
- const s = toString(v);
37204
- const result = fn(s);
37205
- return isRuntimeChar(v) ? RTV.char(result) : RTV.string(result);
37206
- }
37803
+ apply: (args) => applyTextFn(args[0], fn)
37207
37804
  };
37208
37805
  };
37209
37806
  }
@@ -37581,6 +38178,150 @@ registerIBuiltin({
37581
38178
  };
37582
38179
  }
37583
38180
  });
38181
+ var RE_ALPHA = /\p{L}/u;
38182
+ var RE_ALPHANUM = /[\p{L}\p{N}]/u;
38183
+ var RE_CNTRL = /\p{Cc}/u;
38184
+ var RE_GRAPHIC = /[\p{L}\p{N}\p{P}\p{S}\p{M}]/u;
38185
+ var RE_LOWER = /\p{Ll}/u;
38186
+ var RE_PUNCT = /\p{P}/u;
38187
+ var RE_UPPER = /\p{Lu}/u;
38188
+ var RE_WSPACE = /\s/u;
38189
+ function isstrpropPredicate(category) {
38190
+ switch (category) {
38191
+ case "alpha":
38192
+ return (cp) => RE_ALPHA.test(String.fromCodePoint(cp));
38193
+ case "alphanum":
38194
+ return (cp) => RE_ALPHANUM.test(String.fromCodePoint(cp));
38195
+ case "cntrl":
38196
+ return (cp) => RE_CNTRL.test(String.fromCodePoint(cp));
38197
+ case "digit":
38198
+ return (cp) => cp >= 48 && cp <= 57;
38199
+ case "graphic":
38200
+ return (cp) => RE_GRAPHIC.test(String.fromCodePoint(cp));
38201
+ case "lower":
38202
+ return (cp) => RE_LOWER.test(String.fromCodePoint(cp));
38203
+ case "print":
38204
+ return (cp) => cp === 32 || RE_GRAPHIC.test(String.fromCodePoint(cp));
38205
+ case "punct":
38206
+ return (cp) => RE_PUNCT.test(String.fromCodePoint(cp));
38207
+ case "wspace":
38208
+ return (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
38209
+ case "upper":
38210
+ return (cp) => RE_UPPER.test(String.fromCodePoint(cp));
38211
+ case "xdigit":
38212
+ return (cp) => cp >= 48 && cp <= 57 || cp >= 65 && cp <= 70 || cp >= 97 && cp <= 102;
38213
+ default:
38214
+ return null;
38215
+ }
38216
+ }
38217
+ function stringCodePoints(s) {
38218
+ const out = [];
38219
+ for (const ch of s) out.push(ch.codePointAt(0));
38220
+ return out;
38221
+ }
38222
+ function logicalRowFromString(s, pred, shape) {
38223
+ const cps = stringCodePoints(s);
38224
+ const data = new FloatXArray(cps.length);
38225
+ for (let i = 0; i < cps.length; i++) data[i] = pred(cps[i]) ? 1 : 0;
38226
+ return {
38227
+ kind: "tensor",
38228
+ data,
38229
+ shape: shape ?? [1, cps.length],
38230
+ _isLogical: true,
38231
+ _rc: 1
38232
+ };
38233
+ }
38234
+ function logicalFromNumericTensor(data, shape, pred) {
38235
+ const out = new FloatXArray(data.length);
38236
+ for (let i = 0; i < data.length; i++) {
38237
+ out[i] = pred(Math.round(data[i])) ? 1 : 0;
38238
+ }
38239
+ return {
38240
+ kind: "tensor",
38241
+ data: out,
38242
+ shape: [...shape],
38243
+ _isLogical: true,
38244
+ _rc: 1
38245
+ };
38246
+ }
38247
+ function isstrpropApply(args) {
38248
+ const v = args[0];
38249
+ const category = toString(args[1]);
38250
+ const pred = isstrpropPredicate(category);
38251
+ if (!pred) {
38252
+ throw new RuntimeError(`isstrprop: unknown category '${category}'`);
38253
+ }
38254
+ let forceCell = false;
38255
+ if (args.length >= 3) {
38256
+ const flagName = toString(args[2]).toLowerCase();
38257
+ if (flagName !== "forcecelloutput") {
38258
+ throw new RuntimeError(`isstrprop: unknown name '${toString(args[2])}'`);
38259
+ }
38260
+ if (args.length < 4) {
38261
+ throw new RuntimeError("isstrprop: 'ForceCellOutput' requires a value");
38262
+ }
38263
+ forceCell = toNumber(args[3]) !== 0;
38264
+ }
38265
+ if (isRuntimeCell(v)) {
38266
+ const out = new Array(v.data.length);
38267
+ for (let i = 0; i < v.data.length; i++) {
38268
+ const elem = v.data[i];
38269
+ const s = isText(elem) ? toString(elem) : "";
38270
+ out[i] = logicalRowFromString(s, pred);
38271
+ }
38272
+ return RTV.cell(out, [...v.shape]);
38273
+ }
38274
+ let result;
38275
+ if (isRuntimeString(v)) {
38276
+ result = logicalRowFromString(toString(v), pred);
38277
+ } else if (isRuntimeChar(v)) {
38278
+ const c = v;
38279
+ result = logicalRowFromString(
38280
+ c.value,
38281
+ pred,
38282
+ c.shape ? [...c.shape] : void 0
38283
+ );
38284
+ } else if (isRuntimeTensor(v)) {
38285
+ result = logicalFromNumericTensor(v.data, v.shape, pred);
38286
+ } else if (isRuntimeNumber(v) || isRuntimeLogical(v)) {
38287
+ const cp = Math.round(toNumber(v));
38288
+ const data = new FloatXArray(1);
38289
+ data[0] = pred(cp) ? 1 : 0;
38290
+ result = { kind: "tensor", data, shape: [1, 1], _isLogical: true, _rc: 1 };
38291
+ } else {
38292
+ throw new RuntimeError("isstrprop: unsupported input type");
38293
+ }
38294
+ if (forceCell) return RTV.cell([result], [1, 1]);
38295
+ return result;
38296
+ }
38297
+ registerIBuiltin({
38298
+ name: "isstrprop",
38299
+ help: {
38300
+ signatures: [
38301
+ "TF = isstrprop(str, category)",
38302
+ "TF = isstrprop(str, category, 'ForceCellOutput', tf)"
38303
+ ],
38304
+ description: "Test which characters in str belong to a category (alpha, alphanum, cntrl, digit, graphic, lower, print, punct, upper, wspace, xdigit). Returns a logical array, or a cell of logical vectors when str is a cell array or ForceCellOutput is true. Numeric input is treated as Unicode code points."
38305
+ },
38306
+ resolve: (argTypes) => {
38307
+ if (argTypes.length < 2 || argTypes.length > 4) return null;
38308
+ if (!isTextType(argTypes[1])) return null;
38309
+ if (argTypes.length >= 3 && !isTextType(argTypes[2])) return null;
38310
+ if (argTypes.length === 4) {
38311
+ const k = argTypes[3].kind;
38312
+ if (k !== "number" && k !== "boolean") return null;
38313
+ }
38314
+ const t0 = argTypes[0];
38315
+ if (t0.kind !== "char" && t0.kind !== "string" && t0.kind !== "number" && t0.kind !== "boolean" && t0.kind !== "tensor" && t0.kind !== "cell") {
38316
+ return null;
38317
+ }
38318
+ const isCell = t0.kind === "cell";
38319
+ return {
38320
+ outputTypes: isCell ? [{ kind: "cell" }] : [{ kind: "tensor", isComplex: false, isLogical: true }],
38321
+ apply: (args) => isstrpropApply(args)
38322
+ };
38323
+ }
38324
+ });
37584
38325
  registerIBuiltin({
37585
38326
  name: "contains",
37586
38327
  resolve: (argTypes) => {
@@ -40966,11 +41707,15 @@ defineBuiltin({
40966
41707
  apply: (args) => {
40967
41708
  if (args.length !== 2)
40968
41709
  throw new RuntimeError("kron requires 2 arguments");
40969
- const a = args[0], b = args[1];
40970
- const A = isRuntimeNumber(a) ? RTV.tensor(new FloatXArray([a]), [1, 1]) : a;
40971
- const B = isRuntimeNumber(b) ? RTV.tensor(new FloatXArray([b]), [1, 1]) : b;
40972
- if (!isRuntimeTensor(A) || !isRuntimeTensor(B))
41710
+ const coerce = (v) => {
41711
+ if (isRuntimeNumber(v))
41712
+ return RTV.tensor(new FloatXArray([v]), [1, 1]);
41713
+ if (isRuntimeSparseMatrix(v)) return sparseToDense(v);
41714
+ if (isRuntimeTensor(v)) return v;
40973
41715
  throw new RuntimeError("kron: arguments must be numeric");
41716
+ };
41717
+ const A = coerce(args[0]);
41718
+ const B = coerce(args[1]);
40974
41719
  const [m, n] = tensorSize2D(A);
40975
41720
  const [p2, q] = tensorSize2D(B);
40976
41721
  const rows = m * p2, cols = n * q;
@@ -43288,6 +44033,65 @@ defineBuiltin({
43288
44033
  }
43289
44034
  ]
43290
44035
  });
44036
+ defineBuiltin({
44037
+ name: "bitget",
44038
+ cases: [
44039
+ {
44040
+ match: (argTypes) => {
44041
+ if (argTypes.length !== 2) return null;
44042
+ return [{ kind: "unknown" }];
44043
+ },
44044
+ // bitget(A, bit) returns bit number `bit` (1-based, 1 = LSB) of each
44045
+ // element of A. MATLAB supports vector `bit` with broadcasting; we
44046
+ // match the scalar-or-same-shape rules used by the other bitwise ops.
44047
+ apply: (args) => bitwiseOp(
44048
+ args[0],
44049
+ args[1],
44050
+ (a, bit) => {
44051
+ const b = Math.round(bit);
44052
+ if (b < 1) return 0;
44053
+ if (b <= 31) return a >>> b - 1 & 1;
44054
+ const bi = BigInt(a);
44055
+ return Number(bi >> BigInt(b - 1) & 1n);
44056
+ },
44057
+ "bitget"
44058
+ )
44059
+ }
44060
+ ]
44061
+ });
44062
+ defineBuiltin({
44063
+ name: "bitset",
44064
+ cases: [
44065
+ {
44066
+ match: (argTypes) => {
44067
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
44068
+ return [{ kind: "unknown" }];
44069
+ },
44070
+ // bitset(A, bit) → set bit to 1
44071
+ // bitset(A, bit, v) → set bit to v (0 or 1)
44072
+ apply: (args) => {
44073
+ const v = args.length >= 3 ? Math.round(toNumber(args[2])) : 1;
44074
+ return bitwiseOp(
44075
+ args[0],
44076
+ args[1],
44077
+ (a, bit) => {
44078
+ const b = Math.round(bit);
44079
+ if (b < 1) return a;
44080
+ if (b <= 31) {
44081
+ const mask2 = 1 << b - 1;
44082
+ return v ? a | mask2 : a & ~mask2;
44083
+ }
44084
+ const bi = BigInt(a);
44085
+ const mask = 1n << BigInt(b - 1);
44086
+ const out = v ? bi | mask : bi & ~mask;
44087
+ return Number(out);
44088
+ },
44089
+ "bitset"
44090
+ );
44091
+ }
44092
+ }
44093
+ ]
44094
+ });
43291
44095
  function coordTransform(name, nArgs, nOut, fn) {
43292
44096
  defineBuiltin({
43293
44097
  name,
@@ -44417,6 +45221,131 @@ defineBuiltin({
44417
45221
  }
44418
45222
  ]
44419
45223
  });
45224
+ var INT_RANGES = [
45225
+ { name: "int8", min: -128, max: 127 },
45226
+ { name: "int16", min: -32768, max: 32767 },
45227
+ { name: "int32", min: -2147483648, max: 2147483647 },
45228
+ // int64/uint64 can't represent their full native range as doubles;
45229
+ // clamp at Number.MAX_SAFE_INTEGER to avoid silent precision loss.
45230
+ {
45231
+ name: "int64",
45232
+ min: -Number.MAX_SAFE_INTEGER,
45233
+ max: Number.MAX_SAFE_INTEGER
45234
+ },
45235
+ { name: "uint8", min: 0, max: 255 },
45236
+ { name: "uint16", min: 0, max: 65535 },
45237
+ { name: "uint32", min: 0, max: 4294967295 },
45238
+ { name: "uint64", min: 0, max: Number.MAX_SAFE_INTEGER }
45239
+ ];
45240
+ function saturateRoundToward(x, min, max) {
45241
+ if (isNaN(x)) return 0;
45242
+ const r = x >= 0 ? Math.floor(x + 0.5) : -Math.floor(-x + 0.5);
45243
+ if (r < min) return min;
45244
+ if (r > max) return max;
45245
+ return r;
45246
+ }
45247
+ for (const { name, min, max } of INT_RANGES) {
45248
+ defineBuiltin({
45249
+ name,
45250
+ cases: [
45251
+ {
45252
+ match: (argTypes) => {
45253
+ if (argTypes.length !== 1) return null;
45254
+ const a = argTypes[0];
45255
+ if (a.kind === "number" || a.kind === "boolean" || a.kind === "char" || a.kind === "complex_or_number")
45256
+ return [{ kind: "number" }];
45257
+ if (a.kind === "tensor")
45258
+ return [
45259
+ {
45260
+ kind: "tensor",
45261
+ isComplex: false,
45262
+ shape: a.shape,
45263
+ ndim: a.ndim
45264
+ }
45265
+ ];
45266
+ return null;
45267
+ },
45268
+ apply: (args) => {
45269
+ const v = args[0];
45270
+ if (isRuntimeNumber(v))
45271
+ return RTV.num(saturateRoundToward(v, min, max));
45272
+ if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
45273
+ if (isRuntimeComplexNumber(v))
45274
+ return RTV.num(saturateRoundToward(v.re, min, max));
45275
+ if (isRuntimeChar(v)) {
45276
+ if (v.value.length === 0)
45277
+ return RTV.tensor(new FloatXArray(0), [0, 0]);
45278
+ if (v.value.length === 1)
45279
+ return RTV.num(
45280
+ saturateRoundToward(v.value.charCodeAt(0), min, max)
45281
+ );
45282
+ const out = new FloatXArray(v.value.length);
45283
+ for (let i = 0; i < v.value.length; i++) {
45284
+ out[i] = saturateRoundToward(v.value.charCodeAt(i), min, max);
45285
+ }
45286
+ return RTV.row(Array.from(out));
45287
+ }
45288
+ if (isRuntimeTensor(v)) {
45289
+ const data = new FloatXArray(v.data.length);
45290
+ for (let i = 0; i < v.data.length; i++) {
45291
+ data[i] = saturateRoundToward(v.data[i], min, max);
45292
+ }
45293
+ return RTV.tensor(data, [...v.shape]);
45294
+ }
45295
+ return RTV.num(saturateRoundToward(toNumber(v), min, max));
45296
+ }
45297
+ }
45298
+ ]
45299
+ });
45300
+ }
45301
+ defineBuiltin({
45302
+ name: "idivide",
45303
+ cases: [
45304
+ {
45305
+ match: (argTypes) => {
45306
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
45307
+ return [{ kind: "unknown" }];
45308
+ },
45309
+ apply: (args) => {
45310
+ const divFix = (a2, b2) => {
45311
+ if (b2 === 0) {
45312
+ return 0;
45313
+ }
45314
+ const q = a2 / b2;
45315
+ return q >= 0 ? Math.floor(q) : -Math.floor(-q);
45316
+ };
45317
+ const a = args[0];
45318
+ const b = args[1];
45319
+ if (isRuntimeNumber(a) && isRuntimeNumber(b)) {
45320
+ return RTV.num(divFix(a, b));
45321
+ }
45322
+ if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
45323
+ const bv = b;
45324
+ const data = new FloatXArray(a.data.length);
45325
+ for (let i = 0; i < a.data.length; i++)
45326
+ data[i] = divFix(a.data[i], bv);
45327
+ return RTV.tensor(data, [...a.shape]);
45328
+ }
45329
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
45330
+ const av = a;
45331
+ const data = new FloatXArray(b.data.length);
45332
+ for (let i = 0; i < b.data.length; i++)
45333
+ data[i] = divFix(av, b.data[i]);
45334
+ return RTV.tensor(data, [...b.shape]);
45335
+ }
45336
+ if (isRuntimeTensor(a) && isRuntimeTensor(b)) {
45337
+ if (a.data.length !== b.data.length)
45338
+ throw new RuntimeError("idivide: arrays must be the same size");
45339
+ const data = new FloatXArray(a.data.length);
45340
+ for (let i = 0; i < a.data.length; i++)
45341
+ data[i] = divFix(a.data[i], b.data[i]);
45342
+ return RTV.tensor(data, [...a.shape]);
45343
+ }
45344
+ throw new RuntimeError("idivide: arguments must be numeric");
45345
+ }
45346
+ }
45347
+ ]
45348
+ });
44420
45349
  defineBuiltin({
44421
45350
  name: "logical",
44422
45351
  cases: [
@@ -47654,6 +48583,15 @@ var H = {
47654
48583
  signatures: ["TF = isequal(A, B, ...)"],
47655
48584
  description: "True if all inputs are equal (NaN ~= NaN)."
47656
48585
  },
48586
+ // ── Dynamic evaluation ────────────────────────────────────────────────
48587
+ evalin: {
48588
+ signatures: ["V = evalin(WS, EXPR)", "V = evalin(WS, EXPR, DEFAULT)"],
48589
+ description: "Evaluate EXPR in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables read by evalin must be declared in the\nfunction that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
48590
+ },
48591
+ assignin: {
48592
+ signatures: ["assignin(WS, NAME, VALUE)"],
48593
+ description: "Assign VALUE to variable NAME in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables written by assignin must be declared in\nthe function that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
48594
+ },
47657
48595
  // ── Misc ──────────────────────────────────────────────────────────────
47658
48596
  disp: {
47659
48597
  signatures: ["disp(X)"],
@@ -48551,7 +49489,7 @@ function getSourceLine(getSource, file, line) {
48551
49489
  }
48552
49490
 
48553
49491
  // src/numbl-core/version.ts
48554
- var NUMBL_VERSION = "0.1.3";
49492
+ var NUMBL_VERSION = "0.1.5";
48555
49493
 
48556
49494
  // src/cli-repl.ts
48557
49495
  import { createInterface } from "readline";
@@ -50958,6 +51896,9 @@ function execStmt(stmt) {
50958
51896
  case "ExprStmt": {
50959
51897
  const val = this.evalExprNargout(stmt.expr, 0);
50960
51898
  const singleVal = Array.isArray(val) ? val[0] : val;
51899
+ if (singleVal === void 0) {
51900
+ return null;
51901
+ }
50961
51902
  const rv = ensureRuntimeValue(singleVal);
50962
51903
  this.ans = rv;
50963
51904
  this.env.set("ans", rv);
@@ -50981,19 +51922,25 @@ function execStmt(stmt) {
50981
51922
  if (stmt.lvalues.length === 1 && stmt.lvalues[0].type === "IndexCell") {
50982
51923
  const lv = stmt.lvalues[0];
50983
51924
  const cellBase = lv.base.type === "Ident" ? this.env.get(lv.base.name) ?? RTV.cell([], [0, 0]) : this.evalExpr(lv.base);
50984
- const indices = lv.indices.map((idx) => this.evalExpr(idx));
50985
- const idxVal = ensureRuntimeValue(indices[0]);
51925
+ const indices = this.evalIndicesWithEnd(cellBase, lv.indices);
50986
51926
  let expandedCount = 1;
50987
- if (isRuntimeTensor(idxVal)) {
50988
- expandedCount = idxVal.data.length;
50989
- } else if (typeof idxVal === "number") {
50990
- expandedCount = 1;
51927
+ const idx0 = indices[0];
51928
+ if (idx0 === COLON_SENTINEL) {
51929
+ const baseRv = ensureRuntimeValue(cellBase);
51930
+ expandedCount = isRuntimeCell(baseRv) ? baseRv.data.length : 0;
51931
+ } else {
51932
+ const idxVal = ensureRuntimeValue(idx0);
51933
+ if (isRuntimeTensor(idxVal)) {
51934
+ expandedCount = idxVal.data.length;
51935
+ } else if (typeof idxVal === "number") {
51936
+ expandedCount = 1;
51937
+ }
50991
51938
  }
50992
51939
  const val2 = this.evalExprNargout(stmt.expr, expandedCount);
50993
51940
  const values2 = Array.isArray(val2) ? val2 : [val2];
50994
51941
  const result = this.rt.multiOutputCellAssign(
50995
51942
  cellBase,
50996
- indices[0],
51943
+ idx0,
50997
51944
  values2.map((v) => ensureRuntimeValue(v))
50998
51945
  );
50999
51946
  if (lv.base.type === "Ident") {
@@ -51555,7 +52502,11 @@ function evalAnonFunc(expr) {
51555
52502
  capturedMethodName,
51556
52503
  () => {
51557
52504
  try {
51558
- return this.evalExprNargout(bodyExpr, narg);
52505
+ const result = this.evalExprNargout(bodyExpr, narg);
52506
+ if (narg > 1 && !(Array.isArray(result) && result.length >= narg)) {
52507
+ throw new RuntimeError("Too many output arguments.");
52508
+ }
52509
+ return result;
51559
52510
  } finally {
51560
52511
  this.env = savedEnv;
51561
52512
  }
@@ -52035,6 +52986,17 @@ register("exist", (ctx, args) => {
52035
52986
  }
52036
52987
  return FALL_THROUGH;
52037
52988
  });
52989
+ register("which", (ctx, args) => {
52990
+ if (args.length < 1) return FALL_THROUGH;
52991
+ const nameArg = toString(ensureRuntimeValue(args[0]));
52992
+ if (ctx.env.has(nameArg)) return RTV.char("variable");
52993
+ const filePath = ctx.lookupWorkspaceFile(nameArg);
52994
+ if (filePath) return RTV.char(filePath);
52995
+ if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) {
52996
+ return RTV.char("built-in");
52997
+ }
52998
+ return RTV.char("");
52999
+ });
52038
53000
  register("isfolder", (ctx, args) => {
52039
53001
  if (args.length < 1) return FALL_THROUGH;
52040
53002
  const fio = ctx.rt.fileIO;
@@ -52137,6 +53099,9 @@ register("run", (ctx, args) => {
52137
53099
  } catch {
52138
53100
  throw new RuntimeError(`Cannot change directory to '${scriptDir}'`);
52139
53101
  }
53102
+ if (ctx.rt.onCwdChange) {
53103
+ ctx.rt.onCwdChange(sys.cwd());
53104
+ }
52140
53105
  }
52141
53106
  const cwdAfterCd = sys?.cwd() ?? "/";
52142
53107
  try {
@@ -52148,6 +53113,9 @@ register("run", (ctx, args) => {
52148
53113
  sys.chdir(oldCwd);
52149
53114
  } catch {
52150
53115
  }
53116
+ if (ctx.rt.onCwdChange) {
53117
+ ctx.rt.onCwdChange(sys.cwd());
53118
+ }
52151
53119
  }
52152
53120
  }
52153
53121
  return void 0;
@@ -52167,7 +53135,14 @@ function callFunction(name, args, nargout) {
52167
53135
  workspaceEnv: this.workspaceEnv,
52168
53136
  evalInLocalScope: (codeArg, fileName) => this.evalInLocalScope(codeArg, fileName),
52169
53137
  callFunction: (n, a, no) => this.callFunction(n, a, no),
52170
- rt: this.rt
53138
+ rt: this.rt,
53139
+ lookupWorkspaceFile: (n) => {
53140
+ const entry = this.ctx.registry.filesByFuncName.get(n);
53141
+ if (entry) return entry.fileName;
53142
+ const classInfo = this.ctx.getClassInfo(n);
53143
+ if (classInfo) return classInfo.fileName;
53144
+ return void 0;
53145
+ }
52171
53146
  };
52172
53147
  const result = specialHandler(ctx, args, nargout);
52173
53148
  if (result !== FALL_THROUGH) return result;
@@ -52201,13 +53176,18 @@ function interpretTarget(target, args, nargout) {
52201
53176
  const argTypes = margs.map(inferJitType);
52202
53177
  const resolution = ib.resolve(argTypes, nargout);
52203
53178
  if (resolution) {
53179
+ const isVoid = resolution.outputTypes.length === 0;
53180
+ if (isVoid && nargout > 0) {
53181
+ throw new RuntimeError("Too many output arguments.");
53182
+ }
52204
53183
  if (this.rt.profilingEnabled) {
52205
53184
  this.rt.profileEnter("builtin:interp:" + target.name);
52206
- const result = resolution.apply(margs, nargout);
53185
+ const result2 = resolution.apply(margs, nargout);
52207
53186
  this.rt.profileLeave();
52208
- return result;
53187
+ return isVoid ? void 0 : result2;
52209
53188
  }
52210
- return resolution.apply(margs, nargout);
53189
+ const result = resolution.apply(margs, nargout);
53190
+ return isVoid ? void 0 : result;
52211
53191
  }
52212
53192
  }
52213
53193
  const builtin = this.rt.builtins[target.name];
@@ -52299,7 +53279,19 @@ function interpretLocalFunction(target, args, nargout) {
52299
53279
  function interpretWorkspaceFunction(target, args, nargout) {
52300
53280
  const dotIdx = target.name.lastIndexOf(".");
52301
53281
  const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
52302
- const fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
53282
+ let fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
53283
+ if (!fn) {
53284
+ const entry = this.ctx.registry.filesByFuncName.get(target.name);
53285
+ if (entry) {
53286
+ const ast = this.ctx.getCachedAST(entry.fileName);
53287
+ for (const stmt of ast.body) {
53288
+ if (stmt.type === "Function") {
53289
+ fn = funcDefFromStmt(stmt);
53290
+ break;
53291
+ }
53292
+ }
53293
+ }
53294
+ }
52303
53295
  if (!fn) {
52304
53296
  const entry = this.ctx.registry.filesByFuncName.get(target.name);
52305
53297
  if (entry) {
@@ -52443,6 +53435,11 @@ function interpretConstructor(classInfo, args, nargout) {
52443
53435
  return args[0];
52444
53436
  }
52445
53437
  function callUserFunction(fn, args, nargout, narginOverride) {
53438
+ const hasVarargoutDecl = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
53439
+ const declaredRegularOutputs = hasVarargoutDecl ? fn.outputs.length - 1 : fn.outputs.length;
53440
+ if (!hasVarargoutDecl && nargout > declaredRegularOutputs) {
53441
+ throw new RuntimeError("Too many output arguments.");
53442
+ }
52446
53443
  if (this.optimization >= 1 && narginOverride === void 0) {
52447
53444
  const jitResult = tryJitCall(this, fn, args, nargout);
52448
53445
  if (jitResult !== JIT_SKIP) return jitResult;
@@ -52492,7 +53489,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
52492
53489
  }
52493
53490
  }
52494
53491
  }
52495
- const hasVarargout = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
53492
+ const hasVarargout = hasVarargoutDecl;
52496
53493
  const regularOutputs = hasVarargout ? fn.outputs.slice(0, -1) : fn.outputs;
52497
53494
  const collectCount = nargout === 0 && regularOutputs.length > 0 ? 1 : Math.min(regularOutputs.length, nargout);
52498
53495
  const outputs = [];
@@ -54097,6 +55094,31 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54097
55094
  }
54098
55095
  }
54099
55096
  }
55097
+ let implicitCwdPath = null;
55098
+ if (options.implicitCwdPath !== null && options.system && options.fileIO?.scanDirectory) {
55099
+ try {
55100
+ const cwd = options.implicitCwdPath ?? options.system.cwd();
55101
+ const absCwd = options.fileIO.resolvePath?.(cwd) ?? cwd;
55102
+ const explicitPaths = searchPaths ?? [];
55103
+ if (!explicitPaths.includes(absCwd)) {
55104
+ implicitCwdPath = absCwd;
55105
+ const prefix = absCwd.endsWith("/") ? absCwd : absCwd + "/";
55106
+ const alreadyHave = mWorkspaceFiles.some(
55107
+ (f) => f.name === absCwd || f.name.startsWith(prefix)
55108
+ );
55109
+ if (!alreadyHave) {
55110
+ const cwdFiles = options.fileIO.scanDirectory(absCwd);
55111
+ for (const f of cwdFiles) {
55112
+ if (f.name.endsWith(".m")) {
55113
+ mWorkspaceFiles.push(f);
55114
+ }
55115
+ }
55116
+ }
55117
+ }
55118
+ } catch {
55119
+ implicitCwdPath = null;
55120
+ }
55121
+ }
54100
55122
  const jsUserFunctions = loadJsUserFunctions(
54101
55123
  jsWorkspaceFiles,
54102
55124
  wasmWorkspaceFiles,
@@ -54114,6 +55136,9 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54114
55136
  stdlibShimNames.add(shimName);
54115
55137
  }
54116
55138
  ctx.registry.searchPaths = [...searchPaths ?? [], SHIM_SEARCH_PATH];
55139
+ if (implicitCwdPath !== null) {
55140
+ ctx.registry.searchPaths.unshift(implicitCwdPath);
55141
+ }
54117
55142
  ctx.fileASTCache.set(mainFileName, ast);
54118
55143
  const skippedFiles = /* @__PURE__ */ new Set();
54119
55144
  for (const f of mWorkspaceFiles) {
@@ -54147,6 +55172,11 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54147
55172
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
54148
55173
  }
54149
55174
  const functionIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
55175
+ const savedSpecialBuiltins = /* @__PURE__ */ new Map();
55176
+ for (const name of SPECIAL_BUILTIN_NAMES) {
55177
+ const existing = getIBuiltin(name);
55178
+ if (existing) savedSpecialBuiltins.set(name, existing);
55179
+ }
54150
55180
  const rt = new Runtime(options, options.initialVariableValues);
54151
55181
  const savedIBuiltins = /* @__PURE__ */ new Map();
54152
55182
  for (const ib of jsUserFunctions) {
@@ -54188,13 +55218,39 @@ ${jsCode}`
54188
55218
  interpreter.installRuntimeCallbacks();
54189
55219
  rt.searchPaths = ctx.registry.searchPaths;
54190
55220
  let pathsModified = false;
55221
+ const rebuildWorkspace = () => {
55222
+ const paths = ctx.registry.searchPaths;
55223
+ const priorityOf = (name) => {
55224
+ let bestIdx = paths.length;
55225
+ let bestLen = -1;
55226
+ for (let i = 0; i < paths.length; i++) {
55227
+ const p2 = paths[i];
55228
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
55229
+ if (name === p2 || name.startsWith(withSep)) {
55230
+ if (p2.length > bestLen) {
55231
+ bestLen = p2.length;
55232
+ bestIdx = i;
55233
+ }
55234
+ }
55235
+ }
55236
+ return bestIdx;
55237
+ };
55238
+ mWorkspaceFiles.sort((a, b) => priorityOf(a.name) - priorityOf(b.name));
55239
+ ctx.clearWorkspaceRegistrations();
55240
+ ctx.registerWorkspaceFiles(mWorkspaceFiles);
55241
+ const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
55242
+ interpreter.functionIndex = newIndex;
55243
+ interpreter.clearAllCaches();
55244
+ pathsModified = true;
55245
+ };
54191
55246
  rt.onPathChange = (action, dir, position) => {
54192
55247
  const fileIO = options.fileIO;
54193
55248
  const absDir = fileIO?.resolvePath?.(dir) ?? dir;
54194
55249
  if (action === "add") {
54195
55250
  if (ctx.registry.searchPaths.includes(absDir)) return;
54196
55251
  if (position === "begin") {
54197
- ctx.registry.searchPaths.unshift(absDir);
55252
+ const cwdIsFirst = implicitCwdPath !== null && ctx.registry.searchPaths[0] === implicitCwdPath;
55253
+ ctx.registry.searchPaths.splice(cwdIsFirst ? 1 : 0, 0, absDir);
54198
55254
  } else {
54199
55255
  const shimIdx = ctx.registry.searchPaths.indexOf(SHIM_SEARCH_PATH);
54200
55256
  if (shimIdx >= 0) {
@@ -54212,10 +55268,14 @@ ${jsCode}`
54212
55268
  try {
54213
55269
  ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
54214
55270
  } catch (e) {
54215
- if (e instanceof SyntaxError && e.file === null) {
54216
- e.file = f.name;
55271
+ if (e instanceof SyntaxError) {
55272
+ console.warn(
55273
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
55274
+ );
55275
+ } else {
55276
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
54217
55277
  }
54218
- throw e;
55278
+ continue;
54219
55279
  }
54220
55280
  interpreter.fileSources.set(f.name, f.source);
54221
55281
  mWorkspaceFiles.push(f);
@@ -54255,26 +55315,76 @@ ${jsCode}`
54255
55315
  }
54256
55316
  }
54257
55317
  }
54258
- const paths = ctx.registry.searchPaths;
54259
- mWorkspaceFiles.sort((a, b) => {
54260
- const ai = paths.findIndex(
54261
- (p2) => a.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || a.name === p2
54262
- );
54263
- const bi = paths.findIndex(
54264
- (p2) => b.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || b.name === p2
55318
+ rebuildWorkspace();
55319
+ };
55320
+ rt.onCwdChange = (newCwd) => {
55321
+ const fileIO = options.fileIO;
55322
+ const absNewCwd = fileIO?.resolvePath?.(newCwd) ?? newCwd;
55323
+ if (implicitCwdPath === absNewCwd) return;
55324
+ if (implicitCwdPath !== null) {
55325
+ const oldPrefix = implicitCwdPath.endsWith("/") ? implicitCwdPath : implicitCwdPath + "/";
55326
+ const deeperPaths = ctx.registry.searchPaths.filter(
55327
+ (p2) => p2 !== implicitCwdPath && p2 !== SHIM_SEARCH_PATH && (p2 === implicitCwdPath || p2.startsWith(oldPrefix))
54265
55328
  );
54266
- const aPri = ai >= 0 ? ai : paths.length;
54267
- const bPri = bi >= 0 ? bi : paths.length;
54268
- return aPri - bPri;
54269
- });
54270
- ctx.clearWorkspaceRegistrations();
54271
- ctx.registerWorkspaceFiles(mWorkspaceFiles);
54272
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
54273
- interpreter.functionIndex = newIndex;
54274
- interpreter.clearAllCaches();
54275
- pathsModified = true;
55329
+ const fileBelongsToDeeperPath = (fname) => {
55330
+ for (const p2 of deeperPaths) {
55331
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
55332
+ if (fname === p2 || fname.startsWith(withSep)) return true;
55333
+ }
55334
+ return false;
55335
+ };
55336
+ for (let i = mWorkspaceFiles.length - 1; i >= 0; i--) {
55337
+ const fname = mWorkspaceFiles[i].name;
55338
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
55339
+ ctx.fileASTCache.delete(fname);
55340
+ interpreter.fileSources.delete(fname);
55341
+ mWorkspaceFiles.splice(i, 1);
55342
+ }
55343
+ }
55344
+ const oldIdx = ctx.registry.searchPaths.indexOf(implicitCwdPath);
55345
+ if (oldIdx >= 0) ctx.registry.searchPaths.splice(oldIdx, 1);
55346
+ implicitCwdPath = null;
55347
+ }
55348
+ if (ctx.registry.searchPaths.includes(absNewCwd)) {
55349
+ rebuildWorkspace();
55350
+ return;
55351
+ }
55352
+ ctx.registry.searchPaths.unshift(absNewCwd);
55353
+ implicitCwdPath = absNewCwd;
55354
+ if (fileIO?.scanDirectory) {
55355
+ let newFiles = [];
55356
+ try {
55357
+ newFiles = fileIO.scanDirectory(absNewCwd);
55358
+ } catch {
55359
+ }
55360
+ for (const f of newFiles) {
55361
+ if (f.name.endsWith(".m") && !ctx.fileASTCache.has(f.name)) {
55362
+ try {
55363
+ ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
55364
+ } catch (e) {
55365
+ if (e instanceof SyntaxError) {
55366
+ console.warn(
55367
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
55368
+ );
55369
+ } else {
55370
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
55371
+ }
55372
+ continue;
55373
+ }
55374
+ interpreter.fileSources.set(f.name, f.source);
55375
+ mWorkspaceFiles.push(f);
55376
+ }
55377
+ }
55378
+ }
55379
+ rebuildWorkspace();
54276
55380
  };
54277
55381
  rt.evalLocalCallback = (code, initialVars, onOutput, fileName) => {
55382
+ const nestedSearchPaths = ctx.registry.searchPaths.filter(
55383
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
55384
+ );
55385
+ const nestedWorkspaceFiles = mWorkspaceFiles.filter(
55386
+ (f) => !stdlibShimNames.has(f.name)
55387
+ );
54278
55388
  const evalResult = executeCode(
54279
55389
  code,
54280
55390
  {
@@ -54282,11 +55392,18 @@ ${jsCode}`
54282
55392
  displayResults: false,
54283
55393
  initialVariableValues: initialVars,
54284
55394
  fileIO: options.fileIO,
54285
- onInput: options.onInput
55395
+ system: options.system,
55396
+ onInput: options.onInput,
55397
+ implicitCwdPath
54286
55398
  },
54287
- void 0,
54288
- fileName
55399
+ nestedWorkspaceFiles,
55400
+ fileName,
55401
+ nestedSearchPaths,
55402
+ nativeBridge2
54289
55403
  );
55404
+ if (evalResult.implicitCwdPath !== void 0) {
55405
+ implicitCwdPath = evalResult.implicitCwdPath;
55406
+ }
54290
55407
  return {
54291
55408
  returnValue: evalResult.returnValue,
54292
55409
  variableValues: evalResult.variableValues,
@@ -54318,12 +55435,13 @@ ${jitSections.join("\n\n")}` : "// No JS generated",
54318
55435
  }
54319
55436
  if (pathsModified) {
54320
55437
  result.searchPaths = ctx.registry.searchPaths.filter(
54321
- (p2) => p2 !== SHIM_SEARCH_PATH
55438
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
54322
55439
  );
54323
55440
  result.workspaceFiles = mWorkspaceFiles.filter(
54324
55441
  (f) => !stdlibShimNames.has(f.name)
54325
55442
  );
54326
55443
  }
55444
+ result.implicitCwdPath = implicitCwdPath;
54327
55445
  return result;
54328
55446
  } catch (e) {
54329
55447
  const generatedJS = jitSections.length > 0 ? `// Interpreter mode \u2014 JIT compiled sections:
@@ -54359,6 +55477,9 @@ ${jitSections.join("\n\n")}` : "// No JS generated";
54359
55477
  unregisterIBuiltin(ib.name);
54360
55478
  }
54361
55479
  }
55480
+ for (const [, ib] of savedSpecialBuiltins) {
55481
+ registerDynamicIBuiltin(ib);
55482
+ }
54362
55483
  }
54363
55484
  }
54364
55485
 
@@ -54376,7 +55497,9 @@ import {
54376
55497
  unlinkSync,
54377
55498
  readdirSync as readdirSync2,
54378
55499
  rmSync,
54379
- rmdirSync
55500
+ rmdirSync,
55501
+ renameSync,
55502
+ chmodSync
54380
55503
  } from "fs";
54381
55504
  import { unzipSync } from "fflate";
54382
55505
  import { execFileSync } from "child_process";
@@ -54435,6 +55558,18 @@ function expandTilde(filepath) {
54435
55558
  }
54436
55559
  return filepath;
54437
55560
  }
55561
+ function copyDirRecursive(src, dst) {
55562
+ mkdirSync(dst, { recursive: true });
55563
+ for (const entry of readdirSync2(src, { withFileTypes: true })) {
55564
+ const s = join3(src, entry.name);
55565
+ const d = join3(dst, entry.name);
55566
+ if (entry.isDirectory()) {
55567
+ copyDirRecursive(s, d);
55568
+ } else {
55569
+ writeFileSync(d, readFileSync3(s));
55570
+ }
55571
+ }
55572
+ }
54438
55573
  function permissionToFlags(permission) {
54439
55574
  switch (permission) {
54440
55575
  case "r":
@@ -54700,6 +55835,59 @@ var NodeFileIOAdapter = class {
54700
55835
  return false;
54701
55836
  }
54702
55837
  }
55838
+ movefile(source, destination, force) {
55839
+ try {
55840
+ const src = expandTilde(source);
55841
+ const srcStat = statSync2(src);
55842
+ let dst = expandTilde(destination);
55843
+ let dstType = null;
55844
+ try {
55845
+ const s = statSync2(dst);
55846
+ dstType = s.isDirectory() ? "dir" : "file";
55847
+ } catch {
55848
+ dstType = null;
55849
+ }
55850
+ if (dstType === "dir") {
55851
+ dst = join3(dst, basename(src));
55852
+ try {
55853
+ dstType = statSync2(dst).isDirectory() ? "dir" : "file";
55854
+ } catch {
55855
+ dstType = null;
55856
+ }
55857
+ }
55858
+ if (dstType === "file" && force) {
55859
+ try {
55860
+ chmodSync(dst, 438);
55861
+ } catch {
55862
+ }
55863
+ }
55864
+ if (srcStat.isDirectory() && dstType === "file") {
55865
+ return false;
55866
+ }
55867
+ const parent = dirname(dst);
55868
+ if (parent && parent !== "." && parent !== "/") {
55869
+ try {
55870
+ mkdirSync(parent, { recursive: true });
55871
+ } catch {
55872
+ }
55873
+ }
55874
+ try {
55875
+ renameSync(src, dst);
55876
+ return true;
55877
+ } catch {
55878
+ if (srcStat.isDirectory()) {
55879
+ copyDirRecursive(src, dst);
55880
+ rmSync(src, { recursive: true });
55881
+ } else {
55882
+ writeFileSync(dst, readFileSync3(src));
55883
+ unlinkSync(src);
55884
+ }
55885
+ return true;
55886
+ }
55887
+ } catch {
55888
+ return false;
55889
+ }
55890
+ }
54703
55891
  deleteFile(pattern) {
54704
55892
  const p2 = expandTilde(pattern);
54705
55893
  if (p2.includes("*") || p2.includes("?")) {
@@ -55027,6 +56215,7 @@ function saveHistoryEntry(entry, hist) {
55027
56215
  async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths, nativeBridge2, optimization) {
55028
56216
  let variableValues = {};
55029
56217
  let holdState = false;
56218
+ let implicitCwdPath;
55030
56219
  const workspaceFiles = [...initialWorkspaceFiles];
55031
56220
  const searchPaths = [...initialSearchPaths ?? []];
55032
56221
  const fileIO = new NodeFileIOAdapter();
@@ -55079,7 +56268,8 @@ async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths, nat
55079
56268
  initialHoldState: holdState,
55080
56269
  optimization,
55081
56270
  fileIO,
55082
- system
56271
+ system,
56272
+ implicitCwdPath
55083
56273
  },
55084
56274
  workspaceFiles,
55085
56275
  "repl",
@@ -55094,6 +56284,9 @@ async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths, nat
55094
56284
  workspaceFiles.length = 0;
55095
56285
  workspaceFiles.push(...result.workspaceFiles ?? []);
55096
56286
  }
56287
+ if (result.implicitCwdPath !== void 0) {
56288
+ implicitCwdPath = result.implicitCwdPath;
56289
+ }
55097
56290
  if (result.plotInstructions.length > 0 && onDrawnow) {
55098
56291
  onDrawnow(result.plotInstructions);
55099
56292
  }
@@ -55192,7 +56385,8 @@ async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths, nat
55192
56385
  initialHoldState: holdState,
55193
56386
  optimization,
55194
56387
  fileIO,
55195
- system
56388
+ system,
56389
+ implicitCwdPath
55196
56390
  },
55197
56391
  workspaceFiles,
55198
56392
  "repl",
@@ -55207,6 +56401,9 @@ async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths, nat
55207
56401
  workspaceFiles.length = 0;
55208
56402
  workspaceFiles.push(...result.workspaceFiles ?? []);
55209
56403
  }
56404
+ if (result.implicitCwdPath !== void 0) {
56405
+ implicitCwdPath = result.implicitCwdPath;
56406
+ }
55210
56407
  if (result.plotInstructions.length > 0 && onDrawnow) {
55211
56408
  onDrawnow(result.plotInstructions);
55212
56409
  }
@@ -55708,7 +56905,6 @@ Options (for run and eval):
55708
56905
  --path <dir> Add extra workspace directory
55709
56906
  --plot Enable plot server
55710
56907
  --plot-port <port> Set plot server port (implies --plot)
55711
- --add-script-path Add the script's directory to the workspace (run only)
55712
56908
  --opt <level> Optimization level (0=none, 1=JIT scalar functions; default: 1)
55713
56909
 
55714
56910
  Environment variables:
@@ -55722,7 +56918,6 @@ function parseOptions(args) {
55722
56918
  stream: false,
55723
56919
  plot: false,
55724
56920
  plotPort: void 0,
55725
- addScriptPath: false,
55726
56921
  extraPaths: [],
55727
56922
  positional: [],
55728
56923
  profileOutput: void 0,
@@ -55762,9 +56957,6 @@ function parseOptions(args) {
55762
56957
  case "--plot":
55763
56958
  opts.plot = true;
55764
56959
  break;
55765
- case "--add-script-path":
55766
- opts.addScriptPath = true;
55767
- break;
55768
56960
  case "--plot-port":
55769
56961
  i++;
55770
56962
  if (i >= args.length) {
@@ -55830,13 +57022,9 @@ async function cmdRun(args) {
55830
57022
  const filepath = resolve2(process.cwd(), opts.positional[0]);
55831
57023
  const code = readFileSync5(filepath, "utf-8");
55832
57024
  const mainFileName = filepath;
57025
+ process.chdir(dirname2(filepath));
55833
57026
  const searchPaths = [];
55834
- let workspaceFiles = [];
55835
- if (opts.addScriptPath) {
55836
- const scriptDir = dirname2(filepath);
55837
- searchPaths.push(scriptDir);
55838
- workspaceFiles = scanMFiles(scriptDir, filepath);
55839
- }
57027
+ const workspaceFiles = [];
55840
57028
  for (const p2 of opts.extraPaths) {
55841
57029
  searchPaths.push(p2);
55842
57030
  workspaceFiles.push(...scanMFiles(p2));