numbl 0.1.2 → 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
@@ -18259,6 +18259,28 @@ function binaryNumberOnly(argTypes) {
18259
18259
  return null;
18260
18260
  return [{ kind: "number" }];
18261
18261
  }
18262
+ var nativeUnaryOpCode = /* @__PURE__ */ new Map([
18263
+ [Math.exp, 0],
18264
+ [Math.log, 1],
18265
+ [Math.log2, 2],
18266
+ [Math.log10, 3],
18267
+ [Math.sqrt, 4],
18268
+ [Math.abs, 5],
18269
+ [Math.floor, 6],
18270
+ [Math.ceil, 7],
18271
+ [Math.round, 8],
18272
+ [Math.trunc, 9],
18273
+ [Math.sin, 10],
18274
+ [Math.cos, 11],
18275
+ [Math.tan, 12],
18276
+ [Math.asin, 13],
18277
+ [Math.acos, 14],
18278
+ [Math.atan, 15],
18279
+ [Math.sinh, 16],
18280
+ [Math.cosh, 17],
18281
+ [Math.tanh, 18],
18282
+ [Math.sign, 19]
18283
+ ]);
18262
18284
  function applyUnaryElemwise(v, realFn, complexFn, name) {
18263
18285
  if (isRuntimeSparseMatrix(v))
18264
18286
  return applyUnaryElemwise(sparseToDense(v), realFn, complexFn, name);
@@ -18271,6 +18293,14 @@ function applyUnaryElemwise(v, realFn, complexFn, name) {
18271
18293
  if (isRuntimeTensor(v)) {
18272
18294
  const n = v.data.length;
18273
18295
  if (!v.imag) {
18296
+ const opCode = nativeUnaryOpCode.get(realFn);
18297
+ if (opCode !== void 0) {
18298
+ const bridge = getLapackBridge();
18299
+ if (bridge?.unaryElemwise && v.data instanceof Float64Array) {
18300
+ const result = bridge.unaryElemwise(v.data, opCode);
18301
+ return RTV.tensorRaw(result, v.shape.slice());
18302
+ }
18303
+ }
18274
18304
  const out = new FloatXArray(n);
18275
18305
  for (let i = 0; i < n; i++) out[i] = realFn(v.data[i]);
18276
18306
  return makeTensor(out, void 0, v.shape.slice());
@@ -18759,6 +18789,258 @@ function coerceToTensor(v, name) {
18759
18789
  throw new RuntimeError(`${name}: argument must be numeric`);
18760
18790
  }
18761
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
+
18762
19044
  // src/numbl-core/helpers/arithmetic.ts
18763
19045
  function toComplex(v) {
18764
19046
  if (isRuntimeComplexNumber(v)) return { re: v.re, im: v.im };
@@ -18934,6 +19216,18 @@ function tryNativeElemwiseReal(at, bt, opCode) {
18934
19216
  );
18935
19217
  return RTV.tensorRaw(result, at.shape);
18936
19218
  }
19219
+ function tryNativeElemwiseScalar(scalar, tensor, opCode, scalarOnLeft) {
19220
+ if (tensor.imag) return null;
19221
+ const bridge = getLapackBridge();
19222
+ if (!bridge?.elemwiseScalar) return null;
19223
+ const result = bridge.elemwiseScalar(
19224
+ scalar,
19225
+ tensor.data,
19226
+ opCode,
19227
+ scalarOnLeft
19228
+ );
19229
+ return RTV.tensorRaw(result, tensor.shape);
19230
+ }
18937
19231
  function tensorElemwiseComplex(at, bt, opCode, jsOp) {
18938
19232
  const bridge = getLapackBridge();
18939
19233
  if (bridge?.elemwiseComplex) {
@@ -18974,7 +19268,26 @@ function tensorElemwiseComplex(at, bt, opCode, jsOp) {
18974
19268
  const isReal = resultIm.every((x) => x === 0);
18975
19269
  return RTV.tensor(resultRe, at.shape, isReal ? void 0 : resultIm);
18976
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
+ }
18977
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
+ }
18978
19291
  const m = matchSameShapeTensors(a, b);
18979
19292
  if (m) {
18980
19293
  const [at, bt] = m;
@@ -19004,6 +19317,13 @@ function mAdd(a, b) {
19004
19317
  im: aIm + bIm
19005
19318
  }));
19006
19319
  }
19320
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
19321
+ const nr = tryNativeElemwiseScalar(a, b, ELEMWISE_ADD, true);
19322
+ if (nr) return nr;
19323
+ } else if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
19324
+ const nr = tryNativeElemwiseScalar(b, a, ELEMWISE_ADD, false);
19325
+ if (nr) return nr;
19326
+ }
19007
19327
  return binaryOp(a, b, (x, y) => x + y);
19008
19328
  }
19009
19329
  function mSub(a, b) {
@@ -19036,6 +19356,13 @@ function mSub(a, b) {
19036
19356
  im: aIm - bIm
19037
19357
  }));
19038
19358
  }
19359
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
19360
+ const nr = tryNativeElemwiseScalar(a, b, ELEMWISE_SUB, true);
19361
+ if (nr) return nr;
19362
+ } else if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
19363
+ const nr = tryNativeElemwiseScalar(b, a, ELEMWISE_SUB, false);
19364
+ if (nr) return nr;
19365
+ }
19039
19366
  return binaryOp(a, b, (x, y) => x - y);
19040
19367
  }
19041
19368
  function mMul(a, b) {
@@ -19052,6 +19379,13 @@ function mMul(a, b) {
19052
19379
  im: aRe * bIm + aIm * bRe
19053
19380
  }));
19054
19381
  }
19382
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
19383
+ const nr = tryNativeElemwiseScalar(a, b, ELEMWISE_MUL, true);
19384
+ if (nr) return nr;
19385
+ } else if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
19386
+ const nr = tryNativeElemwiseScalar(b, a, ELEMWISE_MUL, false);
19387
+ if (nr) return nr;
19388
+ }
19055
19389
  return binaryOp(a, b, (x, y) => x * y);
19056
19390
  }
19057
19391
  function mElemMul(a, b) {
@@ -19084,6 +19418,13 @@ function mElemMul(a, b) {
19084
19418
  im: aRe * bIm + aIm * bRe
19085
19419
  }));
19086
19420
  }
19421
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
19422
+ const nr = tryNativeElemwiseScalar(a, b, ELEMWISE_MUL, true);
19423
+ if (nr) return nr;
19424
+ } else if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
19425
+ const nr = tryNativeElemwiseScalar(b, a, ELEMWISE_MUL, false);
19426
+ if (nr) return nr;
19427
+ }
19087
19428
  return binaryOp(a, b, (x, y) => x * y);
19088
19429
  }
19089
19430
  function mDiv(a, b) {
@@ -19122,6 +19463,13 @@ function mElemDiv(a, b) {
19122
19463
  if (isComplexOrMixed(a, b)) {
19123
19464
  return complexBinaryOp(a, b, complexDivide);
19124
19465
  }
19466
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
19467
+ const nr = tryNativeElemwiseScalar(a, b, ELEMWISE_DIV, true);
19468
+ if (nr) return nr;
19469
+ } else if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
19470
+ const nr = tryNativeElemwiseScalar(b, a, ELEMWISE_DIV, false);
19471
+ if (nr) return nr;
19472
+ }
19125
19473
  return binaryOp(a, b, (x, y) => x / y);
19126
19474
  }
19127
19475
  function mLeftDiv(a, b) {
@@ -19834,6 +20182,7 @@ function extractTensorElement(base, i) {
19834
20182
  const im2 = base.imag[i];
19835
20183
  return im2 === 0 ? RTV.num(base.data[i]) : RTV.complex(base.data[i], im2);
19836
20184
  }
20185
+ if (base._isLogical === true) return base.data[i] !== 0;
19837
20186
  return RTV.num(base.data[i]);
19838
20187
  }
19839
20188
  function resolveIndex(idx, dimSize, boundsLimit = dimSize) {
@@ -19887,17 +20236,35 @@ function growTensor2D(base, newRows, newCols) {
19887
20236
  }
19888
20237
  function assignSlice(base, rowIndices, colIndices, rhs, curRows) {
19889
20238
  if (isRuntimeTensor(rhs)) {
20239
+ const nR = rowIndices.length;
20240
+ const nC = colIndices.length;
19890
20241
  const [rhsRows, rhsCols] = tensorSize2D(rhs);
19891
- 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)) {
19892
20247
  throw new RuntimeError("Subscripted assignment dimension mismatch");
19893
20248
  }
19894
20249
  if (rhs.imag || base.imag) ensureImag(base);
19895
- for (let ri = 0; ri < rowIndices.length; ri++) {
19896
- for (let ci = 0; ci < colIndices.length; ci++) {
19897
- const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
19898
- const srcLi = colMajorIndex(ri, ci, rhsRows);
19899
- base.data[dstLi] = rhs.data[srcLi];
19900
- 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
+ }
19901
20268
  }
19902
20269
  }
19903
20270
  } else {
@@ -20234,6 +20601,23 @@ function indexIntoScalar(base, indices) {
20234
20601
  return RTV.tensor(out, [1, count]);
20235
20602
  }
20236
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
+ }
20237
20621
  for (const idx of indices) {
20238
20622
  if (isColonIndex(idx)) continue;
20239
20623
  const i = toNumber(idx);
@@ -20245,6 +20629,22 @@ function indexIntoTensor(base, indices) {
20245
20629
  if (indices.length === 1) {
20246
20630
  return indexIntoTensor1D(base, indices[0]);
20247
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
+ }
20248
20648
  if (indices.length === 2) {
20249
20649
  return indexIntoTensor2D(base, indices[0], indices[1]);
20250
20650
  }
@@ -20273,6 +20673,11 @@ function indexIntoTensor1D(base, idx) {
20273
20673
  }
20274
20674
  function indexIntoTensor2D(base, rowIdx, colIdx) {
20275
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
+ };
20276
20681
  if (isRuntimeNumber(rowIdx) && isColonIndex(colIdx)) {
20277
20682
  const r = Math.round(rowIdx) - 1;
20278
20683
  if (r < 0 || r >= rows)
@@ -20283,7 +20688,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20283
20688
  resultData2[ci] = base.data[r + ci * rows];
20284
20689
  if (resultImag2 && base.imag) resultImag2[ci] = base.imag[r + ci * rows];
20285
20690
  }
20286
- return RTV.tensor(resultData2, [1, cols], resultImag2);
20691
+ return markLogical(RTV.tensor(resultData2, [1, cols], resultImag2));
20287
20692
  }
20288
20693
  if (isColonIndex(rowIdx) && isRuntimeNumber(colIdx)) {
20289
20694
  const c = Math.round(colIdx) - 1;
@@ -20295,7 +20700,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20295
20700
  const resultImag2 = base.imag ? new FloatXArray(rows) : void 0;
20296
20701
  if (resultImag2 && base.imag)
20297
20702
  for (let ri = 0; ri < rows; ri++) resultImag2[ri] = base.imag[offset + ri];
20298
- return RTV.tensor(resultData2, [rows, 1], resultImag2);
20703
+ return markLogical(RTV.tensor(resultData2, [rows, 1], resultImag2));
20299
20704
  }
20300
20705
  const rowIdxArr = resolveIndex(rowIdx, rows);
20301
20706
  const colIdxArr = resolveIndex(colIdx, cols);
@@ -20317,7 +20722,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20317
20722
  }
20318
20723
  }
20319
20724
  }
20320
- return RTV.tensor(resultData, [numR, numC], resultImag);
20725
+ return markLogical(RTV.tensor(resultData, [numR, numC], resultImag));
20321
20726
  }
20322
20727
  function indexIntoTensorND(base, indices) {
20323
20728
  const shape = base.shape;
@@ -20365,7 +20770,13 @@ function indexIntoTensorND(base, indices) {
20365
20770
  subs[d] = 0;
20366
20771
  }
20367
20772
  }
20368
- 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;
20369
20780
  }
20370
20781
  function indexIntoCell(base, indices) {
20371
20782
  if (indices.length === 1) {
@@ -20530,6 +20941,7 @@ function indexIntoLogical(base, indices) {
20530
20941
  return base;
20531
20942
  }
20532
20943
  function indexIntoTensorWithTensor(base, idx) {
20944
+ const baseLogical = base._isLogical === true;
20533
20945
  if (idx._isLogical) {
20534
20946
  const selected = [];
20535
20947
  const selectedIm = [];
@@ -20543,12 +20955,19 @@ function indexIntoTensorWithTensor(base, idx) {
20543
20955
  if (selected.length === 1) {
20544
20956
  if (hasImag2 && selectedIm[0] !== 0)
20545
20957
  return RTV.complex(selected[0], selectedIm[0]);
20958
+ if (baseLogical) return selected[0] !== 0;
20546
20959
  return RTV.num(selected[0]);
20547
20960
  }
20548
20961
  const imOut2 = hasImag2 && selectedIm.some((x) => x !== 0) ? new FloatXArray(selectedIm) : void 0;
20549
20962
  const isRow = base.shape.length === 2 && base.shape[0] === 1;
20550
20963
  const outShape2 = isRow ? [1, selected.length] : [selected.length, 1];
20551
- 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;
20552
20971
  }
20553
20972
  const resultData = [];
20554
20973
  const hasImag = base.imag !== void 0;
@@ -20564,7 +20983,13 @@ function indexIntoTensorWithTensor(base, idx) {
20564
20983
  const baseIsVector = base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
20565
20984
  const outShape = idxIs0x0 ? [0, 0] : baseIsVector ? base.shape[0] === 1 ? [1, resultData.length] : [resultData.length, 1] : idx.shape;
20566
20985
  const imOut = hasImag && imIndices.some((x) => x !== 0) ? new FloatXArray(imIndices) : void 0;
20567
- 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;
20568
20993
  }
20569
20994
  function indexIntoRTValue(base, indices) {
20570
20995
  if (isRuntimeNumber(base) || isRuntimeComplexNumber(base)) {
@@ -20914,10 +21339,18 @@ function storeIntoTensor2D(base, indices, rhs) {
20914
21339
  return storeIntoTensorColonCol(base, indices[0], rhs, rows, cols);
20915
21340
  }
20916
21341
  if (rowIsTensor || colIsTensor) {
20917
- const rowIndices = resolveIndex(indices[0], rows, 0);
20918
- const colIndices = resolveIndex(indices[1], cols, 0);
20919
- const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : rows;
20920
- 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;
20921
21354
  base = growTensor2D(base, maxRow, maxCol);
20922
21355
  const [curRows] = tensorSize2D(base);
20923
21356
  assignSlice(base, rowIndices, colIndices, rhs, curRows);
@@ -21345,6 +21778,9 @@ function horzcat(...values) {
21345
21778
  if (values.some((v) => isRuntimeCell(v))) {
21346
21779
  return cellCatAlongDim(values, 1);
21347
21780
  }
21781
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21782
+ return structCat(values);
21783
+ }
21348
21784
  return catAlongDim(values, 1);
21349
21785
  }
21350
21786
  function vertcat(...values) {
@@ -21356,8 +21792,47 @@ function vertcat(...values) {
21356
21792
  if (values.some((v) => isRuntimeCell(v))) {
21357
21793
  return cellCatAlongDim(values, 0);
21358
21794
  }
21795
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21796
+ return structCat(values);
21797
+ }
21359
21798
  return catAlongDim(values, 0);
21360
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
+ }
21361
21836
  function toSparseForCat(v) {
21362
21837
  if (isRuntimeSparseMatrix(v)) return v;
21363
21838
  if (isRuntimeNumber(v)) {
@@ -21965,7 +22440,7 @@ var token_config_default = {
21965
22440
  stripUnderscores: true,
21966
22441
  integerToken: "Integer",
21967
22442
  floatToken: "Float",
21968
- exponentChars: ["e", "E"],
22443
+ exponentChars: ["e", "E", "d", "D"],
21969
22444
  decimalPoint: ".",
21970
22445
  dotOperatorPrefixes: ["*", "/", "\\", "^"]
21971
22446
  },
@@ -22270,6 +22745,9 @@ function tokenizeDetailed(input) {
22270
22745
  if (numCfg.stripUnderscores) {
22271
22746
  lexeme = lexeme.replace(/_/g, "");
22272
22747
  }
22748
+ if (isFloat) {
22749
+ lexeme = lexeme.replace(/[dD]/g, "e");
22750
+ }
22273
22751
  const tok = isFloat ? Token[numCfg.floatToken] : Token[numCfg.integerToken];
22274
22752
  lineStart = false;
22275
22753
  out.push({ token: tok, lexeme, start, end: pos });
@@ -27430,224 +27908,88 @@ defineBuiltin({
27430
27908
  }
27431
27909
  });
27432
27910
 
27433
- // src/numbl-core/helpers/string.ts
27434
- function numStr(n) {
27435
- if (n === Infinity) return "Inf";
27436
- if (n === -Infinity) return "-Inf";
27437
- if (isNaN(n)) return "NaN";
27438
- if (n === 0) return "0";
27439
- const prec = 5;
27440
- const exp = Math.floor(Math.log10(Math.abs(n)));
27441
- let s;
27442
- if (exp < -4 || exp >= prec) {
27443
- s = n.toExponential(prec - 1);
27444
- const ePos = s.indexOf("e");
27445
- let mantissa = s.slice(0, ePos);
27446
- const expPart0 = s.slice(ePos);
27447
- if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
27448
- const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
27449
- s = mantissa + expPart;
27450
- } else {
27451
- if (Number.isInteger(n)) return String(n);
27452
- s = n.toPrecision(prec);
27453
- 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 }];
27454
27924
  }
27455
- return s;
27456
- }
27457
- function applyWidth(spec, str) {
27458
- const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
27459
- if (!m) return str;
27460
- const explicitFlags = m[1] || "";
27461
- const leftAlign = explicitFlags.includes("-");
27462
- const afterPercent = spec.slice(1);
27463
- const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
27464
- const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
27465
- const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
27466
- if (width <= str.length) return str;
27467
- const zeroPad = !leftAlign && zeroFlag;
27468
- const padLen = width - str.length;
27469
- if (leftAlign) return str + " ".repeat(padLen);
27470
- if (zeroPad) {
27471
- if (str[0] === "-" || str[0] === "+") {
27472
- return str[0] + "0".repeat(padLen) + str.slice(1);
27473
- }
27474
- 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 }];
27475
27927
  }
27476
- return " ".repeat(padLen) + str;
27928
+ return [{ kind: "boolean" }];
27477
27929
  }
27478
- function sprintfFormat(fmt, args) {
27479
- const flatArgs = [];
27480
- for (const arg of args) {
27481
- if (isRuntimeTensor(arg)) {
27482
- for (let k = 0; k < arg.data.length; k++) {
27483
- flatArgs.push(arg.data[k]);
27484
- }
27485
- } else {
27486
- flatArgs.push(arg);
27487
- }
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);
27488
27938
  }
27489
- let result = "";
27490
- let argIdx = 0;
27491
- do {
27492
- const startArgIdx = argIdx;
27493
- let outOfArgs = false;
27494
- let i = 0;
27495
- while (i < fmt.length && !outOfArgs) {
27496
- if (fmt[i] === "%" && i + 1 < fmt.length) {
27497
- i++;
27498
- let spec = "%";
27499
- while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
27500
- spec += fmt[i];
27501
- i++;
27502
- }
27503
- if (i < fmt.length) {
27504
- const ch = fmt[i];
27505
- i++;
27506
- if (ch === "%") {
27507
- result += "%";
27508
- } else if (argIdx >= flatArgs.length) {
27509
- outOfArgs = true;
27510
- } else if (ch === "d" || ch === "i" || ch === "u") {
27511
- const raw = toNumber(flatArgs[argIdx++]);
27512
- const isInt = Number.isInteger(raw);
27513
- const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
27514
- if (!canPrintAsInt) {
27515
- let eStr = raw.toExponential(6);
27516
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27517
- result += applyWidth(spec, eStr);
27518
- } else {
27519
- const n = raw;
27520
- const flags = spec.slice(1);
27521
- const hasPlus = flags.includes("+");
27522
- const leftAlign = flags.includes("-");
27523
- const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
27524
- const width = widthMatch ? parseInt(widthMatch[1]) : 0;
27525
- const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
27526
- const s = String(Math.abs(n));
27527
- const sign = n < 0 ? "-" : hasPlus ? "+" : "";
27528
- if (width > 0) {
27529
- const padChar = zeroPad ? "0" : " ";
27530
- const padLen = Math.max(0, width - sign.length - s.length);
27531
- const pad = padChar.repeat(padLen);
27532
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
27533
- } else {
27534
- result += sign + s;
27535
- }
27536
- }
27537
- } else if (ch === "f") {
27538
- const n = toNumber(flatArgs[argIdx++]);
27539
- if (!isFinite(n) || isNaN(n)) {
27540
- result += applyWidth(spec, numStr(n));
27541
- } else {
27542
- const fFlags = spec.slice(1);
27543
- const fHasPlus = fFlags.includes("+");
27544
- const precMatch = spec.match(/\.(\d+)/);
27545
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27546
- const formatted = n.toFixed(prec);
27547
- const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
27548
- result += applyWidth(spec, fSign + formatted);
27549
- }
27550
- } else if (ch === "e" || ch === "E") {
27551
- const n = toNumber(flatArgs[argIdx++]);
27552
- if (!isFinite(n) || isNaN(n)) {
27553
- result += applyWidth(spec, numStr(n));
27554
- } else {
27555
- const precMatch = spec.match(/\.(\d+)/);
27556
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27557
- let eStr = n.toExponential(prec);
27558
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27559
- if (ch === "E") eStr = eStr.toUpperCase();
27560
- result += applyWidth(spec, eStr);
27561
- }
27562
- } else if (ch === "x" || ch === "X") {
27563
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27564
- let s = Math.abs(n).toString(16);
27565
- if (ch === "X") s = s.toUpperCase();
27566
- result += applyWidth(spec, s);
27567
- } else if (ch === "o") {
27568
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27569
- result += applyWidth(spec, Math.abs(n).toString(8));
27570
- } else if (ch === "g" || ch === "G") {
27571
- const gVal = toNumber(flatArgs[argIdx++]);
27572
- if (!isFinite(gVal) || isNaN(gVal)) {
27573
- result += applyWidth(spec, numStr(gVal));
27574
- } else {
27575
- const precMatch = spec.match(/\.(\d+)/);
27576
- const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
27577
- let gStr;
27578
- if (gVal === 0) {
27579
- gStr = "0";
27580
- } else {
27581
- const exp = Math.floor(Math.log10(Math.abs(gVal)));
27582
- if (exp < -4 || exp >= gPrec) {
27583
- gStr = gVal.toExponential(gPrec - 1);
27584
- const ePos = gStr.indexOf("e");
27585
- let mantissa = gStr.slice(0, ePos);
27586
- let expPart = gStr.slice(ePos);
27587
- if (mantissa.includes(".")) {
27588
- mantissa = mantissa.replace(/\.?0+$/, "");
27589
- }
27590
- expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
27591
- gStr = mantissa + expPart;
27592
- } else {
27593
- gStr = gVal.toPrecision(gPrec);
27594
- if (gStr.includes(".")) {
27595
- gStr = gStr.replace(/\.?0+$/, "");
27596
- }
27597
- if (gStr.includes("e")) {
27598
- gStr = String(parseFloat(gStr));
27599
- }
27600
- }
27601
- }
27602
- if (ch === "G") gStr = gStr.toUpperCase();
27603
- result += applyWidth(spec, gStr);
27604
- }
27605
- } else if (ch === "s") {
27606
- const sVal = toString(flatArgs[argIdx++]);
27607
- const sFlags = spec.slice(1);
27608
- const sLeftAlign = sFlags.includes("-");
27609
- const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
27610
- const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
27611
- if (sWidth > sVal.length) {
27612
- const sPad = " ".repeat(sWidth - sVal.length);
27613
- result += sLeftAlign ? sVal + sPad : sPad + sVal;
27614
- } else {
27615
- result += sVal;
27616
- }
27617
- } else if (ch === "c") {
27618
- result += String.fromCharCode(
27619
- Math.round(toNumber(flatArgs[argIdx++]))
27620
- );
27621
- } else {
27622
- result += spec + ch;
27623
- argIdx++;
27624
- }
27625
- }
27626
- } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
27627
- i++;
27628
- switch (fmt[i]) {
27629
- case "n":
27630
- result += "\n";
27631
- break;
27632
- case "t":
27633
- result += " ";
27634
- break;
27635
- case "\\":
27636
- result += "\\";
27637
- break;
27638
- default:
27639
- 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 }];
27640
27986
  }
27641
- i++;
27642
- } else {
27643
- result += fmt[i];
27644
- i++;
27645
- }
27987
+ return [{ kind: "boolean" }];
27988
+ },
27989
+ apply: (args) => not(args[0])
27646
27990
  }
27647
- if (argIdx === startArgIdx) break;
27648
- } while (argIdx < flatArgs.length);
27649
- return result;
27650
- }
27991
+ ]
27992
+ });
27651
27993
 
27652
27994
  // src/numbl-core/interpreter/builtins/utility.ts
27653
27995
  function sparseToDense2(S) {
@@ -27757,7 +28099,7 @@ defineBuiltin({
27757
28099
  {
27758
28100
  match: (argTypes) => {
27759
28101
  if (argTypes.length < 1) return null;
27760
- return [{ kind: "number" }];
28102
+ return [];
27761
28103
  },
27762
28104
  apply: (args) => {
27763
28105
  const v = args[0];
@@ -27778,7 +28120,7 @@ defineBuiltin({
27778
28120
  const msg = args.length > 1 ? textValue(args[1]) ?? String(args[1]) : "Assertion failed";
27779
28121
  throw new Error(msg);
27780
28122
  }
27781
- return 0;
28123
+ return void 0;
27782
28124
  }
27783
28125
  }
27784
28126
  ]
@@ -27789,7 +28131,7 @@ defineBuiltin({
27789
28131
  {
27790
28132
  match: (argTypes) => {
27791
28133
  if (argTypes.length === 0) return null;
27792
- return [{ kind: "number" }];
28134
+ return [];
27793
28135
  },
27794
28136
  apply: (args) => {
27795
28137
  const first = textValue(args[0]) ?? String(args[0]);
@@ -28461,6 +28803,19 @@ defineBuiltin({
28461
28803
  name: "NaN",
28462
28804
  cases: arrayConstructorCases(nanFill, NaN)
28463
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
+ });
28464
28819
  defineBuiltin({
28465
28820
  name: "eye",
28466
28821
  cases: arrayConstructorCases(
@@ -29298,19 +29653,33 @@ function preserveTextType(t) {
29298
29653
  if (t.kind === "string") return { kind: "string" };
29299
29654
  return null;
29300
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
+ }
29301
29668
  function textPreserveResolve(fn) {
29302
29669
  return (argTypes) => {
29303
29670
  if (argTypes.length !== 1) return null;
29304
- 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);
29305
29679
  if (!out) return null;
29306
29680
  return {
29307
29681
  outputTypes: [out],
29308
- apply: (args) => {
29309
- const v = args[0];
29310
- const s = toString(v);
29311
- const result = fn(s);
29312
- return isRuntimeChar(v) ? RTV.char(result) : RTV.string(result);
29313
- }
29682
+ apply: (args) => applyTextFn(args[0], fn)
29314
29683
  };
29315
29684
  };
29316
29685
  }
@@ -31361,7 +31730,7 @@ defineBuiltin({
31361
31730
  cases: [
31362
31731
  {
31363
31732
  match: (argTypes, nargout) => {
31364
- if (nargout !== 1 || argTypes.length < 1 || argTypes.length > 2)
31733
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2)
31365
31734
  return null;
31366
31735
  if (!isNumericJitType(argTypes[0])) return null;
31367
31736
  return [NUM];
@@ -31478,7 +31847,7 @@ defineBuiltin({
31478
31847
  cases: [
31479
31848
  {
31480
31849
  match: (argTypes, nargout) => {
31481
- if (nargout !== 1 || argTypes.length < 1 || argTypes.length > 3)
31850
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 3)
31482
31851
  return null;
31483
31852
  if (!isNumericJitType(argTypes[0])) return null;
31484
31853
  const a = argTypes[0];
@@ -31557,7 +31926,7 @@ defineBuiltin({
31557
31926
  cases: [
31558
31927
  {
31559
31928
  match: (argTypes, nargout) => {
31560
- if (nargout !== 1 || argTypes.length !== 2) return null;
31929
+ if (nargout > 1 || argTypes.length !== 2) return null;
31561
31930
  if (!isNumericJitType(argTypes[0]) || !isNumericJitType(argTypes[1]))
31562
31931
  return null;
31563
31932
  const hasTensor = argTypes.some((t) => t.kind === "tensor");
@@ -31641,7 +32010,7 @@ defineBuiltin({
31641
32010
  cases: [
31642
32011
  {
31643
32012
  match: (argTypes, nargout) => {
31644
- if (argTypes.length !== 1 || nargout !== 1) return null;
32013
+ if (argTypes.length !== 1 || nargout > 1) return null;
31645
32014
  if (!isNumericJitType(argTypes[0])) return null;
31646
32015
  const hasComplex = argTypes[0].kind === "complex_or_number" || argTypes[0].kind === "tensor" && argTypes[0].isComplex;
31647
32016
  return [hasComplex ? COMPLEX_OR_NUM : NUM];
@@ -31753,7 +32122,7 @@ defineBuiltin({
31753
32122
  cases: [
31754
32123
  {
31755
32124
  match: (argTypes, nargout) => {
31756
- if (argTypes.length !== 1 || nargout !== 1) return null;
32125
+ if (argTypes.length !== 1 || nargout > 1) return null;
31757
32126
  if (!isNumericJitType(argTypes[0])) return null;
31758
32127
  return [NUM];
31759
32128
  },
@@ -31778,7 +32147,7 @@ defineBuiltin({
31778
32147
  cases: [
31779
32148
  {
31780
32149
  match: (argTypes, nargout) => {
31781
- if (nargout !== 1) return null;
32150
+ if (nargout > 1) return null;
31782
32151
  if (argTypes.length !== 2 && argTypes.length !== 3) return null;
31783
32152
  if (!isNumericJitType(argTypes[0]) || !isNumericJitType(argTypes[1]))
31784
32153
  return null;
@@ -31844,7 +32213,7 @@ defineBuiltin({
31844
32213
  cases: [
31845
32214
  {
31846
32215
  match: (argTypes, nargout) => {
31847
- if (argTypes.length !== 1 || nargout !== 1) return null;
32216
+ if (argTypes.length !== 1 || nargout > 1) return null;
31848
32217
  if (!isNumericJitType(argTypes[0])) return null;
31849
32218
  const a = argTypes[0];
31850
32219
  if (a.kind === "number" || a.kind === "boolean") return [NUM];
@@ -31957,11 +32326,11 @@ function invComplexJS(dataRe, dataIm, n) {
31957
32326
  registerIBuiltin({
31958
32327
  name: "svd",
31959
32328
  resolve: (argTypes, nargout) => {
31960
- if (nargout < 1 || nargout > 3 || argTypes.length < 1 || argTypes.length > 2)
32329
+ if (nargout < 0 || nargout > 3 || argTypes.length < 1 || argTypes.length > 2)
31961
32330
  return null;
31962
32331
  if (!isNumericJitType(argTypes[0])) return null;
31963
32332
  const c = tensorType();
31964
- if (nargout === 1)
32333
+ if (nargout <= 1)
31965
32334
  return { outputTypes: [c], apply: (args, n) => svdApply(args, n) };
31966
32335
  return { outputTypes: [c, c, c], apply: (args, n) => svdApply(args, n) };
31967
32336
  }
@@ -31972,7 +32341,7 @@ function svdApply(args, nargout) {
31972
32341
  const A = args[0];
31973
32342
  if (isRuntimeNumber(A)) {
31974
32343
  const val = Math.abs(A);
31975
- if (nargout === 1) return RTV.tensor(new FloatXArray([val]), [1, 1]);
32344
+ if (nargout <= 1) return RTV.tensor(new FloatXArray([val]), [1, 1]);
31976
32345
  return [
31977
32346
  RTV.tensor(new FloatXArray([A >= 0 ? 1 : -1]), [1, 1]),
31978
32347
  RTV.tensor(new FloatXArray([val]), [1, 1]),
@@ -31999,7 +32368,7 @@ function svdApply(args, nargout) {
31999
32368
  nargout === 3
32000
32369
  );
32001
32370
  if (!result) throw new RuntimeError("svd: complex SVD failed");
32002
- if (nargout === 1) return RTV.tensor(new FloatXArray(result.S), [k, 1]);
32371
+ if (nargout <= 1) return RTV.tensor(new FloatXArray(result.S), [k, 1]);
32003
32372
  const uCols = econ ? k : m;
32004
32373
  const vCols = econ ? k : n;
32005
32374
  return [
@@ -32020,7 +32389,7 @@ function svdApply(args, nargout) {
32020
32389
  if (bridge?.svd) {
32021
32390
  const result = bridge.svd(toF64(A.data), m, n, econ, nargout === 3);
32022
32391
  if (result) {
32023
- if (nargout === 1) return RTV.tensor(new FloatXArray(result.S), [k, 1]);
32392
+ if (nargout <= 1) return RTV.tensor(new FloatXArray(result.S), [k, 1]);
32024
32393
  const uCols = econ ? k : m;
32025
32394
  const vCols = econ ? k : n;
32026
32395
  return [
@@ -32090,12 +32459,12 @@ function powerIterationEigenvalues(A_data, n, numEigenvalues) {
32090
32459
  registerIBuiltin({
32091
32460
  name: "qr",
32092
32461
  resolve: (argTypes, nargout) => {
32093
- if (nargout < 1 || nargout > 3 || argTypes.length < 1 || argTypes.length > 2)
32462
+ if (nargout < 0 || nargout > 3 || argTypes.length < 1 || argTypes.length > 2)
32094
32463
  return null;
32095
32464
  if (!isNumericJitType(argTypes[0])) return null;
32096
32465
  const isComplex2 = argTypes[0].kind === "tensor" && argTypes[0].isComplex;
32097
32466
  const t = tensorType(isComplex2 || void 0);
32098
- if (nargout === 1)
32467
+ if (nargout <= 1)
32099
32468
  return { outputTypes: [t], apply: (args, n) => qrApply(args, n) };
32100
32469
  if (nargout === 3)
32101
32470
  return {
@@ -32112,7 +32481,7 @@ function qrApply(args, nargout) {
32112
32481
  if (isRuntimeNumber(A)) {
32113
32482
  const val = A;
32114
32483
  const s = val >= 0 ? 1 : -1;
32115
- if (nargout === 1) return RTV.tensor(new FloatXArray([s * val]), [1, 1]);
32484
+ if (nargout <= 1) return RTV.tensor(new FloatXArray([s * val]), [1, 1]);
32116
32485
  if (nargout === 3)
32117
32486
  return [
32118
32487
  RTV.tensor(new FloatXArray([s]), [1, 1]),
@@ -32145,7 +32514,7 @@ function qrApply(args, nargout) {
32145
32514
  nargout === 2
32146
32515
  );
32147
32516
  if (!result) throw new RuntimeError("qr: complex QR failed");
32148
- if (nargout === 1) {
32517
+ if (nargout <= 1) {
32149
32518
  const rRows = econ ? k : m;
32150
32519
  return RTV.tensor(
32151
32520
  new FloatXArray(result.RRe),
@@ -32171,7 +32540,7 @@ function qrApply(args, nargout) {
32171
32540
  if (bridge?.qr) {
32172
32541
  const result = bridge.qr(toF64(A.data), m, n, econ, nargout === 2);
32173
32542
  if (result) {
32174
- if (nargout === 1)
32543
+ if (nargout <= 1)
32175
32544
  return RTV.tensor(new FloatXArray(result.R), [econ ? k : m, n]);
32176
32545
  const qCols = econ ? k : m;
32177
32546
  return [
@@ -32214,7 +32583,7 @@ function qrApply(args, nargout) {
32214
32583
  R_data[colMajorIndex(j + i, c, m)] -= scale * v[i];
32215
32584
  }
32216
32585
  }
32217
- if (nargout === 1) {
32586
+ if (nargout <= 1) {
32218
32587
  if (econ) {
32219
32588
  const R_econ = new FloatXArray(k * n);
32220
32589
  for (let r = 0; r < k; r++)
@@ -32430,11 +32799,11 @@ function qrPivotApply(A, m, n, k, econ) {
32430
32799
  registerIBuiltin({
32431
32800
  name: "lu",
32432
32801
  resolve: (argTypes, nargout) => {
32433
- if (nargout < 1 || nargout > 3) return null;
32802
+ if (nargout < 0 || nargout > 3) return null;
32434
32803
  if (argTypes.length < 1 || argTypes.length > 2) return null;
32435
32804
  if (!isNumericJitType(argTypes[0])) return null;
32436
32805
  const t = tensorType();
32437
- if (nargout === 1)
32806
+ if (nargout <= 1)
32438
32807
  return { outputTypes: [t], apply: (args, n) => luApply(args, n) };
32439
32808
  if (nargout === 2)
32440
32809
  return { outputTypes: [t, t], apply: (args, n) => luApply(args, n) };
@@ -32453,7 +32822,7 @@ function luApply(args, nargout) {
32453
32822
  if (nargout <= 2) {
32454
32823
  const L2 = RTV.tensor(new FloatXArray([1]), [1, 1]);
32455
32824
  const U2 = RTV.tensor(new FloatXArray([val]), [1, 1]);
32456
- if (nargout === 1) return L2;
32825
+ if (nargout <= 1) return L2;
32457
32826
  return [L2, U2];
32458
32827
  }
32459
32828
  const L = RTV.tensor(new FloatXArray([1]), [1, 1]);
@@ -32491,7 +32860,7 @@ function luApply(args, nargout) {
32491
32860
  if (U_im && LU_im) U_im[i + j * k] = LU_im[i + j * m];
32492
32861
  }
32493
32862
  }
32494
- if (nargout === 1)
32863
+ if (nargout <= 1)
32495
32864
  return RTV.tensor(
32496
32865
  new FloatXArray(LU_re),
32497
32866
  [m, n],
@@ -32567,11 +32936,11 @@ function ipivToPermVector(ipiv, m) {
32567
32936
  registerIBuiltin({
32568
32937
  name: "eig",
32569
32938
  resolve: (argTypes, nargout) => {
32570
- if (nargout < 1 || nargout > 3 || argTypes.length < 1 || argTypes.length > 3)
32939
+ if (nargout < 0 || nargout > 3 || argTypes.length < 1 || argTypes.length > 3)
32571
32940
  return null;
32572
32941
  if (!isNumericJitType(argTypes[0])) return null;
32573
32942
  const c = tensorType(true);
32574
- if (nargout === 1)
32943
+ if (nargout <= 1)
32575
32944
  return { outputTypes: [c], apply: (args, n) => eigApply(args, n) };
32576
32945
  if (nargout === 2)
32577
32946
  return { outputTypes: [c, c], apply: (args, n) => eigApply(args, n) };
@@ -32585,7 +32954,7 @@ function eigApply(args, nargout) {
32585
32954
  const { balance, outputForm } = parseEigOptionsRuntime(args);
32586
32955
  if (isRuntimeNumber(A)) {
32587
32956
  const val = A;
32588
- if (nargout === 1) return RTV.num(val);
32957
+ if (nargout <= 1) return RTV.num(val);
32589
32958
  const V = RTV.tensor(new FloatXArray([1]), [1, 1]);
32590
32959
  if (nargout === 2) {
32591
32960
  if (outputForm === "vector") return [V, RTV.num(val)];
@@ -32614,7 +32983,7 @@ function eigApply(args, nargout) {
32614
32983
  );
32615
32984
  if (!result2) throw new RuntimeError("eig: complex eig failed");
32616
32985
  const { wRe, wIm, VLRe, VLIm, VRRe, VRIm } = result2;
32617
- if (nargout === 1) return maybeComplexTensor(wRe, [n, 1], wIm);
32986
+ if (nargout <= 1) return maybeComplexTensor(wRe, [n, 1], wIm);
32618
32987
  const Vout2 = computeVR && VRRe && VRIm ? maybeComplexTensor(VRRe, [n, n], VRIm) : RTV.tensor(new FloatXArray(n * n), [n, n]);
32619
32988
  const Dout2 = outputForm === "vector" ? maybeComplexTensor(wRe, [n, 1], wIm) : buildDiagMatrix(wRe, wIm, n);
32620
32989
  if (nargout === 2) return [Vout2, Dout2];
@@ -32627,7 +32996,7 @@ function eigApply(args, nargout) {
32627
32996
  if (!result) throw new RuntimeError("eig: LAPACK eig failed");
32628
32997
  const { wr, wi, VL, VR } = result;
32629
32998
  const hasComplex = wi.some((v) => v !== 0);
32630
- if (nargout === 1) return maybeComplexTensor(wr, [n, 1], wi);
32999
+ if (nargout <= 1) return maybeComplexTensor(wr, [n, 1], wi);
32631
33000
  const Vout = computeVR && VR ? buildEigenvectorMatrix(VR, wi, n, hasComplex) : RTV.tensor(new FloatXArray(n * n), [n, n]);
32632
33001
  const Dout = outputForm === "vector" ? maybeComplexTensor(wr, [n, 1], wi) : buildDiagMatrix(wr, wi, n);
32633
33002
  if (nargout === 2) return [Vout, Dout];
@@ -32651,11 +33020,11 @@ function parseEigOptionsRuntime(args) {
32651
33020
  registerIBuiltin({
32652
33021
  name: "chol",
32653
33022
  resolve: (argTypes, nargout) => {
32654
- if (nargout < 1 || nargout > 3 || argTypes.length < 1 || argTypes.length > 3)
33023
+ if (nargout < 0 || nargout > 3 || argTypes.length < 1 || argTypes.length > 3)
32655
33024
  return null;
32656
33025
  if (!isNumericJitType(argTypes[0])) return null;
32657
33026
  const t = tensorType();
32658
- if (nargout === 1)
33027
+ if (nargout <= 1)
32659
33028
  return { outputTypes: [t], apply: (args, n) => cholApply(args, n) };
32660
33029
  if (nargout === 2)
32661
33030
  return { outputTypes: [t, NUM], apply: (args, n) => cholApply(args, n) };
@@ -32816,7 +33185,7 @@ defineBuiltin({
32816
33185
  cases: [
32817
33186
  {
32818
33187
  match: (argTypes, nargout) => {
32819
- if (argTypes.length !== 2 || nargout !== 1) return null;
33188
+ if (argTypes.length !== 2 || nargout > 1) return null;
32820
33189
  if (!isNumericJitType(argTypes[0]) || !isNumericJitType(argTypes[1]))
32821
33190
  return null;
32822
33191
  return [tensorType()];
@@ -32859,7 +33228,7 @@ defineBuiltin({
32859
33228
  cases: [
32860
33229
  {
32861
33230
  match: (argTypes, nargout) => {
32862
- if (nargout !== 1 || argTypes.length < 1 || argTypes.length > 2)
33231
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2)
32863
33232
  return null;
32864
33233
  if (!isNumericJitType(argTypes[0])) return null;
32865
33234
  return [NUM];
@@ -32920,7 +33289,7 @@ defineBuiltin({
32920
33289
  cases: [
32921
33290
  {
32922
33291
  match: (argTypes, nargout) => {
32923
- if (nargout !== 1 || argTypes.length < 1 || argTypes.length > 2)
33292
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2)
32924
33293
  return null;
32925
33294
  if (!isNumericJitType(argTypes[0])) return null;
32926
33295
  return [NUM];
@@ -32968,7 +33337,7 @@ defineBuiltin({
32968
33337
  cases: [
32969
33338
  {
32970
33339
  match: (argTypes, nargout) => {
32971
- if (nargout !== 1 || argTypes.length < 1 || argTypes.length > 2)
33340
+ if (nargout > 1 || argTypes.length < 1 || argTypes.length > 2)
32972
33341
  return null;
32973
33342
  if (!isNumericJitType(argTypes[0])) return null;
32974
33343
  const a = argTypes[0];
@@ -33065,7 +33434,7 @@ defineBuiltin({
33065
33434
  cases: [
33066
33435
  {
33067
33436
  match: (argTypes, nargout) => {
33068
- if (nargout !== 1 || argTypes.length !== 2) return null;
33437
+ if (nargout > 1 || argTypes.length !== 2) return null;
33069
33438
  if (!isNumericJitType(argTypes[0]) || !isNumericJitType(argTypes[1]))
33070
33439
  return null;
33071
33440
  return [tensorType()];
@@ -33073,11 +33442,15 @@ defineBuiltin({
33073
33442
  apply: (args) => {
33074
33443
  if (args.length !== 2)
33075
33444
  throw new RuntimeError("kron requires 2 arguments");
33076
- const a = args[0], b = args[1];
33077
- const A = isRuntimeNumber(a) ? RTV.tensor(new FloatXArray([a]), [1, 1]) : a;
33078
- const B = isRuntimeNumber(b) ? RTV.tensor(new FloatXArray([b]), [1, 1]) : b;
33079
- 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;
33080
33450
  throw new RuntimeError("kron: arguments must be numeric");
33451
+ };
33452
+ const A = coerce(args[0]);
33453
+ const B = coerce(args[1]);
33081
33454
  const [m, n] = tensorSize2D(A);
33082
33455
  const [p2, q] = tensorSize2D(B);
33083
33456
  const rows = m * p2, cols = n * q;
@@ -33101,7 +33474,7 @@ defineBuiltin({
33101
33474
  cases: [
33102
33475
  {
33103
33476
  match: (argTypes, nargout) => {
33104
- if (nargout !== 1 || argTypes.length === 0) return null;
33477
+ if (nargout > 1 || argTypes.length === 0) return null;
33105
33478
  if (!argTypes.every(isNumericJitType)) return null;
33106
33479
  return [tensorType()];
33107
33480
  },
@@ -33144,7 +33517,7 @@ defineBuiltin({
33144
33517
  cases: [
33145
33518
  {
33146
33519
  match: (argTypes, nargout) => {
33147
- if (nargout !== 1) return null;
33520
+ if (nargout > 1) return null;
33148
33521
  if (argTypes.length !== 2 && argTypes.length !== 4) return null;
33149
33522
  const xIdx = 0, yIdx = argTypes.length === 4 ? 2 : 1;
33150
33523
  if (!isNumericJitType(argTypes[xIdx]) || !isNumericJitType(argTypes[yIdx]))
@@ -33273,7 +33646,7 @@ defineBuiltin({
33273
33646
  cases: [
33274
33647
  {
33275
33648
  match: (argTypes, nargout) => {
33276
- if (nargout !== 1 || argTypes.length !== 1) return null;
33649
+ if (nargout > 1 || argTypes.length !== 1) return null;
33277
33650
  if (argTypes[0].kind !== "tensor") return null;
33278
33651
  return [tensorType(argTypes[0].isComplex)];
33279
33652
  },
@@ -35395,6 +35768,65 @@ defineBuiltin({
35395
35768
  }
35396
35769
  ]
35397
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
+ });
35398
35830
  function coordTransform(name, nArgs, nOut, fn) {
35399
35831
  defineBuiltin({
35400
35832
  name,
@@ -36524,6 +36956,131 @@ defineBuiltin({
36524
36956
  }
36525
36957
  ]
36526
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
+ });
36527
37084
  defineBuiltin({
36528
37085
  name: "logical",
36529
37086
  cases: [
@@ -37135,9 +37692,11 @@ registerIBuiltin({
37135
37692
  // src/numbl-core/helpers/prng.ts
37136
37693
  var _rngState = null;
37137
37694
  var _rngSeed = 0;
37695
+ var _bmSpare = null;
37138
37696
  function setRngShuffle() {
37139
37697
  _rngState = null;
37140
37698
  _rngSeed = 0;
37699
+ _bmSpare = null;
37141
37700
  }
37142
37701
  function setRngSeed(seed) {
37143
37702
  _rngSeed = seed;
@@ -37153,6 +37712,7 @@ function splitmix32(seed) {
37153
37712
  };
37154
37713
  }
37155
37714
  function seedRng(seed) {
37715
+ _bmSpare = null;
37156
37716
  const sm = splitmix32(seed);
37157
37717
  _rngState = new Uint32Array([sm(), sm(), sm(), sm()]);
37158
37718
  if (_rngState[0] === 0 && _rngState[1] === 0 && _rngState[2] === 0 && _rngState[3] === 0) {
@@ -37179,10 +37739,60 @@ function rngRandom() {
37179
37739
  return (xoshiro128ss() >>> 0) / 4294967296;
37180
37740
  }
37181
37741
  function boxMullerRandom() {
37182
- let u = 0, v = 0;
37183
- while (u === 0) u = rngRandom();
37184
- while (v === 0) v = rngRandom();
37185
- return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
37742
+ if (_bmSpare !== null) {
37743
+ const s2 = _bmSpare;
37744
+ _bmSpare = null;
37745
+ return s2;
37746
+ }
37747
+ let u, v, s;
37748
+ do {
37749
+ u = 2 * rngRandom() - 1;
37750
+ v = 2 * rngRandom() - 1;
37751
+ s = u * u + v * v;
37752
+ } while (s >= 1 || s === 0);
37753
+ const mul = Math.sqrt(-2 * Math.log(s) / s);
37754
+ _bmSpare = v * mul;
37755
+ return u * mul;
37756
+ }
37757
+ function fillRandn(data) {
37758
+ if (_rngState !== null) {
37759
+ const bridge = getLapackBridge();
37760
+ if (bridge?.fillRandn) {
37761
+ const r = bridge.fillRandn(
37762
+ _rngState,
37763
+ data.length,
37764
+ _bmSpare ?? 0,
37765
+ _bmSpare !== null
37766
+ );
37767
+ _bmSpare = r.hasSpare ? r.spare : null;
37768
+ if (data instanceof Float64Array) {
37769
+ data.set(r.data);
37770
+ } else {
37771
+ for (let i2 = 0; i2 < data.length; i2++) data[i2] = r.data[i2];
37772
+ }
37773
+ return;
37774
+ }
37775
+ }
37776
+ const n = data.length;
37777
+ let i = 0;
37778
+ if (_bmSpare !== null && i < n) {
37779
+ data[i++] = _bmSpare;
37780
+ _bmSpare = null;
37781
+ }
37782
+ for (; i + 1 < n; i += 2) {
37783
+ let u, v, s;
37784
+ do {
37785
+ u = 2 * rngRandom() - 1;
37786
+ v = 2 * rngRandom() - 1;
37787
+ s = u * u + v * v;
37788
+ } while (s >= 1 || s === 0);
37789
+ const mul = Math.sqrt(-2 * Math.log(s) / s);
37790
+ data[i] = u * mul;
37791
+ data[i + 1] = v * mul;
37792
+ }
37793
+ if (i < n) {
37794
+ data[i] = boxMullerRandom();
37795
+ }
37186
37796
  }
37187
37797
  function getRngStateStruct() {
37188
37798
  const stateArray = _rngState ? RTV.tensor(new FloatXArray(Array.from(_rngState).map((v) => v)), [4, 1]) : RTV.tensor(new FloatXArray(0), [0, 1]);
@@ -37250,7 +37860,7 @@ registerIBuiltin({
37250
37860
  };
37251
37861
  }
37252
37862
  });
37253
- function registerRandBuiltin(name, gen) {
37863
+ function registerRandBuiltin(name, gen, bulkFill) {
37254
37864
  defineBuiltin({
37255
37865
  name,
37256
37866
  cases: [
@@ -37289,7 +37899,11 @@ function registerRandBuiltin(name, gen) {
37289
37899
  if (shape.length === 1) shape.push(shape[0]);
37290
37900
  const n = numel(shape);
37291
37901
  const data = new FloatXArray(n);
37292
- for (let i = 0; i < n; i++) data[i] = gen();
37902
+ if (bulkFill) {
37903
+ bulkFill(data);
37904
+ } else {
37905
+ for (let i = 0; i < n; i++) data[i] = gen();
37906
+ }
37293
37907
  return RTV.tensor(data, shape);
37294
37908
  }
37295
37909
  }
@@ -37297,7 +37911,7 @@ function registerRandBuiltin(name, gen) {
37297
37911
  });
37298
37912
  }
37299
37913
  registerRandBuiltin("rand", rngRandom);
37300
- registerRandBuiltin("randn", boxMullerRandom);
37914
+ registerRandBuiltin("randn", boxMullerRandom, fillRandn);
37301
37915
  defineBuiltin({
37302
37916
  name: "randi",
37303
37917
  cases: [
@@ -37744,6 +38358,9 @@ registerIBuiltin({
37744
38358
 
37745
38359
  // src/numbl-core/interpreter/builtins/time-system.ts
37746
38360
  var ticTime = 0;
38361
+ function getTicTime() {
38362
+ return ticTime;
38363
+ }
37747
38364
  defineBuiltin({
37748
38365
  name: "tic",
37749
38366
  cases: [
@@ -37756,15 +38373,6 @@ defineBuiltin({
37756
38373
  }
37757
38374
  ]
37758
38375
  });
37759
- defineBuiltin({
37760
- name: "toc",
37761
- cases: [
37762
- {
37763
- match: (argTypes) => argTypes.length === 0 ? [{ kind: "number" }] : null,
37764
- apply: () => RTV.num((performance.now() - ticTime) / 1e3)
37765
- }
37766
- ]
37767
- });
37768
38376
  defineBuiltin({
37769
38377
  name: "clock",
37770
38378
  cases: [
@@ -37855,6 +38463,24 @@ registerIBuiltin({
37855
38463
  }
37856
38464
  })
37857
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
+ });
37858
38484
  var _platform = typeof process !== "undefined" ? process.platform : "linux";
37859
38485
  for (const [name, val] of [
37860
38486
  ["ismac", _platform === "darwin"],
@@ -38753,6 +39379,44 @@ registerIBuiltin({
38753
39379
  }
38754
39380
  })
38755
39381
  });
39382
+ var WEBOPTIONS_DEFAULTS = {
39383
+ CharacterEncoding: RTV.char("auto"),
39384
+ UserAgent: RTV.char("numbl"),
39385
+ Timeout: 5,
39386
+ Username: RTV.char(""),
39387
+ Password: RTV.char(""),
39388
+ KeyName: RTV.char(""),
39389
+ KeyValue: RTV.char(""),
39390
+ ContentType: RTV.char("auto"),
39391
+ MediaType: RTV.char("auto"),
39392
+ RequestMethod: RTV.char("auto"),
39393
+ ArrayFormat: RTV.char("csv"),
39394
+ CertificateFilename: RTV.char("default")
39395
+ };
39396
+ registerIBuiltin({
39397
+ name: "weboptions",
39398
+ resolve: () => ({
39399
+ outputTypes: [{ kind: "struct" }],
39400
+ apply: (args) => {
39401
+ const fields = /* @__PURE__ */ new Map();
39402
+ for (const [k, v] of Object.entries(WEBOPTIONS_DEFAULTS)) {
39403
+ fields.set(k, v);
39404
+ }
39405
+ if (args.length % 2 !== 0)
39406
+ throw new RuntimeError("weboptions: expected name-value pairs");
39407
+ for (let i = 0; i < args.length; i += 2) {
39408
+ const key = args[i];
39409
+ if (!isRuntimeChar(key) && !isRuntimeString(key))
39410
+ throw new RuntimeError("weboptions: option name must be a string");
39411
+ const name = isRuntimeChar(key) ? key.value : key;
39412
+ if (!(name in WEBOPTIONS_DEFAULTS))
39413
+ throw new RuntimeError(`weboptions: unknown option '${name}'`);
39414
+ fields.set(name, args[i + 1]);
39415
+ }
39416
+ return RTV.struct(Object.fromEntries(fields));
39417
+ }
39418
+ })
39419
+ });
38756
39420
  registerIBuiltin({
38757
39421
  name: "odeget",
38758
39422
  resolve: () => ({
@@ -38862,7 +39526,7 @@ for (const name of ["clear", "clc", "clf"]) {
38862
39526
  name,
38863
39527
  resolve: () => ({
38864
39528
  outputTypes: [],
38865
- apply: () => 0
39529
+ apply: () => void 0
38866
39530
  })
38867
39531
  });
38868
39532
  }
@@ -38996,7 +39660,7 @@ registerIBuiltin({
38996
39660
  name: "set",
38997
39661
  resolve: () => ({
38998
39662
  outputTypes: [],
38999
- apply: () => 0
39663
+ apply: () => void 0
39000
39664
  })
39001
39665
  });
39002
39666
  for (const name of [
@@ -39018,7 +39682,7 @@ for (const name of [
39018
39682
  name,
39019
39683
  resolve: () => ({
39020
39684
  outputTypes: [],
39021
- apply: () => 0
39685
+ apply: () => void 0
39022
39686
  })
39023
39687
  });
39024
39688
  }
@@ -43738,6 +44402,114 @@ function bisectEvent(eventIdx, step, events) {
43738
44402
  return [tOld + xFinal * h, denseOutputEval(yOld, Q, h, xFinal)];
43739
44403
  }
43740
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
+
43741
44513
  // src/numbl-core/runtime/specialBuiltinNames.ts
43742
44514
  var SPECIAL_BUILTIN_NAMES = [
43743
44515
  "help",
@@ -43826,6 +44598,7 @@ var SPECIAL_BUILTIN_NAMES = [
43826
44598
  "webread",
43827
44599
  "delete",
43828
44600
  "rmdir",
44601
+ "movefile",
43829
44602
  "unzip",
43830
44603
  "dir",
43831
44604
  "warning",
@@ -43838,7 +44611,9 @@ var SPECIAL_BUILTIN_NAMES = [
43838
44611
  "cd",
43839
44612
  "ode45",
43840
44613
  "ode23",
43841
- "deval"
44614
+ "deval",
44615
+ "toc",
44616
+ "quadgk"
43842
44617
  ];
43843
44618
 
43844
44619
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -43852,49 +44627,74 @@ function registerSpecial(name, fn) {
43852
44627
  })
43853
44628
  });
43854
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
+ }
43855
44642
  function registerSpecialBuiltins(rt) {
43856
- 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
+ };
43857
44649
  if (args.length === 0) {
43858
44650
  const names = getAllBuiltinNames().sort();
43859
- rt.output("Available builtins:\n");
43860
- rt.output(" " + names.join(", ") + "\n");
43861
- rt.output("\nType 'help <name>' for help on a specific builtin.\n");
43862
- return 0;
43863
- }
43864
- const name = toString(args[0]);
43865
- const h = getIBuiltinHelp(name);
43866
- if (!h) {
43867
- const allNames = getAllBuiltinNames();
43868
- if (allNames.includes(name)) {
43869
- 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}'.
43870
44661
  `);
43871
- } else {
43872
- rt.output(`Unknown function '${name}'.
44662
+ } else {
44663
+ emit(`Unknown function '${name}'.
43873
44664
  `);
43874
- }
43875
- return 0;
43876
- }
43877
- rt.output(` ${h.signatures.join("\n ")}
44665
+ }
44666
+ } else {
44667
+ emit(` ${h.signatures.join("\n ")}
43878
44668
 
43879
44669
  `);
43880
- rt.output(`${h.description}
44670
+ emit(`${h.description}
43881
44671
  `);
43882
- return 0;
44672
+ }
44673
+ }
44674
+ return nargout >= 1 ? RTV.char(text) : void 0;
43883
44675
  });
43884
- registerSpecial("disp", (_nargout, args) => {
44676
+ registerSpecialVoid("disp", (args) => {
43885
44677
  if (args.length >= 1) {
43886
44678
  const mv = ensureRuntimeValue(args[0]);
43887
- if (isRuntimeTensor(mv) && mv.data.length === 0) return 0;
44679
+ if (isRuntimeTensor(mv) && mv.data.length === 0) return;
43888
44680
  rt.output(displayValue(mv) + "\n");
43889
44681
  }
43890
- return 0;
43891
44682
  });
43892
- registerSpecial("warning", (_nargout, args) => {
43893
- if (args.length === 0) return RTV.num(0);
44683
+ registerSpecial("toc", (nargout) => {
44684
+ const elapsed = (performance.now() - getTicTime()) / 1e3;
44685
+ if (nargout === 0) {
44686
+ rt.output(`Elapsed time is ${elapsed.toFixed(6)} seconds.
44687
+ `);
44688
+ }
44689
+ return RTV.num(elapsed);
44690
+ });
44691
+ registerSpecial("warning", (nargout, args) => {
44692
+ if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
43894
44693
  const margs = args.map((a) => ensureRuntimeValue(a));
43895
44694
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
43896
44695
  const state = toString(margs[0]);
43897
44696
  if (state === "on" || state === "off") {
44697
+ if (nargout === 0) return void 0;
43898
44698
  return RTV.struct(
43899
44699
  /* @__PURE__ */ new Map([
43900
44700
  ["state", RTV.char("on")],
@@ -43923,9 +44723,9 @@ function registerSpecialBuiltins(rt) {
43923
44723
  } else {
43924
44724
  rt.output("Warning: " + sprintfFormat(fmt, fmtArgs) + "\n");
43925
44725
  }
43926
- return RTV.num(0);
44726
+ return nargout >= 1 ? RTV.num(0) : void 0;
43927
44727
  });
43928
- registerSpecial("fprintf", (_nargout, args) => {
44728
+ registerSpecial("fprintf", (nargout, args) => {
43929
44729
  let output = "";
43930
44730
  if (args.length >= 1) {
43931
44731
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -43936,27 +44736,7 @@ function registerSpecialBuiltins(rt) {
43936
44736
  fmtIdx = 1;
43937
44737
  }
43938
44738
  const fmt = toString(margs[fmtIdx]);
43939
- const scalarArgs = [];
43940
- for (let i = fmtIdx + 1; i < margs.length; i++) {
43941
- const a = margs[i];
43942
- if (isRuntimeTensor(a)) {
43943
- for (let j = 0; j < a.data.length; j++)
43944
- scalarArgs.push(RTV.num(a.data[j]));
43945
- } else {
43946
- scalarArgs.push(a);
43947
- }
43948
- }
43949
- const specCount = (fmt.match(/%[^%]/g) || []).length;
43950
- if (specCount === 0 || scalarArgs.length === 0) {
43951
- output = sprintfFormat(fmt, scalarArgs);
43952
- } else {
43953
- let idx = 0;
43954
- while (idx < scalarArgs.length) {
43955
- const batch = scalarArgs.slice(idx, idx + specCount);
43956
- output += sprintfFormat(fmt, batch);
43957
- idx += specCount;
43958
- }
43959
- }
44739
+ output = sprintfFormat(fmt, margs.slice(fmtIdx + 1));
43960
44740
  if (fid === 1 || fid === 2) {
43961
44741
  rt.output(output);
43962
44742
  } else {
@@ -43967,7 +44747,7 @@ function registerSpecialBuiltins(rt) {
43967
44747
  rt.fileIO.fwrite(fid, output);
43968
44748
  }
43969
44749
  }
43970
- return output.length;
44750
+ return nargout >= 1 ? output.length : void 0;
43971
44751
  });
43972
44752
  registerSpecial("arrayfun", (nargout, args) => {
43973
44753
  return arrayfunImpl(rt, nargout, args);
@@ -43999,6 +44779,55 @@ function registerSpecialBuiltins(rt) {
43999
44779
  registerSpecial("bsxfun", (nargout, args) => {
44000
44780
  return bsxfunImpl(rt, nargout, args);
44001
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
+ });
44002
44831
  registerSpecial("subsref", (nargout, args) => {
44003
44832
  return subsrefBuiltin(rt, nargout, args);
44004
44833
  });
@@ -44527,6 +45356,40 @@ function registerSpecialBuiltins(rt) {
44527
45356
  RTV.char("")
44528
45357
  ];
44529
45358
  });
45359
+ function extractWebOptions(arg) {
45360
+ if (!isRuntimeStruct(arg)) return void 0;
45361
+ const s = arg;
45362
+ if (!s.fields.has("Timeout")) return void 0;
45363
+ const opts = {};
45364
+ const t = s.fields.get("Timeout");
45365
+ if (t !== void 0) opts.timeout = toNumber(t);
45366
+ const rm = s.fields.get("RequestMethod");
45367
+ if (rm !== void 0) {
45368
+ const v = toString(rm);
45369
+ if (v !== "auto") opts.requestMethod = v;
45370
+ }
45371
+ const un = s.fields.get("Username");
45372
+ if (un !== void 0) {
45373
+ const v = toString(un);
45374
+ if (v) opts.username = v;
45375
+ }
45376
+ const pw = s.fields.get("Password");
45377
+ if (pw !== void 0) {
45378
+ const v = toString(pw);
45379
+ if (v) opts.password = v;
45380
+ }
45381
+ const kn = s.fields.get("KeyName");
45382
+ if (kn !== void 0) {
45383
+ const v = toString(kn);
45384
+ if (v) opts.keyName = v;
45385
+ }
45386
+ const kv = s.fields.get("KeyValue");
45387
+ if (kv !== void 0) {
45388
+ const v = toString(kv);
45389
+ if (v) opts.keyValue = v;
45390
+ }
45391
+ return opts;
45392
+ }
44530
45393
  registerSpecial("websave", (_nargout, args) => {
44531
45394
  const io = requireFileIO();
44532
45395
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44536,8 +45399,14 @@ function registerSpecialBuiltins(rt) {
44536
45399
  throw new RuntimeError("websave is not available in this environment");
44537
45400
  const filename = toString(margs[0]);
44538
45401
  let url = toString(margs[1]);
45402
+ let webOpts;
45403
+ let queryEnd = margs.length;
45404
+ if (margs.length > 2) {
45405
+ webOpts = extractWebOptions(margs[margs.length - 1]);
45406
+ if (webOpts) queryEnd = margs.length - 1;
45407
+ }
44539
45408
  const queryParts = [];
44540
- for (let i = 2; i + 1 < margs.length; i += 2) {
45409
+ for (let i = 2; i + 1 < queryEnd; i += 2) {
44541
45410
  const name = encodeURIComponent(toString(margs[i]));
44542
45411
  const value = encodeURIComponent(toString(margs[i + 1]));
44543
45412
  queryParts.push(`${name}=${value}`);
@@ -44546,7 +45415,7 @@ function registerSpecialBuiltins(rt) {
44546
45415
  const sep = url.includes("?") ? "&" : "?";
44547
45416
  url += sep + queryParts.join("&");
44548
45417
  }
44549
- io.websave(url, filename);
45418
+ io.websave(url, filename, webOpts);
44550
45419
  return RTV.char(filename);
44551
45420
  });
44552
45421
  registerSpecial("webread", (_nargout, args) => {
@@ -44557,8 +45426,14 @@ function registerSpecialBuiltins(rt) {
44557
45426
  if (!io.webread)
44558
45427
  throw new RuntimeError("webread is not available in this environment");
44559
45428
  let url = toString(margs[0]);
45429
+ let webOpts;
45430
+ let queryEnd = margs.length;
45431
+ if (margs.length > 1) {
45432
+ webOpts = extractWebOptions(margs[margs.length - 1]);
45433
+ if (webOpts) queryEnd = margs.length - 1;
45434
+ }
44560
45435
  const queryParts = [];
44561
- for (let i = 1; i + 1 < margs.length; i += 2) {
45436
+ for (let i = 1; i + 1 < queryEnd; i += 2) {
44562
45437
  const name = encodeURIComponent(toString(margs[i]));
44563
45438
  const value = encodeURIComponent(toString(margs[i + 1]));
44564
45439
  queryParts.push(`${name}=${value}`);
@@ -44567,7 +45442,7 @@ function registerSpecialBuiltins(rt) {
44567
45442
  const sep = url.includes("?") ? "&" : "?";
44568
45443
  url += sep + queryParts.join("&");
44569
45444
  }
44570
- const text = io.webread(url);
45445
+ const text = io.webread(url, webOpts);
44571
45446
  try {
44572
45447
  const parsed = JSON.parse(text);
44573
45448
  return convertJsonValue(parsed);
@@ -44575,7 +45450,7 @@ function registerSpecialBuiltins(rt) {
44575
45450
  return RTV.char(text);
44576
45451
  }
44577
45452
  });
44578
- registerSpecial("delete", (_nargout, args) => {
45453
+ registerSpecialVoid("delete", (args) => {
44579
45454
  const io = requireFileIO();
44580
45455
  if (!io.deleteFile)
44581
45456
  throw new RuntimeError("delete is not available in this environment");
@@ -44585,7 +45460,6 @@ function registerSpecialBuiltins(rt) {
44585
45460
  for (const arg of margs) {
44586
45461
  io.deleteFile(toString(arg));
44587
45462
  }
44588
- return 0;
44589
45463
  });
44590
45464
  registerSpecial("rmdir", (nargout, args) => {
44591
45465
  const io = requireFileIO();
@@ -44608,6 +45482,34 @@ function registerSpecialBuiltins(rt) {
44608
45482
  RTV.char("")
44609
45483
  ];
44610
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
+ });
44611
45513
  registerSpecial("unzip", (nargout, args) => {
44612
45514
  const io = requireFileIO();
44613
45515
  if (!io.unzip)
@@ -44659,7 +45561,7 @@ function registerSpecialBuiltins(rt) {
44659
45561
  const cellData = extracted.map((f) => RTV.char(f));
44660
45562
  return RTV.cell(cellData, [1, cellData.length]);
44661
45563
  }
44662
- return 0;
45564
+ return void 0;
44663
45565
  });
44664
45566
  registerSpecial("dir", (nargout, args) => {
44665
45567
  const io = requireFileIO();
@@ -44756,7 +45658,7 @@ function registerSpecialBuiltins(rt) {
44756
45658
  const parts = margs.map((a) => toString(a));
44757
45659
  return RTV.char(parts.join("/"));
44758
45660
  });
44759
- registerSpecial("assignin", (_nargout, args) => {
45661
+ registerSpecialVoid("assignin", (args) => {
44760
45662
  if (args.length < 3)
44761
45663
  throw new RuntimeError("assignin requires 3 arguments");
44762
45664
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44771,7 +45673,6 @@ function registerSpecialBuiltins(rt) {
44771
45673
  } else {
44772
45674
  rt.setWorkspaceVariable(varName, args[2]);
44773
45675
  }
44774
- return 0;
44775
45676
  });
44776
45677
  registerSpecial("evalin", (_nargout, args) => {
44777
45678
  if (args.length < 2)
@@ -44792,13 +45693,12 @@ function registerSpecialBuiltins(rt) {
44792
45693
  }
44793
45694
  return val;
44794
45695
  });
44795
- registerSpecial("drawnow", () => {
45696
+ registerSpecialVoid("drawnow", () => {
44796
45697
  rt.drawnow();
44797
- return 0;
44798
45698
  });
44799
- registerSpecial("pause", (_nargout, args) => {
45699
+ registerSpecial("pause", (nargout, args) => {
44800
45700
  rt.pause(args[0] ?? 0);
44801
- return 0;
45701
+ return nargout >= 1 ? RTV.char("on") : void 0;
44802
45702
  });
44803
45703
  registerSpecial("mfilename", (_nargout, args) => {
44804
45704
  const file = rt.$file ?? "";
@@ -44842,7 +45742,7 @@ function registerSpecialBuiltins(rt) {
44842
45742
  }
44843
45743
  }
44844
45744
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
44845
- return 0;
45745
+ return void 0;
44846
45746
  });
44847
45747
  registerSpecial("rmpath", (nargout, args) => {
44848
45748
  if (!rt.onPathChange) {
@@ -44862,11 +45762,11 @@ function registerSpecialBuiltins(rt) {
44862
45762
  }
44863
45763
  }
44864
45764
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
44865
- return 0;
45765
+ return void 0;
44866
45766
  });
44867
- registerSpecial("savepath", () => {
45767
+ registerSpecial("savepath", (nargout) => {
44868
45768
  rt.output("Warning: savepath is a no-op in numbl\n");
44869
- return 0;
45769
+ return nargout >= 1 ? RTV.num(0) : void 0;
44870
45770
  });
44871
45771
  registerSpecial("input", (_nargout, args) => {
44872
45772
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44929,11 +45829,11 @@ function registerSpecialBuiltins(rt) {
44929
45829
  }
44930
45830
  return RTV.char(sys?.getEnv(toString(args[0])) ?? "");
44931
45831
  });
44932
- registerSpecial("setenv", (_nargout, args) => {
45832
+ registerSpecialVoid("setenv", (args) => {
44933
45833
  const sys = rt.system;
44934
45834
  if (args.length === 2) {
44935
45835
  sys?.setEnv(toString(args[0]), toString(args[1]));
44936
- return 0;
45836
+ return;
44937
45837
  }
44938
45838
  if (args.length === 1) {
44939
45839
  const d = args[0];
@@ -44941,20 +45841,26 @@ function registerSpecialBuiltins(rt) {
44941
45841
  for (const { key, value } of d.entries.values()) {
44942
45842
  sys?.setEnv(toString(key), toString(value));
44943
45843
  }
44944
- return 0;
45844
+ return;
44945
45845
  }
44946
45846
  sys?.setEnv(toString(d), "");
44947
- return 0;
45847
+ return;
44948
45848
  }
44949
45849
  throw new RuntimeError("setenv: invalid arguments");
44950
45850
  });
44951
45851
  registerSpecial("pwd", () => {
44952
45852
  return RTV.char(rt.system?.cwd() ?? "/");
44953
45853
  });
44954
- registerSpecial("cd", (_nargout, args) => {
45854
+ registerSpecial("cd", (nargout, args) => {
44955
45855
  const sys = rt.system;
44956
45856
  const curDir = sys?.cwd() ?? "/";
44957
- 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
+ }
44958
45864
  const target = toString(args[0]);
44959
45865
  if (sys) {
44960
45866
  try {
@@ -44962,15 +45868,18 @@ function registerSpecialBuiltins(rt) {
44962
45868
  } catch {
44963
45869
  throw new RuntimeError(`Cannot change directory to '${target}'`);
44964
45870
  }
45871
+ if (rt.onCwdChange) {
45872
+ rt.onCwdChange(sys.cwd());
45873
+ }
44965
45874
  }
44966
- return RTV.char(curDir);
45875
+ return nargout >= 1 ? RTV.char(curDir) : void 0;
44967
45876
  });
44968
- registerSpecial("figure", (_nargout, args) => {
45877
+ registerSpecial("figure", (nargout, args) => {
44969
45878
  const handle = args.length > 0 ? args[0] : 1;
44970
45879
  plotInstr(rt.plotInstructions, { type: "set_figure_handle", handle });
44971
- return 0;
45880
+ return nargout >= 1 ? RTV.num(toNumber(ensureRuntimeValue(handle))) : void 0;
44972
45881
  });
44973
- registerSpecial("subplot", (_nargout, args) => {
45882
+ registerSpecial("subplot", (nargout, args) => {
44974
45883
  if (args.length >= 3) {
44975
45884
  plotInstr(rt.plotInstructions, {
44976
45885
  type: "set_subplot",
@@ -44979,43 +45888,41 @@ function registerSpecialBuiltins(rt) {
44979
45888
  index: args[2]
44980
45889
  });
44981
45890
  }
44982
- return 0;
45891
+ return nargout >= 1 ? RTV.num(0) : void 0;
44983
45892
  });
44984
- registerSpecial("title", (_nargout, args) => {
45893
+ registerSpecial("title", (nargout, args) => {
44985
45894
  if (args.length > 0) {
44986
45895
  plotInstr(rt.plotInstructions, { type: "set_title", text: args[0] });
44987
45896
  }
44988
- return 0;
45897
+ return nargout >= 1 ? RTV.num(0) : void 0;
44989
45898
  });
44990
- registerSpecial("xlabel", (_nargout, args) => {
45899
+ registerSpecial("xlabel", (nargout, args) => {
44991
45900
  if (args.length > 0) {
44992
45901
  plotInstr(rt.plotInstructions, { type: "set_xlabel", text: args[0] });
44993
45902
  }
44994
- return 0;
45903
+ return nargout >= 1 ? RTV.num(0) : void 0;
44995
45904
  });
44996
- registerSpecial("ylabel", (_nargout, args) => {
45905
+ registerSpecial("ylabel", (nargout, args) => {
44997
45906
  if (args.length > 0) {
44998
45907
  plotInstr(rt.plotInstructions, { type: "set_ylabel", text: args[0] });
44999
45908
  }
45000
- return 0;
45909
+ return nargout >= 1 ? RTV.num(0) : void 0;
45001
45910
  });
45002
- registerSpecial("hold", (_nargout, args) => {
45911
+ registerSpecialVoid("hold", (args) => {
45003
45912
  if (args.length > 0) {
45004
45913
  plotInstr(rt.plotInstructions, { type: "set_hold", value: args[0] });
45005
45914
  }
45006
- return 0;
45007
45915
  });
45008
- registerSpecial("grid", (_nargout, args) => {
45916
+ registerSpecialVoid("grid", (args) => {
45009
45917
  if (args.length > 0) {
45010
45918
  plotInstr(rt.plotInstructions, { type: "set_grid", value: args[0] });
45011
45919
  }
45012
- return 0;
45013
45920
  });
45014
- registerSpecial("legend", (_nargout, args) => {
45921
+ registerSpecial("legend", (nargout, args) => {
45015
45922
  legendCall(rt.plotInstructions, args);
45016
- return 0;
45923
+ return nargout >= 1 ? RTV.num(0) : void 0;
45017
45924
  });
45018
- registerSpecial("close", (_nargout, args) => {
45925
+ registerSpecial("close", (nargout, args) => {
45019
45926
  if (args.length > 0) {
45020
45927
  const val = toString(args[0]);
45021
45928
  if (val === "all") {
@@ -45026,26 +45933,25 @@ function registerSpecialBuiltins(rt) {
45026
45933
  } else {
45027
45934
  plotInstr(rt.plotInstructions, { type: "close" });
45028
45935
  }
45029
- return 0;
45936
+ return nargout >= 1 ? RTV.num(1) : void 0;
45030
45937
  });
45031
- registerSpecial("sgtitle", (_nargout, args) => {
45938
+ registerSpecial("sgtitle", (nargout, args) => {
45032
45939
  if (args.length > 0) {
45033
45940
  plotInstr(rt.plotInstructions, { type: "set_sgtitle", text: args[0] });
45034
45941
  }
45035
- return 0;
45942
+ return nargout >= 1 ? RTV.num(0) : void 0;
45036
45943
  });
45037
- registerSpecial("shading", (_nargout, args) => {
45944
+ registerSpecialVoid("shading", (args) => {
45038
45945
  if (args.length > 0) {
45039
45946
  plotInstr(rt.plotInstructions, {
45040
45947
  type: "set_shading",
45041
45948
  shading: args[0]
45042
45949
  });
45043
45950
  }
45044
- return 0;
45045
45951
  });
45046
- registerSpecial("clf", () => {
45952
+ registerSpecial("clf", (nargout) => {
45047
45953
  plotInstr(rt.plotInstructions, { type: "clf" });
45048
- return 0;
45954
+ return nargout >= 1 ? RTV.num(1) : void 0;
45049
45955
  });
45050
45956
  registerSpecial("ode45", (nargout, args) => {
45051
45957
  return _ode45Impl(rt, nargout, args, dormandPrince45);
@@ -45590,6 +46496,29 @@ function methodDispatch(rt, name, nargout, args) {
45590
46496
  }
45591
46497
  return fieldVal;
45592
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
+ }
45593
46522
  try {
45594
46523
  return callClassMethod(rt, firstRV.className, name, nargout, args);
45595
46524
  } catch (e) {
@@ -46158,6 +47087,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46158
47087
  const im2 = t.imag[i];
46159
47088
  return im2 === 0 ? t.data[i] : RTV.complex(t.data[i], im2);
46160
47089
  }
47090
+ if (t._isLogical === true) return t.data[i] !== 0;
46161
47091
  return t.data[i];
46162
47092
  }
46163
47093
  } else if (nIdx === 2) {
@@ -46166,7 +47096,14 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46166
47096
  if (typeof ri === "number" && typeof ci === "number") {
46167
47097
  const s = t.shape;
46168
47098
  const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
46169
- 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
+ }
46170
47107
  const r = Math.round(ri) - 1;
46171
47108
  const c = Math.round(ci) - 1;
46172
47109
  if (r < 0 || r >= rows || c < 0 || c >= cols)
@@ -46176,6 +47113,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46176
47113
  const im2 = t.imag[lin];
46177
47114
  return im2 === 0 ? t.data[lin] : RTV.complex(t.data[lin], im2);
46178
47115
  }
47116
+ if (t._isLogical === true) return t.data[lin] !== 0;
46179
47117
  return t.data[lin];
46180
47118
  }
46181
47119
  } else if (nIdx >= 3) {
@@ -46566,14 +47504,20 @@ function multiOutputCellAssign(base, indices, results) {
46566
47504
  if (base === void 0 || base === null) base = RTV.cell([], [0, 0]);
46567
47505
  let mv = ensureRuntimeValue(base);
46568
47506
  if (!isRuntimeCell(mv)) mv = RTV.cell([], [0, 0]);
46569
- const idxMv = ensureRuntimeValue(indices);
46570
47507
  let idxArray;
46571
- if (isRuntimeTensor(idxMv)) {
46572
- idxArray = Array.from(idxMv.data);
46573
- } else if (isRuntimeNumber(idxMv)) {
46574
- 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);
46575
47512
  } else {
46576
- 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
+ }
46577
47521
  }
46578
47522
  for (let i = 0; i < idxArray.length && i < results.length; i++) {
46579
47523
  const idx = idxArray[i];
@@ -46800,6 +47744,8 @@ var Runtime = class _Runtime {
46800
47744
  classMethodCache = /* @__PURE__ */ new Map();
46801
47745
  /** Callback for addpath/rmpath — mutates search paths and rebuilds function index. */
46802
47746
  onPathChange = null;
47747
+ /** Callback invoked after cd() to update the implicit cwd search path. */
47748
+ onCwdChange = null;
46803
47749
  /** Reference to the active search paths (set by executeCode). */
46804
47750
  searchPaths = [];
46805
47751
  // Workspace accessors: varName → { get, set } closures over script-level vars
@@ -47079,23 +48025,26 @@ var Runtime = class _Runtime {
47079
48025
  if (e instanceof RuntimeError) {
47080
48026
  return RTV.struct(
47081
48027
  /* @__PURE__ */ new Map([
47082
- ["message", RTV.string(e.message)],
47083
- ["identifier", RTV.string(e.identifier)]
48028
+ ["message", RTV.char(e.message)],
48029
+ ["identifier", RTV.char(e.identifier)],
48030
+ ["stack", buildStackField(e)]
47084
48031
  ])
47085
48032
  );
47086
48033
  }
47087
48034
  if (e instanceof Error) {
47088
48035
  return RTV.struct(
47089
48036
  /* @__PURE__ */ new Map([
47090
- ["message", RTV.string(e.message)],
47091
- ["identifier", RTV.string("")]
48037
+ ["message", RTV.char(e.message)],
48038
+ ["identifier", RTV.char("")],
48039
+ ["stack", emptyStackField()]
47092
48040
  ])
47093
48041
  );
47094
48042
  }
47095
48043
  return RTV.struct(
47096
48044
  /* @__PURE__ */ new Map([
47097
- ["message", RTV.string(String(e))],
47098
- ["identifier", RTV.string("")]
48045
+ ["message", RTV.char(String(e))],
48046
+ ["identifier", RTV.char("")],
48047
+ ["stack", emptyStackField()]
47099
48048
  ])
47100
48049
  );
47101
48050
  }
@@ -47927,6 +48876,63 @@ var rstr = (s) => {
47927
48876
  if (isRuntimeChar(s)) return s.value;
47928
48877
  throw new RuntimeError(`Expected string or char, got ${kstr(s)}`);
47929
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
+ }
47930
48936
 
47931
48937
  // src/numbl-core/jsUserFunctions.ts
47932
48938
  function funcNameFromFile(fileName) {
@@ -49175,9 +50181,7 @@ function lowerStmt(ctx, stmt) {
49175
50181
  result = lowerAssign(ctx, stmt);
49176
50182
  break;
49177
50183
  case "ExprStmt":
49178
- if (!stmt.suppressed) return null;
49179
- result = lowerExprStmt(ctx, stmt);
49180
- break;
50184
+ return null;
49181
50185
  case "If":
49182
50186
  result = lowerIf(ctx, stmt);
49183
50187
  break;
@@ -49252,11 +50256,6 @@ function lowerMultiAssign(ctx, stmt) {
49252
50256
  }
49253
50257
  ];
49254
50258
  }
49255
- function lowerExprStmt(ctx, stmt) {
49256
- const expr = lowerExpr(ctx, stmt.expr);
49257
- if (!expr) return null;
49258
- return [{ tag: "ExprStmt", expr }];
49259
- }
49260
50259
  function lowerIf(ctx, stmt) {
49261
50260
  const cond = lowerExpr(ctx, stmt.cond);
49262
50261
  if (!cond) return null;
@@ -50325,10 +51324,14 @@ function execStmt(stmt) {
50325
51324
  }
50326
51325
  switch (stmt.type) {
50327
51326
  case "ExprStmt": {
50328
- const val = this.evalExpr(stmt.expr);
51327
+ const val = this.evalExprNargout(stmt.expr, 0);
50329
51328
  const singleVal = Array.isArray(val) ? val[0] : val;
51329
+ if (singleVal === void 0) {
51330
+ return null;
51331
+ }
50330
51332
  const rv = ensureRuntimeValue(singleVal);
50331
51333
  this.ans = rv;
51334
+ this.env.set("ans", rv);
50332
51335
  if (!stmt.suppressed && !this.isOutputExpr(stmt.expr)) {
50333
51336
  this.rt.displayResult(rv);
50334
51337
  }
@@ -50349,19 +51352,25 @@ function execStmt(stmt) {
50349
51352
  if (stmt.lvalues.length === 1 && stmt.lvalues[0].type === "IndexCell") {
50350
51353
  const lv = stmt.lvalues[0];
50351
51354
  const cellBase = lv.base.type === "Ident" ? this.env.get(lv.base.name) ?? RTV.cell([], [0, 0]) : this.evalExpr(lv.base);
50352
- const indices = lv.indices.map((idx) => this.evalExpr(idx));
50353
- const idxVal = ensureRuntimeValue(indices[0]);
51355
+ const indices = this.evalIndicesWithEnd(cellBase, lv.indices);
50354
51356
  let expandedCount = 1;
50355
- if (isRuntimeTensor(idxVal)) {
50356
- expandedCount = idxVal.data.length;
50357
- } else if (typeof idxVal === "number") {
50358
- 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
+ }
50359
51368
  }
50360
51369
  const val2 = this.evalExprNargout(stmt.expr, expandedCount);
50361
51370
  const values2 = Array.isArray(val2) ? val2 : [val2];
50362
51371
  const result = this.rt.multiOutputCellAssign(
50363
51372
  cellBase,
50364
- indices[0],
51373
+ idx0,
50365
51374
  values2.map((v) => ensureRuntimeValue(v))
50366
51375
  );
50367
51376
  if (lv.base.type === "Ident") {
@@ -50378,10 +51387,12 @@ function execStmt(stmt) {
50378
51387
  const rv = this.rt.share(i < values.length ? values[i] : void 0);
50379
51388
  this.assignLValue(lv, rv);
50380
51389
  }
50381
- if (!stmt.suppressed && values.length > 0) {
50382
- const firstLv = stmt.lvalues[0];
50383
- if (firstLv.type === "Var") {
50384
- this.rt.displayAssign(firstLv.name, ensureRuntimeValue(values[0]));
51390
+ if (!stmt.suppressed) {
51391
+ for (let i = 0; i < stmt.lvalues.length; i++) {
51392
+ const lv = stmt.lvalues[i];
51393
+ if (lv.type === "Var" && i < values.length) {
51394
+ this.rt.displayAssign(lv.name, ensureRuntimeValue(values[i]));
51395
+ }
50385
51396
  }
50386
51397
  }
50387
51398
  return null;
@@ -50738,6 +51749,18 @@ function evalArgs(argExprs) {
50738
51749
  }
50739
51750
  return args;
50740
51751
  }
51752
+ var binopProfileName = {
51753
+ ["Add" /* Add */]: "plus",
51754
+ ["Sub" /* Sub */]: "minus",
51755
+ ["Mul" /* Mul */]: "mtimes",
51756
+ ["ElemMul" /* ElemMul */]: "times",
51757
+ ["Div" /* Div */]: "mrdivide",
51758
+ ["ElemDiv" /* ElemDiv */]: "rdivide",
51759
+ ["LeftDiv" /* LeftDiv */]: "mldivide",
51760
+ ["ElemLeftDiv" /* ElemLeftDiv */]: "ldivide",
51761
+ ["Pow" /* Pow */]: "mpower",
51762
+ ["ElemPow" /* ElemPow */]: "power"
51763
+ };
50741
51764
  function evalBinary(expr) {
50742
51765
  if (expr.op === "AndAnd" /* AndAnd */) {
50743
51766
  const left2 = this.evalExpr(expr.left);
@@ -50763,6 +51786,13 @@ function evalBinary(expr) {
50763
51786
  if (!isNaN(r)) return r;
50764
51787
  return mPow(ensureRuntimeValue(left), ensureRuntimeValue(right));
50765
51788
  }
51789
+ if (this.rt.profilingEnabled && (typeof left !== "number" || typeof right !== "number")) {
51790
+ const opName = binopProfileName[expr.op] ?? expr.op;
51791
+ this.rt.profileEnter("builtin:interp:" + opName);
51792
+ const result = binop(expr.op, left, right);
51793
+ this.rt.profileLeave();
51794
+ return result;
51795
+ }
50766
51796
  return binop(expr.op, left, right);
50767
51797
  }
50768
51798
  function evalUnary(expr) {
@@ -50902,7 +51932,11 @@ function evalAnonFunc(expr) {
50902
51932
  capturedMethodName,
50903
51933
  () => {
50904
51934
  try {
50905
- 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;
50906
51940
  } finally {
50907
51941
  this.env = savedEnv;
50908
51942
  }
@@ -51161,7 +52195,6 @@ function switchMatch2(switchVal, caseVal) {
51161
52195
  return switchMatch(switchVal, caseVal);
51162
52196
  }
51163
52197
  function isOutputExpr(expr) {
51164
- if (expr.type !== "FuncCall") return false;
51165
52198
  const outputFunctions = [
51166
52199
  "disp",
51167
52200
  "display",
@@ -51169,9 +52202,12 @@ function isOutputExpr(expr) {
51169
52202
  "warning",
51170
52203
  "assert",
51171
52204
  "tic",
52205
+ "toc",
51172
52206
  "help"
51173
52207
  ];
51174
- return outputFunctions.includes(expr.name);
52208
+ if (expr.type === "FuncCall") return outputFunctions.includes(expr.name);
52209
+ if (expr.type === "Ident") return outputFunctions.includes(expr.name);
52210
+ return false;
51175
52211
  }
51176
52212
 
51177
52213
  // src/numbl-core/interpreter/interpreterFunctions.ts
@@ -51380,6 +52416,17 @@ register("exist", (ctx, args) => {
51380
52416
  }
51381
52417
  return FALL_THROUGH;
51382
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
+ });
51383
52430
  register("isfolder", (ctx, args) => {
51384
52431
  if (args.length < 1) return FALL_THROUGH;
51385
52432
  const fio = ctx.rt.fileIO;
@@ -51482,6 +52529,9 @@ register("run", (ctx, args) => {
51482
52529
  } catch {
51483
52530
  throw new RuntimeError(`Cannot change directory to '${scriptDir}'`);
51484
52531
  }
52532
+ if (ctx.rt.onCwdChange) {
52533
+ ctx.rt.onCwdChange(sys.cwd());
52534
+ }
51485
52535
  }
51486
52536
  const cwdAfterCd = sys?.cwd() ?? "/";
51487
52537
  try {
@@ -51493,6 +52543,9 @@ register("run", (ctx, args) => {
51493
52543
  sys.chdir(oldCwd);
51494
52544
  } catch {
51495
52545
  }
52546
+ if (ctx.rt.onCwdChange) {
52547
+ ctx.rt.onCwdChange(sys.cwd());
52548
+ }
51496
52549
  }
51497
52550
  }
51498
52551
  return void 0;
@@ -51512,7 +52565,14 @@ function callFunction(name, args, nargout) {
51512
52565
  workspaceEnv: this.workspaceEnv,
51513
52566
  evalInLocalScope: (codeArg, fileName) => this.evalInLocalScope(codeArg, fileName),
51514
52567
  callFunction: (n, a, no) => this.callFunction(n, a, no),
51515
- 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
+ }
51516
52576
  };
51517
52577
  const result = specialHandler(ctx, args, nargout);
51518
52578
  if (result !== FALL_THROUGH) return result;
@@ -51546,13 +52606,18 @@ function interpretTarget(target, args, nargout) {
51546
52606
  const argTypes = margs.map(inferJitType);
51547
52607
  const resolution = ib.resolve(argTypes, nargout);
51548
52608
  if (resolution) {
52609
+ const isVoid = resolution.outputTypes.length === 0;
52610
+ if (isVoid && nargout > 0) {
52611
+ throw new RuntimeError("Too many output arguments.");
52612
+ }
51549
52613
  if (this.rt.profilingEnabled) {
51550
52614
  this.rt.profileEnter("builtin:interp:" + target.name);
51551
- const result = resolution.apply(margs, nargout);
52615
+ const result2 = resolution.apply(margs, nargout);
51552
52616
  this.rt.profileLeave();
51553
- return result;
52617
+ return isVoid ? void 0 : result2;
51554
52618
  }
51555
- return resolution.apply(margs, nargout);
52619
+ const result = resolution.apply(margs, nargout);
52620
+ return isVoid ? void 0 : result;
51556
52621
  }
51557
52622
  }
51558
52623
  const builtin = this.rt.builtins[target.name];
@@ -51644,7 +52709,19 @@ function interpretLocalFunction(target, args, nargout) {
51644
52709
  function interpretWorkspaceFunction(target, args, nargout) {
51645
52710
  const dotIdx = target.name.lastIndexOf(".");
51646
52711
  const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
51647
- 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
+ }
51648
52725
  if (!fn) {
51649
52726
  const entry = this.ctx.registry.filesByFuncName.get(target.name);
51650
52727
  if (entry) {
@@ -51788,6 +52865,11 @@ function interpretConstructor(classInfo, args, nargout) {
51788
52865
  return args[0];
51789
52866
  }
51790
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
+ }
51791
52873
  if (this.optimization >= 1 && narginOverride === void 0) {
51792
52874
  const jitResult = tryJitCall(this, fn, args, nargout);
51793
52875
  if (jitResult !== JIT_SKIP) return jitResult;
@@ -51837,10 +52919,11 @@ function callUserFunction(fn, args, nargout, narginOverride) {
51837
52919
  }
51838
52920
  }
51839
52921
  }
51840
- const hasVarargout = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
52922
+ const hasVarargout = hasVarargoutDecl;
51841
52923
  const regularOutputs = hasVarargout ? fn.outputs.slice(0, -1) : fn.outputs;
52924
+ const collectCount = nargout === 0 && regularOutputs.length > 0 ? 1 : Math.min(regularOutputs.length, nargout);
51842
52925
  const outputs = [];
51843
- for (let i = 0; i < Math.min(regularOutputs.length, nargout); i++) {
52926
+ for (let i = 0; i < collectCount; i++) {
51844
52927
  const val = this.env.get(regularOutputs[i]);
51845
52928
  if (val === void 0 && nargout >= i + 1) {
51846
52929
  throw new RuntimeError(
@@ -53410,10 +54493,17 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53410
54493
  if (options.log) {
53411
54494
  options.log("AST parsed, starting interpretation");
53412
54495
  }
54496
+ const isRepl = mainFileName === "repl";
53413
54497
  const localFunctions = [];
53414
54498
  const localClasses = [];
53415
54499
  for (const stmt of ast.body) {
53416
54500
  if (stmt.type === "Function") {
54501
+ if (isRepl) {
54502
+ throw new RuntimeError(
54503
+ "Function definitions are not supported in the REPL. Save the function to a .m file instead.",
54504
+ stmt.span
54505
+ );
54506
+ }
53417
54507
  localFunctions.push(stmt);
53418
54508
  } else if (stmt.type === "ClassDef") {
53419
54509
  localClasses.push(stmt);
@@ -53434,6 +54524,31 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53434
54524
  }
53435
54525
  }
53436
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
+ }
53437
54552
  const jsUserFunctions = loadJsUserFunctions(
53438
54553
  jsWorkspaceFiles,
53439
54554
  wasmWorkspaceFiles,
@@ -53451,6 +54566,9 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53451
54566
  stdlibShimNames.add(shimName);
53452
54567
  }
53453
54568
  ctx.registry.searchPaths = [...searchPaths ?? [], SHIM_SEARCH_PATH];
54569
+ if (implicitCwdPath !== null) {
54570
+ ctx.registry.searchPaths.unshift(implicitCwdPath);
54571
+ }
53454
54572
  ctx.fileASTCache.set(mainFileName, ast);
53455
54573
  const skippedFiles = /* @__PURE__ */ new Set();
53456
54574
  for (const f of mWorkspaceFiles) {
@@ -53484,6 +54602,11 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53484
54602
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
53485
54603
  }
53486
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
+ }
53487
54610
  const rt = new Runtime(options, options.initialVariableValues);
53488
54611
  const savedIBuiltins = /* @__PURE__ */ new Map();
53489
54612
  for (const ib of jsUserFunctions) {
@@ -53525,13 +54648,39 @@ ${jsCode}`
53525
54648
  interpreter.installRuntimeCallbacks();
53526
54649
  rt.searchPaths = ctx.registry.searchPaths;
53527
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
+ };
53528
54676
  rt.onPathChange = (action, dir, position) => {
53529
54677
  const fileIO = options.fileIO;
53530
54678
  const absDir = fileIO?.resolvePath?.(dir) ?? dir;
53531
54679
  if (action === "add") {
53532
54680
  if (ctx.registry.searchPaths.includes(absDir)) return;
53533
54681
  if (position === "begin") {
53534
- 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);
53535
54684
  } else {
53536
54685
  const shimIdx = ctx.registry.searchPaths.indexOf(SHIM_SEARCH_PATH);
53537
54686
  if (shimIdx >= 0) {
@@ -53549,10 +54698,14 @@ ${jsCode}`
53549
54698
  try {
53550
54699
  ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
53551
54700
  } catch (e) {
53552
- if (e instanceof SyntaxError && e.file === null) {
53553
- 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)`);
53554
54707
  }
53555
- throw e;
54708
+ continue;
53556
54709
  }
53557
54710
  interpreter.fileSources.set(f.name, f.source);
53558
54711
  mWorkspaceFiles.push(f);
@@ -53592,26 +54745,76 @@ ${jsCode}`
53592
54745
  }
53593
54746
  }
53594
54747
  }
53595
- const paths = ctx.registry.searchPaths;
53596
- mWorkspaceFiles.sort((a, b) => {
53597
- const ai = paths.findIndex(
53598
- (p2) => a.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || a.name === p2
53599
- );
53600
- const bi = paths.findIndex(
53601
- (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))
53602
54758
  );
53603
- const aPri = ai >= 0 ? ai : paths.length;
53604
- const bPri = bi >= 0 ? bi : paths.length;
53605
- return aPri - bPri;
53606
- });
53607
- ctx.clearWorkspaceRegistrations();
53608
- ctx.registerWorkspaceFiles(mWorkspaceFiles);
53609
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
53610
- interpreter.functionIndex = newIndex;
53611
- interpreter.clearAllCaches();
53612
- 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();
53613
54810
  };
53614
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
+ );
53615
54818
  const evalResult = executeCode(
53616
54819
  code,
53617
54820
  {
@@ -53619,11 +54822,18 @@ ${jsCode}`
53619
54822
  displayResults: false,
53620
54823
  initialVariableValues: initialVars,
53621
54824
  fileIO: options.fileIO,
53622
- onInput: options.onInput
54825
+ system: options.system,
54826
+ onInput: options.onInput,
54827
+ implicitCwdPath
53623
54828
  },
53624
- void 0,
53625
- fileName
54829
+ nestedWorkspaceFiles,
54830
+ fileName,
54831
+ nestedSearchPaths,
54832
+ nativeBridge
53626
54833
  );
54834
+ if (evalResult.implicitCwdPath !== void 0) {
54835
+ implicitCwdPath = evalResult.implicitCwdPath;
54836
+ }
53627
54837
  return {
53628
54838
  returnValue: evalResult.returnValue,
53629
54839
  variableValues: evalResult.variableValues,
@@ -53639,7 +54849,7 @@ ${jsCode}`
53639
54849
  output: rt.outputLines,
53640
54850
  generatedJS: jitSections.length > 0 ? `// Interpreter mode \u2014 JIT compiled sections:
53641
54851
 
53642
- ${jitSections.join("\n\n")}` : "// interpreted mode \u2014 no JS generated",
54852
+ ${jitSections.join("\n\n")}` : "// No JS generated",
53643
54853
  plotInstructions: rt.plotInstructions,
53644
54854
  returnValue: interpreter.ans ?? RTV.num(0),
53645
54855
  variableValues: interpreter.getVariableValues(),
@@ -53655,17 +54865,18 @@ ${jitSections.join("\n\n")}` : "// interpreted mode \u2014 no JS generated",
53655
54865
  }
53656
54866
  if (pathsModified) {
53657
54867
  result.searchPaths = ctx.registry.searchPaths.filter(
53658
- (p2) => p2 !== SHIM_SEARCH_PATH
54868
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
53659
54869
  );
53660
54870
  result.workspaceFiles = mWorkspaceFiles.filter(
53661
54871
  (f) => !stdlibShimNames.has(f.name)
53662
54872
  );
53663
54873
  }
54874
+ result.implicitCwdPath = implicitCwdPath;
53664
54875
  return result;
53665
54876
  } catch (e) {
53666
54877
  const generatedJS = jitSections.length > 0 ? `// Interpreter mode \u2014 JIT compiled sections:
53667
54878
 
53668
- ${jitSections.join("\n\n")}` : "// interpreted mode \u2014 no JS generated";
54879
+ ${jitSections.join("\n\n")}` : "// No JS generated";
53669
54880
  if (e instanceof RuntimeError) {
53670
54881
  if (e.line === null && rt.$file && rt.$line > 0) {
53671
54882
  e.file = rt.$file;
@@ -53696,6 +54907,9 @@ ${jitSections.join("\n\n")}` : "// interpreted mode \u2014 no JS generated";
53696
54907
  unregisterIBuiltin(ib.name);
53697
54908
  }
53698
54909
  }
54910
+ for (const [, ib] of savedSpecialBuiltins) {
54911
+ registerDynamicIBuiltin(ib);
54912
+ }
53699
54913
  }
53700
54914
  }
53701
54915
  export {