numbl 0.1.3 → 0.1.4

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-lib/lib.js CHANGED
@@ -18789,6 +18789,258 @@ function coerceToTensor(v, name) {
18789
18789
  throw new RuntimeError(`${name}: argument must be numeric`);
18790
18790
  }
18791
18791
 
18792
+ // src/numbl-core/helpers/string.ts
18793
+ function numStr(n) {
18794
+ if (n === Infinity) return "Inf";
18795
+ if (n === -Infinity) return "-Inf";
18796
+ if (isNaN(n)) return "NaN";
18797
+ if (n === 0) return "0";
18798
+ const prec = 5;
18799
+ const exp = Math.floor(Math.log10(Math.abs(n)));
18800
+ let s;
18801
+ if (exp < -4 || exp >= prec) {
18802
+ s = n.toExponential(prec - 1);
18803
+ const ePos = s.indexOf("e");
18804
+ let mantissa = s.slice(0, ePos);
18805
+ const expPart0 = s.slice(ePos);
18806
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
18807
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
18808
+ s = mantissa + expPart;
18809
+ } else {
18810
+ if (Number.isInteger(n)) return String(n);
18811
+ s = n.toPrecision(prec);
18812
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
18813
+ }
18814
+ return s;
18815
+ }
18816
+ function num2strScalar(n) {
18817
+ if (n === Infinity) return "Inf";
18818
+ if (n === -Infinity) return "-Inf";
18819
+ if (isNaN(n)) return "NaN";
18820
+ if (n === 0) return "0";
18821
+ if (Number.isInteger(n)) return String(n);
18822
+ const prec = 5;
18823
+ const exp = Math.floor(Math.log10(Math.abs(n)));
18824
+ let s;
18825
+ if (exp < -4 || exp >= prec) {
18826
+ s = n.toExponential(prec - 1);
18827
+ const ePos = s.indexOf("e");
18828
+ let mantissa = s.slice(0, ePos);
18829
+ const expPart0 = s.slice(ePos);
18830
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
18831
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
18832
+ s = mantissa + expPart;
18833
+ } else {
18834
+ s = n.toPrecision(prec);
18835
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
18836
+ }
18837
+ return s;
18838
+ }
18839
+ function applyWidth(spec, str) {
18840
+ const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
18841
+ if (!m) return str;
18842
+ const explicitFlags = m[1] || "";
18843
+ const leftAlign = explicitFlags.includes("-");
18844
+ const afterPercent = spec.slice(1);
18845
+ const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
18846
+ const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
18847
+ const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
18848
+ if (width <= str.length) return str;
18849
+ const zeroPad = !leftAlign && zeroFlag;
18850
+ const padLen = width - str.length;
18851
+ if (leftAlign) return str + " ".repeat(padLen);
18852
+ if (zeroPad) {
18853
+ if (str[0] === "-" || str[0] === "+") {
18854
+ return str[0] + "0".repeat(padLen) + str.slice(1);
18855
+ }
18856
+ return "0".repeat(padLen) + str;
18857
+ }
18858
+ return " ".repeat(padLen) + str;
18859
+ }
18860
+ function sprintfFormat(fmt, args) {
18861
+ const flatArgs = [];
18862
+ for (const arg of args) {
18863
+ if (isRuntimeTensor(arg)) {
18864
+ for (let k = 0; k < arg.data.length; k++) {
18865
+ flatArgs.push(arg.data[k]);
18866
+ }
18867
+ } else {
18868
+ flatArgs.push(arg);
18869
+ }
18870
+ }
18871
+ let result = "";
18872
+ let argIdx = 0;
18873
+ do {
18874
+ const startArgIdx = argIdx;
18875
+ let outOfArgs = false;
18876
+ let i = 0;
18877
+ while (i < fmt.length && !outOfArgs) {
18878
+ if (fmt[i] === "%" && i + 1 < fmt.length) {
18879
+ i++;
18880
+ let spec = "%";
18881
+ while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
18882
+ if (fmt[i] === "*") {
18883
+ if (argIdx >= flatArgs.length) {
18884
+ outOfArgs = true;
18885
+ break;
18886
+ }
18887
+ spec += String(Math.round(toNumber(flatArgs[argIdx++])));
18888
+ i++;
18889
+ } else {
18890
+ spec += fmt[i];
18891
+ i++;
18892
+ }
18893
+ }
18894
+ if (outOfArgs) break;
18895
+ if (i < fmt.length) {
18896
+ const ch = fmt[i];
18897
+ i++;
18898
+ if (ch === "%") {
18899
+ result += "%";
18900
+ } else if (argIdx >= flatArgs.length) {
18901
+ outOfArgs = true;
18902
+ } else if (ch === "d" || ch === "i" || ch === "u") {
18903
+ const raw = toNumber(flatArgs[argIdx++]);
18904
+ const isInt = Number.isInteger(raw);
18905
+ const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
18906
+ if (!canPrintAsInt) {
18907
+ let eStr = raw.toExponential(6);
18908
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
18909
+ result += applyWidth(spec, eStr);
18910
+ } else {
18911
+ const n = raw;
18912
+ const flags = spec.slice(1);
18913
+ const hasPlus = flags.includes("+");
18914
+ const leftAlign = flags.includes("-");
18915
+ const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
18916
+ const width = widthMatch ? parseInt(widthMatch[1]) : 0;
18917
+ const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
18918
+ const s = String(Math.abs(n));
18919
+ const sign = n < 0 ? "-" : hasPlus ? "+" : "";
18920
+ if (width > 0) {
18921
+ const padChar = zeroPad ? "0" : " ";
18922
+ const padLen = Math.max(0, width - sign.length - s.length);
18923
+ const pad = padChar.repeat(padLen);
18924
+ result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
18925
+ } else {
18926
+ result += sign + s;
18927
+ }
18928
+ }
18929
+ } else if (ch === "f") {
18930
+ const n = toNumber(flatArgs[argIdx++]);
18931
+ if (!isFinite(n) || isNaN(n)) {
18932
+ result += applyWidth(spec, numStr(n));
18933
+ } else {
18934
+ const fFlags = spec.slice(1);
18935
+ const fHasPlus = fFlags.includes("+");
18936
+ const precMatch = spec.match(/\.(\d+)/);
18937
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
18938
+ const formatted = n.toFixed(prec);
18939
+ const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
18940
+ result += applyWidth(spec, fSign + formatted);
18941
+ }
18942
+ } else if (ch === "e" || ch === "E") {
18943
+ const n = toNumber(flatArgs[argIdx++]);
18944
+ if (!isFinite(n) || isNaN(n)) {
18945
+ result += applyWidth(spec, numStr(n));
18946
+ } else {
18947
+ const precMatch = spec.match(/\.(\d+)/);
18948
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
18949
+ let eStr = n.toExponential(prec);
18950
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
18951
+ if (ch === "E") eStr = eStr.toUpperCase();
18952
+ result += applyWidth(spec, eStr);
18953
+ }
18954
+ } else if (ch === "x" || ch === "X") {
18955
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
18956
+ let s = Math.abs(n).toString(16);
18957
+ if (ch === "X") s = s.toUpperCase();
18958
+ result += applyWidth(spec, s);
18959
+ } else if (ch === "o") {
18960
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
18961
+ result += applyWidth(spec, Math.abs(n).toString(8));
18962
+ } else if (ch === "g" || ch === "G") {
18963
+ const gVal = toNumber(flatArgs[argIdx++]);
18964
+ if (!isFinite(gVal) || isNaN(gVal)) {
18965
+ result += applyWidth(spec, numStr(gVal));
18966
+ } else {
18967
+ const precMatch = spec.match(/\.(\d+)/);
18968
+ const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
18969
+ let gStr;
18970
+ if (gVal === 0) {
18971
+ gStr = "0";
18972
+ } else {
18973
+ const exp = Math.floor(Math.log10(Math.abs(gVal)));
18974
+ if (exp < -4 || exp >= gPrec) {
18975
+ gStr = gVal.toExponential(gPrec - 1);
18976
+ const ePos = gStr.indexOf("e");
18977
+ let mantissa = gStr.slice(0, ePos);
18978
+ let expPart = gStr.slice(ePos);
18979
+ if (mantissa.includes(".")) {
18980
+ mantissa = mantissa.replace(/\.?0+$/, "");
18981
+ }
18982
+ expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
18983
+ gStr = mantissa + expPart;
18984
+ } else {
18985
+ gStr = gVal.toPrecision(gPrec);
18986
+ if (gStr.includes(".")) {
18987
+ gStr = gStr.replace(/\.?0+$/, "");
18988
+ }
18989
+ if (gStr.includes("e")) {
18990
+ gStr = String(parseFloat(gStr));
18991
+ }
18992
+ }
18993
+ }
18994
+ if (ch === "G") gStr = gStr.toUpperCase();
18995
+ result += applyWidth(spec, gStr);
18996
+ }
18997
+ } else if (ch === "s") {
18998
+ const sVal = toString(flatArgs[argIdx++]);
18999
+ const sFlags = spec.slice(1);
19000
+ const sLeftAlign = sFlags.includes("-");
19001
+ const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
19002
+ const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
19003
+ if (sWidth > sVal.length) {
19004
+ const sPad = " ".repeat(sWidth - sVal.length);
19005
+ result += sLeftAlign ? sVal + sPad : sPad + sVal;
19006
+ } else {
19007
+ result += sVal;
19008
+ }
19009
+ } else if (ch === "c") {
19010
+ result += String.fromCharCode(
19011
+ Math.round(toNumber(flatArgs[argIdx++]))
19012
+ );
19013
+ } else {
19014
+ result += spec + ch;
19015
+ argIdx++;
19016
+ }
19017
+ }
19018
+ } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
19019
+ i++;
19020
+ switch (fmt[i]) {
19021
+ case "n":
19022
+ result += "\n";
19023
+ break;
19024
+ case "t":
19025
+ result += " ";
19026
+ break;
19027
+ case "\\":
19028
+ result += "\\";
19029
+ break;
19030
+ default:
19031
+ result += "\\" + fmt[i];
19032
+ }
19033
+ i++;
19034
+ } else {
19035
+ result += fmt[i];
19036
+ i++;
19037
+ }
19038
+ }
19039
+ if (argIdx === startArgIdx) break;
19040
+ } while (argIdx < flatArgs.length);
19041
+ return result;
19042
+ }
19043
+
18792
19044
  // src/numbl-core/helpers/arithmetic.ts
18793
19045
  function toComplex(v) {
18794
19046
  if (isRuntimeComplexNumber(v)) return { re: v.re, im: v.im };
@@ -19016,7 +19268,26 @@ function tensorElemwiseComplex(at, bt, opCode, jsOp) {
19016
19268
  const isReal = resultIm.every((x) => x === 0);
19017
19269
  return RTV.tensor(resultRe, at.shape, isReal ? void 0 : resultIm);
19018
19270
  }
19271
+ function coerceToConcatString(v) {
19272
+ if (isRuntimeString(v)) return v;
19273
+ if (isRuntimeChar(v)) return v.value;
19274
+ if (isRuntimeLogical(v)) return v ? "true" : "false";
19275
+ if (isRuntimeNumber(v)) return num2strScalar(v);
19276
+ if (isRuntimeTensor(v) && v.data.length === 1 && !v.imag) {
19277
+ const x = v.data[0];
19278
+ if (v._isLogical === true) return x ? "true" : "false";
19279
+ return num2strScalar(x);
19280
+ }
19281
+ return null;
19282
+ }
19019
19283
  function mAdd(a, b) {
19284
+ if (isRuntimeString(a) || isRuntimeString(b)) {
19285
+ const aStr = coerceToConcatString(a);
19286
+ const bStr = coerceToConcatString(b);
19287
+ if (aStr !== null && bStr !== null) {
19288
+ return RTV.string(aStr + bStr);
19289
+ }
19290
+ }
19020
19291
  const m = matchSameShapeTensors(a, b);
19021
19292
  if (m) {
19022
19293
  const [at, bt] = m;
@@ -19911,6 +20182,7 @@ function extractTensorElement(base, i) {
19911
20182
  const im2 = base.imag[i];
19912
20183
  return im2 === 0 ? RTV.num(base.data[i]) : RTV.complex(base.data[i], im2);
19913
20184
  }
20185
+ if (base._isLogical === true) return base.data[i] !== 0;
19914
20186
  return RTV.num(base.data[i]);
19915
20187
  }
19916
20188
  function resolveIndex(idx, dimSize, boundsLimit = dimSize) {
@@ -19964,17 +20236,35 @@ function growTensor2D(base, newRows, newCols) {
19964
20236
  }
19965
20237
  function assignSlice(base, rowIndices, colIndices, rhs, curRows) {
19966
20238
  if (isRuntimeTensor(rhs)) {
20239
+ const nR = rowIndices.length;
20240
+ const nC = colIndices.length;
19967
20241
  const [rhsRows, rhsCols] = tensorSize2D(rhs);
19968
- if (rhsRows !== rowIndices.length || rhsCols !== colIndices.length) {
20242
+ const shapeMatches = rhsRows === nR && rhsCols === nC;
20243
+ const sliceIsVector = nR === 1 || nC === 1;
20244
+ const rhsIsVector = rhsRows === 1 || rhsCols === 1;
20245
+ const countMatches = rhs.data.length === nR * nC;
20246
+ if (!shapeMatches && !(sliceIsVector && rhsIsVector && countMatches)) {
19969
20247
  throw new RuntimeError("Subscripted assignment dimension mismatch");
19970
20248
  }
19971
20249
  if (rhs.imag || base.imag) ensureImag(base);
19972
- for (let ri = 0; ri < rowIndices.length; ri++) {
19973
- for (let ci = 0; ci < colIndices.length; ci++) {
19974
- const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
19975
- const srcLi = colMajorIndex(ri, ci, rhsRows);
19976
- base.data[dstLi] = rhs.data[srcLi];
19977
- if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20250
+ if (shapeMatches) {
20251
+ for (let ri = 0; ri < nR; ri++) {
20252
+ for (let ci = 0; ci < nC; ci++) {
20253
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20254
+ const srcLi = colMajorIndex(ri, ci, rhsRows);
20255
+ base.data[dstLi] = rhs.data[srcLi];
20256
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20257
+ }
20258
+ }
20259
+ } else {
20260
+ let k = 0;
20261
+ for (let ci = 0; ci < nC; ci++) {
20262
+ for (let ri = 0; ri < nR; ri++) {
20263
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20264
+ base.data[dstLi] = rhs.data[k];
20265
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[k] : 0;
20266
+ k++;
20267
+ }
19978
20268
  }
19979
20269
  }
19980
20270
  } else {
@@ -20311,6 +20601,23 @@ function indexIntoScalar(base, indices) {
20311
20601
  return RTV.tensor(out, [1, count]);
20312
20602
  }
20313
20603
  }
20604
+ if (indices.length === 1 && isRuntimeTensor(indices[0])) {
20605
+ const idx = indices[0];
20606
+ for (let i = 0; i < idx.data.length; i++) {
20607
+ const k = Math.round(idx.data[i]);
20608
+ if (k !== 1) throw new RuntimeError("Index exceeds array bounds");
20609
+ }
20610
+ const n = idx.data.length;
20611
+ const scalarRe = isRuntimeNumber(base) ? base : base.re;
20612
+ const data = new FloatXArray(n);
20613
+ data.fill(scalarRe);
20614
+ if (isRuntimeComplexNumber(base) && base.im !== 0) {
20615
+ const im2 = new FloatXArray(n);
20616
+ im2.fill(base.im);
20617
+ return RTV.tensor(data, [...idx.shape], im2);
20618
+ }
20619
+ return RTV.tensor(data, [...idx.shape]);
20620
+ }
20314
20621
  for (const idx of indices) {
20315
20622
  if (isColonIndex(idx)) continue;
20316
20623
  const i = toNumber(idx);
@@ -20322,6 +20629,22 @@ function indexIntoTensor(base, indices) {
20322
20629
  if (indices.length === 1) {
20323
20630
  return indexIntoTensor1D(base, indices[0]);
20324
20631
  }
20632
+ if (base.shape.length > indices.length) {
20633
+ const collapsedShape = [];
20634
+ for (let d = 0; d < indices.length - 1; d++) {
20635
+ collapsedShape.push(base.shape[d]);
20636
+ }
20637
+ let tail = 1;
20638
+ for (let d = indices.length - 1; d < base.shape.length; d++) {
20639
+ tail *= base.shape[d];
20640
+ }
20641
+ collapsedShape.push(tail);
20642
+ const view = { ...base, shape: collapsedShape };
20643
+ if (indices.length === 2) {
20644
+ return indexIntoTensor2D(view, indices[0], indices[1]);
20645
+ }
20646
+ return indexIntoTensorND(view, indices);
20647
+ }
20325
20648
  if (indices.length === 2) {
20326
20649
  return indexIntoTensor2D(base, indices[0], indices[1]);
20327
20650
  }
@@ -20350,6 +20673,11 @@ function indexIntoTensor1D(base, idx) {
20350
20673
  }
20351
20674
  function indexIntoTensor2D(base, rowIdx, colIdx) {
20352
20675
  const [rows, cols] = tensorSize2D(base);
20676
+ const baseLogical = base._isLogical === true;
20677
+ const markLogical = (t) => {
20678
+ if (baseLogical && isRuntimeTensor(t)) t._isLogical = true;
20679
+ return t;
20680
+ };
20353
20681
  if (isRuntimeNumber(rowIdx) && isColonIndex(colIdx)) {
20354
20682
  const r = Math.round(rowIdx) - 1;
20355
20683
  if (r < 0 || r >= rows)
@@ -20360,7 +20688,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20360
20688
  resultData2[ci] = base.data[r + ci * rows];
20361
20689
  if (resultImag2 && base.imag) resultImag2[ci] = base.imag[r + ci * rows];
20362
20690
  }
20363
- return RTV.tensor(resultData2, [1, cols], resultImag2);
20691
+ return markLogical(RTV.tensor(resultData2, [1, cols], resultImag2));
20364
20692
  }
20365
20693
  if (isColonIndex(rowIdx) && isRuntimeNumber(colIdx)) {
20366
20694
  const c = Math.round(colIdx) - 1;
@@ -20372,7 +20700,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20372
20700
  const resultImag2 = base.imag ? new FloatXArray(rows) : void 0;
20373
20701
  if (resultImag2 && base.imag)
20374
20702
  for (let ri = 0; ri < rows; ri++) resultImag2[ri] = base.imag[offset + ri];
20375
- return RTV.tensor(resultData2, [rows, 1], resultImag2);
20703
+ return markLogical(RTV.tensor(resultData2, [rows, 1], resultImag2));
20376
20704
  }
20377
20705
  const rowIdxArr = resolveIndex(rowIdx, rows);
20378
20706
  const colIdxArr = resolveIndex(colIdx, cols);
@@ -20394,7 +20722,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20394
20722
  }
20395
20723
  }
20396
20724
  }
20397
- return RTV.tensor(resultData, [numR, numC], resultImag);
20725
+ return markLogical(RTV.tensor(resultData, [numR, numC], resultImag));
20398
20726
  }
20399
20727
  function indexIntoTensorND(base, indices) {
20400
20728
  const shape = base.shape;
@@ -20442,7 +20770,13 @@ function indexIntoTensorND(base, indices) {
20442
20770
  subs[d] = 0;
20443
20771
  }
20444
20772
  }
20445
- return RTV.tensor(resultData, resultShape, resultImag);
20773
+ const result = RTV.tensor(
20774
+ resultData,
20775
+ resultShape,
20776
+ resultImag
20777
+ );
20778
+ if (base._isLogical === true) result._isLogical = true;
20779
+ return result;
20446
20780
  }
20447
20781
  function indexIntoCell(base, indices) {
20448
20782
  if (indices.length === 1) {
@@ -20607,6 +20941,7 @@ function indexIntoLogical(base, indices) {
20607
20941
  return base;
20608
20942
  }
20609
20943
  function indexIntoTensorWithTensor(base, idx) {
20944
+ const baseLogical = base._isLogical === true;
20610
20945
  if (idx._isLogical) {
20611
20946
  const selected = [];
20612
20947
  const selectedIm = [];
@@ -20620,12 +20955,19 @@ function indexIntoTensorWithTensor(base, idx) {
20620
20955
  if (selected.length === 1) {
20621
20956
  if (hasImag2 && selectedIm[0] !== 0)
20622
20957
  return RTV.complex(selected[0], selectedIm[0]);
20958
+ if (baseLogical) return selected[0] !== 0;
20623
20959
  return RTV.num(selected[0]);
20624
20960
  }
20625
20961
  const imOut2 = hasImag2 && selectedIm.some((x) => x !== 0) ? new FloatXArray(selectedIm) : void 0;
20626
20962
  const isRow = base.shape.length === 2 && base.shape[0] === 1;
20627
20963
  const outShape2 = isRow ? [1, selected.length] : [selected.length, 1];
20628
- return RTV.tensor(new FloatXArray(selected), outShape2, imOut2);
20964
+ const result2 = RTV.tensor(
20965
+ new FloatXArray(selected),
20966
+ outShape2,
20967
+ imOut2
20968
+ );
20969
+ if (baseLogical) result2._isLogical = true;
20970
+ return result2;
20629
20971
  }
20630
20972
  const resultData = [];
20631
20973
  const hasImag = base.imag !== void 0;
@@ -20641,7 +20983,13 @@ function indexIntoTensorWithTensor(base, idx) {
20641
20983
  const baseIsVector = base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
20642
20984
  const outShape = idxIs0x0 ? [0, 0] : baseIsVector ? base.shape[0] === 1 ? [1, resultData.length] : [resultData.length, 1] : idx.shape;
20643
20985
  const imOut = hasImag && imIndices.some((x) => x !== 0) ? new FloatXArray(imIndices) : void 0;
20644
- return RTV.tensor(new FloatXArray(resultData), outShape, imOut);
20986
+ const result = RTV.tensor(
20987
+ new FloatXArray(resultData),
20988
+ outShape,
20989
+ imOut
20990
+ );
20991
+ if (baseLogical) result._isLogical = true;
20992
+ return result;
20645
20993
  }
20646
20994
  function indexIntoRTValue(base, indices) {
20647
20995
  if (isRuntimeNumber(base) || isRuntimeComplexNumber(base)) {
@@ -20991,10 +21339,18 @@ function storeIntoTensor2D(base, indices, rhs) {
20991
21339
  return storeIntoTensorColonCol(base, indices[0], rhs, rows, cols);
20992
21340
  }
20993
21341
  if (rowIsTensor || colIsTensor) {
20994
- const rowIndices = resolveIndex(indices[0], rows, 0);
20995
- const colIndices = resolveIndex(indices[1], cols, 0);
20996
- const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : rows;
20997
- const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : cols;
21342
+ const isEmpty = base.data.length === 0;
21343
+ let effectiveCols = cols;
21344
+ let effectiveRows = rows;
21345
+ if (isEmpty && isRuntimeTensor(rhs)) {
21346
+ const [rhsRowsG, rhsColsG] = tensorSize2D(rhs);
21347
+ if (rowIsColon && !colIsColon) effectiveRows = rhsRowsG;
21348
+ if (colIsColon && !rowIsColon) effectiveCols = rhsColsG;
21349
+ }
21350
+ const rowIndices = rowIsColon ? Array.from({ length: effectiveRows }, (_, i) => i) : resolveIndex(indices[0], rows, 0);
21351
+ const colIndices = colIsColon ? Array.from({ length: effectiveCols }, (_, i) => i) : resolveIndex(indices[1], cols, 0);
21352
+ const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : effectiveRows;
21353
+ const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : effectiveCols;
20998
21354
  base = growTensor2D(base, maxRow, maxCol);
20999
21355
  const [curRows] = tensorSize2D(base);
21000
21356
  assignSlice(base, rowIndices, colIndices, rhs, curRows);
@@ -21422,6 +21778,9 @@ function horzcat(...values) {
21422
21778
  if (values.some((v) => isRuntimeCell(v))) {
21423
21779
  return cellCatAlongDim(values, 1);
21424
21780
  }
21781
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21782
+ return structCat(values);
21783
+ }
21425
21784
  return catAlongDim(values, 1);
21426
21785
  }
21427
21786
  function vertcat(...values) {
@@ -21433,8 +21792,47 @@ function vertcat(...values) {
21433
21792
  if (values.some((v) => isRuntimeCell(v))) {
21434
21793
  return cellCatAlongDim(values, 0);
21435
21794
  }
21795
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21796
+ return structCat(values);
21797
+ }
21436
21798
  return catAlongDim(values, 0);
21437
21799
  }
21800
+ function structCat(values) {
21801
+ const elements = [];
21802
+ let fieldNames = null;
21803
+ for (const v of values) {
21804
+ if (isRuntimeStruct(v)) {
21805
+ const keys = Array.from(v.fields.keys());
21806
+ if (fieldNames === null) fieldNames = keys;
21807
+ else if (!arraysEqual(fieldNames, keys)) {
21808
+ throw new RuntimeError(
21809
+ "Cannot concatenate structs with different field names"
21810
+ );
21811
+ }
21812
+ elements.push(v);
21813
+ } else if (isRuntimeStructArray(v)) {
21814
+ if (fieldNames === null) fieldNames = [...v.fieldNames];
21815
+ else if (!arraysEqual(fieldNames, v.fieldNames)) {
21816
+ throw new RuntimeError(
21817
+ "Cannot concatenate struct arrays with different field names"
21818
+ );
21819
+ }
21820
+ for (const e of v.elements) elements.push(e);
21821
+ } else {
21822
+ if (isRuntimeTensor(v) && v.data.length === 0 && v.shape.every((d) => d === 0)) {
21823
+ continue;
21824
+ }
21825
+ throw new RuntimeError(`Cannot concatenate ${kstr(v)} into struct`);
21826
+ }
21827
+ }
21828
+ if (fieldNames === null) fieldNames = [];
21829
+ return RTV.structArray(fieldNames, elements);
21830
+ }
21831
+ function arraysEqual(a, b) {
21832
+ if (a.length !== b.length) return false;
21833
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
21834
+ return true;
21835
+ }
21438
21836
  function toSparseForCat(v) {
21439
21837
  if (isRuntimeSparseMatrix(v)) return v;
21440
21838
  if (isRuntimeNumber(v)) {
@@ -22042,7 +22440,7 @@ var token_config_default = {
22042
22440
  stripUnderscores: true,
22043
22441
  integerToken: "Integer",
22044
22442
  floatToken: "Float",
22045
- exponentChars: ["e", "E"],
22443
+ exponentChars: ["e", "E", "d", "D"],
22046
22444
  decimalPoint: ".",
22047
22445
  dotOperatorPrefixes: ["*", "/", "\\", "^"]
22048
22446
  },
@@ -22347,6 +22745,9 @@ function tokenizeDetailed(input) {
22347
22745
  if (numCfg.stripUnderscores) {
22348
22746
  lexeme = lexeme.replace(/_/g, "");
22349
22747
  }
22748
+ if (isFloat) {
22749
+ lexeme = lexeme.replace(/[dD]/g, "e");
22750
+ }
22350
22751
  const tok = isFloat ? Token[numCfg.floatToken] : Token[numCfg.integerToken];
22351
22752
  lineStart = false;
22352
22753
  out.push({ token: tok, lexeme, start, end: pos });
@@ -27507,224 +27908,88 @@ defineBuiltin({
27507
27908
  }
27508
27909
  });
27509
27910
 
27510
- // src/numbl-core/helpers/string.ts
27511
- function numStr(n) {
27512
- if (n === Infinity) return "Inf";
27513
- if (n === -Infinity) return "-Inf";
27514
- if (isNaN(n)) return "NaN";
27515
- if (n === 0) return "0";
27516
- const prec = 5;
27517
- const exp = Math.floor(Math.log10(Math.abs(n)));
27518
- let s;
27519
- if (exp < -4 || exp >= prec) {
27520
- s = n.toExponential(prec - 1);
27521
- const ePos = s.indexOf("e");
27522
- let mantissa = s.slice(0, ePos);
27523
- const expPart0 = s.slice(ePos);
27524
- if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
27525
- const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
27526
- s = mantissa + expPart;
27527
- } else {
27528
- if (Number.isInteger(n)) return String(n);
27529
- s = n.toPrecision(prec);
27530
- if (s.includes(".")) s = s.replace(/\.?0+$/, "");
27911
+ // src/numbl-core/interpreter/builtins/logical.ts
27912
+ var LOGICAL_KINDS = /* @__PURE__ */ new Set([
27913
+ "number",
27914
+ "boolean",
27915
+ "tensor",
27916
+ "sparse_matrix"
27917
+ ]);
27918
+ function binaryLogicalMatch(argTypes) {
27919
+ if (argTypes.length !== 2) return null;
27920
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
27921
+ if (!LOGICAL_KINDS.has(argTypes[1].kind)) return null;
27922
+ if (argTypes[0].kind === "tensor" || argTypes[1].kind === "tensor") {
27923
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27531
27924
  }
27532
- return s;
27533
- }
27534
- function applyWidth(spec, str) {
27535
- const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
27536
- if (!m) return str;
27537
- const explicitFlags = m[1] || "";
27538
- const leftAlign = explicitFlags.includes("-");
27539
- const afterPercent = spec.slice(1);
27540
- const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
27541
- const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
27542
- const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
27543
- if (width <= str.length) return str;
27544
- const zeroPad = !leftAlign && zeroFlag;
27545
- const padLen = width - str.length;
27546
- if (leftAlign) return str + " ".repeat(padLen);
27547
- if (zeroPad) {
27548
- if (str[0] === "-" || str[0] === "+") {
27549
- return str[0] + "0".repeat(padLen) + str.slice(1);
27550
- }
27551
- return "0".repeat(padLen) + str;
27925
+ if (argTypes[0].kind === "sparse_matrix" || argTypes[1].kind === "sparse_matrix") {
27926
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27552
27927
  }
27553
- return " ".repeat(padLen) + str;
27928
+ return [{ kind: "boolean" }];
27554
27929
  }
27555
- function sprintfFormat(fmt, args) {
27556
- const flatArgs = [];
27557
- for (const arg of args) {
27558
- if (isRuntimeTensor(arg)) {
27559
- for (let k = 0; k < arg.data.length; k++) {
27560
- flatArgs.push(arg.data[k]);
27561
- }
27562
- } else {
27563
- flatArgs.push(arg);
27564
- }
27930
+ function isTensorLike(v) {
27931
+ return isRuntimeTensor(v) || isRuntimeSparseMatrix(v);
27932
+ }
27933
+ function applyBinaryLogical(args, op, scalarFn) {
27934
+ const a = args[0];
27935
+ const b = args[1];
27936
+ if (isTensorLike(a) || isTensorLike(b)) {
27937
+ return elementWiseLogicalOp(a, b, op);
27565
27938
  }
27566
- let result = "";
27567
- let argIdx = 0;
27568
- do {
27569
- const startArgIdx = argIdx;
27570
- let outOfArgs = false;
27571
- let i = 0;
27572
- while (i < fmt.length && !outOfArgs) {
27573
- if (fmt[i] === "%" && i + 1 < fmt.length) {
27574
- i++;
27575
- let spec = "%";
27576
- while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
27577
- spec += fmt[i];
27578
- i++;
27579
- }
27580
- if (i < fmt.length) {
27581
- const ch = fmt[i];
27582
- i++;
27583
- if (ch === "%") {
27584
- result += "%";
27585
- } else if (argIdx >= flatArgs.length) {
27586
- outOfArgs = true;
27587
- } else if (ch === "d" || ch === "i" || ch === "u") {
27588
- const raw = toNumber(flatArgs[argIdx++]);
27589
- const isInt = Number.isInteger(raw);
27590
- const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
27591
- if (!canPrintAsInt) {
27592
- let eStr = raw.toExponential(6);
27593
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27594
- result += applyWidth(spec, eStr);
27595
- } else {
27596
- const n = raw;
27597
- const flags = spec.slice(1);
27598
- const hasPlus = flags.includes("+");
27599
- const leftAlign = flags.includes("-");
27600
- const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
27601
- const width = widthMatch ? parseInt(widthMatch[1]) : 0;
27602
- const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
27603
- const s = String(Math.abs(n));
27604
- const sign = n < 0 ? "-" : hasPlus ? "+" : "";
27605
- if (width > 0) {
27606
- const padChar = zeroPad ? "0" : " ";
27607
- const padLen = Math.max(0, width - sign.length - s.length);
27608
- const pad = padChar.repeat(padLen);
27609
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
27610
- } else {
27611
- result += sign + s;
27612
- }
27613
- }
27614
- } else if (ch === "f") {
27615
- const n = toNumber(flatArgs[argIdx++]);
27616
- if (!isFinite(n) || isNaN(n)) {
27617
- result += applyWidth(spec, numStr(n));
27618
- } else {
27619
- const fFlags = spec.slice(1);
27620
- const fHasPlus = fFlags.includes("+");
27621
- const precMatch = spec.match(/\.(\d+)/);
27622
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27623
- const formatted = n.toFixed(prec);
27624
- const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
27625
- result += applyWidth(spec, fSign + formatted);
27626
- }
27627
- } else if (ch === "e" || ch === "E") {
27628
- const n = toNumber(flatArgs[argIdx++]);
27629
- if (!isFinite(n) || isNaN(n)) {
27630
- result += applyWidth(spec, numStr(n));
27631
- } else {
27632
- const precMatch = spec.match(/\.(\d+)/);
27633
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27634
- let eStr = n.toExponential(prec);
27635
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27636
- if (ch === "E") eStr = eStr.toUpperCase();
27637
- result += applyWidth(spec, eStr);
27638
- }
27639
- } else if (ch === "x" || ch === "X") {
27640
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27641
- let s = Math.abs(n).toString(16);
27642
- if (ch === "X") s = s.toUpperCase();
27643
- result += applyWidth(spec, s);
27644
- } else if (ch === "o") {
27645
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27646
- result += applyWidth(spec, Math.abs(n).toString(8));
27647
- } else if (ch === "g" || ch === "G") {
27648
- const gVal = toNumber(flatArgs[argIdx++]);
27649
- if (!isFinite(gVal) || isNaN(gVal)) {
27650
- result += applyWidth(spec, numStr(gVal));
27651
- } else {
27652
- const precMatch = spec.match(/\.(\d+)/);
27653
- const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
27654
- let gStr;
27655
- if (gVal === 0) {
27656
- gStr = "0";
27657
- } else {
27658
- const exp = Math.floor(Math.log10(Math.abs(gVal)));
27659
- if (exp < -4 || exp >= gPrec) {
27660
- gStr = gVal.toExponential(gPrec - 1);
27661
- const ePos = gStr.indexOf("e");
27662
- let mantissa = gStr.slice(0, ePos);
27663
- let expPart = gStr.slice(ePos);
27664
- if (mantissa.includes(".")) {
27665
- mantissa = mantissa.replace(/\.?0+$/, "");
27666
- }
27667
- expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
27668
- gStr = mantissa + expPart;
27669
- } else {
27670
- gStr = gVal.toPrecision(gPrec);
27671
- if (gStr.includes(".")) {
27672
- gStr = gStr.replace(/\.?0+$/, "");
27673
- }
27674
- if (gStr.includes("e")) {
27675
- gStr = String(parseFloat(gStr));
27676
- }
27677
- }
27678
- }
27679
- if (ch === "G") gStr = gStr.toUpperCase();
27680
- result += applyWidth(spec, gStr);
27681
- }
27682
- } else if (ch === "s") {
27683
- const sVal = toString(flatArgs[argIdx++]);
27684
- const sFlags = spec.slice(1);
27685
- const sLeftAlign = sFlags.includes("-");
27686
- const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
27687
- const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
27688
- if (sWidth > sVal.length) {
27689
- const sPad = " ".repeat(sWidth - sVal.length);
27690
- result += sLeftAlign ? sVal + sPad : sPad + sVal;
27691
- } else {
27692
- result += sVal;
27693
- }
27694
- } else if (ch === "c") {
27695
- result += String.fromCharCode(
27696
- Math.round(toNumber(flatArgs[argIdx++]))
27697
- );
27698
- } else {
27699
- result += spec + ch;
27700
- argIdx++;
27701
- }
27702
- }
27703
- } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
27704
- i++;
27705
- switch (fmt[i]) {
27706
- case "n":
27707
- result += "\n";
27708
- break;
27709
- case "t":
27710
- result += " ";
27711
- break;
27712
- case "\\":
27713
- result += "\\";
27714
- break;
27715
- default:
27716
- result += "\\" + fmt[i];
27939
+ return RTV.logical(scalarFn(toBool(a), toBool(b)));
27940
+ }
27941
+ var orCase = {
27942
+ match: binaryLogicalMatch,
27943
+ apply: (args) => applyBinaryLogical(
27944
+ args,
27945
+ (x, y) => x !== 0 || y !== 0 ? 1 : 0,
27946
+ (a, b) => a || b
27947
+ )
27948
+ };
27949
+ defineBuiltin({
27950
+ name: "or",
27951
+ help: {
27952
+ signatures: ["TF = or(A, B)"],
27953
+ description: "Logical OR. Functional form of A | B; returns a logical scalar or tensor."
27954
+ },
27955
+ cases: [orCase]
27956
+ });
27957
+ var andCase = {
27958
+ match: binaryLogicalMatch,
27959
+ apply: (args) => applyBinaryLogical(
27960
+ args,
27961
+ (x, y) => x !== 0 && y !== 0 ? 1 : 0,
27962
+ (a, b) => a && b
27963
+ )
27964
+ };
27965
+ defineBuiltin({
27966
+ name: "and",
27967
+ help: {
27968
+ signatures: ["TF = and(A, B)"],
27969
+ description: "Logical AND. Functional form of A & B; returns a logical scalar or tensor."
27970
+ },
27971
+ cases: [andCase]
27972
+ });
27973
+ defineBuiltin({
27974
+ name: "not",
27975
+ help: {
27976
+ signatures: ["TF = not(A)"],
27977
+ description: "Logical negation. Functional form of ~A; returns a logical scalar or tensor."
27978
+ },
27979
+ cases: [
27980
+ {
27981
+ match: (argTypes) => {
27982
+ if (argTypes.length !== 1) return null;
27983
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
27984
+ if (argTypes[0].kind === "tensor" || argTypes[0].kind === "sparse_matrix") {
27985
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27717
27986
  }
27718
- i++;
27719
- } else {
27720
- result += fmt[i];
27721
- i++;
27722
- }
27987
+ return [{ kind: "boolean" }];
27988
+ },
27989
+ apply: (args) => not(args[0])
27723
27990
  }
27724
- if (argIdx === startArgIdx) break;
27725
- } while (argIdx < flatArgs.length);
27726
- return result;
27727
- }
27991
+ ]
27992
+ });
27728
27993
 
27729
27994
  // src/numbl-core/interpreter/builtins/utility.ts
27730
27995
  function sparseToDense2(S) {
@@ -27834,7 +28099,7 @@ defineBuiltin({
27834
28099
  {
27835
28100
  match: (argTypes) => {
27836
28101
  if (argTypes.length < 1) return null;
27837
- return [{ kind: "number" }];
28102
+ return [];
27838
28103
  },
27839
28104
  apply: (args) => {
27840
28105
  const v = args[0];
@@ -27855,7 +28120,7 @@ defineBuiltin({
27855
28120
  const msg = args.length > 1 ? textValue(args[1]) ?? String(args[1]) : "Assertion failed";
27856
28121
  throw new Error(msg);
27857
28122
  }
27858
- return 0;
28123
+ return void 0;
27859
28124
  }
27860
28125
  }
27861
28126
  ]
@@ -27866,7 +28131,7 @@ defineBuiltin({
27866
28131
  {
27867
28132
  match: (argTypes) => {
27868
28133
  if (argTypes.length === 0) return null;
27869
- return [{ kind: "number" }];
28134
+ return [];
27870
28135
  },
27871
28136
  apply: (args) => {
27872
28137
  const first = textValue(args[0]) ?? String(args[0]);
@@ -28538,6 +28803,19 @@ defineBuiltin({
28538
28803
  name: "NaN",
28539
28804
  cases: arrayConstructorCases(nanFill, NaN)
28540
28805
  });
28806
+ function infFill(shape) {
28807
+ const data = new FloatXArray(numel(shape));
28808
+ data.fill(Infinity);
28809
+ return makeTensor(data, void 0, shape);
28810
+ }
28811
+ defineBuiltin({
28812
+ name: "inf",
28813
+ cases: arrayConstructorCases(infFill, Infinity)
28814
+ });
28815
+ defineBuiltin({
28816
+ name: "Inf",
28817
+ cases: arrayConstructorCases(infFill, Infinity)
28818
+ });
28541
28819
  defineBuiltin({
28542
28820
  name: "eye",
28543
28821
  cases: arrayConstructorCases(
@@ -29375,19 +29653,33 @@ function preserveTextType(t) {
29375
29653
  if (t.kind === "string") return { kind: "string" };
29376
29654
  return null;
29377
29655
  }
29656
+ function applyTextFn(v, fn) {
29657
+ if (isRuntimeCell(v)) {
29658
+ const out = new Array(v.data.length);
29659
+ for (let i = 0; i < v.data.length; i++) {
29660
+ out[i] = applyTextFn(v.data[i], fn);
29661
+ }
29662
+ return RTV.cell(out, [...v.shape]);
29663
+ }
29664
+ if (isRuntimeChar(v)) return RTV.char(fn(v.value));
29665
+ if (isRuntimeString(v)) return RTV.string(fn(toString(v)));
29666
+ return v;
29667
+ }
29378
29668
  function textPreserveResolve(fn) {
29379
29669
  return (argTypes) => {
29380
29670
  if (argTypes.length !== 1) return null;
29381
- const out = preserveTextType(argTypes[0]);
29671
+ const t = argTypes[0];
29672
+ if (t.kind === "cell") {
29673
+ return {
29674
+ outputTypes: [{ kind: "cell" }],
29675
+ apply: (args) => applyTextFn(args[0], fn)
29676
+ };
29677
+ }
29678
+ const out = preserveTextType(t);
29382
29679
  if (!out) return null;
29383
29680
  return {
29384
29681
  outputTypes: [out],
29385
- apply: (args) => {
29386
- const v = args[0];
29387
- const s = toString(v);
29388
- const result = fn(s);
29389
- return isRuntimeChar(v) ? RTV.char(result) : RTV.string(result);
29390
- }
29682
+ apply: (args) => applyTextFn(args[0], fn)
29391
29683
  };
29392
29684
  };
29393
29685
  }
@@ -33150,11 +33442,15 @@ defineBuiltin({
33150
33442
  apply: (args) => {
33151
33443
  if (args.length !== 2)
33152
33444
  throw new RuntimeError("kron requires 2 arguments");
33153
- const a = args[0], b = args[1];
33154
- const A = isRuntimeNumber(a) ? RTV.tensor(new FloatXArray([a]), [1, 1]) : a;
33155
- const B = isRuntimeNumber(b) ? RTV.tensor(new FloatXArray([b]), [1, 1]) : b;
33156
- if (!isRuntimeTensor(A) || !isRuntimeTensor(B))
33445
+ const coerce = (v) => {
33446
+ if (isRuntimeNumber(v))
33447
+ return RTV.tensor(new FloatXArray([v]), [1, 1]);
33448
+ if (isRuntimeSparseMatrix(v)) return sparseToDense(v);
33449
+ if (isRuntimeTensor(v)) return v;
33157
33450
  throw new RuntimeError("kron: arguments must be numeric");
33451
+ };
33452
+ const A = coerce(args[0]);
33453
+ const B = coerce(args[1]);
33158
33454
  const [m, n] = tensorSize2D(A);
33159
33455
  const [p2, q] = tensorSize2D(B);
33160
33456
  const rows = m * p2, cols = n * q;
@@ -35472,6 +35768,65 @@ defineBuiltin({
35472
35768
  }
35473
35769
  ]
35474
35770
  });
35771
+ defineBuiltin({
35772
+ name: "bitget",
35773
+ cases: [
35774
+ {
35775
+ match: (argTypes) => {
35776
+ if (argTypes.length !== 2) return null;
35777
+ return [{ kind: "unknown" }];
35778
+ },
35779
+ // bitget(A, bit) returns bit number `bit` (1-based, 1 = LSB) of each
35780
+ // element of A. MATLAB supports vector `bit` with broadcasting; we
35781
+ // match the scalar-or-same-shape rules used by the other bitwise ops.
35782
+ apply: (args) => bitwiseOp(
35783
+ args[0],
35784
+ args[1],
35785
+ (a, bit) => {
35786
+ const b = Math.round(bit);
35787
+ if (b < 1) return 0;
35788
+ if (b <= 31) return a >>> b - 1 & 1;
35789
+ const bi = BigInt(a);
35790
+ return Number(bi >> BigInt(b - 1) & 1n);
35791
+ },
35792
+ "bitget"
35793
+ )
35794
+ }
35795
+ ]
35796
+ });
35797
+ defineBuiltin({
35798
+ name: "bitset",
35799
+ cases: [
35800
+ {
35801
+ match: (argTypes) => {
35802
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
35803
+ return [{ kind: "unknown" }];
35804
+ },
35805
+ // bitset(A, bit) → set bit to 1
35806
+ // bitset(A, bit, v) → set bit to v (0 or 1)
35807
+ apply: (args) => {
35808
+ const v = args.length >= 3 ? Math.round(toNumber(args[2])) : 1;
35809
+ return bitwiseOp(
35810
+ args[0],
35811
+ args[1],
35812
+ (a, bit) => {
35813
+ const b = Math.round(bit);
35814
+ if (b < 1) return a;
35815
+ if (b <= 31) {
35816
+ const mask2 = 1 << b - 1;
35817
+ return v ? a | mask2 : a & ~mask2;
35818
+ }
35819
+ const bi = BigInt(a);
35820
+ const mask = 1n << BigInt(b - 1);
35821
+ const out = v ? bi | mask : bi & ~mask;
35822
+ return Number(out);
35823
+ },
35824
+ "bitset"
35825
+ );
35826
+ }
35827
+ }
35828
+ ]
35829
+ });
35475
35830
  function coordTransform(name, nArgs, nOut, fn) {
35476
35831
  defineBuiltin({
35477
35832
  name,
@@ -36601,6 +36956,131 @@ defineBuiltin({
36601
36956
  }
36602
36957
  ]
36603
36958
  });
36959
+ var INT_RANGES = [
36960
+ { name: "int8", min: -128, max: 127 },
36961
+ { name: "int16", min: -32768, max: 32767 },
36962
+ { name: "int32", min: -2147483648, max: 2147483647 },
36963
+ // int64/uint64 can't represent their full native range as doubles;
36964
+ // clamp at Number.MAX_SAFE_INTEGER to avoid silent precision loss.
36965
+ {
36966
+ name: "int64",
36967
+ min: -Number.MAX_SAFE_INTEGER,
36968
+ max: Number.MAX_SAFE_INTEGER
36969
+ },
36970
+ { name: "uint8", min: 0, max: 255 },
36971
+ { name: "uint16", min: 0, max: 65535 },
36972
+ { name: "uint32", min: 0, max: 4294967295 },
36973
+ { name: "uint64", min: 0, max: Number.MAX_SAFE_INTEGER }
36974
+ ];
36975
+ function saturateRoundToward(x, min, max) {
36976
+ if (isNaN(x)) return 0;
36977
+ const r = x >= 0 ? Math.floor(x + 0.5) : -Math.floor(-x + 0.5);
36978
+ if (r < min) return min;
36979
+ if (r > max) return max;
36980
+ return r;
36981
+ }
36982
+ for (const { name, min, max } of INT_RANGES) {
36983
+ defineBuiltin({
36984
+ name,
36985
+ cases: [
36986
+ {
36987
+ match: (argTypes) => {
36988
+ if (argTypes.length !== 1) return null;
36989
+ const a = argTypes[0];
36990
+ if (a.kind === "number" || a.kind === "boolean" || a.kind === "char" || a.kind === "complex_or_number")
36991
+ return [{ kind: "number" }];
36992
+ if (a.kind === "tensor")
36993
+ return [
36994
+ {
36995
+ kind: "tensor",
36996
+ isComplex: false,
36997
+ shape: a.shape,
36998
+ ndim: a.ndim
36999
+ }
37000
+ ];
37001
+ return null;
37002
+ },
37003
+ apply: (args) => {
37004
+ const v = args[0];
37005
+ if (isRuntimeNumber(v))
37006
+ return RTV.num(saturateRoundToward(v, min, max));
37007
+ if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
37008
+ if (isRuntimeComplexNumber(v))
37009
+ return RTV.num(saturateRoundToward(v.re, min, max));
37010
+ if (isRuntimeChar(v)) {
37011
+ if (v.value.length === 0)
37012
+ return RTV.tensor(new FloatXArray(0), [0, 0]);
37013
+ if (v.value.length === 1)
37014
+ return RTV.num(
37015
+ saturateRoundToward(v.value.charCodeAt(0), min, max)
37016
+ );
37017
+ const out = new FloatXArray(v.value.length);
37018
+ for (let i = 0; i < v.value.length; i++) {
37019
+ out[i] = saturateRoundToward(v.value.charCodeAt(i), min, max);
37020
+ }
37021
+ return RTV.row(Array.from(out));
37022
+ }
37023
+ if (isRuntimeTensor(v)) {
37024
+ const data = new FloatXArray(v.data.length);
37025
+ for (let i = 0; i < v.data.length; i++) {
37026
+ data[i] = saturateRoundToward(v.data[i], min, max);
37027
+ }
37028
+ return RTV.tensor(data, [...v.shape]);
37029
+ }
37030
+ return RTV.num(saturateRoundToward(toNumber(v), min, max));
37031
+ }
37032
+ }
37033
+ ]
37034
+ });
37035
+ }
37036
+ defineBuiltin({
37037
+ name: "idivide",
37038
+ cases: [
37039
+ {
37040
+ match: (argTypes) => {
37041
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
37042
+ return [{ kind: "unknown" }];
37043
+ },
37044
+ apply: (args) => {
37045
+ const divFix = (a2, b2) => {
37046
+ if (b2 === 0) {
37047
+ return 0;
37048
+ }
37049
+ const q = a2 / b2;
37050
+ return q >= 0 ? Math.floor(q) : -Math.floor(-q);
37051
+ };
37052
+ const a = args[0];
37053
+ const b = args[1];
37054
+ if (isRuntimeNumber(a) && isRuntimeNumber(b)) {
37055
+ return RTV.num(divFix(a, b));
37056
+ }
37057
+ if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
37058
+ const bv = b;
37059
+ const data = new FloatXArray(a.data.length);
37060
+ for (let i = 0; i < a.data.length; i++)
37061
+ data[i] = divFix(a.data[i], bv);
37062
+ return RTV.tensor(data, [...a.shape]);
37063
+ }
37064
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
37065
+ const av = a;
37066
+ const data = new FloatXArray(b.data.length);
37067
+ for (let i = 0; i < b.data.length; i++)
37068
+ data[i] = divFix(av, b.data[i]);
37069
+ return RTV.tensor(data, [...b.shape]);
37070
+ }
37071
+ if (isRuntimeTensor(a) && isRuntimeTensor(b)) {
37072
+ if (a.data.length !== b.data.length)
37073
+ throw new RuntimeError("idivide: arrays must be the same size");
37074
+ const data = new FloatXArray(a.data.length);
37075
+ for (let i = 0; i < a.data.length; i++)
37076
+ data[i] = divFix(a.data[i], b.data[i]);
37077
+ return RTV.tensor(data, [...a.shape]);
37078
+ }
37079
+ throw new RuntimeError("idivide: arguments must be numeric");
37080
+ }
37081
+ }
37082
+ ]
37083
+ });
36604
37084
  defineBuiltin({
36605
37085
  name: "logical",
36606
37086
  cases: [
@@ -37983,6 +38463,24 @@ registerIBuiltin({
37983
38463
  }
37984
38464
  })
37985
38465
  });
38466
+ function getMexExt() {
38467
+ if (typeof process === "undefined") return "";
38468
+ const platform = process.platform;
38469
+ const cpuArch = process.arch;
38470
+ if (platform === "win32") return "mexw64";
38471
+ if (platform === "darwin")
38472
+ return cpuArch === "arm64" ? "mexmaca64" : "mexmaci64";
38473
+ return "mexa64";
38474
+ }
38475
+ defineBuiltin({
38476
+ name: "mexext",
38477
+ cases: [
38478
+ {
38479
+ match: (argTypes) => argTypes.length === 0 ? [{ kind: "char" }] : null,
38480
+ apply: () => RTV.char(getMexExt())
38481
+ }
38482
+ ]
38483
+ });
37986
38484
  var _platform = typeof process !== "undefined" ? process.platform : "linux";
37987
38485
  for (const [name, val] of [
37988
38486
  ["ismac", _platform === "darwin"],
@@ -39028,7 +39526,7 @@ for (const name of ["clear", "clc", "clf"]) {
39028
39526
  name,
39029
39527
  resolve: () => ({
39030
39528
  outputTypes: [],
39031
- apply: () => 0
39529
+ apply: () => void 0
39032
39530
  })
39033
39531
  });
39034
39532
  }
@@ -39162,7 +39660,7 @@ registerIBuiltin({
39162
39660
  name: "set",
39163
39661
  resolve: () => ({
39164
39662
  outputTypes: [],
39165
- apply: () => 0
39663
+ apply: () => void 0
39166
39664
  })
39167
39665
  });
39168
39666
  for (const name of [
@@ -39184,7 +39682,7 @@ for (const name of [
39184
39682
  name,
39185
39683
  resolve: () => ({
39186
39684
  outputTypes: [],
39187
- apply: () => 0
39685
+ apply: () => void 0
39188
39686
  })
39189
39687
  });
39190
39688
  }
@@ -43904,6 +44402,114 @@ function bisectEvent(eventIdx, step, events) {
43904
44402
  return [tOld + xFinal * h, denseOutputEval(yOld, Q, h, xFinal)];
43905
44403
  }
43906
44404
 
44405
+ // src/numbl-core/helpers/quadgk.ts
44406
+ var XK = [
44407
+ -0.9914553711208126,
44408
+ -0.9491079123427585,
44409
+ -0.8648644233597691,
44410
+ -0.7415311855993945,
44411
+ -0.586087235467691,
44412
+ -0.4058451513773972,
44413
+ -0.2077849550078985,
44414
+ 0,
44415
+ 0.2077849550078985,
44416
+ 0.4058451513773972,
44417
+ 0.586087235467691,
44418
+ 0.7415311855993945,
44419
+ 0.8648644233597691,
44420
+ 0.9491079123427585,
44421
+ 0.9914553711208126
44422
+ ];
44423
+ var WK = [
44424
+ 0.0229353220105292,
44425
+ 0.0630920926299786,
44426
+ 0.1047900103222502,
44427
+ 0.1406532597155259,
44428
+ 0.1690047266392679,
44429
+ 0.1903505780647854,
44430
+ 0.2044329400752989,
44431
+ 0.2094821410847278,
44432
+ 0.2044329400752989,
44433
+ 0.1903505780647854,
44434
+ 0.1690047266392679,
44435
+ 0.1406532597155259,
44436
+ 0.1047900103222502,
44437
+ 0.0630920926299786,
44438
+ 0.0229353220105292
44439
+ ];
44440
+ var WG = [
44441
+ 0.1294849661688697,
44442
+ 0.2797053914892767,
44443
+ 0.3818300505051189,
44444
+ 0.4179591836734694,
44445
+ 0.3818300505051189,
44446
+ 0.2797053914892767,
44447
+ 0.1294849661688697
44448
+ ];
44449
+ function kronrodNodes(lo, hi) {
44450
+ const m = (lo + hi) / 2;
44451
+ const h = (hi - lo) / 2;
44452
+ const out = new Array(15);
44453
+ for (let i = 0; i < 15; i++) out[i] = m + h * XK[i];
44454
+ return out;
44455
+ }
44456
+ function segmentEstimate(lo, hi, fv) {
44457
+ const h = (hi - lo) / 2;
44458
+ let K = 0;
44459
+ let G = 0;
44460
+ for (let i = 0; i < 15; i++) K += WK[i] * fv[i];
44461
+ for (let i = 0; i < 7; i++) G += WG[i] * fv[2 * i + 1];
44462
+ K *= h;
44463
+ G *= h;
44464
+ return { K, err: Math.abs(K - G) };
44465
+ }
44466
+ function quadgkAdaptive(integrand, a, b, opts = {}) {
44467
+ const relTol = opts.relTol ?? 1e-6;
44468
+ const absTol = opts.absTol ?? 1e-10;
44469
+ const maxIntervals = opts.maxIntervalCount ?? 650;
44470
+ if (a === b) return { value: 0, errbnd: 0, intervalsUsed: 0 };
44471
+ const sign = a < b ? 1 : -1;
44472
+ const lo0 = Math.min(a, b);
44473
+ const hi0 = Math.max(a, b);
44474
+ const segmentOn = (lo, hi) => {
44475
+ const pts = kronrodNodes(lo, hi);
44476
+ const fv = integrand(pts);
44477
+ if (fv.length !== 15) {
44478
+ throw new Error(
44479
+ `quadgk: integrand must return 15 values for 15 nodes, got ${fv.length}`
44480
+ );
44481
+ }
44482
+ return segmentEstimate(lo, hi, fv);
44483
+ };
44484
+ const initial = segmentOn(lo0, hi0);
44485
+ let totalK = initial.K;
44486
+ let totalErr = initial.err;
44487
+ const worklist = [{ lo: lo0, hi: hi0, ...initial }];
44488
+ const converged = () => totalErr <= Math.max(absTol, relTol * Math.abs(totalK));
44489
+ let iters = 0;
44490
+ while (!converged() && worklist.length < maxIntervals && iters < 1e4) {
44491
+ iters++;
44492
+ let worstIdx = 0;
44493
+ for (let i = 1; i < worklist.length; i++) {
44494
+ if (worklist[i].err > worklist[worstIdx].err) worstIdx = i;
44495
+ }
44496
+ const worst = worklist[worstIdx];
44497
+ if (worst.hi - worst.lo <= 1e-15 * (hi0 - lo0)) break;
44498
+ const mid = (worst.lo + worst.hi) / 2;
44499
+ const s1 = segmentOn(worst.lo, mid);
44500
+ const s2 = segmentOn(mid, worst.hi);
44501
+ totalK += s1.K + s2.K - worst.K;
44502
+ totalErr += s1.err + s2.err - worst.err;
44503
+ worklist[worstIdx] = { lo: worst.lo, hi: mid, ...s1 };
44504
+ worklist.push({ lo: mid, hi: worst.hi, ...s2 });
44505
+ }
44506
+ return {
44507
+ value: sign * totalK,
44508
+ errbnd: totalErr,
44509
+ intervalsUsed: worklist.length
44510
+ };
44511
+ }
44512
+
43907
44513
  // src/numbl-core/runtime/specialBuiltinNames.ts
43908
44514
  var SPECIAL_BUILTIN_NAMES = [
43909
44515
  "help",
@@ -43992,6 +44598,7 @@ var SPECIAL_BUILTIN_NAMES = [
43992
44598
  "webread",
43993
44599
  "delete",
43994
44600
  "rmdir",
44601
+ "movefile",
43995
44602
  "unzip",
43996
44603
  "dir",
43997
44604
  "warning",
@@ -44005,7 +44612,8 @@ var SPECIAL_BUILTIN_NAMES = [
44005
44612
  "ode45",
44006
44613
  "ode23",
44007
44614
  "deval",
44008
- "toc"
44615
+ "toc",
44616
+ "quadgk"
44009
44617
  ];
44010
44618
 
44011
44619
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -44019,42 +44627,58 @@ function registerSpecial(name, fn) {
44019
44627
  })
44020
44628
  });
44021
44629
  }
44630
+ function registerSpecialVoid(name, fn) {
44631
+ registerDynamicIBuiltin({
44632
+ name,
44633
+ resolve: () => ({
44634
+ outputTypes: [],
44635
+ apply: (args) => {
44636
+ fn(args);
44637
+ return void 0;
44638
+ }
44639
+ })
44640
+ });
44641
+ }
44022
44642
  function registerSpecialBuiltins(rt) {
44023
- registerSpecial("help", (_nargout, args) => {
44643
+ registerSpecial("help", (nargout, args) => {
44644
+ let text = "";
44645
+ const emit = (s) => {
44646
+ text += s;
44647
+ if (nargout === 0) rt.output(s);
44648
+ };
44024
44649
  if (args.length === 0) {
44025
44650
  const names = getAllBuiltinNames().sort();
44026
- rt.output("Available builtins:\n");
44027
- rt.output(" " + names.join(", ") + "\n");
44028
- rt.output("\nType 'help <name>' for help on a specific builtin.\n");
44029
- return 0;
44030
- }
44031
- const name = toString(args[0]);
44032
- const h = getIBuiltinHelp(name);
44033
- if (!h) {
44034
- const allNames = getAllBuiltinNames();
44035
- if (allNames.includes(name)) {
44036
- rt.output(`No help available for '${name}'.
44651
+ emit("Available builtins:\n");
44652
+ emit(" " + names.join(", ") + "\n");
44653
+ emit("\nType 'help <name>' for help on a specific builtin.\n");
44654
+ } else {
44655
+ const name = toString(args[0]);
44656
+ const h = getIBuiltinHelp(name);
44657
+ if (!h) {
44658
+ const allNames = getAllBuiltinNames();
44659
+ if (allNames.includes(name)) {
44660
+ emit(`No help available for '${name}'.
44037
44661
  `);
44038
- } else {
44039
- rt.output(`Unknown function '${name}'.
44662
+ } else {
44663
+ emit(`Unknown function '${name}'.
44040
44664
  `);
44041
- }
44042
- return 0;
44043
- }
44044
- rt.output(` ${h.signatures.join("\n ")}
44665
+ }
44666
+ } else {
44667
+ emit(` ${h.signatures.join("\n ")}
44045
44668
 
44046
44669
  `);
44047
- rt.output(`${h.description}
44670
+ emit(`${h.description}
44048
44671
  `);
44049
- return 0;
44672
+ }
44673
+ }
44674
+ return nargout >= 1 ? RTV.char(text) : void 0;
44050
44675
  });
44051
- registerSpecial("disp", (_nargout, args) => {
44676
+ registerSpecialVoid("disp", (args) => {
44052
44677
  if (args.length >= 1) {
44053
44678
  const mv = ensureRuntimeValue(args[0]);
44054
- if (isRuntimeTensor(mv) && mv.data.length === 0) return 0;
44679
+ if (isRuntimeTensor(mv) && mv.data.length === 0) return;
44055
44680
  rt.output(displayValue(mv) + "\n");
44056
44681
  }
44057
- return 0;
44058
44682
  });
44059
44683
  registerSpecial("toc", (nargout) => {
44060
44684
  const elapsed = (performance.now() - getTicTime()) / 1e3;
@@ -44064,12 +44688,13 @@ function registerSpecialBuiltins(rt) {
44064
44688
  }
44065
44689
  return RTV.num(elapsed);
44066
44690
  });
44067
- registerSpecial("warning", (_nargout, args) => {
44068
- if (args.length === 0) return RTV.num(0);
44691
+ registerSpecial("warning", (nargout, args) => {
44692
+ if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
44069
44693
  const margs = args.map((a) => ensureRuntimeValue(a));
44070
44694
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
44071
44695
  const state = toString(margs[0]);
44072
44696
  if (state === "on" || state === "off") {
44697
+ if (nargout === 0) return void 0;
44073
44698
  return RTV.struct(
44074
44699
  /* @__PURE__ */ new Map([
44075
44700
  ["state", RTV.char("on")],
@@ -44098,9 +44723,9 @@ function registerSpecialBuiltins(rt) {
44098
44723
  } else {
44099
44724
  rt.output("Warning: " + sprintfFormat(fmt, fmtArgs) + "\n");
44100
44725
  }
44101
- return RTV.num(0);
44726
+ return nargout >= 1 ? RTV.num(0) : void 0;
44102
44727
  });
44103
- registerSpecial("fprintf", (_nargout, args) => {
44728
+ registerSpecial("fprintf", (nargout, args) => {
44104
44729
  let output = "";
44105
44730
  if (args.length >= 1) {
44106
44731
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44111,27 +44736,7 @@ function registerSpecialBuiltins(rt) {
44111
44736
  fmtIdx = 1;
44112
44737
  }
44113
44738
  const fmt = toString(margs[fmtIdx]);
44114
- const scalarArgs = [];
44115
- for (let i = fmtIdx + 1; i < margs.length; i++) {
44116
- const a = margs[i];
44117
- if (isRuntimeTensor(a)) {
44118
- for (let j = 0; j < a.data.length; j++)
44119
- scalarArgs.push(RTV.num(a.data[j]));
44120
- } else {
44121
- scalarArgs.push(a);
44122
- }
44123
- }
44124
- const specCount = (fmt.match(/%[^%]/g) || []).length;
44125
- if (specCount === 0 || scalarArgs.length === 0) {
44126
- output = sprintfFormat(fmt, scalarArgs);
44127
- } else {
44128
- let idx = 0;
44129
- while (idx < scalarArgs.length) {
44130
- const batch = scalarArgs.slice(idx, idx + specCount);
44131
- output += sprintfFormat(fmt, batch);
44132
- idx += specCount;
44133
- }
44134
- }
44739
+ output = sprintfFormat(fmt, margs.slice(fmtIdx + 1));
44135
44740
  if (fid === 1 || fid === 2) {
44136
44741
  rt.output(output);
44137
44742
  } else {
@@ -44142,7 +44747,7 @@ function registerSpecialBuiltins(rt) {
44142
44747
  rt.fileIO.fwrite(fid, output);
44143
44748
  }
44144
44749
  }
44145
- return output.length;
44750
+ return nargout >= 1 ? output.length : void 0;
44146
44751
  });
44147
44752
  registerSpecial("arrayfun", (nargout, args) => {
44148
44753
  return arrayfunImpl(rt, nargout, args);
@@ -44174,6 +44779,55 @@ function registerSpecialBuiltins(rt) {
44174
44779
  registerSpecial("bsxfun", (nargout, args) => {
44175
44780
  return bsxfunImpl(rt, nargout, args);
44176
44781
  });
44782
+ registerSpecial("quadgk", (nargout, args) => {
44783
+ if (args.length < 3)
44784
+ throw new RuntimeError(
44785
+ "quadgk: requires at least 3 arguments (fun, a, b)"
44786
+ );
44787
+ const fnArg = ensureRuntimeValue(args[0]);
44788
+ if (!isRuntimeFunction(fnArg))
44789
+ throw new RuntimeError("quadgk: first argument must be a function");
44790
+ const a = toNumber(ensureRuntimeValue(args[1]));
44791
+ const b = toNumber(ensureRuntimeValue(args[2]));
44792
+ let relTol;
44793
+ let absTol;
44794
+ let maxIntervalCount;
44795
+ for (let i = 3; i + 1 < args.length; i += 2) {
44796
+ const keyRv = ensureRuntimeValue(args[i]);
44797
+ const key = isRuntimeChar(keyRv) ? keyRv.value : isRuntimeString(keyRv) ? keyRv : "";
44798
+ const lowerKey = key.toLowerCase();
44799
+ const valRv = ensureRuntimeValue(args[i + 1]);
44800
+ if (lowerKey === "reltol") relTol = toNumber(valRv);
44801
+ else if (lowerKey === "abstol") absTol = toNumber(valRv);
44802
+ else if (lowerKey === "maxintervalcount")
44803
+ maxIntervalCount = toNumber(valRv);
44804
+ }
44805
+ const integrand = (pts) => {
44806
+ const vecData = new FloatXArray(pts);
44807
+ const vec = RTV.tensor(vecData, [1, pts.length]);
44808
+ const resultRaw = rt.index(fnArg, [vec], 1);
44809
+ const rv = ensureRuntimeValue(resultRaw);
44810
+ if (isRuntimeNumber(rv)) {
44811
+ return new Array(pts.length).fill(rv);
44812
+ }
44813
+ if (isRuntimeTensor(rv)) {
44814
+ if (rv.data.length !== pts.length) {
44815
+ throw new RuntimeError(
44816
+ `quadgk: integrand returned ${rv.data.length} values for ${pts.length} nodes`
44817
+ );
44818
+ }
44819
+ return Array.from(rv.data);
44820
+ }
44821
+ throw new RuntimeError("quadgk: integrand must return a numeric vector");
44822
+ };
44823
+ const result = quadgkAdaptive(integrand, a, b, {
44824
+ relTol,
44825
+ absTol,
44826
+ maxIntervalCount
44827
+ });
44828
+ if (nargout >= 2) return [result.value, result.errbnd];
44829
+ return result.value;
44830
+ });
44177
44831
  registerSpecial("subsref", (nargout, args) => {
44178
44832
  return subsrefBuiltin(rt, nargout, args);
44179
44833
  });
@@ -44796,7 +45450,7 @@ function registerSpecialBuiltins(rt) {
44796
45450
  return RTV.char(text);
44797
45451
  }
44798
45452
  });
44799
- registerSpecial("delete", (_nargout, args) => {
45453
+ registerSpecialVoid("delete", (args) => {
44800
45454
  const io = requireFileIO();
44801
45455
  if (!io.deleteFile)
44802
45456
  throw new RuntimeError("delete is not available in this environment");
@@ -44806,7 +45460,6 @@ function registerSpecialBuiltins(rt) {
44806
45460
  for (const arg of margs) {
44807
45461
  io.deleteFile(toString(arg));
44808
45462
  }
44809
- return 0;
44810
45463
  });
44811
45464
  registerSpecial("rmdir", (nargout, args) => {
44812
45465
  const io = requireFileIO();
@@ -44829,6 +45482,34 @@ function registerSpecialBuiltins(rt) {
44829
45482
  RTV.char("")
44830
45483
  ];
44831
45484
  });
45485
+ registerSpecial("movefile", (nargout, args) => {
45486
+ const io = requireFileIO();
45487
+ if (!io.movefile)
45488
+ throw new RuntimeError("movefile is not available in this environment");
45489
+ const margs = args.map((a) => ensureRuntimeValue(a));
45490
+ if (margs.length < 1)
45491
+ throw new RuntimeError("movefile requires at least 1 argument");
45492
+ const source = toString(margs[0]);
45493
+ const destination = margs.length >= 2 ? toString(margs[1]) : rt.system?.cwd() ?? ".";
45494
+ let force = false;
45495
+ if (margs.length >= 3) {
45496
+ const third = toString(margs[2]);
45497
+ if (third.toLowerCase() === "f") force = true;
45498
+ }
45499
+ const ok = io.movefile(source, destination, force);
45500
+ if (nargout === 0) {
45501
+ if (!ok)
45502
+ throw new RuntimeError(
45503
+ `movefile: cannot move '${source}' to '${destination}'`
45504
+ );
45505
+ return void 0;
45506
+ }
45507
+ return nargout <= 1 ? RTV.num(ok ? 1 : 0) : [
45508
+ RTV.num(ok ? 1 : 0),
45509
+ RTV.char(ok ? "" : `Cannot move '${source}' to '${destination}'`),
45510
+ RTV.char("")
45511
+ ];
45512
+ });
44832
45513
  registerSpecial("unzip", (nargout, args) => {
44833
45514
  const io = requireFileIO();
44834
45515
  if (!io.unzip)
@@ -44880,7 +45561,7 @@ function registerSpecialBuiltins(rt) {
44880
45561
  const cellData = extracted.map((f) => RTV.char(f));
44881
45562
  return RTV.cell(cellData, [1, cellData.length]);
44882
45563
  }
44883
- return 0;
45564
+ return void 0;
44884
45565
  });
44885
45566
  registerSpecial("dir", (nargout, args) => {
44886
45567
  const io = requireFileIO();
@@ -44977,7 +45658,7 @@ function registerSpecialBuiltins(rt) {
44977
45658
  const parts = margs.map((a) => toString(a));
44978
45659
  return RTV.char(parts.join("/"));
44979
45660
  });
44980
- registerSpecial("assignin", (_nargout, args) => {
45661
+ registerSpecialVoid("assignin", (args) => {
44981
45662
  if (args.length < 3)
44982
45663
  throw new RuntimeError("assignin requires 3 arguments");
44983
45664
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44992,7 +45673,6 @@ function registerSpecialBuiltins(rt) {
44992
45673
  } else {
44993
45674
  rt.setWorkspaceVariable(varName, args[2]);
44994
45675
  }
44995
- return 0;
44996
45676
  });
44997
45677
  registerSpecial("evalin", (_nargout, args) => {
44998
45678
  if (args.length < 2)
@@ -45013,13 +45693,12 @@ function registerSpecialBuiltins(rt) {
45013
45693
  }
45014
45694
  return val;
45015
45695
  });
45016
- registerSpecial("drawnow", () => {
45696
+ registerSpecialVoid("drawnow", () => {
45017
45697
  rt.drawnow();
45018
- return 0;
45019
45698
  });
45020
- registerSpecial("pause", (_nargout, args) => {
45699
+ registerSpecial("pause", (nargout, args) => {
45021
45700
  rt.pause(args[0] ?? 0);
45022
- return 0;
45701
+ return nargout >= 1 ? RTV.char("on") : void 0;
45023
45702
  });
45024
45703
  registerSpecial("mfilename", (_nargout, args) => {
45025
45704
  const file = rt.$file ?? "";
@@ -45063,7 +45742,7 @@ function registerSpecialBuiltins(rt) {
45063
45742
  }
45064
45743
  }
45065
45744
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
45066
- return 0;
45745
+ return void 0;
45067
45746
  });
45068
45747
  registerSpecial("rmpath", (nargout, args) => {
45069
45748
  if (!rt.onPathChange) {
@@ -45083,11 +45762,11 @@ function registerSpecialBuiltins(rt) {
45083
45762
  }
45084
45763
  }
45085
45764
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
45086
- return 0;
45765
+ return void 0;
45087
45766
  });
45088
- registerSpecial("savepath", () => {
45767
+ registerSpecial("savepath", (nargout) => {
45089
45768
  rt.output("Warning: savepath is a no-op in numbl\n");
45090
- return 0;
45769
+ return nargout >= 1 ? RTV.num(0) : void 0;
45091
45770
  });
45092
45771
  registerSpecial("input", (_nargout, args) => {
45093
45772
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -45150,11 +45829,11 @@ function registerSpecialBuiltins(rt) {
45150
45829
  }
45151
45830
  return RTV.char(sys?.getEnv(toString(args[0])) ?? "");
45152
45831
  });
45153
- registerSpecial("setenv", (_nargout, args) => {
45832
+ registerSpecialVoid("setenv", (args) => {
45154
45833
  const sys = rt.system;
45155
45834
  if (args.length === 2) {
45156
45835
  sys?.setEnv(toString(args[0]), toString(args[1]));
45157
- return 0;
45836
+ return;
45158
45837
  }
45159
45838
  if (args.length === 1) {
45160
45839
  const d = args[0];
@@ -45162,20 +45841,26 @@ function registerSpecialBuiltins(rt) {
45162
45841
  for (const { key, value } of d.entries.values()) {
45163
45842
  sys?.setEnv(toString(key), toString(value));
45164
45843
  }
45165
- return 0;
45844
+ return;
45166
45845
  }
45167
45846
  sys?.setEnv(toString(d), "");
45168
- return 0;
45847
+ return;
45169
45848
  }
45170
45849
  throw new RuntimeError("setenv: invalid arguments");
45171
45850
  });
45172
45851
  registerSpecial("pwd", () => {
45173
45852
  return RTV.char(rt.system?.cwd() ?? "/");
45174
45853
  });
45175
- registerSpecial("cd", (_nargout, args) => {
45854
+ registerSpecial("cd", (nargout, args) => {
45176
45855
  const sys = rt.system;
45177
45856
  const curDir = sys?.cwd() ?? "/";
45178
- if (args.length === 0) return RTV.char(curDir);
45857
+ if (args.length === 0) {
45858
+ if (nargout === 0) {
45859
+ rt.output(curDir + "\n");
45860
+ return void 0;
45861
+ }
45862
+ return RTV.char(curDir);
45863
+ }
45179
45864
  const target = toString(args[0]);
45180
45865
  if (sys) {
45181
45866
  try {
@@ -45183,15 +45868,18 @@ function registerSpecialBuiltins(rt) {
45183
45868
  } catch {
45184
45869
  throw new RuntimeError(`Cannot change directory to '${target}'`);
45185
45870
  }
45871
+ if (rt.onCwdChange) {
45872
+ rt.onCwdChange(sys.cwd());
45873
+ }
45186
45874
  }
45187
- return RTV.char(curDir);
45875
+ return nargout >= 1 ? RTV.char(curDir) : void 0;
45188
45876
  });
45189
- registerSpecial("figure", (_nargout, args) => {
45877
+ registerSpecial("figure", (nargout, args) => {
45190
45878
  const handle = args.length > 0 ? args[0] : 1;
45191
45879
  plotInstr(rt.plotInstructions, { type: "set_figure_handle", handle });
45192
- return 0;
45880
+ return nargout >= 1 ? RTV.num(toNumber(ensureRuntimeValue(handle))) : void 0;
45193
45881
  });
45194
- registerSpecial("subplot", (_nargout, args) => {
45882
+ registerSpecial("subplot", (nargout, args) => {
45195
45883
  if (args.length >= 3) {
45196
45884
  plotInstr(rt.plotInstructions, {
45197
45885
  type: "set_subplot",
@@ -45200,43 +45888,41 @@ function registerSpecialBuiltins(rt) {
45200
45888
  index: args[2]
45201
45889
  });
45202
45890
  }
45203
- return 0;
45891
+ return nargout >= 1 ? RTV.num(0) : void 0;
45204
45892
  });
45205
- registerSpecial("title", (_nargout, args) => {
45893
+ registerSpecial("title", (nargout, args) => {
45206
45894
  if (args.length > 0) {
45207
45895
  plotInstr(rt.plotInstructions, { type: "set_title", text: args[0] });
45208
45896
  }
45209
- return 0;
45897
+ return nargout >= 1 ? RTV.num(0) : void 0;
45210
45898
  });
45211
- registerSpecial("xlabel", (_nargout, args) => {
45899
+ registerSpecial("xlabel", (nargout, args) => {
45212
45900
  if (args.length > 0) {
45213
45901
  plotInstr(rt.plotInstructions, { type: "set_xlabel", text: args[0] });
45214
45902
  }
45215
- return 0;
45903
+ return nargout >= 1 ? RTV.num(0) : void 0;
45216
45904
  });
45217
- registerSpecial("ylabel", (_nargout, args) => {
45905
+ registerSpecial("ylabel", (nargout, args) => {
45218
45906
  if (args.length > 0) {
45219
45907
  plotInstr(rt.plotInstructions, { type: "set_ylabel", text: args[0] });
45220
45908
  }
45221
- return 0;
45909
+ return nargout >= 1 ? RTV.num(0) : void 0;
45222
45910
  });
45223
- registerSpecial("hold", (_nargout, args) => {
45911
+ registerSpecialVoid("hold", (args) => {
45224
45912
  if (args.length > 0) {
45225
45913
  plotInstr(rt.plotInstructions, { type: "set_hold", value: args[0] });
45226
45914
  }
45227
- return 0;
45228
45915
  });
45229
- registerSpecial("grid", (_nargout, args) => {
45916
+ registerSpecialVoid("grid", (args) => {
45230
45917
  if (args.length > 0) {
45231
45918
  plotInstr(rt.plotInstructions, { type: "set_grid", value: args[0] });
45232
45919
  }
45233
- return 0;
45234
45920
  });
45235
- registerSpecial("legend", (_nargout, args) => {
45921
+ registerSpecial("legend", (nargout, args) => {
45236
45922
  legendCall(rt.plotInstructions, args);
45237
- return 0;
45923
+ return nargout >= 1 ? RTV.num(0) : void 0;
45238
45924
  });
45239
- registerSpecial("close", (_nargout, args) => {
45925
+ registerSpecial("close", (nargout, args) => {
45240
45926
  if (args.length > 0) {
45241
45927
  const val = toString(args[0]);
45242
45928
  if (val === "all") {
@@ -45247,26 +45933,25 @@ function registerSpecialBuiltins(rt) {
45247
45933
  } else {
45248
45934
  plotInstr(rt.plotInstructions, { type: "close" });
45249
45935
  }
45250
- return 0;
45936
+ return nargout >= 1 ? RTV.num(1) : void 0;
45251
45937
  });
45252
- registerSpecial("sgtitle", (_nargout, args) => {
45938
+ registerSpecial("sgtitle", (nargout, args) => {
45253
45939
  if (args.length > 0) {
45254
45940
  plotInstr(rt.plotInstructions, { type: "set_sgtitle", text: args[0] });
45255
45941
  }
45256
- return 0;
45942
+ return nargout >= 1 ? RTV.num(0) : void 0;
45257
45943
  });
45258
- registerSpecial("shading", (_nargout, args) => {
45944
+ registerSpecialVoid("shading", (args) => {
45259
45945
  if (args.length > 0) {
45260
45946
  plotInstr(rt.plotInstructions, {
45261
45947
  type: "set_shading",
45262
45948
  shading: args[0]
45263
45949
  });
45264
45950
  }
45265
- return 0;
45266
45951
  });
45267
- registerSpecial("clf", () => {
45952
+ registerSpecial("clf", (nargout) => {
45268
45953
  plotInstr(rt.plotInstructions, { type: "clf" });
45269
- return 0;
45954
+ return nargout >= 1 ? RTV.num(1) : void 0;
45270
45955
  });
45271
45956
  registerSpecial("ode45", (nargout, args) => {
45272
45957
  return _ode45Impl(rt, nargout, args, dormandPrince45);
@@ -45811,6 +46496,29 @@ function methodDispatch(rt, name, nargout, args) {
45811
46496
  }
45812
46497
  return fieldVal;
45813
46498
  }
46499
+ {
46500
+ const accessorKey = `${firstRV.className}.get.${name}`;
46501
+ if (!rt.activeAccessors.has(accessorKey)) {
46502
+ const getter = rt.cachedResolveClassMethod(
46503
+ firstRV.className,
46504
+ `get.${name}`
46505
+ );
46506
+ if (getter) {
46507
+ rt.activeAccessors.add(accessorKey);
46508
+ let gotVal;
46509
+ try {
46510
+ gotVal = getter(1, first);
46511
+ } finally {
46512
+ rt.activeAccessors.delete(accessorKey);
46513
+ }
46514
+ const remaining = args.slice(1);
46515
+ if (remaining.length > 0) {
46516
+ return rt.index(gotVal, remaining, nargout);
46517
+ }
46518
+ return gotVal;
46519
+ }
46520
+ }
46521
+ }
45814
46522
  try {
45815
46523
  return callClassMethod(rt, firstRV.className, name, nargout, args);
45816
46524
  } catch (e) {
@@ -46379,6 +47087,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46379
47087
  const im2 = t.imag[i];
46380
47088
  return im2 === 0 ? t.data[i] : RTV.complex(t.data[i], im2);
46381
47089
  }
47090
+ if (t._isLogical === true) return t.data[i] !== 0;
46382
47091
  return t.data[i];
46383
47092
  }
46384
47093
  } else if (nIdx === 2) {
@@ -46387,7 +47096,14 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46387
47096
  if (typeof ri === "number" && typeof ci === "number") {
46388
47097
  const s = t.shape;
46389
47098
  const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
46390
- const cols = s.length === 0 ? 1 : s.length === 1 ? s[0] : s[1];
47099
+ let cols;
47100
+ if (s.length === 0) cols = 1;
47101
+ else if (s.length === 1) cols = s[0];
47102
+ else if (s.length === 2) cols = s[1];
47103
+ else {
47104
+ cols = 1;
47105
+ for (let k = 1; k < s.length; k++) cols *= s[k];
47106
+ }
46391
47107
  const r = Math.round(ri) - 1;
46392
47108
  const c = Math.round(ci) - 1;
46393
47109
  if (r < 0 || r >= rows || c < 0 || c >= cols)
@@ -46397,6 +47113,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46397
47113
  const im2 = t.imag[lin];
46398
47114
  return im2 === 0 ? t.data[lin] : RTV.complex(t.data[lin], im2);
46399
47115
  }
47116
+ if (t._isLogical === true) return t.data[lin] !== 0;
46400
47117
  return t.data[lin];
46401
47118
  }
46402
47119
  } else if (nIdx >= 3) {
@@ -46787,14 +47504,20 @@ function multiOutputCellAssign(base, indices, results) {
46787
47504
  if (base === void 0 || base === null) base = RTV.cell([], [0, 0]);
46788
47505
  let mv = ensureRuntimeValue(base);
46789
47506
  if (!isRuntimeCell(mv)) mv = RTV.cell([], [0, 0]);
46790
- const idxMv = ensureRuntimeValue(indices);
46791
47507
  let idxArray;
46792
- if (isRuntimeTensor(idxMv)) {
46793
- idxArray = Array.from(idxMv.data);
46794
- } else if (isRuntimeNumber(idxMv)) {
46795
- idxArray = [idxMv];
47508
+ if (indices === COLON_SENTINEL) {
47509
+ const n = mv.data.length;
47510
+ idxArray = [];
47511
+ for (let i = 1; i <= n; i++) idxArray.push(i);
46796
47512
  } else {
46797
- idxArray = [1];
47513
+ const idxMv = ensureRuntimeValue(indices);
47514
+ if (isRuntimeTensor(idxMv)) {
47515
+ idxArray = Array.from(idxMv.data);
47516
+ } else if (isRuntimeNumber(idxMv)) {
47517
+ idxArray = [idxMv];
47518
+ } else {
47519
+ idxArray = [1];
47520
+ }
46798
47521
  }
46799
47522
  for (let i = 0; i < idxArray.length && i < results.length; i++) {
46800
47523
  const idx = idxArray[i];
@@ -47021,6 +47744,8 @@ var Runtime = class _Runtime {
47021
47744
  classMethodCache = /* @__PURE__ */ new Map();
47022
47745
  /** Callback for addpath/rmpath — mutates search paths and rebuilds function index. */
47023
47746
  onPathChange = null;
47747
+ /** Callback invoked after cd() to update the implicit cwd search path. */
47748
+ onCwdChange = null;
47024
47749
  /** Reference to the active search paths (set by executeCode). */
47025
47750
  searchPaths = [];
47026
47751
  // Workspace accessors: varName → { get, set } closures over script-level vars
@@ -47300,23 +48025,26 @@ var Runtime = class _Runtime {
47300
48025
  if (e instanceof RuntimeError) {
47301
48026
  return RTV.struct(
47302
48027
  /* @__PURE__ */ new Map([
47303
- ["message", RTV.string(e.message)],
47304
- ["identifier", RTV.string(e.identifier)]
48028
+ ["message", RTV.char(e.message)],
48029
+ ["identifier", RTV.char(e.identifier)],
48030
+ ["stack", buildStackField(e)]
47305
48031
  ])
47306
48032
  );
47307
48033
  }
47308
48034
  if (e instanceof Error) {
47309
48035
  return RTV.struct(
47310
48036
  /* @__PURE__ */ new Map([
47311
- ["message", RTV.string(e.message)],
47312
- ["identifier", RTV.string("")]
48037
+ ["message", RTV.char(e.message)],
48038
+ ["identifier", RTV.char("")],
48039
+ ["stack", emptyStackField()]
47313
48040
  ])
47314
48041
  );
47315
48042
  }
47316
48043
  return RTV.struct(
47317
48044
  /* @__PURE__ */ new Map([
47318
- ["message", RTV.string(String(e))],
47319
- ["identifier", RTV.string("")]
48045
+ ["message", RTV.char(String(e))],
48046
+ ["identifier", RTV.char("")],
48047
+ ["stack", emptyStackField()]
47320
48048
  ])
47321
48049
  );
47322
48050
  }
@@ -48148,6 +48876,63 @@ var rstr = (s) => {
48148
48876
  if (isRuntimeChar(s)) return s.value;
48149
48877
  throw new RuntimeError(`Expected string or char, got ${kstr(s)}`);
48150
48878
  };
48879
+ function emptyStackField() {
48880
+ return RTV.structArray(["file", "name", "line"], []);
48881
+ }
48882
+ function buildStackField(e) {
48883
+ const fieldNames = ["file", "name", "line"];
48884
+ const frames = e.callStack;
48885
+ if (!frames || frames.length === 0) {
48886
+ if (e.file || e.line !== null) {
48887
+ return RTV.structArray(fieldNames, [
48888
+ RTV.struct(
48889
+ /* @__PURE__ */ new Map([
48890
+ ["file", RTV.char(e.file ?? "")],
48891
+ ["name", RTV.char("")],
48892
+ ["line", e.line ?? 0]
48893
+ ])
48894
+ )
48895
+ ]);
48896
+ }
48897
+ return emptyStackField();
48898
+ }
48899
+ const elements = [];
48900
+ const N = frames.length;
48901
+ for (let i = N - 1; i >= 0; i--) {
48902
+ let frameFile;
48903
+ let frameLine;
48904
+ if (i === N - 1) {
48905
+ frameFile = e.file;
48906
+ frameLine = e.line ?? 0;
48907
+ } else {
48908
+ const callerFrame = frames[i + 1];
48909
+ frameFile = callerFrame.callerFile;
48910
+ frameLine = callerFrame.callerLine;
48911
+ }
48912
+ elements.push(
48913
+ RTV.struct(
48914
+ /* @__PURE__ */ new Map([
48915
+ ["file", RTV.char(frameFile ?? "")],
48916
+ ["name", RTV.char(frames[i].name)],
48917
+ ["line", frameLine]
48918
+ ])
48919
+ )
48920
+ );
48921
+ }
48922
+ const outermost = frames[0];
48923
+ if (outermost.callerFile && outermost.callerLine > 0) {
48924
+ elements.push(
48925
+ RTV.struct(
48926
+ /* @__PURE__ */ new Map([
48927
+ ["file", RTV.char(outermost.callerFile)],
48928
+ ["name", RTV.char("")],
48929
+ ["line", outermost.callerLine]
48930
+ ])
48931
+ )
48932
+ );
48933
+ }
48934
+ return RTV.structArray(fieldNames, elements);
48935
+ }
48151
48936
 
48152
48937
  // src/numbl-core/jsUserFunctions.ts
48153
48938
  function funcNameFromFile(fileName) {
@@ -50541,6 +51326,9 @@ function execStmt(stmt) {
50541
51326
  case "ExprStmt": {
50542
51327
  const val = this.evalExprNargout(stmt.expr, 0);
50543
51328
  const singleVal = Array.isArray(val) ? val[0] : val;
51329
+ if (singleVal === void 0) {
51330
+ return null;
51331
+ }
50544
51332
  const rv = ensureRuntimeValue(singleVal);
50545
51333
  this.ans = rv;
50546
51334
  this.env.set("ans", rv);
@@ -50564,19 +51352,25 @@ function execStmt(stmt) {
50564
51352
  if (stmt.lvalues.length === 1 && stmt.lvalues[0].type === "IndexCell") {
50565
51353
  const lv = stmt.lvalues[0];
50566
51354
  const cellBase = lv.base.type === "Ident" ? this.env.get(lv.base.name) ?? RTV.cell([], [0, 0]) : this.evalExpr(lv.base);
50567
- const indices = lv.indices.map((idx) => this.evalExpr(idx));
50568
- const idxVal = ensureRuntimeValue(indices[0]);
51355
+ const indices = this.evalIndicesWithEnd(cellBase, lv.indices);
50569
51356
  let expandedCount = 1;
50570
- if (isRuntimeTensor(idxVal)) {
50571
- expandedCount = idxVal.data.length;
50572
- } else if (typeof idxVal === "number") {
50573
- expandedCount = 1;
51357
+ const idx0 = indices[0];
51358
+ if (idx0 === COLON_SENTINEL) {
51359
+ const baseRv = ensureRuntimeValue(cellBase);
51360
+ expandedCount = isRuntimeCell(baseRv) ? baseRv.data.length : 0;
51361
+ } else {
51362
+ const idxVal = ensureRuntimeValue(idx0);
51363
+ if (isRuntimeTensor(idxVal)) {
51364
+ expandedCount = idxVal.data.length;
51365
+ } else if (typeof idxVal === "number") {
51366
+ expandedCount = 1;
51367
+ }
50574
51368
  }
50575
51369
  const val2 = this.evalExprNargout(stmt.expr, expandedCount);
50576
51370
  const values2 = Array.isArray(val2) ? val2 : [val2];
50577
51371
  const result = this.rt.multiOutputCellAssign(
50578
51372
  cellBase,
50579
- indices[0],
51373
+ idx0,
50580
51374
  values2.map((v) => ensureRuntimeValue(v))
50581
51375
  );
50582
51376
  if (lv.base.type === "Ident") {
@@ -51138,7 +51932,11 @@ function evalAnonFunc(expr) {
51138
51932
  capturedMethodName,
51139
51933
  () => {
51140
51934
  try {
51141
- return this.evalExprNargout(bodyExpr, narg);
51935
+ const result = this.evalExprNargout(bodyExpr, narg);
51936
+ if (narg > 1 && !(Array.isArray(result) && result.length >= narg)) {
51937
+ throw new RuntimeError("Too many output arguments.");
51938
+ }
51939
+ return result;
51142
51940
  } finally {
51143
51941
  this.env = savedEnv;
51144
51942
  }
@@ -51618,6 +52416,17 @@ register("exist", (ctx, args) => {
51618
52416
  }
51619
52417
  return FALL_THROUGH;
51620
52418
  });
52419
+ register("which", (ctx, args) => {
52420
+ if (args.length < 1) return FALL_THROUGH;
52421
+ const nameArg = toString(ensureRuntimeValue(args[0]));
52422
+ if (ctx.env.has(nameArg)) return RTV.char("variable");
52423
+ const filePath = ctx.lookupWorkspaceFile(nameArg);
52424
+ if (filePath) return RTV.char(filePath);
52425
+ if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) {
52426
+ return RTV.char("built-in");
52427
+ }
52428
+ return RTV.char("");
52429
+ });
51621
52430
  register("isfolder", (ctx, args) => {
51622
52431
  if (args.length < 1) return FALL_THROUGH;
51623
52432
  const fio = ctx.rt.fileIO;
@@ -51720,6 +52529,9 @@ register("run", (ctx, args) => {
51720
52529
  } catch {
51721
52530
  throw new RuntimeError(`Cannot change directory to '${scriptDir}'`);
51722
52531
  }
52532
+ if (ctx.rt.onCwdChange) {
52533
+ ctx.rt.onCwdChange(sys.cwd());
52534
+ }
51723
52535
  }
51724
52536
  const cwdAfterCd = sys?.cwd() ?? "/";
51725
52537
  try {
@@ -51731,6 +52543,9 @@ register("run", (ctx, args) => {
51731
52543
  sys.chdir(oldCwd);
51732
52544
  } catch {
51733
52545
  }
52546
+ if (ctx.rt.onCwdChange) {
52547
+ ctx.rt.onCwdChange(sys.cwd());
52548
+ }
51734
52549
  }
51735
52550
  }
51736
52551
  return void 0;
@@ -51750,7 +52565,14 @@ function callFunction(name, args, nargout) {
51750
52565
  workspaceEnv: this.workspaceEnv,
51751
52566
  evalInLocalScope: (codeArg, fileName) => this.evalInLocalScope(codeArg, fileName),
51752
52567
  callFunction: (n, a, no) => this.callFunction(n, a, no),
51753
- rt: this.rt
52568
+ rt: this.rt,
52569
+ lookupWorkspaceFile: (n) => {
52570
+ const entry = this.ctx.registry.filesByFuncName.get(n);
52571
+ if (entry) return entry.fileName;
52572
+ const classInfo = this.ctx.getClassInfo(n);
52573
+ if (classInfo) return classInfo.fileName;
52574
+ return void 0;
52575
+ }
51754
52576
  };
51755
52577
  const result = specialHandler(ctx, args, nargout);
51756
52578
  if (result !== FALL_THROUGH) return result;
@@ -51784,13 +52606,18 @@ function interpretTarget(target, args, nargout) {
51784
52606
  const argTypes = margs.map(inferJitType);
51785
52607
  const resolution = ib.resolve(argTypes, nargout);
51786
52608
  if (resolution) {
52609
+ const isVoid = resolution.outputTypes.length === 0;
52610
+ if (isVoid && nargout > 0) {
52611
+ throw new RuntimeError("Too many output arguments.");
52612
+ }
51787
52613
  if (this.rt.profilingEnabled) {
51788
52614
  this.rt.profileEnter("builtin:interp:" + target.name);
51789
- const result = resolution.apply(margs, nargout);
52615
+ const result2 = resolution.apply(margs, nargout);
51790
52616
  this.rt.profileLeave();
51791
- return result;
52617
+ return isVoid ? void 0 : result2;
51792
52618
  }
51793
- return resolution.apply(margs, nargout);
52619
+ const result = resolution.apply(margs, nargout);
52620
+ return isVoid ? void 0 : result;
51794
52621
  }
51795
52622
  }
51796
52623
  const builtin = this.rt.builtins[target.name];
@@ -51882,7 +52709,19 @@ function interpretLocalFunction(target, args, nargout) {
51882
52709
  function interpretWorkspaceFunction(target, args, nargout) {
51883
52710
  const dotIdx = target.name.lastIndexOf(".");
51884
52711
  const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
51885
- const fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
52712
+ let fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
52713
+ if (!fn) {
52714
+ const entry = this.ctx.registry.filesByFuncName.get(target.name);
52715
+ if (entry) {
52716
+ const ast = this.ctx.getCachedAST(entry.fileName);
52717
+ for (const stmt of ast.body) {
52718
+ if (stmt.type === "Function") {
52719
+ fn = funcDefFromStmt(stmt);
52720
+ break;
52721
+ }
52722
+ }
52723
+ }
52724
+ }
51886
52725
  if (!fn) {
51887
52726
  const entry = this.ctx.registry.filesByFuncName.get(target.name);
51888
52727
  if (entry) {
@@ -52026,6 +52865,11 @@ function interpretConstructor(classInfo, args, nargout) {
52026
52865
  return args[0];
52027
52866
  }
52028
52867
  function callUserFunction(fn, args, nargout, narginOverride) {
52868
+ const hasVarargoutDecl = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
52869
+ const declaredRegularOutputs = hasVarargoutDecl ? fn.outputs.length - 1 : fn.outputs.length;
52870
+ if (!hasVarargoutDecl && nargout > declaredRegularOutputs) {
52871
+ throw new RuntimeError("Too many output arguments.");
52872
+ }
52029
52873
  if (this.optimization >= 1 && narginOverride === void 0) {
52030
52874
  const jitResult = tryJitCall(this, fn, args, nargout);
52031
52875
  if (jitResult !== JIT_SKIP) return jitResult;
@@ -52075,7 +52919,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
52075
52919
  }
52076
52920
  }
52077
52921
  }
52078
- const hasVarargout = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
52922
+ const hasVarargout = hasVarargoutDecl;
52079
52923
  const regularOutputs = hasVarargout ? fn.outputs.slice(0, -1) : fn.outputs;
52080
52924
  const collectCount = nargout === 0 && regularOutputs.length > 0 ? 1 : Math.min(regularOutputs.length, nargout);
52081
52925
  const outputs = [];
@@ -53680,6 +54524,31 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53680
54524
  }
53681
54525
  }
53682
54526
  }
54527
+ let implicitCwdPath = null;
54528
+ if (options.implicitCwdPath !== null && options.system && options.fileIO?.scanDirectory) {
54529
+ try {
54530
+ const cwd = options.implicitCwdPath ?? options.system.cwd();
54531
+ const absCwd = options.fileIO.resolvePath?.(cwd) ?? cwd;
54532
+ const explicitPaths = searchPaths ?? [];
54533
+ if (!explicitPaths.includes(absCwd)) {
54534
+ implicitCwdPath = absCwd;
54535
+ const prefix = absCwd.endsWith("/") ? absCwd : absCwd + "/";
54536
+ const alreadyHave = mWorkspaceFiles.some(
54537
+ (f) => f.name === absCwd || f.name.startsWith(prefix)
54538
+ );
54539
+ if (!alreadyHave) {
54540
+ const cwdFiles = options.fileIO.scanDirectory(absCwd);
54541
+ for (const f of cwdFiles) {
54542
+ if (f.name.endsWith(".m")) {
54543
+ mWorkspaceFiles.push(f);
54544
+ }
54545
+ }
54546
+ }
54547
+ }
54548
+ } catch {
54549
+ implicitCwdPath = null;
54550
+ }
54551
+ }
53683
54552
  const jsUserFunctions = loadJsUserFunctions(
53684
54553
  jsWorkspaceFiles,
53685
54554
  wasmWorkspaceFiles,
@@ -53697,6 +54566,9 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53697
54566
  stdlibShimNames.add(shimName);
53698
54567
  }
53699
54568
  ctx.registry.searchPaths = [...searchPaths ?? [], SHIM_SEARCH_PATH];
54569
+ if (implicitCwdPath !== null) {
54570
+ ctx.registry.searchPaths.unshift(implicitCwdPath);
54571
+ }
53700
54572
  ctx.fileASTCache.set(mainFileName, ast);
53701
54573
  const skippedFiles = /* @__PURE__ */ new Set();
53702
54574
  for (const f of mWorkspaceFiles) {
@@ -53730,6 +54602,11 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53730
54602
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
53731
54603
  }
53732
54604
  const functionIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
54605
+ const savedSpecialBuiltins = /* @__PURE__ */ new Map();
54606
+ for (const name of SPECIAL_BUILTIN_NAMES) {
54607
+ const existing = getIBuiltin(name);
54608
+ if (existing) savedSpecialBuiltins.set(name, existing);
54609
+ }
53733
54610
  const rt = new Runtime(options, options.initialVariableValues);
53734
54611
  const savedIBuiltins = /* @__PURE__ */ new Map();
53735
54612
  for (const ib of jsUserFunctions) {
@@ -53771,13 +54648,39 @@ ${jsCode}`
53771
54648
  interpreter.installRuntimeCallbacks();
53772
54649
  rt.searchPaths = ctx.registry.searchPaths;
53773
54650
  let pathsModified = false;
54651
+ const rebuildWorkspace = () => {
54652
+ const paths = ctx.registry.searchPaths;
54653
+ const priorityOf = (name) => {
54654
+ let bestIdx = paths.length;
54655
+ let bestLen = -1;
54656
+ for (let i = 0; i < paths.length; i++) {
54657
+ const p2 = paths[i];
54658
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
54659
+ if (name === p2 || name.startsWith(withSep)) {
54660
+ if (p2.length > bestLen) {
54661
+ bestLen = p2.length;
54662
+ bestIdx = i;
54663
+ }
54664
+ }
54665
+ }
54666
+ return bestIdx;
54667
+ };
54668
+ mWorkspaceFiles.sort((a, b) => priorityOf(a.name) - priorityOf(b.name));
54669
+ ctx.clearWorkspaceRegistrations();
54670
+ ctx.registerWorkspaceFiles(mWorkspaceFiles);
54671
+ const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
54672
+ interpreter.functionIndex = newIndex;
54673
+ interpreter.clearAllCaches();
54674
+ pathsModified = true;
54675
+ };
53774
54676
  rt.onPathChange = (action, dir, position) => {
53775
54677
  const fileIO = options.fileIO;
53776
54678
  const absDir = fileIO?.resolvePath?.(dir) ?? dir;
53777
54679
  if (action === "add") {
53778
54680
  if (ctx.registry.searchPaths.includes(absDir)) return;
53779
54681
  if (position === "begin") {
53780
- ctx.registry.searchPaths.unshift(absDir);
54682
+ const cwdIsFirst = implicitCwdPath !== null && ctx.registry.searchPaths[0] === implicitCwdPath;
54683
+ ctx.registry.searchPaths.splice(cwdIsFirst ? 1 : 0, 0, absDir);
53781
54684
  } else {
53782
54685
  const shimIdx = ctx.registry.searchPaths.indexOf(SHIM_SEARCH_PATH);
53783
54686
  if (shimIdx >= 0) {
@@ -53795,10 +54698,14 @@ ${jsCode}`
53795
54698
  try {
53796
54699
  ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
53797
54700
  } catch (e) {
53798
- if (e instanceof SyntaxError && e.file === null) {
53799
- e.file = f.name;
54701
+ if (e instanceof SyntaxError) {
54702
+ console.warn(
54703
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
54704
+ );
54705
+ } else {
54706
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
53800
54707
  }
53801
- throw e;
54708
+ continue;
53802
54709
  }
53803
54710
  interpreter.fileSources.set(f.name, f.source);
53804
54711
  mWorkspaceFiles.push(f);
@@ -53838,26 +54745,76 @@ ${jsCode}`
53838
54745
  }
53839
54746
  }
53840
54747
  }
53841
- const paths = ctx.registry.searchPaths;
53842
- mWorkspaceFiles.sort((a, b) => {
53843
- const ai = paths.findIndex(
53844
- (p2) => a.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || a.name === p2
53845
- );
53846
- const bi = paths.findIndex(
53847
- (p2) => b.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || b.name === p2
54748
+ rebuildWorkspace();
54749
+ };
54750
+ rt.onCwdChange = (newCwd) => {
54751
+ const fileIO = options.fileIO;
54752
+ const absNewCwd = fileIO?.resolvePath?.(newCwd) ?? newCwd;
54753
+ if (implicitCwdPath === absNewCwd) return;
54754
+ if (implicitCwdPath !== null) {
54755
+ const oldPrefix = implicitCwdPath.endsWith("/") ? implicitCwdPath : implicitCwdPath + "/";
54756
+ const deeperPaths = ctx.registry.searchPaths.filter(
54757
+ (p2) => p2 !== implicitCwdPath && p2 !== SHIM_SEARCH_PATH && (p2 === implicitCwdPath || p2.startsWith(oldPrefix))
53848
54758
  );
53849
- const aPri = ai >= 0 ? ai : paths.length;
53850
- const bPri = bi >= 0 ? bi : paths.length;
53851
- return aPri - bPri;
53852
- });
53853
- ctx.clearWorkspaceRegistrations();
53854
- ctx.registerWorkspaceFiles(mWorkspaceFiles);
53855
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
53856
- interpreter.functionIndex = newIndex;
53857
- interpreter.clearAllCaches();
53858
- pathsModified = true;
54759
+ const fileBelongsToDeeperPath = (fname) => {
54760
+ for (const p2 of deeperPaths) {
54761
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
54762
+ if (fname === p2 || fname.startsWith(withSep)) return true;
54763
+ }
54764
+ return false;
54765
+ };
54766
+ for (let i = mWorkspaceFiles.length - 1; i >= 0; i--) {
54767
+ const fname = mWorkspaceFiles[i].name;
54768
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
54769
+ ctx.fileASTCache.delete(fname);
54770
+ interpreter.fileSources.delete(fname);
54771
+ mWorkspaceFiles.splice(i, 1);
54772
+ }
54773
+ }
54774
+ const oldIdx = ctx.registry.searchPaths.indexOf(implicitCwdPath);
54775
+ if (oldIdx >= 0) ctx.registry.searchPaths.splice(oldIdx, 1);
54776
+ implicitCwdPath = null;
54777
+ }
54778
+ if (ctx.registry.searchPaths.includes(absNewCwd)) {
54779
+ rebuildWorkspace();
54780
+ return;
54781
+ }
54782
+ ctx.registry.searchPaths.unshift(absNewCwd);
54783
+ implicitCwdPath = absNewCwd;
54784
+ if (fileIO?.scanDirectory) {
54785
+ let newFiles = [];
54786
+ try {
54787
+ newFiles = fileIO.scanDirectory(absNewCwd);
54788
+ } catch {
54789
+ }
54790
+ for (const f of newFiles) {
54791
+ if (f.name.endsWith(".m") && !ctx.fileASTCache.has(f.name)) {
54792
+ try {
54793
+ ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
54794
+ } catch (e) {
54795
+ if (e instanceof SyntaxError) {
54796
+ console.warn(
54797
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
54798
+ );
54799
+ } else {
54800
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
54801
+ }
54802
+ continue;
54803
+ }
54804
+ interpreter.fileSources.set(f.name, f.source);
54805
+ mWorkspaceFiles.push(f);
54806
+ }
54807
+ }
54808
+ }
54809
+ rebuildWorkspace();
53859
54810
  };
53860
54811
  rt.evalLocalCallback = (code, initialVars, onOutput, fileName) => {
54812
+ const nestedSearchPaths = ctx.registry.searchPaths.filter(
54813
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
54814
+ );
54815
+ const nestedWorkspaceFiles = mWorkspaceFiles.filter(
54816
+ (f) => !stdlibShimNames.has(f.name)
54817
+ );
53861
54818
  const evalResult = executeCode(
53862
54819
  code,
53863
54820
  {
@@ -53865,11 +54822,18 @@ ${jsCode}`
53865
54822
  displayResults: false,
53866
54823
  initialVariableValues: initialVars,
53867
54824
  fileIO: options.fileIO,
53868
- onInput: options.onInput
54825
+ system: options.system,
54826
+ onInput: options.onInput,
54827
+ implicitCwdPath
53869
54828
  },
53870
- void 0,
53871
- fileName
54829
+ nestedWorkspaceFiles,
54830
+ fileName,
54831
+ nestedSearchPaths,
54832
+ nativeBridge
53872
54833
  );
54834
+ if (evalResult.implicitCwdPath !== void 0) {
54835
+ implicitCwdPath = evalResult.implicitCwdPath;
54836
+ }
53873
54837
  return {
53874
54838
  returnValue: evalResult.returnValue,
53875
54839
  variableValues: evalResult.variableValues,
@@ -53901,12 +54865,13 @@ ${jitSections.join("\n\n")}` : "// No JS generated",
53901
54865
  }
53902
54866
  if (pathsModified) {
53903
54867
  result.searchPaths = ctx.registry.searchPaths.filter(
53904
- (p2) => p2 !== SHIM_SEARCH_PATH
54868
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
53905
54869
  );
53906
54870
  result.workspaceFiles = mWorkspaceFiles.filter(
53907
54871
  (f) => !stdlibShimNames.has(f.name)
53908
54872
  );
53909
54873
  }
54874
+ result.implicitCwdPath = implicitCwdPath;
53910
54875
  return result;
53911
54876
  } catch (e) {
53912
54877
  const generatedJS = jitSections.length > 0 ? `// Interpreter mode \u2014 JIT compiled sections:
@@ -53942,6 +54907,9 @@ ${jitSections.join("\n\n")}` : "// No JS generated";
53942
54907
  unregisterIBuiltin(ib.name);
53943
54908
  }
53944
54909
  }
54910
+ for (const [, ib] of savedSpecialBuiltins) {
54911
+ registerDynamicIBuiltin(ib);
54912
+ }
53945
54913
  }
53946
54914
  }
53947
54915
  export {