numbl 0.4.11 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist-browser/browser.js +1 -1
  2. package/dist-browser/numbl-core/fileIOAdapter.d.ts +4 -0
  3. package/dist-browser/numbl-core/native/lapack-bridge.d.ts +1 -1
  4. package/dist-browser/numbl-core/native/wasm-lapack-bridge.d.ts +114 -0
  5. package/dist-browser/numbl-core/native/wasm-lapack-browser.d.ts +34 -0
  6. package/dist-browser/numbl-core/runtime/datetime-format.d.ts +10 -0
  7. package/dist-browser/numbl-core/runtime/display.d.ts +5 -1
  8. package/dist-browser/numbl-core/runtime/index.d.ts +1 -1
  9. package/dist-browser/numbl-core/version.d.ts +1 -1
  10. package/dist-browser/vfs/BrowserFileIOAdapter.d.ts +1 -0
  11. package/dist-cli/cli.js +434 -131
  12. package/dist-lib/cli-fileio.d.ts +62 -0
  13. package/dist-lib/cli-scan.d.ts +6 -0
  14. package/dist-lib/cli-system.d.ts +13 -0
  15. package/dist-lib/lib.js +368 -73
  16. package/dist-lib/node.d.ts +30 -0
  17. package/dist-lib/node.js +829 -0
  18. package/dist-lib/numbl-core/fileIOAdapter.d.ts +4 -0
  19. package/dist-lib/numbl-core/native/lapack-bridge.d.ts +1 -1
  20. package/dist-lib/numbl-core/native/wasm-lapack-bridge.d.ts +114 -0
  21. package/dist-lib/numbl-core/native/wasm-lapack-browser.d.ts +34 -0
  22. package/dist-lib/numbl-core/runtime/datetime-format.d.ts +10 -0
  23. package/dist-lib/numbl-core/runtime/display.d.ts +5 -1
  24. package/dist-lib/numbl-core/runtime/index.d.ts +1 -1
  25. package/dist-lib/numbl-core/version.d.ts +1 -1
  26. package/dist-lib/vfs/BrowserFileIOAdapter.d.ts +1 -0
  27. package/dist-site-viewer/assets/{index-fhKbvuOM.js → index-C2ItFYSf.js} +322 -322
  28. package/dist-site-viewer/assets/{numbl-worker-BFP9xj5e.js → numbl-worker-CtfdLxJh.js} +114 -102
  29. package/dist-site-viewer/index.html +1 -1
  30. package/package.json +6 -2
package/dist-cli/cli.js CHANGED
@@ -312,7 +312,7 @@ import {
312
312
  delimiter,
313
313
  dirname as dirname3,
314
314
  join as join7,
315
- relative,
315
+ relative as relative2,
316
316
  resolve as resolve2,
317
317
  sep
318
318
  } from "path";
@@ -21701,6 +21701,114 @@ var valuesAreEqual = (a, b) => {
21701
21701
  }
21702
21702
  };
21703
21703
 
21704
+ // src/numbl-core/runtime/datetime-format.ts
21705
+ var MONTH_ABBR = [
21706
+ "Jan",
21707
+ "Feb",
21708
+ "Mar",
21709
+ "Apr",
21710
+ "May",
21711
+ "Jun",
21712
+ "Jul",
21713
+ "Aug",
21714
+ "Sep",
21715
+ "Oct",
21716
+ "Nov",
21717
+ "Dec"
21718
+ ];
21719
+ var MONTH_FULL = [
21720
+ "January",
21721
+ "February",
21722
+ "March",
21723
+ "April",
21724
+ "May",
21725
+ "June",
21726
+ "July",
21727
+ "August",
21728
+ "September",
21729
+ "October",
21730
+ "November",
21731
+ "December"
21732
+ ];
21733
+ function pad(n, width) {
21734
+ return String(n).padStart(width, "0");
21735
+ }
21736
+ function formatDatetimePattern(pattern, year, month, day, hour, minute, second) {
21737
+ const wholeSec = Math.floor(second);
21738
+ const frac = second - wholeSec;
21739
+ const monthIdx = Math.max(0, Math.min(11, Math.floor(month) - 1));
21740
+ const formatToken = (c, n) => {
21741
+ switch (c) {
21742
+ case "y":
21743
+ case "u":
21744
+ if (n === 2) return pad(Math.floor(year) % 100, 2);
21745
+ return pad(Math.floor(year), n);
21746
+ case "M":
21747
+ if (n === 1) return String(Math.floor(month));
21748
+ if (n === 2) return pad(Math.floor(month), 2);
21749
+ if (n === 3) return MONTH_ABBR[monthIdx];
21750
+ return MONTH_FULL[monthIdx];
21751
+ case "d":
21752
+ return pad(Math.floor(day), n);
21753
+ case "H":
21754
+ return pad(Math.floor(hour), n);
21755
+ case "h":
21756
+ return pad(Math.floor(hour) % 12 || 12, n);
21757
+ case "m":
21758
+ return pad(Math.floor(minute), n);
21759
+ case "s":
21760
+ return pad(wholeSec, n);
21761
+ case "S":
21762
+ return pad(Math.floor(frac * Math.pow(10, n)), n);
21763
+ case "a":
21764
+ return hour < 12 ? "AM" : "PM";
21765
+ default:
21766
+ throw new RuntimeError(
21767
+ `datetime: unsupported format token '${c.repeat(n)}' in '${pattern}'`
21768
+ );
21769
+ }
21770
+ };
21771
+ let out = "";
21772
+ let i = 0;
21773
+ while (i < pattern.length) {
21774
+ const c = pattern[i];
21775
+ if (c === "'") {
21776
+ i++;
21777
+ let lit = "";
21778
+ let closed = false;
21779
+ while (i < pattern.length) {
21780
+ if (pattern[i] === "'") {
21781
+ if (pattern[i + 1] === "'") {
21782
+ lit += "'";
21783
+ i += 2;
21784
+ continue;
21785
+ }
21786
+ i++;
21787
+ closed = true;
21788
+ break;
21789
+ }
21790
+ lit += pattern[i++];
21791
+ }
21792
+ if (!closed && lit !== "")
21793
+ throw new RuntimeError(
21794
+ `datetime: unterminated quoted literal in format '${pattern}'`
21795
+ );
21796
+ out += lit === "" ? "'" : lit;
21797
+ continue;
21798
+ }
21799
+ if (!/[a-zA-Z]/.test(c)) {
21800
+ out += c;
21801
+ i++;
21802
+ continue;
21803
+ }
21804
+ let j = i;
21805
+ while (j < pattern.length && pattern[j] === c) j++;
21806
+ out += formatToken(c, j - i);
21807
+ i = j;
21808
+ }
21809
+ return out;
21810
+ }
21811
+
21704
21812
  // src/numbl-core/runtime/display.ts
21705
21813
  function displayValue(v) {
21706
21814
  if (isRuntimeNumber(v)) {
@@ -21975,6 +22083,13 @@ function formatDatetimeOrDuration(v) {
21975
22083
  if (v.className === "duration") return formatDurationInstance(v);
21976
22084
  return null;
21977
22085
  }
22086
+ function textField(v, name) {
22087
+ const fv = v.fields.get(name);
22088
+ if (fv === void 0) return null;
22089
+ if (isRuntimeString(fv)) return fv;
22090
+ if (isRuntimeChar(fv)) return fv.value;
22091
+ return null;
22092
+ }
21978
22093
  function formatDatetimeInstance(v) {
21979
22094
  const year = numField(v, "Year");
21980
22095
  const month = numField(v, "Month");
@@ -21984,6 +22099,10 @@ function formatDatetimeInstance(v) {
21984
22099
  const second = numField(v, "Second");
21985
22100
  if (year === null || month === null || day === null || hour === null || minute === null || second === null)
21986
22101
  return null;
22102
+ const fmt = textField(v, "Format");
22103
+ if (fmt !== null && fmt !== "") {
22104
+ return formatDatetimePattern(fmt, year, month, day, hour, minute, second);
22105
+ }
21987
22106
  const mIdx = Math.max(0, Math.min(11, Math.floor(month) - 1));
21988
22107
  const mon = DATETIME_MONTH_ABBR[mIdx];
21989
22108
  const dd = String(Math.floor(day)).padStart(2, "0");
@@ -23069,7 +23188,7 @@ function indexIntoTensorWithTensor(base, idx) {
23069
23188
  if (hasImag) imIndices.push(base.imag[k]);
23070
23189
  }
23071
23190
  const idxIs0x0 = idx.data.length === 0 && idx.shape.length === 2 && idx.shape[0] === 0 && idx.shape[1] === 0;
23072
- const baseIsVector = base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
23191
+ const baseIsVector = base.data.length !== 1 && base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
23073
23192
  const idxIsVector = idx.shape.length <= 2 && (idx.shape[0] === 1 || idx.shape[1] === 1 || idx.shape.length === 1);
23074
23193
  const outShape = idxIs0x0 ? [0, 0] : baseIsVector && idxIsVector ? base.shape[0] === 1 ? [1, resultData.length] : [resultData.length, 1] : idx.shape;
23075
23194
  const imOut = hasImag && imIndices.some((x) => x !== 0) ? allocFloat64Array(imIndices) : void 0;
@@ -23131,6 +23250,17 @@ function indexIntoRTValue(base, indices) {
23131
23250
  return base.elements[i];
23132
23251
  }
23133
23252
  if (isRuntimeTensor(idx)) {
23253
+ if (idx._isLogical) {
23254
+ const newElements2 = [];
23255
+ for (let k = 0; k < idx.data.length; k++) {
23256
+ if (idx.data[k] !== 0) {
23257
+ if (k >= base.elements.length)
23258
+ throw new RuntimeError("Index exceeds struct array bounds");
23259
+ newElements2.push(base.elements[k]);
23260
+ }
23261
+ }
23262
+ return RTV.structArray(base.fieldNames, newElements2);
23263
+ }
23134
23264
  const newElements = [];
23135
23265
  for (let k = 0; k < idx.data.length; k++) {
23136
23266
  const i = Math.round(idx.data[k]) - 1;
@@ -27482,8 +27612,8 @@ function sprintfFormat(fmt, args) {
27482
27612
  if (width > 0) {
27483
27613
  const padChar = zeroPad ? "0" : " ";
27484
27614
  const padLen = Math.max(0, width - sign.length - s.length);
27485
- const pad = padChar.repeat(padLen);
27486
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
27615
+ const pad2 = padChar.repeat(padLen);
27616
+ result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad2 + s : pad2 + sign + s;
27487
27617
  } else {
27488
27618
  result += sign + s;
27489
27619
  }
@@ -37818,6 +37948,32 @@ function datetimeFromUtcMs(ms) {
37818
37948
  function datetimeFromDatenum(dn) {
37819
37949
  return datetimeFromUtcMs((dn - MATLAB_EPOCH_DAYS) * 864e5);
37820
37950
  }
37951
+ function withTimeZoneFormat(dt, timeZone, format) {
37952
+ if (timeZone === void 0 && format === void 0) return dt;
37953
+ const fields = new Map(dt.fields);
37954
+ if (timeZone !== void 0) fields.set("TimeZone", RTV.char(timeZone));
37955
+ if (format !== void 0) fields.set("Format", RTV.char(format));
37956
+ return new RuntimeClassInstance("datetime", fields, false);
37957
+ }
37958
+ function todayParts(offsetDays, utc) {
37959
+ const now = /* @__PURE__ */ new Date();
37960
+ if (utc) {
37961
+ const d2 = new Date(
37962
+ Date.UTC(
37963
+ now.getUTCFullYear(),
37964
+ now.getUTCMonth(),
37965
+ now.getUTCDate() + offsetDays
37966
+ )
37967
+ );
37968
+ return [d2.getUTCFullYear(), d2.getUTCMonth() + 1, d2.getUTCDate()];
37969
+ }
37970
+ const d = new Date(
37971
+ now.getFullYear(),
37972
+ now.getMonth(),
37973
+ now.getDate() + offsetDays
37974
+ );
37975
+ return [d.getFullYear(), d.getMonth() + 1, d.getDate()];
37976
+ }
37821
37977
  function makeDuration(totalSeconds) {
37822
37978
  const fields = /* @__PURE__ */ new Map([
37823
37979
  ["Seconds", RTV.num(totalSeconds)]
@@ -37867,9 +38023,10 @@ registerIBuiltin({
37867
38023
  "t = datetime('now' | 'today' | 'yesterday' | 'tomorrow')",
37868
38024
  "t = datetime(Y, M, D)",
37869
38025
  "t = datetime(Y, M, D, H, MI, S)",
37870
- "t = datetime(X, 'ConvertFrom', dateType)"
38026
+ "t = datetime(X, 'ConvertFrom', dateType)",
38027
+ "t = datetime(..., 'TimeZone', tz, 'Format', fmt)"
37871
38028
  ],
37872
- description: "Create a datetime value. Supported dateType values for ConvertFrom: 'datenum', 'posixtime', 'excel', 'excel1904'."
38029
+ description: "Create a datetime value. Supported dateType values for ConvertFrom: 'datenum', 'posixtime', 'excel', 'excel1904'. Trailing 'TimeZone' (only '', 'local', 'UTC') and 'Format' name-value pairs are supported; the Format pattern is used by char() and display."
37873
38030
  },
37874
38031
  resolve: () => ({
37875
38032
  outputTypes: [
@@ -37881,75 +38038,83 @@ registerIBuiltin({
37881
38038
  }
37882
38039
  ],
37883
38040
  apply: (args) => {
37884
- if (args.length === 0) {
37885
- return datetimeFromDate(/* @__PURE__ */ new Date());
38041
+ let timeZone;
38042
+ let format;
38043
+ let positional = args;
38044
+ while (positional.length >= 2) {
38045
+ const name = argToString(positional[positional.length - 2]);
38046
+ if (name === null) break;
38047
+ const lc = name.toLowerCase();
38048
+ if (lc !== "timezone" && lc !== "format") break;
38049
+ const value = argToString(positional[positional.length - 1]);
38050
+ if (value === null)
38051
+ throw new RuntimeError(`datetime: ${name} value must be text`);
38052
+ if (lc === "timezone") timeZone = value;
38053
+ else format = value;
38054
+ positional = positional.slice(0, -2);
38055
+ }
38056
+ const tzLc = timeZone?.toLowerCase();
38057
+ if (tzLc !== void 0 && tzLc !== "" && tzLc !== "local" && tzLc !== "utc") {
38058
+ throw new RuntimeError(
38059
+ `datetime: unsupported TimeZone '${timeZone}' (supported: '', 'local', 'UTC')`
38060
+ );
37886
38061
  }
37887
- if (args.length === 1) {
37888
- const s = argToString(args[0]);
38062
+ const utc = tzLc === "utc";
38063
+ const finish = (dt) => withTimeZoneFormat(dt, timeZone, format);
38064
+ if (positional.length === 0) {
38065
+ return finish(
38066
+ utc ? datetimeFromUtcMs(Date.now()) : datetimeFromDate(/* @__PURE__ */ new Date())
38067
+ );
38068
+ }
38069
+ if (positional.length === 1) {
38070
+ const s = argToString(positional[0]);
37889
38071
  if (s !== null) {
37890
- const now = /* @__PURE__ */ new Date();
37891
38072
  const lc = s.toLowerCase();
37892
- if (lc === "now") return datetimeFromDate(now);
37893
- if (lc === "today") {
37894
- return makeDatetime(
37895
- now.getFullYear(),
37896
- now.getMonth() + 1,
37897
- now.getDate(),
37898
- 0,
37899
- 0,
37900
- 0
38073
+ if (lc === "now") {
38074
+ return finish(
38075
+ utc ? datetimeFromUtcMs(Date.now()) : datetimeFromDate(/* @__PURE__ */ new Date())
37901
38076
  );
37902
38077
  }
37903
- if (lc === "yesterday" || lc === "tomorrow") {
37904
- const d = new Date(
37905
- now.getFullYear(),
37906
- now.getMonth(),
37907
- now.getDate() + (lc === "tomorrow" ? 1 : -1)
37908
- );
37909
- return makeDatetime(
37910
- d.getFullYear(),
37911
- d.getMonth() + 1,
37912
- d.getDate(),
37913
- 0,
37914
- 0,
37915
- 0
37916
- );
38078
+ if (lc === "today" || lc === "yesterday" || lc === "tomorrow") {
38079
+ const offset = lc === "tomorrow" ? 1 : lc === "yesterday" ? -1 : 0;
38080
+ const [Y, M, D] = todayParts(offset, utc);
38081
+ return finish(makeDatetime(Y, M, D, 0, 0, 0));
37917
38082
  }
37918
38083
  throw new RuntimeError(
37919
38084
  `datetime: unrecognized relative-day string '${s}'`
37920
38085
  );
37921
38086
  }
37922
38087
  }
37923
- if (args.length === 3) {
37924
- const flag = argToString(args[1]);
37925
- const kind = argToString(args[2]);
38088
+ if (positional.length === 3) {
38089
+ const flag = argToString(positional[1]);
38090
+ const kind = argToString(positional[2]);
37926
38091
  if (flag !== null && flag.toLowerCase() === "convertfrom") {
37927
38092
  if (kind === null)
37928
38093
  throw new RuntimeError("datetime: dateType must be text");
37929
- const x = toNumber(args[0]);
38094
+ const x = toNumber(positional[0]);
37930
38095
  const k = kind.toLowerCase();
37931
- if (k === "datenum") return datetimeFromDatenum(x);
37932
- if (k === "posixtime") return datetimeFromUtcMs(x * 1e3);
38096
+ if (k === "datenum") return finish(datetimeFromDatenum(x));
38097
+ if (k === "posixtime") return finish(datetimeFromUtcMs(x * 1e3));
37933
38098
  if (k === "excel") {
37934
- return datetimeFromUtcMs((x - 25569) * 864e5);
38099
+ return finish(datetimeFromUtcMs((x - 25569) * 864e5));
37935
38100
  }
37936
38101
  if (k === "excel1904") {
37937
- return datetimeFromUtcMs((x - 24107) * 864e5);
38102
+ return finish(datetimeFromUtcMs((x - 24107) * 864e5));
37938
38103
  }
37939
38104
  throw new RuntimeError(
37940
38105
  `datetime: unsupported ConvertFrom type '${kind}'`
37941
38106
  );
37942
38107
  }
37943
38108
  }
37944
- if (args.length === 3 || args.length === 6 || args.length === 7) {
37945
- const Y = toNumber(args[0]);
37946
- const M = toNumber(args[1]);
37947
- const D = toNumber(args[2]);
37948
- const H2 = args.length >= 6 ? toNumber(args[3]) : 0;
37949
- const MI = args.length >= 6 ? toNumber(args[4]) : 0;
37950
- const S = args.length >= 6 ? toNumber(args[5]) : 0;
37951
- const MS = args.length >= 7 ? toNumber(args[6]) : 0;
37952
- return makeDatetime(Y, M, D, H2, MI, S + MS / 1e3);
38109
+ if (positional.length === 3 || positional.length === 6 || positional.length === 7) {
38110
+ const Y = toNumber(positional[0]);
38111
+ const M = toNumber(positional[1]);
38112
+ const D = toNumber(positional[2]);
38113
+ const H2 = positional.length >= 6 ? toNumber(positional[3]) : 0;
38114
+ const MI = positional.length >= 6 ? toNumber(positional[4]) : 0;
38115
+ const S = positional.length >= 6 ? toNumber(positional[5]) : 0;
38116
+ const MS = positional.length >= 7 ? toNumber(positional[6]) : 0;
38117
+ return finish(makeDatetime(Y, M, D, H2, MI, S + MS / 1e3));
37953
38118
  }
37954
38119
  throw new RuntimeError(
37955
38120
  `datetime: unsupported argument pattern (nargin=${args.length})`
@@ -39906,6 +40071,7 @@ var SPECIAL_BUILTIN_NAMES = [
39906
40071
  "copyfile",
39907
40072
  "fileattrib",
39908
40073
  "unzip",
40074
+ "zip",
39909
40075
  "dir",
39910
40076
  "warning",
39911
40077
  "input",
@@ -41018,6 +41184,26 @@ function registerSpecialBuiltins(rt) {
41018
41184
  }
41019
41185
  return void 0;
41020
41186
  });
41187
+ registerSpecial("zip", (nargout, args) => {
41188
+ const io = requireFileIO();
41189
+ if (!io.zip)
41190
+ throw new RuntimeError("zip is not available in this environment");
41191
+ const margs = args.map((a) => ensureRuntimeValue(a));
41192
+ if (margs.length < 2)
41193
+ throw new RuntimeError("zip requires at least 2 arguments");
41194
+ let zipfilename = toString(margs[0]);
41195
+ const base = zipfilename.split(/[/\\]/).pop() ?? zipfilename;
41196
+ if (!base.includes(".")) zipfilename += ".zip";
41197
+ const fnames = margs[1];
41198
+ const filenames = isRuntimeCell(fnames) ? fnames.data.map((v) => toString(ensureRuntimeValue(v))) : [toString(fnames)];
41199
+ const rootdir = margs.length >= 3 ? toString(margs[2]) : ".";
41200
+ const entryNames = io.zip(zipfilename, filenames, rootdir);
41201
+ if (nargout >= 1) {
41202
+ const cellData = entryNames.map((f) => RTV.char(f));
41203
+ return RTV.cell(cellData, [1, cellData.length]);
41204
+ }
41205
+ return void 0;
41206
+ });
41021
41207
  registerSpecial("dir", (nargout, args) => {
41022
41208
  const io = requireFileIO();
41023
41209
  if (!io.listDir)
@@ -43278,8 +43464,8 @@ function padExtent(e, tight) {
43278
43464
  return [min2, max2];
43279
43465
  }
43280
43466
  if (tight) return [min2, max2];
43281
- const pad = (max2 - min2) * 0.05;
43282
- return [min2 - pad, max2 + pad];
43467
+ const pad2 = (max2 - min2) * 0.05;
43468
+ return [min2 - pad2, max2 + pad2];
43283
43469
  }
43284
43470
  function applyExplicit(auto, explicit) {
43285
43471
  if (!explicit) return auto;
@@ -49484,13 +49670,19 @@ registerIBuiltin({
49484
49670
  resolve: (argTypes) => {
49485
49671
  if (argTypes.length !== 1) return null;
49486
49672
  const a = argTypes[0];
49487
- if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor" && a.kind !== "cell" && a.kind !== "unknown")
49673
+ if (a.kind !== "char" && a.kind !== "string" && a.kind !== "number" && a.kind !== "tensor" && a.kind !== "cell" && a.kind !== "unknown" && // datetime/duration only — other classes keep their own char methods
49674
+ // (or the generic "does not support these argument types" error).
49675
+ !(a.kind === "class_instance" && (a.className === "datetime" || a.className === "duration")))
49488
49676
  return null;
49489
49677
  return {
49490
49678
  outputTypes: [{ kind: "char" }],
49491
49679
  apply: (args) => {
49492
49680
  const v = args[0];
49493
49681
  if (isRuntimeChar(v)) return v;
49682
+ if (isRuntimeClassInstance(v)) {
49683
+ const s = formatDatetimeOrDuration(v);
49684
+ if (s !== null) return RTV.char(s);
49685
+ }
49494
49686
  if (isRuntimeString(v)) return RTV.char(v);
49495
49687
  if (isRuntimeNumber(v))
49496
49688
  return RTV.char(String.fromCharCode(Math.round(v)));
@@ -51878,7 +52070,7 @@ defineBuiltin({
51878
52070
  const result = invComplexJS(A.data, A.imag, n);
51879
52071
  return RTV.tensor(result.re, [n, n], result.im);
51880
52072
  }
51881
- const bridge = getEffectiveBridge("inv");
52073
+ const bridge = getEffectiveBridge("inv", "inv");
51882
52074
  return RTV.tensor(bridge.inv(toF64(A.data), n), [n, n]);
51883
52075
  }
51884
52076
  }
@@ -54225,6 +54417,16 @@ defineBuiltin({
54225
54417
  parseReshapeDims(args, 1);
54226
54418
  return v;
54227
54419
  }
54420
+ if (isRuntimeCell(v)) {
54421
+ const total = v.data.length;
54422
+ const s = parseReshapeDims(args, total);
54423
+ while (s.length > 2 && s[s.length - 1] === 1) s.pop();
54424
+ if (s.length > 2)
54425
+ throw new RuntimeError("reshape: cell arrays must be 2-D");
54426
+ const nr = s[0];
54427
+ const nc = s.length >= 2 ? s[1] : 1;
54428
+ return RTV.cell(v.data.slice(), [nr, nc]);
54429
+ }
54228
54430
  if (isRuntimeClassInstanceArray(v)) {
54229
54431
  const total = v.elements.length;
54230
54432
  const s = parseReshapeDims(args, total);
@@ -64075,7 +64277,7 @@ function getSourceLine(getSource, file, line) {
64075
64277
  }
64076
64278
 
64077
64279
  // src/numbl-core/version.ts
64078
- var NUMBL_VERSION = "0.4.11";
64280
+ var NUMBL_VERSION = "0.4.13";
64079
64281
 
64080
64282
  // src/cli-repl.ts
64081
64283
  import { createInterface } from "readline";
@@ -66709,6 +66911,12 @@ function evalMember(expr, nargout) {
66709
66911
  }
66710
66912
  }
66711
66913
  const base = this.evalExpr(expr.base);
66914
+ const baseRv = ensureRuntimeValue(base);
66915
+ if (isRuntimeStructArray(baseRv) && baseRv.elements.length !== 1) {
66916
+ return baseRv.elements.map(
66917
+ (el) => ensureRuntimeValue(this.rt.getMember(el, expr.name))
66918
+ );
66919
+ }
66712
66920
  return this.rt.getMember(base, expr.name);
66713
66921
  }
66714
66922
  function tryExtractDottedName(expr) {
@@ -66920,12 +67128,31 @@ function evalCellLiteral(expr) {
66920
67128
  }
66921
67129
  return RTV.cell(elements2, [1, elements2.length]);
66922
67130
  }
66923
- const numRows = expr.rows.length;
66924
- const numCols = expr.rows[0].length;
67131
+ const rowValues = expr.rows.map((row) => {
67132
+ const vals = [];
67133
+ for (const e of row) {
67134
+ const v = this.evalExpr(e);
67135
+ if (Array.isArray(v)) {
67136
+ for (const elem of v) vals.push(ensureRuntimeValue(elem));
67137
+ } else {
67138
+ vals.push(ensureRuntimeValue(v));
67139
+ }
67140
+ }
67141
+ return vals;
67142
+ });
67143
+ const numRows = rowValues.length;
67144
+ const numCols = rowValues[0].length;
67145
+ for (const rv of rowValues) {
67146
+ if (rv.length !== numCols) {
67147
+ throw new RuntimeError(
67148
+ "Cell array rows must have the same number of columns"
67149
+ );
67150
+ }
67151
+ }
66925
67152
  const elements = [];
66926
67153
  for (let c = 0; c < numCols; c++) {
66927
67154
  for (let r = 0; r < numRows; r++) {
66928
- elements.push(ensureRuntimeValue(this.evalExpr(expr.rows[r][c])));
67155
+ elements.push(rowValues[r][c]);
66929
67156
  }
66930
67157
  }
66931
67158
  return RTV.cell(elements, [numRows, numCols]);
@@ -71400,8 +71627,8 @@ function mtoc2_sprintf_format(fmt, args) {
71400
71627
  if (width > 0) {
71401
71628
  const padChar = zeroPad ? "0" : " ";
71402
71629
  const padLen = Math.max(0, width - sign.length - s.length);
71403
- const pad = padChar.repeat(padLen);
71404
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
71630
+ const pad2 = padChar.repeat(padLen);
71631
+ result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad2 + s : pad2 + sign + s;
71405
71632
  } else {
71406
71633
  result += sign + s;
71407
71634
  }
@@ -71686,8 +71913,8 @@ function disp_complex_slice(re, im, offset, rows, cols) {
71686
71913
  for (let c = 0; c < cols; c++) {
71687
71914
  const idx = r + c * rows;
71688
71915
  const cell = cells[idx];
71689
- const pad = colWidths[c] - cell.length;
71690
- for (let i = 0; i < pad; i++) line += " ";
71916
+ const pad2 = colWidths[c] - cell.length;
71917
+ for (let i = 0; i < pad2; i++) line += " ";
71691
71918
  line += cell;
71692
71919
  if (c < cols - 1) line += " ";
71693
71920
  }
@@ -73099,6 +73326,14 @@ function mtoc2_tensor_mtimes_real(a, b) {
73099
73326
  `mtoc2_tensor_mtimes_real: inner dimensions disagree (${k} vs ${k2})`
73100
73327
  );
73101
73328
  }
73329
+ if (typeof $matmulAccel === "function") {
73330
+ const accel = $matmulAccel(a.data, m, k, b.data, n);
73331
+ if (accel) {
73332
+ const acc = mtoc2_tensor_alloc(m, n);
73333
+ acc.data.set(accel);
73334
+ return acc;
73335
+ }
73336
+ }
73102
73337
  const r = mtoc2_tensor_alloc(m, n);
73103
73338
  for (let j = 0; j < n; j++) {
73104
73339
  for (let i = 0; i < m; i++) {
@@ -74310,7 +74545,7 @@ var JS_SNIPPETS = {
74310
74545
  "tensor_logspace.js": "// JS sibling of `tensor_logspace.h`. Build a 1\xD7n row tensor of n\n// logarithmically-spaced values from 10^a to 10^b. Byte-for-byte with\n// numbl's interpreter `logspace`, including the MATLAB special case\n// where an upper limit of exactly `pi` makes the last point `pi`.\n\n\nfunction mtoc2_tensor_logspace(a, b, n) {\n if (n <= 0) return mtoc2_tensor_alloc(1, 0);\n const isPi = b === Math.PI;\n const endVal = isPi ? Math.PI : Math.pow(10, b);\n const out = mtoc2_tensor_alloc(1, n);\n if (n === 1) {\n out.data[0] = endVal;\n return out;\n }\n if (isPi) {\n const logStart = Math.log10(Math.pow(10, a));\n const logEnd = Math.log10(Math.PI);\n for (let i = 0; i < n; i++) {\n const t = logStart + ((logEnd - logStart) * i) / (n - 1);\n out.data[i] = Math.pow(10, t);\n }\n } else {\n for (let i = 0; i < n; i++) {\n const t = a + ((b - a) * i) / (n - 1);\n out.data[i] = Math.pow(10, t);\n }\n }\n return out;\n}\n",
74311
74546
  "tensor_meshgrid.js": "// JS sibling of `tensor_meshgrid.h`. MATLAB-style coordinate grid.\n\n\nfunction numel(t) {\n let n = 1;\n for (const s of t.shape) n *= s;\n return n;\n}\n\nfunction mtoc2_meshgrid_x(x, y) {\n const M = numel(x);\n const N = numel(y);\n const X = mtoc2_tensor_alloc(N, M);\n for (let j = 0; j < M; j++) {\n const xj = x.data[j];\n for (let i = 0; i < N; i++) X.data[i + j * N] = xj;\n }\n return X;\n}\n\nfunction mtoc2_meshgrid(x, y) {\n const M = numel(x);\n const N = numel(y);\n const X = mtoc2_tensor_alloc(N, M);\n const Y = mtoc2_tensor_alloc(N, M);\n for (let j = 0; j < M; j++) {\n const xj = x.data[j];\n for (let i = 0; i < N; i++) {\n X.data[i + j * N] = xj;\n Y.data[i + j * N] = y.data[i];\n }\n }\n return { X, Y };\n}\n\nfunction mtoc2_meshgrid_1arg(x) {\n return mtoc2_meshgrid(x, x);\n}\n",
74312
74547
  "tensor_mtimes_complex.js": '// JS sibling of `tensor_mtimes_complex.h`. 2-D matrix multiplication\n// producing a complex result. Handles all three mixed cases\n// (complex \xD7 complex, complex \xD7 real, real \xD7 complex) by treating an\n// undefined imag lane as implicit zeros.\n\n\nfunction mtoc2_tensor_mtimes_complex_scalar(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_complex_scalar: inputs must be 2-D");\n }\n if (a.shape[0] !== 1 || b.shape[1] !== 1) {\n throw new Error("mtoc2_tensor_mtimes_complex_scalar: requires 1\xD7k * k\xD71");\n }\n if (a.shape[1] !== b.shape[0]) {\n throw new Error("mtoc2_tensor_mtimes_complex_scalar: inner-dim mismatch");\n }\n const k = a.shape[1];\n const aim = a.imag;\n const bim = b.imag;\n let accR = 0;\n let accI = 0;\n for (let p = 0; p < k; p++) {\n const ar = a.data[p];\n const ai = aim !== undefined ? aim[p] : 0;\n const br = b.data[p];\n const bi = bim !== undefined ? bim[p] : 0;\n accR += ar * br - ai * bi;\n accI += ar * bi + ai * br;\n }\n return { re: accR, im: accI };\n}\n\nfunction mtoc2_tensor_mtimes_complex(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_complex: inputs must be 2-D");\n }\n const m = a.shape[0];\n const k = a.shape[1];\n const k2 = b.shape[0];\n const n = b.shape[1];\n if (k !== k2) {\n throw new Error(\n `mtoc2_tensor_mtimes_complex: inner-dim mismatch (${m}\xD7${k} * ${k2}\xD7${n})`\n );\n }\n const out = mtoc2_tensor_alloc_complex(m, n);\n const aim = a.imag;\n const bim = b.imag;\n for (let j = 0; j < n; j++) {\n for (let i = 0; i < m; i++) {\n let accR = 0;\n let accI = 0;\n for (let p = 0; p < k; p++) {\n const ar = a.data[i + p * m];\n const ai = aim !== undefined ? aim[i + p * m] : 0;\n const br = b.data[p + j * k];\n const bi = bim !== undefined ? bim[p + j * k] : 0;\n accR += ar * br - ai * bi;\n accI += ar * bi + ai * br;\n }\n out.data[i + j * m] = accR;\n out.imag[i + j * m] = accI;\n }\n }\n return out;\n}\n',
74313
- "tensor_mtimes_real.js": '// JS sibling of `tensor_mtimes_real.h`. Real 2-D matrix\n// multiplication A * B. Column-major in, column-major out.\n// `mtoc2_tensor_mtimes_real_scalar` mirrors the C 1\xD7k * k\xD71\n// inner-product path that returns a bare number instead of a\n// freshly-allocated 1\xD71 tensor.\n\n\nfunction mtoc2_tensor_mtimes_real(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_real: inputs must be 2-D");\n }\n const m = a.shape[0];\n const k = a.shape[1];\n const k2 = b.shape[0];\n const n = b.shape[1];\n if (k !== k2) {\n throw new Error(\n `mtoc2_tensor_mtimes_real: inner dimensions disagree (${k} vs ${k2})`\n );\n }\n const r = mtoc2_tensor_alloc(m, n);\n for (let j = 0; j < n; j++) {\n for (let i = 0; i < m; i++) {\n let s = 0;\n for (let p = 0; p < k; p++) {\n s += a.data[i + p * m] * b.data[p + j * k];\n }\n r.data[i + j * m] = s;\n }\n }\n return r;\n}\n\nfunction mtoc2_tensor_mtimes_real_scalar(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_real_scalar: inputs must be 2-D");\n }\n // The translate-time type system has already proven a is 1\xD7k and b\n // is k\xD71; this helper trusts that and just runs the inner product.\n const k = a.shape[1];\n let s = 0;\n for (let p = 0; p < k; p++) {\n s += a.data[p * a.shape[0]] * b.data[p];\n }\n return s;\n}\n',
74548
+ "tensor_mtimes_real.js": '// JS sibling of `tensor_mtimes_real.h`. Real 2-D matrix\n// multiplication A * B. Column-major in, column-major out.\n// `mtoc2_tensor_mtimes_real_scalar` mirrors the C 1\xD7k * k\xD71\n// inner-product path that returns a bare number instead of a\n// freshly-allocated 1\xD71 tensor.\n\n\nfunction mtoc2_tensor_mtimes_real(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_real: inputs must be 2-D");\n }\n const m = a.shape[0];\n const k = a.shape[1];\n const k2 = b.shape[0];\n const n = b.shape[1];\n if (k !== k2) {\n throw new Error(\n `mtoc2_tensor_mtimes_real: inner dimensions disagree (${k} vs ${k2})`\n );\n }\n // Optional WASM LAPACK accelerator. When a host installs `$matmulAccel`\n // (browser worker, after loading a bridge), delegate the multiply; a\n // falsy return means "declined" (too small / error) and we fall through\n // to the JS loop below. `typeof` guard is safe even when unset.\n if (typeof $matmulAccel === "function") {\n const accel = $matmulAccel(a.data, m, k, b.data, n);\n if (accel) {\n const acc = mtoc2_tensor_alloc(m, n);\n acc.data.set(accel);\n return acc;\n }\n }\n const r = mtoc2_tensor_alloc(m, n);\n for (let j = 0; j < n; j++) {\n for (let i = 0; i < m; i++) {\n let s = 0;\n for (let p = 0; p < k; p++) {\n s += a.data[i + p * m] * b.data[p + j * k];\n }\n r.data[i + j * m] = s;\n }\n }\n return r;\n}\n\nfunction mtoc2_tensor_mtimes_real_scalar(a, b) {\n if (a.shape.length !== 2 || b.shape.length !== 2) {\n throw new Error("mtoc2_tensor_mtimes_real_scalar: inputs must be 2-D");\n }\n // The translate-time type system has already proven a is 1\xD7k and b\n // is k\xD71; this helper trusts that and just runs the inner product.\n const k = a.shape[1];\n let s = 0;\n for (let p = 0; p < k; p++) {\n s += a.data[p * a.shape[0]] * b.data[p];\n }\n return s;\n}\n',
74314
74549
  "tensor_norm.js": "// JS sibling of `tensor_norm.h`. Vector norms for real and complex\n// tensors. The complex variants tolerate a missing imag lane (real\n// tensor flowing through a complex-typed norm route) by treating it\n// as zero.\n\nfunction mtoc2_norm2_real(a) {\n let acc = 0;\n for (let i = 0; i < a.data.length; i++) {\n const x = a.data[i];\n acc += x * x;\n }\n return Math.sqrt(acc);\n}\n\nfunction mtoc2_norm2_complex(a) {\n let acc = 0;\n const im = a.imag;\n for (let i = 0; i < a.data.length; i++) {\n const re = a.data[i];\n const imv = im !== undefined ? im[i] : 0;\n acc += re * re + imv * imv;\n }\n return Math.sqrt(acc);\n}\n\nfunction mtoc2_norm_p_real(a, p) {\n const n = a.data.length;\n if (n === 0) return 0;\n if (p === Infinity) {\n let m = 0;\n for (let i = 0; i < n; i++) {\n const x = Math.abs(a.data[i]);\n if (x > m) m = x;\n }\n return m;\n }\n if (p === -Infinity) {\n let m = Math.abs(a.data[0]);\n for (let i = 1; i < n; i++) {\n const x = Math.abs(a.data[i]);\n if (x < m) m = x;\n }\n return m;\n }\n if (p === 1) {\n let acc = 0;\n for (let i = 0; i < n; i++) acc += Math.abs(a.data[i]);\n return acc;\n }\n if (p === 2) return mtoc2_norm2_real(a);\n let acc = 0;\n for (let i = 0; i < n; i++) acc += Math.pow(Math.abs(a.data[i]), p);\n return Math.pow(acc, 1 / p);\n}\n\nfunction mtoc2_norm_p_complex(a, p) {\n const n = a.data.length;\n if (n === 0) return 0;\n const im = a.imag;\n const abs = i => {\n const re = a.data[i];\n const imv = im !== undefined ? im[i] : 0;\n return Math.hypot(re, imv);\n };\n if (p === Infinity) {\n let m = 0;\n for (let i = 0; i < n; i++) {\n const x = abs(i);\n if (x > m) m = x;\n }\n return m;\n }\n if (p === -Infinity) {\n let m = abs(0);\n for (let i = 1; i < n; i++) {\n const x = abs(i);\n if (x < m) m = x;\n }\n return m;\n }\n if (p === 2) return mtoc2_norm2_complex(a);\n if (p === 1) {\n let acc = 0;\n for (let i = 0; i < n; i++) acc += abs(i);\n return acc;\n }\n let acc = 0;\n for (let i = 0; i < n; i++) acc += Math.pow(abs(i), p);\n return Math.pow(acc, 1 / p);\n}\n",
74315
74550
  "tensor_ones_nd.js": "// JS sibling of `tensor_ones_nd.h`. Fill the freshly-allocated\n// tensor with `1.0`.\n\n\nfunction mtoc2_tensor_ones_nd(ndim, dims) {\n const t = mtoc2_tensor_alloc_nd(ndim, dims);\n t.data.fill(1);\n return t;\n}\n",
74316
74551
  "tensor_ones_square.js": "// JS sibling of `tensor_ones_square.h`. See `tensor_zeros_square.js`\n// for the rationale.\n\n\nfunction mtoc2_tensor_ones_square(n) {\n return mtoc2_tensor_ones_nd(2, [n, n]);\n}\n",
@@ -82980,9 +83215,9 @@ function tileExact(src, padShape2, padReps) {
82980
83215
  }
82981
83216
  return out;
82982
83217
  }
82983
- function padTo(arr, pad, len) {
83218
+ function padTo(arr, pad2, len) {
82984
83219
  const out = arr.slice();
82985
- while (out.length < len) out.push(pad);
83220
+ while (out.length < len) out.push(pad2);
82986
83221
  return out;
82987
83222
  }
82988
83223
  function trimTrailingOnes(shape) {
@@ -96988,10 +97223,10 @@ import {
96988
97223
  chmodSync,
96989
97224
  realpathSync
96990
97225
  } from "fs";
96991
- import { unzipSync } from "fflate";
97226
+ import { unzipSync, zipSync } from "fflate";
96992
97227
  import { execFileSync } from "child_process";
96993
97228
  import { homedir, tmpdir } from "os";
96994
- import { join as join3, resolve, dirname, basename } from "path";
97229
+ import { join as join3, resolve, relative, dirname, basename } from "path";
96995
97230
 
96996
97231
  // src/cli-scan.ts
96997
97232
  import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
@@ -97474,6 +97709,69 @@ var NodeFileIOAdapter = class {
97474
97709
  }
97475
97710
  return extracted;
97476
97711
  }
97712
+ zip(zipfilename, filenames, rootDir) {
97713
+ const root = resolve(expandTilde(rootDir));
97714
+ const dest = resolve(expandTilde(zipfilename));
97715
+ const entries = {};
97716
+ const names = [];
97717
+ const toEntryName = (abs2) => {
97718
+ const rel = relative(root, abs2);
97719
+ if (rel.startsWith("..")) return basename(abs2);
97720
+ return rel.split("\\").join("/");
97721
+ };
97722
+ const addFile = (abs2) => {
97723
+ const name = toEntryName(abs2);
97724
+ const buf = readFileSync3(abs2);
97725
+ entries[name] = new Uint8Array(
97726
+ buf.buffer,
97727
+ buf.byteOffset,
97728
+ buf.byteLength
97729
+ );
97730
+ names.push(name);
97731
+ };
97732
+ const addDir = (absDir) => {
97733
+ for (const entry of readdirSync2(absDir)) {
97734
+ const abs2 = join3(absDir, entry);
97735
+ let st2;
97736
+ try {
97737
+ st2 = statSync2(abs2);
97738
+ } catch {
97739
+ continue;
97740
+ }
97741
+ if (st2.isDirectory()) addDir(abs2);
97742
+ else addFile(abs2);
97743
+ }
97744
+ };
97745
+ const addPath = (abs2) => {
97746
+ const st2 = statSync2(abs2);
97747
+ if (st2.isDirectory()) addDir(abs2);
97748
+ else addFile(abs2);
97749
+ };
97750
+ for (const f of filenames) {
97751
+ const p2 = expandTilde(f);
97752
+ const abs2 = resolve(root, p2);
97753
+ const last = basename(p2);
97754
+ if (last.includes("*") || last.includes("?")) {
97755
+ const dir = dirname(abs2);
97756
+ const re = new RegExp(
97757
+ "^" + last.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
97758
+ );
97759
+ let dirEntries;
97760
+ try {
97761
+ dirEntries = readdirSync2(dir);
97762
+ } catch {
97763
+ continue;
97764
+ }
97765
+ for (const entry of dirEntries) {
97766
+ if (re.test(entry)) addPath(join3(dir, entry));
97767
+ }
97768
+ } else {
97769
+ addPath(abs2);
97770
+ }
97771
+ }
97772
+ writeFileSync(dest, zipSync(entries));
97773
+ return names;
97774
+ }
97477
97775
  listDir(dirPath) {
97478
97776
  const p2 = expandTilde(dirPath);
97479
97777
  if (p2.includes("**")) {
@@ -97579,51 +97877,43 @@ var NodeFileIOAdapter = class {
97579
97877
  return results;
97580
97878
  }
97581
97879
  listDirRecursive(pattern) {
97582
- const idx = pattern.indexOf("**");
97583
- let baseDir = pattern.slice(0, idx);
97584
- if (baseDir.endsWith("/") || baseDir.endsWith("\\")) {
97880
+ const norm2 = pattern.replace(/\\/g, "/");
97881
+ const idx = norm2.indexOf("**");
97882
+ let baseDir = norm2.slice(0, idx);
97883
+ if (baseDir.endsWith("/")) {
97585
97884
  baseDir = baseDir.slice(0, -1);
97586
97885
  }
97587
97886
  if (!baseDir) baseDir = ".";
97588
97887
  const absBase = resolve(baseDir);
97888
+ try {
97889
+ if (!statSync2(absBase).isDirectory()) return [];
97890
+ } catch {
97891
+ return [];
97892
+ }
97893
+ const suffix = norm2.slice(idx);
97894
+ const DIRS = "\0";
97895
+ const ANY = "";
97896
+ const re = new RegExp(
97897
+ "^" + suffix.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, DIRS).replace(/\*\*/g, ANY).replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(new RegExp(DIRS, "g"), "(?:.*/)?").replace(new RegExp(ANY, "g"), ".*") + "$"
97898
+ );
97589
97899
  const results = [];
97590
- const walkDir = (dir) => {
97900
+ const pushIfMatch = (rel, name, folder, bytes, isdir, mtimeMs) => {
97901
+ if (re.test(rel)) results.push({ name, folder, bytes, isdir, mtimeMs });
97902
+ };
97903
+ const walkDir = (dir, rel) => {
97904
+ const relPrefix = rel ? rel + "/" : "";
97905
+ let dirMtime = 0;
97591
97906
  try {
97592
- const ds = statSync2(dir);
97593
- results.push({
97594
- name: ".",
97595
- folder: dir,
97596
- bytes: 0,
97597
- isdir: true,
97598
- mtimeMs: ds.mtimeMs
97599
- });
97907
+ dirMtime = statSync2(dir).mtimeMs;
97600
97908
  } catch {
97601
- results.push({
97602
- name: ".",
97603
- folder: dir,
97604
- bytes: 0,
97605
- isdir: true,
97606
- mtimeMs: 0
97607
- });
97608
97909
  }
97910
+ pushIfMatch(relPrefix + ".", ".", dir, 0, true, dirMtime);
97911
+ let parentMtime = 0;
97609
97912
  try {
97610
- const ps = statSync2(resolve(dir, ".."));
97611
- results.push({
97612
- name: "..",
97613
- folder: dir,
97614
- bytes: 0,
97615
- isdir: true,
97616
- mtimeMs: ps.mtimeMs
97617
- });
97913
+ parentMtime = statSync2(resolve(dir, "..")).mtimeMs;
97618
97914
  } catch {
97619
- results.push({
97620
- name: "..",
97621
- folder: dir,
97622
- bytes: 0,
97623
- isdir: true,
97624
- mtimeMs: 0
97625
- });
97626
97915
  }
97916
+ pushIfMatch(relPrefix + "..", "..", dir, 0, true, parentMtime);
97627
97917
  let entries;
97628
97918
  try {
97629
97919
  entries = readdirSync2(dir);
@@ -97634,22 +97924,23 @@ var NodeFileIOAdapter = class {
97634
97924
  for (const entry of entries) {
97635
97925
  try {
97636
97926
  const es = statSync2(join3(dir, entry));
97637
- results.push({
97638
- name: entry,
97639
- folder: dir,
97640
- bytes: es.isDirectory() ? 0 : es.size,
97641
- isdir: es.isDirectory(),
97642
- mtimeMs: es.mtimeMs
97643
- });
97927
+ pushIfMatch(
97928
+ relPrefix + entry,
97929
+ entry,
97930
+ dir,
97931
+ es.isDirectory() ? 0 : es.size,
97932
+ es.isDirectory(),
97933
+ es.mtimeMs
97934
+ );
97644
97935
  if (es.isDirectory()) subdirs.push(entry);
97645
97936
  } catch {
97646
97937
  }
97647
97938
  }
97648
97939
  for (const sub2 of subdirs) {
97649
- walkDir(join3(dir, sub2));
97940
+ walkDir(join3(dir, sub2), relPrefix + sub2);
97650
97941
  }
97651
97942
  };
97652
- walkDir(absBase);
97943
+ walkDir(absBase, "");
97653
97944
  return results;
97654
97945
  }
97655
97946
  getEntry(fid) {
@@ -100069,7 +100360,7 @@ function unzipToFiles(zipData) {
100069
100360
  }
100070
100361
 
100071
100362
  // src/cli.ts
100072
- import { zipSync } from "fflate";
100363
+ import { zipSync as zipSync2 } from "fflate";
100073
100364
 
100074
100365
  // src/numbl-core/native/qhull-node.ts
100075
100366
  var promise = null;
@@ -100094,8 +100385,8 @@ function variantAddonPath(fastMath) {
100094
100385
  const name = fastMath ? "numbl_addon.fastmath.node" : "numbl_addon.nofastmath.node";
100095
100386
  return join7(packageDir, "build", "Release", name);
100096
100387
  }
100097
- function loadNativeAddon(fastMath) {
100098
- if (process.env.NUMBL_NO_NATIVE) return;
100388
+ function loadNativeAddon(fastMath, noNative = false) {
100389
+ if (noNative || process.env.NUMBL_NO_NATIVE) return;
100099
100390
  const variantPath = variantAddonPath(fastMath);
100100
100391
  const flag = fastMath ? " --fast-math" : "";
100101
100392
  if (!existsSync3(variantPath)) {
@@ -100219,8 +100510,8 @@ function findTestFiles(dir) {
100219
100510
  walk2(dir);
100220
100511
  return results;
100221
100512
  }
100222
- async function runTests(dir, optimization, fastMath) {
100223
- loadNativeAddon(fastMath ?? false);
100513
+ async function runTests(dir, optimization, fastMath, noNative) {
100514
+ loadNativeAddon(fastMath ?? false, noNative ?? false);
100224
100515
  await loadQhullBackend();
100225
100516
  const absDir = resolve2(process.cwd(), dir);
100226
100517
  const testFiles = findTestFiles(absDir);
@@ -100232,7 +100523,7 @@ async function runTests(dir, optimization, fastMath) {
100232
100523
  let fail = 0;
100233
100524
  const failedScripts = [];
100234
100525
  for (const filepath of testFiles) {
100235
- const rel = relative(process.cwd(), filepath);
100526
+ const rel = relative2(process.cwd(), filepath);
100236
100527
  const source = readFileSync6(filepath, "utf-8");
100237
100528
  const mainFileName = filepath;
100238
100529
  const scriptDir = dirname3(filepath);
@@ -100351,9 +100642,12 @@ Options (for run and eval):
100351
100642
  across --opt levels). Off by default so all --opt
100352
100643
  levels agree; --no-fast-math is accepted as the
100353
100644
  (now default) opt-out.
100645
+ --no-native Do not load the native LAPACK addon; use the pure-JS
100646
+ fallbacks (same effect as NUMBL_NO_NATIVE=1)
100354
100647
 
100355
100648
  Environment variables:
100356
- NUMBL_PATH Extra workspace directories (separated by ${delimiter})`);
100649
+ NUMBL_PATH Extra workspace directories (separated by ${delimiter})
100650
+ NUMBL_NO_NATIVE If set, do not load the native LAPACK addon`);
100357
100651
  }
100358
100652
  function parseOptions(args) {
100359
100653
  const opts = {
@@ -100372,7 +100666,8 @@ function parseOptions(args) {
100372
100666
  // and vectorizes transcendentals, so results drift from the JIT
100373
100667
  // kernels and diverge across --opt levels. Opt back in with
100374
100668
  // `--fast-math` (and a matching `build-addon --fast-math`).
100375
- fastMath: false
100669
+ fastMath: false,
100670
+ noNative: false
100376
100671
  };
100377
100672
  const envPath = process.env.NUMBL_PATH;
100378
100673
  if (envPath) {
@@ -100410,6 +100705,9 @@ function parseOptions(args) {
100410
100705
  case "--no-fast-math":
100411
100706
  opts.fastMath = false;
100412
100707
  break;
100708
+ case "--no-native":
100709
+ opts.noNative = true;
100710
+ break;
100413
100711
  case "--dump-ast":
100414
100712
  opts.dumpAst = true;
100415
100713
  break;
@@ -100583,7 +100881,7 @@ async function cmdEval(args) {
100583
100881
  );
100584
100882
  }
100585
100883
  async function executeWithOptions(code, mainFileName, workspaceFiles, opts, searchPaths) {
100586
- loadNativeAddon(opts.fastMath);
100884
+ loadNativeAddon(opts.fastMath, opts.noNative);
100587
100885
  await loadQhullBackend();
100588
100886
  const profiling = !!opts.profileOutput;
100589
100887
  const totalStart = performance.now();
@@ -100895,7 +101193,7 @@ function collectSiteFiles(projectDir, outDirAbs, ignore) {
100895
101193
  for (const e of entries) {
100896
101194
  const full = join7(dir, e.name);
100897
101195
  if (resolve2(full) === outDirAbs) continue;
100898
- const rel = relative(projectDir, full).split(sep).join("/");
101196
+ const rel = relative2(projectDir, full).split(sep).join("/");
100899
101197
  if (matchesIgnore(rel)) continue;
100900
101198
  if (e.isDirectory()) {
100901
101199
  if (SITE_DEFAULT_IGNORE_DIRS.has(e.name)) continue;
@@ -101006,7 +101304,7 @@ async function cmdBuildSite(args) {
101006
101304
  zipEntries["numbl-project.json"] = new TextEncoder().encode(
101007
101305
  JSON.stringify(manifest, null, 2)
101008
101306
  );
101009
- const zip = zipSync(zipEntries, { level: 6 });
101307
+ const zip = zipSync2(zipEntries, { level: 6 });
101010
101308
  writeFileSync4(join7(outDirAbs, "project.zip"), zip);
101011
101309
  const totalMb = zip.length / 1048576;
101012
101310
  console.log(
@@ -101071,7 +101369,7 @@ function cmdListBuiltins(flags) {
101071
101369
  }
101072
101370
  async function cmdRepl(args) {
101073
101371
  const opts = parseOptions(args);
101074
- loadNativeAddon(opts.fastMath);
101372
+ loadNativeAddon(opts.fastMath, opts.noNative);
101075
101373
  await ensureMipCorePackage();
101076
101374
  const replSearchPaths = [...opts.extraPaths];
101077
101375
  const replFiles = opts.extraPaths.flatMap((d) => scanMFiles(d));
@@ -101104,7 +101402,7 @@ function cmdShowProfile(args) {
101104
101402
  }
101105
101403
  const data = JSON.parse(readFileSync6(filepath, "utf-8"));
101106
101404
  const fmt = (n, decimals = 1) => n.toFixed(decimals);
101107
- const pad = (s, w) => s.padStart(w);
101405
+ const pad2 = (s, w) => s.padStart(w);
101108
101406
  const execTime = data.executionTimeMs || 1;
101109
101407
  const jitTime = data.jitCompileTimeMs ?? 0;
101110
101408
  const dispatches = data.dispatches ?? [];
@@ -101132,27 +101430,27 @@ function cmdShowProfile(args) {
101132
101430
  console.log("\nAll timings are self-time (exclusive) and mutually disjoint.");
101133
101431
  if (dispatches.length > 0) {
101134
101432
  const nameW = Math.max(20, ...dispatches.map((b) => b.name.length)) + 2;
101135
- const header = "Function".padEnd(nameW) + pad("Calls", 10) + pad("Self (ms)", 13) + pad("Avg (ms)", 11) + pad("% Exec", 9);
101433
+ const header = "Function".padEnd(nameW) + pad2("Calls", 10) + pad2("Self (ms)", 13) + pad2("Avg (ms)", 11) + pad2("% Exec", 9);
101136
101434
  console.log("\nDispatched functions (user-defined / workspace):");
101137
101435
  console.log(header);
101138
101436
  console.log("\u2500".repeat(header.length));
101139
101437
  for (const b of dispatches) {
101140
101438
  const avg = b.callCount > 0 ? b.totalTimeMs / b.callCount : 0;
101141
101439
  const pct = b.totalTimeMs / execTime * 100;
101142
- const line = b.name.padEnd(nameW) + pad(String(b.callCount), 10) + pad(fmt(b.totalTimeMs, 2), 13) + pad(fmt(avg, 3), 11) + pad(fmt(pct, 1) + "%", 9);
101440
+ const line = b.name.padEnd(nameW) + pad2(String(b.callCount), 10) + pad2(fmt(b.totalTimeMs, 2), 13) + pad2(fmt(avg, 3), 11) + pad2(fmt(pct, 1) + "%", 9);
101143
101441
  console.log(line);
101144
101442
  }
101145
101443
  }
101146
101444
  if (builtins.length > 0) {
101147
101445
  const nameW = Math.max(20, ...builtins.map((b) => b.name.length)) + 2;
101148
- const header = "Builtin".padEnd(nameW) + pad("Interp", 10) + pad("Fallback", 10) + pad("JIT", 10) + pad("Self (ms)", 13) + pad("% Exec", 9);
101446
+ const header = "Builtin".padEnd(nameW) + pad2("Interp", 10) + pad2("Fallback", 10) + pad2("JIT", 10) + pad2("Self (ms)", 13) + pad2("% Exec", 9);
101149
101447
  console.log("\nBuiltin functions (call counts by source):");
101150
101448
  console.log(header);
101151
101449
  console.log("\u2500".repeat(header.length));
101152
101450
  for (const b of builtins) {
101153
101451
  const totalTime = b.fallback.totalTimeMs + b.interp.totalTimeMs + b.jit.totalTimeMs;
101154
101452
  const pct = totalTime / execTime * 100;
101155
- const line = b.name.padEnd(nameW) + pad(String(b.interp.callCount), 10) + pad(String(b.fallback.callCount), 10) + pad(String(b.jit.callCount), 10) + pad(fmt(totalTime, 2), 13) + pad(fmt(pct, 1) + "%", 9);
101453
+ const line = b.name.padEnd(nameW) + pad2(String(b.interp.callCount), 10) + pad2(String(b.fallback.callCount), 10) + pad2(String(b.jit.callCount), 10) + pad2(fmt(totalTime, 2), 13) + pad2(fmt(pct, 1) + "%", 9);
101156
101454
  console.log(line);
101157
101455
  }
101158
101456
  } else {
@@ -101162,13 +101460,13 @@ function cmdShowProfile(args) {
101162
101460
  if (hotLoops.length > 0) {
101163
101461
  hotLoops.sort((a, b) => b.totalTimeMs - a.totalTimeMs);
101164
101462
  const locW = Math.max(20, ...hotLoops.map((l) => `${l.file}:${l.line}`.length)) + 2;
101165
- const header = "Location".padEnd(locW) + pad("Kind", 8) + pad("Calls", 8) + pad("Max Iters", 12) + pad("Total (ms)", 13);
101463
+ const header = "Location".padEnd(locW) + pad2("Kind", 8) + pad2("Calls", 8) + pad2("Max Iters", 12) + pad2("Total (ms)", 13);
101166
101464
  console.log("\nInterpreted loops (>1000 iterations):");
101167
101465
  console.log(header);
101168
101466
  console.log("\u2500".repeat(header.length));
101169
101467
  for (const l of hotLoops) {
101170
101468
  const loc = `${l.file}:${l.line}`;
101171
- const line = loc.padEnd(locW) + pad(l.kind, 8) + pad(String(l.callCount ?? 1), 8) + pad(String(l.iterations), 12) + pad(fmt(l.totalTimeMs ?? 0, 2), 13);
101469
+ const line = loc.padEnd(locW) + pad2(l.kind, 8) + pad2(String(l.callCount ?? 1), 8) + pad2(String(l.iterations), 12) + pad2(fmt(l.totalTimeMs ?? 0, 2), 13);
101172
101470
  console.log(line);
101173
101471
  }
101174
101472
  }
@@ -101230,7 +101528,12 @@ async function main() {
101230
101528
  case "run-tests": {
101231
101529
  const testOpts = parseOptions(rest);
101232
101530
  const dir = testOpts.positional.length > 0 ? testOpts.positional[0] : join7(packageDir, "numbl_test_scripts");
101233
- await runTests(dir, testOpts.optimization, testOpts.fastMath);
101531
+ await runTests(
101532
+ dir,
101533
+ testOpts.optimization,
101534
+ testOpts.fastMath,
101535
+ testOpts.noNative
101536
+ );
101234
101537
  break;
101235
101538
  }
101236
101539
  case "build-addon":