numbl 0.4.12 → 0.4.14
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/README.md +0 -4
- package/dist-browser/browser/index.d.ts +13 -4
- package/dist-browser/browser/protocol.d.ts +25 -1
- package/dist-browser/browser/session.d.ts +20 -10
- package/dist-browser/browser.js +26 -4
- package/dist-browser/numbl-core/fileIOAdapter.d.ts +4 -0
- package/dist-browser/numbl-core/native/lapack-bridge.d.ts +1 -1
- package/dist-browser/numbl-core/native/wasm-lapack-bridge.d.ts +114 -0
- package/dist-browser/numbl-core/native/wasm-lapack-browser.d.ts +34 -0
- package/dist-browser/numbl-core/runtime/datetime-format.d.ts +10 -0
- package/dist-browser/numbl-core/runtime/display.d.ts +5 -1
- package/dist-browser/numbl-core/runtime/index.d.ts +1 -1
- package/dist-browser/numbl-core/version.d.ts +1 -1
- package/dist-browser/vfs/BrowserFileIOAdapter.d.ts +1 -0
- package/dist-browser/workerErrorFormat.d.ts +7 -0
- package/dist-cli/cli.js +463 -135
- package/dist-graphics/graphics-lib.d.ts +1 -0
- package/dist-graphics/index.js +33 -0
- package/dist-lib/cli-fileio.d.ts +62 -0
- package/dist-lib/cli-scan.d.ts +6 -0
- package/dist-lib/cli-system.d.ts +13 -0
- package/dist-lib/lib.js +386 -73
- package/dist-lib/node.d.ts +30 -0
- package/dist-lib/node.js +829 -0
- package/dist-lib/numbl-core/fileIOAdapter.d.ts +4 -0
- package/dist-lib/numbl-core/native/lapack-bridge.d.ts +1 -1
- package/dist-lib/numbl-core/native/wasm-lapack-bridge.d.ts +114 -0
- package/dist-lib/numbl-core/native/wasm-lapack-browser.d.ts +34 -0
- package/dist-lib/numbl-core/runtime/datetime-format.d.ts +10 -0
- package/dist-lib/numbl-core/runtime/display.d.ts +5 -1
- package/dist-lib/numbl-core/runtime/index.d.ts +1 -1
- package/dist-lib/numbl-core/version.d.ts +1 -1
- package/dist-lib/vfs/BrowserFileIOAdapter.d.ts +1 -0
- package/dist-site-viewer/assets/{index-CGE1_vZ-.js → index-CqtJ-i-7.js} +322 -322
- package/dist-site-viewer/assets/{numbl-worker-DddtCPW_.js → numbl-worker-B-MPuV6f.js} +133 -103
- package/dist-site-viewer/index.html +1 -1
- 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");
|
|
@@ -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
|
|
27486
|
-
result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign +
|
|
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
|
-
|
|
37885
|
-
|
|
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
|
-
|
|
37888
|
-
|
|
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")
|
|
37893
|
-
|
|
37894
|
-
|
|
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
|
|
37905
|
-
|
|
37906
|
-
|
|
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 (
|
|
37924
|
-
const flag = argToString(
|
|
37925
|
-
const kind = argToString(
|
|
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(
|
|
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 (
|
|
37945
|
-
const Y = toNumber(
|
|
37946
|
-
const M = toNumber(
|
|
37947
|
-
const D = toNumber(
|
|
37948
|
-
const H2 =
|
|
37949
|
-
const MI =
|
|
37950
|
-
const S =
|
|
37951
|
-
const MS =
|
|
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
|
|
43282
|
-
return [min2 -
|
|
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.
|
|
64280
|
+
var NUMBL_VERSION = "0.4.14";
|
|
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
|
|
66924
|
-
|
|
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(
|
|
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
|
|
71404
|
-
result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign +
|
|
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
|
}
|
|
@@ -71503,6 +71730,24 @@ function mtoc2_sprintf_format(fmt, args) {
|
|
|
71503
71730
|
case "t":
|
|
71504
71731
|
result += " ";
|
|
71505
71732
|
break;
|
|
71733
|
+
case "r":
|
|
71734
|
+
result += "\r";
|
|
71735
|
+
break;
|
|
71736
|
+
case "a":
|
|
71737
|
+
result += "\x07";
|
|
71738
|
+
break;
|
|
71739
|
+
case "b":
|
|
71740
|
+
result += "\b";
|
|
71741
|
+
break;
|
|
71742
|
+
case "f":
|
|
71743
|
+
result += "\f";
|
|
71744
|
+
break;
|
|
71745
|
+
case "v":
|
|
71746
|
+
result += "\v";
|
|
71747
|
+
break;
|
|
71748
|
+
case "0":
|
|
71749
|
+
result += "\0";
|
|
71750
|
+
break;
|
|
71506
71751
|
case "\\":
|
|
71507
71752
|
result += "\\";
|
|
71508
71753
|
break;
|
|
@@ -71686,8 +71931,8 @@ function disp_complex_slice(re, im, offset, rows, cols) {
|
|
|
71686
71931
|
for (let c = 0; c < cols; c++) {
|
|
71687
71932
|
const idx = r + c * rows;
|
|
71688
71933
|
const cell = cells[idx];
|
|
71689
|
-
const
|
|
71690
|
-
for (let i = 0; i <
|
|
71934
|
+
const pad2 = colWidths[c] - cell.length;
|
|
71935
|
+
for (let i = 0; i < pad2; i++) line += " ";
|
|
71691
71936
|
line += cell;
|
|
71692
71937
|
if (c < cols - 1) line += " ";
|
|
71693
71938
|
}
|
|
@@ -73099,6 +73344,14 @@ function mtoc2_tensor_mtimes_real(a, b) {
|
|
|
73099
73344
|
`mtoc2_tensor_mtimes_real: inner dimensions disagree (${k} vs ${k2})`
|
|
73100
73345
|
);
|
|
73101
73346
|
}
|
|
73347
|
+
if (typeof $matmulAccel === "function") {
|
|
73348
|
+
const accel = $matmulAccel(a.data, m, k, b.data, n);
|
|
73349
|
+
if (accel) {
|
|
73350
|
+
const acc = mtoc2_tensor_alloc(m, n);
|
|
73351
|
+
acc.data.set(accel);
|
|
73352
|
+
return acc;
|
|
73353
|
+
}
|
|
73354
|
+
}
|
|
73102
73355
|
const r = mtoc2_tensor_alloc(m, n);
|
|
73103
73356
|
for (let j = 0; j < n; j++) {
|
|
73104
73357
|
for (let i = 0; i < m; i++) {
|
|
@@ -74272,7 +74525,7 @@ var JS_SNIPPETS = {
|
|
|
74272
74525
|
"error_fmt.js": "// JS sibling of `error_fmt.h`. Format the message + arguments and\n// throw a runtime error. Mirrors numbl's `error(...)` behavior\n// (stderr + halt).\n//\n// The thrown error carries an `identifier` string property so a\n// `try / catch ME` arm in the interpreter can populate the `ME`\n// struct's `identifier` field. When the caller passes no identifier\n// (the common `error('msg')` form), it's the empty string.\n\n\nfunction mtoc2_error_fmt(fmt, ...args) {\n const msg = mtoc2_sprintf_format(fmt, args);\n // Numbl writes to stderr; in the JS host we route through the same\n // `$write` channel by emitting a \"stderr\" sentinel prefix the host\n // can split on. For now, throw \u2014 the CLI's runner formats the\n // exception to stderr, matching the user-visible result.\n const err = new Error(msg);\n err.identifier = \"\";\n throw err;\n}\n\nfunction mtoc2_error_fmt_id(identifier, fmt, ...args) {\n const msg = mtoc2_sprintf_format(fmt, args);\n const err = new Error(msg);\n err.identifier = identifier;\n throw err;\n}\n",
|
|
74273
74526
|
"format_complex.js": '// JS sibling of `format_complex.h`. Format a scalar complex `{re, im}`\n// the way numbl\'s `formatComplex` does. Each component is rendered\n// through `mtoc2_format_double` so the scalar formatting stays in\n// lockstep with numbl\'s `formatNumber`.\n\n\nfunction mtoc2_format_complex(z) {\n const re = z.re;\n const im = z.im;\n if (im === 0) return mtoc2_format_double(re);\n if (re === 0) return mtoc2_format_double(im) + "i";\n if (im < 0) {\n return mtoc2_format_double(re) + " - " + mtoc2_format_double(-im) + "i";\n }\n return mtoc2_format_double(re) + " + " + mtoc2_format_double(im) + "i";\n}\n',
|
|
74274
74527
|
"format_double.js": '// JS sibling of `format_double.h` \u2014 same numbl-compatible formatter,\n// in JS. Used by `emitJs` for the `disp` scalar path and (later) by\n// the interpreter via the re-export in `snippets.gen.ts`.\n\nfunction mtoc2_format_double(x) {\n if (Number.isNaN(x)) return "NaN";\n // MATLAB spells these Inf / -Inf (matches the interpreter\'s formatNumber).\n if (x === Infinity) return "Inf";\n if (x === -Infinity) return "-Inf";\n if (x === 0) x = 0;\n if (Math.abs(x) < 1e15 && x === Math.trunc(x)) return String(x);\n\n const buf = x.toExponential(4);\n const ePos = buf.indexOf("e");\n if (ePos < 0) return buf;\n const decExp = parseInt(buf.slice(ePos + 1), 10);\n\n if (decExp < -6 || decExp >= 5) {\n let mantissaEnd = ePos - 1;\n while (mantissaEnd > 0 && buf[mantissaEnd] === "0") mantissaEnd--;\n if (mantissaEnd >= 0 && buf[mantissaEnd] === ".") mantissaEnd--;\n let expPart = buf.slice(ePos + 1);\n let sign = "+";\n if (expPart[0] === "+" || expPart[0] === "-") {\n sign = expPart[0];\n expPart = expPart.slice(1);\n }\n while (expPart.length > 1 && expPart[0] === "0") expPart = expPart.slice(1);\n return buf.slice(0, mantissaEnd + 1) + "e" + sign + expPart;\n }\n\n let fracDigits = 4 - decExp;\n if (fracDigits < 0) fracDigits = 0;\n let s = x.toFixed(fracDigits);\n if (s.includes(".")) {\n while (s.endsWith("0")) s = s.slice(0, -1);\n if (s.endsWith(".")) s = s.slice(0, -1);\n }\n return s;\n}\n',
|
|
74275
|
-
"format_engine.js": '// JS sibling of `format_engine.h`. Numbl-compatible printf-style\n// formatter, ported from `numbl/src/numbl-core/helpers/string.ts`\'s\n// `sprintfFormat`. Shared by fprintf / sprintf / error / assert on\n// the JS side.\n//\n// Spec set: d i u f e E g G x X o s c %\n// Flags: - + 0 # space. Precision `.N`. Width digits. `*` consumes the\n// next numeric arg as a width.\n// Escapes interpreted AT FORMAT TIME: \\n \\t \\\\.\n// Numeric tensors flatten column-major into the scalar stream; the\n// format string cycles through args until consumed (with a "no arg\n// consumed this pass" guard against infinite output).\n\nfunction isTensor(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "tensor";\n}\nfunction isChar(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "char";\n}\n\nfunction toNumber(v) {\n if (typeof v === "number") return v;\n if (typeof v === "boolean") return v ? 1 : 0;\n if (typeof v === "string") return Number(v);\n if (isChar(v)) {\n // Match the interpreter\'s toNumber (runtime/convert.ts): a single\n // char yields its code point; a multi-char value has no scalar\n // numeric form. `Number("A")` would wrongly give NaN.\n if (v.value.length === 1) return v.value.charCodeAt(0);\n throw new Error("Cannot convert multi-char to number");\n }\n if (isTensor(v) && v.data.length === 1) return v.data[0];\n return Number(v);\n}\n\nfunction toString(v) {\n if (typeof v === "string") return v;\n if (typeof v === "number") return String(v);\n if (typeof v === "boolean") return v ? "1" : "0";\n if (isChar(v)) return v.value;\n return String(v);\n}\n\nfunction numStr(n) {\n if (n === Infinity) return "Inf";\n if (n === -Infinity) return "-Inf";\n if (Number.isNaN(n)) return "NaN";\n if (n === 0) return "0";\n const prec = 5;\n const exp = Math.floor(Math.log10(Math.abs(n)));\n let s;\n if (exp < -4 || exp >= prec) {\n s = n.toExponential(prec - 1);\n const ePos = s.indexOf("e");\n let mantissa = s.slice(0, ePos);\n const expPart0 = s.slice(ePos);\n if (mantissa.includes(".")) mantissa = mantissa.replace(/\\.?0+$/, "");\n const expPart = expPart0.replace(/([eE][+-])(\\d)$/, "$1" + "0$2");\n s = mantissa + expPart;\n } else {\n if (Number.isInteger(n)) return String(n);\n s = n.toPrecision(prec);\n if (s.includes(".")) s = s.replace(/\\.?0+$/, "");\n }\n return s;\n}\n\nfunction applyWidth(spec, str) {\n const m = spec.match(/^%([-+ #]*)0?(\\d+)?/);\n if (!m) return str;\n const explicitFlags = m[1] || "";\n const leftAlign = explicitFlags.includes("-");\n const afterPercent = spec.slice(1);\n const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\\d+)?/);\n const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;\n const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;\n if (width <= str.length) return str;\n const zeroPad = !leftAlign && zeroFlag;\n const padLen = width - str.length;\n if (leftAlign) return str + " ".repeat(padLen);\n if (zeroPad) {\n if (str[0] === "-" || str[0] === "+") {\n return str[0] + "0".repeat(padLen) + str.slice(1);\n }\n return "0".repeat(padLen) + str;\n }\n return " ".repeat(padLen) + str;\n}\n\nfunction mtoc2_sprintf_format(fmt, args) {\n // Char literals (`\'foo\'`) come through wrapped as\n // `{mtoc2Tag:"char", value:"..."}` to match the interpreter\'s\n // RuntimeValue convention; unwrap so the format walker indexes\n // a plain JS string.\n if (isChar(fmt)) fmt = fmt.value;\n // Flatten args column-major (tensors expand element-by-element).\n const flat = [];\n for (const arg of args) {\n if (isTensor(arg)) {\n for (let k = 0; k < arg.data.length; k++) flat.push(arg.data[k]);\n } else {\n flat.push(arg);\n }\n }\n\n let result = "";\n let argIdx = 0;\n\n do {\n const startArgIdx = argIdx;\n let outOfArgs = false;\n let i = 0;\n while (i < fmt.length && !outOfArgs) {\n if (fmt[i] === "%" && i + 1 < fmt.length) {\n i++;\n let spec = "%";\n while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {\n if (fmt[i] === "*") {\n if (argIdx >= flat.length) {\n outOfArgs = true;\n break;\n }\n spec += String(Math.round(toNumber(flat[argIdx++])));\n i++;\n } else {\n spec += fmt[i];\n i++;\n }\n }\n if (outOfArgs) break;\n if (i < fmt.length) {\n const ch = fmt[i];\n i++;\n if (ch === "%") {\n result += "%";\n } else if (argIdx >= flat.length) {\n outOfArgs = true;\n } else if (ch === "d" || ch === "i" || ch === "u") {\n const raw = toNumber(flat[argIdx++]);\n const isInt = Number.isInteger(raw);\n const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;\n if (!canPrintAsInt) {\n let eStr = raw.toExponential(6);\n eStr = eStr.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n result += applyWidth(spec, eStr);\n } else {\n const n = raw;\n const flags = spec.slice(1);\n const hasPlus = flags.includes("+");\n const leftAlign = flags.includes("-");\n const widthMatch = spec.match(/^%[^0-9]*(\\d+)/);\n const width = widthMatch ? parseInt(widthMatch[1]) : 0;\n const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));\n const s = String(Math.abs(n));\n const sign = n < 0 ? "-" : hasPlus ? "+" : "";\n if (width > 0) {\n const padChar = zeroPad ? "0" : " ";\n const padLen = Math.max(0, width - sign.length - s.length);\n const pad = padChar.repeat(padLen);\n result += leftAlign\n ? sign + s + " ".repeat(padLen)\n : zeroPad\n ? sign + pad + s\n : pad + sign + s;\n } else {\n result += sign + s;\n }\n }\n } else if (ch === "f") {\n const n = toNumber(flat[argIdx++]);\n if (!isFinite(n) || isNaN(n)) {\n result += applyWidth(spec, numStr(n));\n } else {\n const fFlags = spec.slice(1);\n const fHasPlus = fFlags.includes("+");\n const precMatch = spec.match(/\\.(\\d+)/);\n const prec = precMatch ? parseInt(precMatch[1]) : 6;\n const formatted = n.toFixed(prec);\n const fSign = n < 0 ? "" : fHasPlus ? "+" : "";\n result += applyWidth(spec, fSign + formatted);\n }\n } else if (ch === "e" || ch === "E") {\n const n = toNumber(flat[argIdx++]);\n if (!isFinite(n) || isNaN(n)) {\n result += applyWidth(spec, numStr(n));\n } else {\n const precMatch = spec.match(/\\.(\\d+)/);\n const prec = precMatch ? parseInt(precMatch[1]) : 6;\n let eStr = n.toExponential(prec);\n eStr = eStr.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n if (ch === "E") eStr = eStr.toUpperCase();\n result += applyWidth(spec, eStr);\n }\n } else if (ch === "x" || ch === "X") {\n const n = Math.round(toNumber(flat[argIdx++]));\n let s = Math.abs(n).toString(16);\n if (ch === "X") s = s.toUpperCase();\n result += applyWidth(spec, s);\n } else if (ch === "o") {\n const n = Math.round(toNumber(flat[argIdx++]));\n result += applyWidth(spec, Math.abs(n).toString(8));\n } else if (ch === "g" || ch === "G") {\n const gVal = toNumber(flat[argIdx++]);\n if (!isFinite(gVal) || isNaN(gVal)) {\n result += applyWidth(spec, numStr(gVal));\n } else {\n const precMatch = spec.match(/\\.(\\d+)/);\n const gPrec = precMatch ? parseInt(precMatch[1]) : 6;\n let gStr;\n if (gVal === 0) {\n gStr = "0";\n } else {\n const exp = Math.floor(Math.log10(Math.abs(gVal)));\n if (exp < -4 || exp >= gPrec) {\n gStr = gVal.toExponential(gPrec - 1);\n const ePos = gStr.indexOf("e");\n let mantissa = gStr.slice(0, ePos);\n let expPart = gStr.slice(ePos);\n if (mantissa.includes(".")) {\n mantissa = mantissa.replace(/\\.?0+$/, "");\n }\n expPart = expPart.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n gStr = mantissa + expPart;\n } else {\n gStr = gVal.toPrecision(gPrec);\n if (gStr.includes(".")) {\n gStr = gStr.replace(/\\.?0+$/, "");\n }\n if (gStr.includes("e")) {\n gStr = String(parseFloat(gStr));\n }\n }\n }\n if (ch === "G") gStr = gStr.toUpperCase();\n result += applyWidth(spec, gStr);\n }\n } else if (ch === "s") {\n const sArg = flat[argIdx++];\n // A numeric/logical argument to %s is a character code (MATLAB:\n // sprintf(\'%s\', 65) -> \'A\').\n const sVal =\n typeof sArg === "number" || typeof sArg === "boolean"\n ? String.fromCharCode(Math.round(toNumber(sArg)))\n : toString(sArg);\n const sFlags = spec.slice(1);\n const sLeftAlign = sFlags.includes("-");\n const sWidthMatch = spec.match(/^%[^0-9]*(\\d+)/);\n const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;\n if (sWidth > sVal.length) {\n const sPad = " ".repeat(sWidth - sVal.length);\n result += sLeftAlign ? sVal + sPad : sPad + sVal;\n } else {\n result += sVal;\n }\n } else if (ch === "c") {\n result += String.fromCharCode(Math.round(toNumber(flat[argIdx++])));\n } else {\n result += spec + ch;\n argIdx++;\n }\n }\n } else if (fmt[i] === "\\\\" && i + 1 < fmt.length) {\n i++;\n switch (fmt[i]) {\n case "n":\n result += "\\n";\n break;\n case "t":\n result += "\\t";\n break;\n case "\\\\":\n result += "\\\\";\n break;\n default:\n result += "\\\\" + fmt[i];\n }\n i++;\n } else {\n result += fmt[i];\n i++;\n }\n }\n if (argIdx === startArgIdx) break;\n } while (argIdx < flat.length);\n\n return result;\n}\n',
|
|
74528
|
+
"format_engine.js": '// JS sibling of `format_engine.h`. Numbl-compatible printf-style\n// formatter, ported from `numbl/src/numbl-core/helpers/string.ts`\'s\n// `sprintfFormat`. Shared by fprintf / sprintf / error / assert on\n// the JS side.\n//\n// Spec set: d i u f e E g G x X o s c %\n// Flags: - + 0 # space. Precision `.N`. Width digits. `*` consumes the\n// next numeric arg as a width.\n// Escapes interpreted AT FORMAT TIME: \\n \\t \\r \\a \\b \\f \\v \\0 \\\\.\n// Numeric tensors flatten column-major into the scalar stream; the\n// format string cycles through args until consumed (with a "no arg\n// consumed this pass" guard against infinite output).\n\nfunction isTensor(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "tensor";\n}\nfunction isChar(v) {\n return typeof v === "object" && v !== null && v.mtoc2Tag === "char";\n}\n\nfunction toNumber(v) {\n if (typeof v === "number") return v;\n if (typeof v === "boolean") return v ? 1 : 0;\n if (typeof v === "string") return Number(v);\n if (isChar(v)) {\n // Match the interpreter\'s toNumber (runtime/convert.ts): a single\n // char yields its code point; a multi-char value has no scalar\n // numeric form. `Number("A")` would wrongly give NaN.\n if (v.value.length === 1) return v.value.charCodeAt(0);\n throw new Error("Cannot convert multi-char to number");\n }\n if (isTensor(v) && v.data.length === 1) return v.data[0];\n return Number(v);\n}\n\nfunction toString(v) {\n if (typeof v === "string") return v;\n if (typeof v === "number") return String(v);\n if (typeof v === "boolean") return v ? "1" : "0";\n if (isChar(v)) return v.value;\n return String(v);\n}\n\nfunction numStr(n) {\n if (n === Infinity) return "Inf";\n if (n === -Infinity) return "-Inf";\n if (Number.isNaN(n)) return "NaN";\n if (n === 0) return "0";\n const prec = 5;\n const exp = Math.floor(Math.log10(Math.abs(n)));\n let s;\n if (exp < -4 || exp >= prec) {\n s = n.toExponential(prec - 1);\n const ePos = s.indexOf("e");\n let mantissa = s.slice(0, ePos);\n const expPart0 = s.slice(ePos);\n if (mantissa.includes(".")) mantissa = mantissa.replace(/\\.?0+$/, "");\n const expPart = expPart0.replace(/([eE][+-])(\\d)$/, "$1" + "0$2");\n s = mantissa + expPart;\n } else {\n if (Number.isInteger(n)) return String(n);\n s = n.toPrecision(prec);\n if (s.includes(".")) s = s.replace(/\\.?0+$/, "");\n }\n return s;\n}\n\nfunction applyWidth(spec, str) {\n const m = spec.match(/^%([-+ #]*)0?(\\d+)?/);\n if (!m) return str;\n const explicitFlags = m[1] || "";\n const leftAlign = explicitFlags.includes("-");\n const afterPercent = spec.slice(1);\n const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\\d+)?/);\n const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;\n const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;\n if (width <= str.length) return str;\n const zeroPad = !leftAlign && zeroFlag;\n const padLen = width - str.length;\n if (leftAlign) return str + " ".repeat(padLen);\n if (zeroPad) {\n if (str[0] === "-" || str[0] === "+") {\n return str[0] + "0".repeat(padLen) + str.slice(1);\n }\n return "0".repeat(padLen) + str;\n }\n return " ".repeat(padLen) + str;\n}\n\nfunction mtoc2_sprintf_format(fmt, args) {\n // Char literals (`\'foo\'`) come through wrapped as\n // `{mtoc2Tag:"char", value:"..."}` to match the interpreter\'s\n // RuntimeValue convention; unwrap so the format walker indexes\n // a plain JS string.\n if (isChar(fmt)) fmt = fmt.value;\n // Flatten args column-major (tensors expand element-by-element).\n const flat = [];\n for (const arg of args) {\n if (isTensor(arg)) {\n for (let k = 0; k < arg.data.length; k++) flat.push(arg.data[k]);\n } else {\n flat.push(arg);\n }\n }\n\n let result = "";\n let argIdx = 0;\n\n do {\n const startArgIdx = argIdx;\n let outOfArgs = false;\n let i = 0;\n while (i < fmt.length && !outOfArgs) {\n if (fmt[i] === "%" && i + 1 < fmt.length) {\n i++;\n let spec = "%";\n while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {\n if (fmt[i] === "*") {\n if (argIdx >= flat.length) {\n outOfArgs = true;\n break;\n }\n spec += String(Math.round(toNumber(flat[argIdx++])));\n i++;\n } else {\n spec += fmt[i];\n i++;\n }\n }\n if (outOfArgs) break;\n if (i < fmt.length) {\n const ch = fmt[i];\n i++;\n if (ch === "%") {\n result += "%";\n } else if (argIdx >= flat.length) {\n outOfArgs = true;\n } else if (ch === "d" || ch === "i" || ch === "u") {\n const raw = toNumber(flat[argIdx++]);\n const isInt = Number.isInteger(raw);\n const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;\n if (!canPrintAsInt) {\n let eStr = raw.toExponential(6);\n eStr = eStr.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n result += applyWidth(spec, eStr);\n } else {\n const n = raw;\n const flags = spec.slice(1);\n const hasPlus = flags.includes("+");\n const leftAlign = flags.includes("-");\n const widthMatch = spec.match(/^%[^0-9]*(\\d+)/);\n const width = widthMatch ? parseInt(widthMatch[1]) : 0;\n const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));\n const s = String(Math.abs(n));\n const sign = n < 0 ? "-" : hasPlus ? "+" : "";\n if (width > 0) {\n const padChar = zeroPad ? "0" : " ";\n const padLen = Math.max(0, width - sign.length - s.length);\n const pad = padChar.repeat(padLen);\n result += leftAlign\n ? sign + s + " ".repeat(padLen)\n : zeroPad\n ? sign + pad + s\n : pad + sign + s;\n } else {\n result += sign + s;\n }\n }\n } else if (ch === "f") {\n const n = toNumber(flat[argIdx++]);\n if (!isFinite(n) || isNaN(n)) {\n result += applyWidth(spec, numStr(n));\n } else {\n const fFlags = spec.slice(1);\n const fHasPlus = fFlags.includes("+");\n const precMatch = spec.match(/\\.(\\d+)/);\n const prec = precMatch ? parseInt(precMatch[1]) : 6;\n const formatted = n.toFixed(prec);\n const fSign = n < 0 ? "" : fHasPlus ? "+" : "";\n result += applyWidth(spec, fSign + formatted);\n }\n } else if (ch === "e" || ch === "E") {\n const n = toNumber(flat[argIdx++]);\n if (!isFinite(n) || isNaN(n)) {\n result += applyWidth(spec, numStr(n));\n } else {\n const precMatch = spec.match(/\\.(\\d+)/);\n const prec = precMatch ? parseInt(precMatch[1]) : 6;\n let eStr = n.toExponential(prec);\n eStr = eStr.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n if (ch === "E") eStr = eStr.toUpperCase();\n result += applyWidth(spec, eStr);\n }\n } else if (ch === "x" || ch === "X") {\n const n = Math.round(toNumber(flat[argIdx++]));\n let s = Math.abs(n).toString(16);\n if (ch === "X") s = s.toUpperCase();\n result += applyWidth(spec, s);\n } else if (ch === "o") {\n const n = Math.round(toNumber(flat[argIdx++]));\n result += applyWidth(spec, Math.abs(n).toString(8));\n } else if (ch === "g" || ch === "G") {\n const gVal = toNumber(flat[argIdx++]);\n if (!isFinite(gVal) || isNaN(gVal)) {\n result += applyWidth(spec, numStr(gVal));\n } else {\n const precMatch = spec.match(/\\.(\\d+)/);\n const gPrec = precMatch ? parseInt(precMatch[1]) : 6;\n let gStr;\n if (gVal === 0) {\n gStr = "0";\n } else {\n const exp = Math.floor(Math.log10(Math.abs(gVal)));\n if (exp < -4 || exp >= gPrec) {\n gStr = gVal.toExponential(gPrec - 1);\n const ePos = gStr.indexOf("e");\n let mantissa = gStr.slice(0, ePos);\n let expPart = gStr.slice(ePos);\n if (mantissa.includes(".")) {\n mantissa = mantissa.replace(/\\.?0+$/, "");\n }\n expPart = expPart.replace(/e([+-])(\\d)$/, "e$1" + "0$2");\n gStr = mantissa + expPart;\n } else {\n gStr = gVal.toPrecision(gPrec);\n if (gStr.includes(".")) {\n gStr = gStr.replace(/\\.?0+$/, "");\n }\n if (gStr.includes("e")) {\n gStr = String(parseFloat(gStr));\n }\n }\n }\n if (ch === "G") gStr = gStr.toUpperCase();\n result += applyWidth(spec, gStr);\n }\n } else if (ch === "s") {\n const sArg = flat[argIdx++];\n // A numeric/logical argument to %s is a character code (MATLAB:\n // sprintf(\'%s\', 65) -> \'A\').\n const sVal =\n typeof sArg === "number" || typeof sArg === "boolean"\n ? String.fromCharCode(Math.round(toNumber(sArg)))\n : toString(sArg);\n const sFlags = spec.slice(1);\n const sLeftAlign = sFlags.includes("-");\n const sWidthMatch = spec.match(/^%[^0-9]*(\\d+)/);\n const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;\n if (sWidth > sVal.length) {\n const sPad = " ".repeat(sWidth - sVal.length);\n result += sLeftAlign ? sVal + sPad : sPad + sVal;\n } else {\n result += sVal;\n }\n } else if (ch === "c") {\n result += String.fromCharCode(Math.round(toNumber(flat[argIdx++])));\n } else {\n result += spec + ch;\n argIdx++;\n }\n }\n } else if (fmt[i] === "\\\\" && i + 1 < fmt.length) {\n i++;\n switch (fmt[i]) {\n case "n":\n result += "\\n";\n break;\n case "t":\n result += "\\t";\n break;\n case "r":\n result += "\\r";\n break;\n case "a":\n result += "\\x07";\n break;\n case "b":\n result += "\\b";\n break;\n case "f":\n result += "\\f";\n break;\n case "v":\n result += "\\v";\n break;\n case "0":\n result += "\\0";\n break;\n case "\\\\":\n result += "\\\\";\n break;\n default:\n result += "\\\\" + fmt[i];\n }\n i++;\n } else {\n result += fmt[i];\n i++;\n }\n }\n if (argIdx === startArgIdx) break;\n } while (argIdx < flat.length);\n\n return result;\n}\n',
|
|
74276
74529
|
"fprintf.js": "// JS sibling of `fprintf.h`. Routes formatted output through\n// `mtoc2_sprintf_format` then writes via `$write` (the JS analog of\n// C's stdout). Numbl-compatible spec set & semantics.\n\n\nfunction mtoc2_fprintf(fmt, ...args) {\n $write(mtoc2_sprintf_format(fmt, args));\n}\n",
|
|
74277
74530
|
"sprintf.js": '// JS sibling of `sprintf.h`. Two output variants:\n// - `mtoc2_sprintf_str(fmt, args)` returns the formatted text as a\n// plain JS string (the equivalent of numbl\'s `String` kind).\n// - `mtoc2_sprintf_char(fmt, args)` returns it as a `mtoc2_char_tensor_t`\n// placeholder \u2014 we keep the JS shape as a `{mtoc2Tag: "char", value}`\n// object so disp/length etc. dispatch correctly.\n\n\nfunction mtoc2_sprintf_str(fmt, ...args) {\n return mtoc2_sprintf_format(fmt, args);\n}\n\nfunction mtoc2_sprintf_char(fmt, ...args) {\n return { mtoc2Tag: "char", value: mtoc2_sprintf_format(fmt, args) };\n}\n',
|
|
74278
74531
|
"warning_fmt.js": '// JS sibling of `warning_fmt.h`. Format the message and write\n// `Warning: <msg>\\n` via `$write`. Matches numbl\n// (`runtime/specialBuiltins.ts:190` \u2014 `rt.output("Warning: " + msg + "\\n")`).\n\n\nfunction mtoc2_warning_fmt(fmt, ...args) {\n $write("Warning: " + mtoc2_sprintf_format(fmt, args) + "\\n");\n}\n\nfunction mtoc2_warning_fmt_id(_id, fmt, ...args) {\n // The id is informational \u2014 numbl threads it to lastwarn state,\n // which mtoc2 doesn\'t expose. Drop it here so the visible output\n // matches the bare form.\n $write("Warning: " + mtoc2_sprintf_format(fmt, args) + "\\n");\n}\n',
|
|
@@ -74310,7 +74563,7 @@ var JS_SNIPPETS = {
|
|
|
74310
74563
|
"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
74564
|
"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
74565
|
"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',
|
|
74566
|
+
"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
74567
|
"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
74568
|
"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
74569
|
"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 +83233,9 @@ function tileExact(src, padShape2, padReps) {
|
|
|
82980
83233
|
}
|
|
82981
83234
|
return out;
|
|
82982
83235
|
}
|
|
82983
|
-
function padTo(arr,
|
|
83236
|
+
function padTo(arr, pad2, len) {
|
|
82984
83237
|
const out = arr.slice();
|
|
82985
|
-
while (out.length < len) out.push(
|
|
83238
|
+
while (out.length < len) out.push(pad2);
|
|
82986
83239
|
return out;
|
|
82987
83240
|
}
|
|
82988
83241
|
function trimTrailingOnes(shape) {
|
|
@@ -96988,10 +97241,10 @@ import {
|
|
|
96988
97241
|
chmodSync,
|
|
96989
97242
|
realpathSync
|
|
96990
97243
|
} from "fs";
|
|
96991
|
-
import { unzipSync } from "fflate";
|
|
97244
|
+
import { unzipSync, zipSync } from "fflate";
|
|
96992
97245
|
import { execFileSync } from "child_process";
|
|
96993
97246
|
import { homedir, tmpdir } from "os";
|
|
96994
|
-
import { join as join3, resolve, dirname, basename } from "path";
|
|
97247
|
+
import { join as join3, resolve, relative, dirname, basename } from "path";
|
|
96995
97248
|
|
|
96996
97249
|
// src/cli-scan.ts
|
|
96997
97250
|
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
@@ -97474,6 +97727,69 @@ var NodeFileIOAdapter = class {
|
|
|
97474
97727
|
}
|
|
97475
97728
|
return extracted;
|
|
97476
97729
|
}
|
|
97730
|
+
zip(zipfilename, filenames, rootDir) {
|
|
97731
|
+
const root = resolve(expandTilde(rootDir));
|
|
97732
|
+
const dest = resolve(expandTilde(zipfilename));
|
|
97733
|
+
const entries = {};
|
|
97734
|
+
const names = [];
|
|
97735
|
+
const toEntryName = (abs2) => {
|
|
97736
|
+
const rel = relative(root, abs2);
|
|
97737
|
+
if (rel.startsWith("..")) return basename(abs2);
|
|
97738
|
+
return rel.split("\\").join("/");
|
|
97739
|
+
};
|
|
97740
|
+
const addFile = (abs2) => {
|
|
97741
|
+
const name = toEntryName(abs2);
|
|
97742
|
+
const buf = readFileSync3(abs2);
|
|
97743
|
+
entries[name] = new Uint8Array(
|
|
97744
|
+
buf.buffer,
|
|
97745
|
+
buf.byteOffset,
|
|
97746
|
+
buf.byteLength
|
|
97747
|
+
);
|
|
97748
|
+
names.push(name);
|
|
97749
|
+
};
|
|
97750
|
+
const addDir = (absDir) => {
|
|
97751
|
+
for (const entry of readdirSync2(absDir)) {
|
|
97752
|
+
const abs2 = join3(absDir, entry);
|
|
97753
|
+
let st2;
|
|
97754
|
+
try {
|
|
97755
|
+
st2 = statSync2(abs2);
|
|
97756
|
+
} catch {
|
|
97757
|
+
continue;
|
|
97758
|
+
}
|
|
97759
|
+
if (st2.isDirectory()) addDir(abs2);
|
|
97760
|
+
else addFile(abs2);
|
|
97761
|
+
}
|
|
97762
|
+
};
|
|
97763
|
+
const addPath = (abs2) => {
|
|
97764
|
+
const st2 = statSync2(abs2);
|
|
97765
|
+
if (st2.isDirectory()) addDir(abs2);
|
|
97766
|
+
else addFile(abs2);
|
|
97767
|
+
};
|
|
97768
|
+
for (const f of filenames) {
|
|
97769
|
+
const p2 = expandTilde(f);
|
|
97770
|
+
const abs2 = resolve(root, p2);
|
|
97771
|
+
const last = basename(p2);
|
|
97772
|
+
if (last.includes("*") || last.includes("?")) {
|
|
97773
|
+
const dir = dirname(abs2);
|
|
97774
|
+
const re = new RegExp(
|
|
97775
|
+
"^" + last.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$"
|
|
97776
|
+
);
|
|
97777
|
+
let dirEntries;
|
|
97778
|
+
try {
|
|
97779
|
+
dirEntries = readdirSync2(dir);
|
|
97780
|
+
} catch {
|
|
97781
|
+
continue;
|
|
97782
|
+
}
|
|
97783
|
+
for (const entry of dirEntries) {
|
|
97784
|
+
if (re.test(entry)) addPath(join3(dir, entry));
|
|
97785
|
+
}
|
|
97786
|
+
} else {
|
|
97787
|
+
addPath(abs2);
|
|
97788
|
+
}
|
|
97789
|
+
}
|
|
97790
|
+
writeFileSync(dest, zipSync(entries));
|
|
97791
|
+
return names;
|
|
97792
|
+
}
|
|
97477
97793
|
listDir(dirPath) {
|
|
97478
97794
|
const p2 = expandTilde(dirPath);
|
|
97479
97795
|
if (p2.includes("**")) {
|
|
@@ -97579,51 +97895,43 @@ var NodeFileIOAdapter = class {
|
|
|
97579
97895
|
return results;
|
|
97580
97896
|
}
|
|
97581
97897
|
listDirRecursive(pattern) {
|
|
97582
|
-
const
|
|
97583
|
-
|
|
97584
|
-
|
|
97898
|
+
const norm2 = pattern.replace(/\\/g, "/");
|
|
97899
|
+
const idx = norm2.indexOf("**");
|
|
97900
|
+
let baseDir = norm2.slice(0, idx);
|
|
97901
|
+
if (baseDir.endsWith("/")) {
|
|
97585
97902
|
baseDir = baseDir.slice(0, -1);
|
|
97586
97903
|
}
|
|
97587
97904
|
if (!baseDir) baseDir = ".";
|
|
97588
97905
|
const absBase = resolve(baseDir);
|
|
97906
|
+
try {
|
|
97907
|
+
if (!statSync2(absBase).isDirectory()) return [];
|
|
97908
|
+
} catch {
|
|
97909
|
+
return [];
|
|
97910
|
+
}
|
|
97911
|
+
const suffix = norm2.slice(idx);
|
|
97912
|
+
const DIRS = "\0";
|
|
97913
|
+
const ANY = "";
|
|
97914
|
+
const re = new RegExp(
|
|
97915
|
+
"^" + suffix.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, DIRS).replace(/\*\*/g, ANY).replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(new RegExp(DIRS, "g"), "(?:.*/)?").replace(new RegExp(ANY, "g"), ".*") + "$"
|
|
97916
|
+
);
|
|
97589
97917
|
const results = [];
|
|
97590
|
-
const
|
|
97918
|
+
const pushIfMatch = (rel, name, folder, bytes, isdir, mtimeMs) => {
|
|
97919
|
+
if (re.test(rel)) results.push({ name, folder, bytes, isdir, mtimeMs });
|
|
97920
|
+
};
|
|
97921
|
+
const walkDir = (dir, rel) => {
|
|
97922
|
+
const relPrefix = rel ? rel + "/" : "";
|
|
97923
|
+
let dirMtime = 0;
|
|
97591
97924
|
try {
|
|
97592
|
-
|
|
97593
|
-
results.push({
|
|
97594
|
-
name: ".",
|
|
97595
|
-
folder: dir,
|
|
97596
|
-
bytes: 0,
|
|
97597
|
-
isdir: true,
|
|
97598
|
-
mtimeMs: ds.mtimeMs
|
|
97599
|
-
});
|
|
97925
|
+
dirMtime = statSync2(dir).mtimeMs;
|
|
97600
97926
|
} catch {
|
|
97601
|
-
results.push({
|
|
97602
|
-
name: ".",
|
|
97603
|
-
folder: dir,
|
|
97604
|
-
bytes: 0,
|
|
97605
|
-
isdir: true,
|
|
97606
|
-
mtimeMs: 0
|
|
97607
|
-
});
|
|
97608
97927
|
}
|
|
97928
|
+
pushIfMatch(relPrefix + ".", ".", dir, 0, true, dirMtime);
|
|
97929
|
+
let parentMtime = 0;
|
|
97609
97930
|
try {
|
|
97610
|
-
|
|
97611
|
-
results.push({
|
|
97612
|
-
name: "..",
|
|
97613
|
-
folder: dir,
|
|
97614
|
-
bytes: 0,
|
|
97615
|
-
isdir: true,
|
|
97616
|
-
mtimeMs: ps.mtimeMs
|
|
97617
|
-
});
|
|
97931
|
+
parentMtime = statSync2(resolve(dir, "..")).mtimeMs;
|
|
97618
97932
|
} catch {
|
|
97619
|
-
results.push({
|
|
97620
|
-
name: "..",
|
|
97621
|
-
folder: dir,
|
|
97622
|
-
bytes: 0,
|
|
97623
|
-
isdir: true,
|
|
97624
|
-
mtimeMs: 0
|
|
97625
|
-
});
|
|
97626
97933
|
}
|
|
97934
|
+
pushIfMatch(relPrefix + "..", "..", dir, 0, true, parentMtime);
|
|
97627
97935
|
let entries;
|
|
97628
97936
|
try {
|
|
97629
97937
|
entries = readdirSync2(dir);
|
|
@@ -97634,22 +97942,23 @@ var NodeFileIOAdapter = class {
|
|
|
97634
97942
|
for (const entry of entries) {
|
|
97635
97943
|
try {
|
|
97636
97944
|
const es = statSync2(join3(dir, entry));
|
|
97637
|
-
|
|
97638
|
-
|
|
97639
|
-
|
|
97640
|
-
|
|
97641
|
-
|
|
97642
|
-
|
|
97643
|
-
|
|
97945
|
+
pushIfMatch(
|
|
97946
|
+
relPrefix + entry,
|
|
97947
|
+
entry,
|
|
97948
|
+
dir,
|
|
97949
|
+
es.isDirectory() ? 0 : es.size,
|
|
97950
|
+
es.isDirectory(),
|
|
97951
|
+
es.mtimeMs
|
|
97952
|
+
);
|
|
97644
97953
|
if (es.isDirectory()) subdirs.push(entry);
|
|
97645
97954
|
} catch {
|
|
97646
97955
|
}
|
|
97647
97956
|
}
|
|
97648
97957
|
for (const sub2 of subdirs) {
|
|
97649
|
-
walkDir(join3(dir, sub2));
|
|
97958
|
+
walkDir(join3(dir, sub2), relPrefix + sub2);
|
|
97650
97959
|
}
|
|
97651
97960
|
};
|
|
97652
|
-
walkDir(absBase);
|
|
97961
|
+
walkDir(absBase, "");
|
|
97653
97962
|
return results;
|
|
97654
97963
|
}
|
|
97655
97964
|
getEntry(fid) {
|
|
@@ -98326,8 +98635,9 @@ static int mtoc2_format_double(char *out, size_t cap, double x) {
|
|
|
98326
98635
|
* - spec set: d i u f e E g G x X o s c %
|
|
98327
98636
|
* - flags - + 0 # space; precision \`.N\`; width digits; \`*\` consumes
|
|
98328
98637
|
* the next numeric arg as a width
|
|
98329
|
-
* - escapes \\n \\t \\\\ interpreted AT FORMAT TIME
|
|
98330
|
-
* lexer, which preserves backslash bytes
|
|
98638
|
+
* - escapes \\n \\t \\r \\a \\b \\f \\v \\0 \\\\ interpreted AT FORMAT TIME
|
|
98639
|
+
* (matching the numbl lexer, which preserves backslash bytes
|
|
98640
|
+
* verbatim)
|
|
98331
98641
|
* - numeric tensors flatten column-major into the scalar stream;
|
|
98332
98642
|
* format string cycles through the args until the stream is
|
|
98333
98643
|
* exhausted (with a "no arg consumed this pass" guard against
|
|
@@ -98636,13 +98946,19 @@ static long mtoc2__num_to_str(char *buf, size_t cap, double x) {
|
|
|
98636
98946
|
return n;
|
|
98637
98947
|
}
|
|
98638
98948
|
|
|
98639
|
-
/* Emit one \`\\\` escape sequence \u2014 numbl interprets
|
|
98640
|
-
* at format time; any other \`\\x\` passes through verbatim. */
|
|
98949
|
+
/* Emit one \`\\\` escape sequence \u2014 numbl interprets \\n, \\t, \\r, \\a, \\b,
|
|
98950
|
+
* \\f, \\v, \\0, \\\\ at format time; any other \`\\x\` passes through verbatim. */
|
|
98641
98951
|
static long mtoc2__emit_escape(mtoc2__writer_fn writer, void *ctx,
|
|
98642
98952
|
char next) {
|
|
98643
98953
|
switch (next) {
|
|
98644
98954
|
case 'n': writer(ctx, "\\n", 1); return 1;
|
|
98645
98955
|
case 't': writer(ctx, "\\t", 1); return 1;
|
|
98956
|
+
case 'r': writer(ctx, "\\r", 1); return 1;
|
|
98957
|
+
case 'a': writer(ctx, "\\a", 1); return 1;
|
|
98958
|
+
case 'b': writer(ctx, "\\b", 1); return 1;
|
|
98959
|
+
case 'f': writer(ctx, "\\f", 1); return 1;
|
|
98960
|
+
case 'v': writer(ctx, "\\v", 1); return 1;
|
|
98961
|
+
case '0': writer(ctx, "\\0", 1); return 1;
|
|
98646
98962
|
case '\\\\': writer(ctx, "\\\\", 1); return 1;
|
|
98647
98963
|
default: {
|
|
98648
98964
|
char buf[2] = { '\\\\', next };
|
|
@@ -100069,7 +100385,7 @@ function unzipToFiles(zipData) {
|
|
|
100069
100385
|
}
|
|
100070
100386
|
|
|
100071
100387
|
// src/cli.ts
|
|
100072
|
-
import { zipSync } from "fflate";
|
|
100388
|
+
import { zipSync as zipSync2 } from "fflate";
|
|
100073
100389
|
|
|
100074
100390
|
// src/numbl-core/native/qhull-node.ts
|
|
100075
100391
|
var promise = null;
|
|
@@ -100094,8 +100410,8 @@ function variantAddonPath(fastMath) {
|
|
|
100094
100410
|
const name = fastMath ? "numbl_addon.fastmath.node" : "numbl_addon.nofastmath.node";
|
|
100095
100411
|
return join7(packageDir, "build", "Release", name);
|
|
100096
100412
|
}
|
|
100097
|
-
function loadNativeAddon(fastMath) {
|
|
100098
|
-
if (process.env.NUMBL_NO_NATIVE) return;
|
|
100413
|
+
function loadNativeAddon(fastMath, noNative = false) {
|
|
100414
|
+
if (noNative || process.env.NUMBL_NO_NATIVE) return;
|
|
100099
100415
|
const variantPath = variantAddonPath(fastMath);
|
|
100100
100416
|
const flag = fastMath ? " --fast-math" : "";
|
|
100101
100417
|
if (!existsSync3(variantPath)) {
|
|
@@ -100219,8 +100535,8 @@ function findTestFiles(dir) {
|
|
|
100219
100535
|
walk2(dir);
|
|
100220
100536
|
return results;
|
|
100221
100537
|
}
|
|
100222
|
-
async function runTests(dir, optimization, fastMath) {
|
|
100223
|
-
loadNativeAddon(fastMath ?? false);
|
|
100538
|
+
async function runTests(dir, optimization, fastMath, noNative) {
|
|
100539
|
+
loadNativeAddon(fastMath ?? false, noNative ?? false);
|
|
100224
100540
|
await loadQhullBackend();
|
|
100225
100541
|
const absDir = resolve2(process.cwd(), dir);
|
|
100226
100542
|
const testFiles = findTestFiles(absDir);
|
|
@@ -100232,7 +100548,7 @@ async function runTests(dir, optimization, fastMath) {
|
|
|
100232
100548
|
let fail = 0;
|
|
100233
100549
|
const failedScripts = [];
|
|
100234
100550
|
for (const filepath of testFiles) {
|
|
100235
|
-
const rel =
|
|
100551
|
+
const rel = relative2(process.cwd(), filepath);
|
|
100236
100552
|
const source = readFileSync6(filepath, "utf-8");
|
|
100237
100553
|
const mainFileName = filepath;
|
|
100238
100554
|
const scriptDir = dirname3(filepath);
|
|
@@ -100351,9 +100667,12 @@ Options (for run and eval):
|
|
|
100351
100667
|
across --opt levels). Off by default so all --opt
|
|
100352
100668
|
levels agree; --no-fast-math is accepted as the
|
|
100353
100669
|
(now default) opt-out.
|
|
100670
|
+
--no-native Do not load the native LAPACK addon; use the pure-JS
|
|
100671
|
+
fallbacks (same effect as NUMBL_NO_NATIVE=1)
|
|
100354
100672
|
|
|
100355
100673
|
Environment variables:
|
|
100356
|
-
NUMBL_PATH Extra workspace directories (separated by ${delimiter})
|
|
100674
|
+
NUMBL_PATH Extra workspace directories (separated by ${delimiter})
|
|
100675
|
+
NUMBL_NO_NATIVE If set, do not load the native LAPACK addon`);
|
|
100357
100676
|
}
|
|
100358
100677
|
function parseOptions(args) {
|
|
100359
100678
|
const opts = {
|
|
@@ -100372,7 +100691,8 @@ function parseOptions(args) {
|
|
|
100372
100691
|
// and vectorizes transcendentals, so results drift from the JIT
|
|
100373
100692
|
// kernels and diverge across --opt levels. Opt back in with
|
|
100374
100693
|
// `--fast-math` (and a matching `build-addon --fast-math`).
|
|
100375
|
-
fastMath: false
|
|
100694
|
+
fastMath: false,
|
|
100695
|
+
noNative: false
|
|
100376
100696
|
};
|
|
100377
100697
|
const envPath = process.env.NUMBL_PATH;
|
|
100378
100698
|
if (envPath) {
|
|
@@ -100410,6 +100730,9 @@ function parseOptions(args) {
|
|
|
100410
100730
|
case "--no-fast-math":
|
|
100411
100731
|
opts.fastMath = false;
|
|
100412
100732
|
break;
|
|
100733
|
+
case "--no-native":
|
|
100734
|
+
opts.noNative = true;
|
|
100735
|
+
break;
|
|
100413
100736
|
case "--dump-ast":
|
|
100414
100737
|
opts.dumpAst = true;
|
|
100415
100738
|
break;
|
|
@@ -100583,7 +100906,7 @@ async function cmdEval(args) {
|
|
|
100583
100906
|
);
|
|
100584
100907
|
}
|
|
100585
100908
|
async function executeWithOptions(code, mainFileName, workspaceFiles, opts, searchPaths) {
|
|
100586
|
-
loadNativeAddon(opts.fastMath);
|
|
100909
|
+
loadNativeAddon(opts.fastMath, opts.noNative);
|
|
100587
100910
|
await loadQhullBackend();
|
|
100588
100911
|
const profiling = !!opts.profileOutput;
|
|
100589
100912
|
const totalStart = performance.now();
|
|
@@ -100895,7 +101218,7 @@ function collectSiteFiles(projectDir, outDirAbs, ignore) {
|
|
|
100895
101218
|
for (const e of entries) {
|
|
100896
101219
|
const full = join7(dir, e.name);
|
|
100897
101220
|
if (resolve2(full) === outDirAbs) continue;
|
|
100898
|
-
const rel =
|
|
101221
|
+
const rel = relative2(projectDir, full).split(sep).join("/");
|
|
100899
101222
|
if (matchesIgnore(rel)) continue;
|
|
100900
101223
|
if (e.isDirectory()) {
|
|
100901
101224
|
if (SITE_DEFAULT_IGNORE_DIRS.has(e.name)) continue;
|
|
@@ -101006,7 +101329,7 @@ async function cmdBuildSite(args) {
|
|
|
101006
101329
|
zipEntries["numbl-project.json"] = new TextEncoder().encode(
|
|
101007
101330
|
JSON.stringify(manifest, null, 2)
|
|
101008
101331
|
);
|
|
101009
|
-
const zip =
|
|
101332
|
+
const zip = zipSync2(zipEntries, { level: 6 });
|
|
101010
101333
|
writeFileSync4(join7(outDirAbs, "project.zip"), zip);
|
|
101011
101334
|
const totalMb = zip.length / 1048576;
|
|
101012
101335
|
console.log(
|
|
@@ -101071,7 +101394,7 @@ function cmdListBuiltins(flags) {
|
|
|
101071
101394
|
}
|
|
101072
101395
|
async function cmdRepl(args) {
|
|
101073
101396
|
const opts = parseOptions(args);
|
|
101074
|
-
loadNativeAddon(opts.fastMath);
|
|
101397
|
+
loadNativeAddon(opts.fastMath, opts.noNative);
|
|
101075
101398
|
await ensureMipCorePackage();
|
|
101076
101399
|
const replSearchPaths = [...opts.extraPaths];
|
|
101077
101400
|
const replFiles = opts.extraPaths.flatMap((d) => scanMFiles(d));
|
|
@@ -101104,7 +101427,7 @@ function cmdShowProfile(args) {
|
|
|
101104
101427
|
}
|
|
101105
101428
|
const data = JSON.parse(readFileSync6(filepath, "utf-8"));
|
|
101106
101429
|
const fmt = (n, decimals = 1) => n.toFixed(decimals);
|
|
101107
|
-
const
|
|
101430
|
+
const pad2 = (s, w) => s.padStart(w);
|
|
101108
101431
|
const execTime = data.executionTimeMs || 1;
|
|
101109
101432
|
const jitTime = data.jitCompileTimeMs ?? 0;
|
|
101110
101433
|
const dispatches = data.dispatches ?? [];
|
|
@@ -101132,27 +101455,27 @@ function cmdShowProfile(args) {
|
|
|
101132
101455
|
console.log("\nAll timings are self-time (exclusive) and mutually disjoint.");
|
|
101133
101456
|
if (dispatches.length > 0) {
|
|
101134
101457
|
const nameW = Math.max(20, ...dispatches.map((b) => b.name.length)) + 2;
|
|
101135
|
-
const header = "Function".padEnd(nameW) +
|
|
101458
|
+
const header = "Function".padEnd(nameW) + pad2("Calls", 10) + pad2("Self (ms)", 13) + pad2("Avg (ms)", 11) + pad2("% Exec", 9);
|
|
101136
101459
|
console.log("\nDispatched functions (user-defined / workspace):");
|
|
101137
101460
|
console.log(header);
|
|
101138
101461
|
console.log("\u2500".repeat(header.length));
|
|
101139
101462
|
for (const b of dispatches) {
|
|
101140
101463
|
const avg = b.callCount > 0 ? b.totalTimeMs / b.callCount : 0;
|
|
101141
101464
|
const pct = b.totalTimeMs / execTime * 100;
|
|
101142
|
-
const line = b.name.padEnd(nameW) +
|
|
101465
|
+
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
101466
|
console.log(line);
|
|
101144
101467
|
}
|
|
101145
101468
|
}
|
|
101146
101469
|
if (builtins.length > 0) {
|
|
101147
101470
|
const nameW = Math.max(20, ...builtins.map((b) => b.name.length)) + 2;
|
|
101148
|
-
const header = "Builtin".padEnd(nameW) +
|
|
101471
|
+
const header = "Builtin".padEnd(nameW) + pad2("Interp", 10) + pad2("Fallback", 10) + pad2("JIT", 10) + pad2("Self (ms)", 13) + pad2("% Exec", 9);
|
|
101149
101472
|
console.log("\nBuiltin functions (call counts by source):");
|
|
101150
101473
|
console.log(header);
|
|
101151
101474
|
console.log("\u2500".repeat(header.length));
|
|
101152
101475
|
for (const b of builtins) {
|
|
101153
101476
|
const totalTime = b.fallback.totalTimeMs + b.interp.totalTimeMs + b.jit.totalTimeMs;
|
|
101154
101477
|
const pct = totalTime / execTime * 100;
|
|
101155
|
-
const line = b.name.padEnd(nameW) +
|
|
101478
|
+
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
101479
|
console.log(line);
|
|
101157
101480
|
}
|
|
101158
101481
|
} else {
|
|
@@ -101162,13 +101485,13 @@ function cmdShowProfile(args) {
|
|
|
101162
101485
|
if (hotLoops.length > 0) {
|
|
101163
101486
|
hotLoops.sort((a, b) => b.totalTimeMs - a.totalTimeMs);
|
|
101164
101487
|
const locW = Math.max(20, ...hotLoops.map((l) => `${l.file}:${l.line}`.length)) + 2;
|
|
101165
|
-
const header = "Location".padEnd(locW) +
|
|
101488
|
+
const header = "Location".padEnd(locW) + pad2("Kind", 8) + pad2("Calls", 8) + pad2("Max Iters", 12) + pad2("Total (ms)", 13);
|
|
101166
101489
|
console.log("\nInterpreted loops (>1000 iterations):");
|
|
101167
101490
|
console.log(header);
|
|
101168
101491
|
console.log("\u2500".repeat(header.length));
|
|
101169
101492
|
for (const l of hotLoops) {
|
|
101170
101493
|
const loc = `${l.file}:${l.line}`;
|
|
101171
|
-
const line = loc.padEnd(locW) +
|
|
101494
|
+
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
101495
|
console.log(line);
|
|
101173
101496
|
}
|
|
101174
101497
|
}
|
|
@@ -101230,7 +101553,12 @@ async function main() {
|
|
|
101230
101553
|
case "run-tests": {
|
|
101231
101554
|
const testOpts = parseOptions(rest);
|
|
101232
101555
|
const dir = testOpts.positional.length > 0 ? testOpts.positional[0] : join7(packageDir, "numbl_test_scripts");
|
|
101233
|
-
await runTests(
|
|
101556
|
+
await runTests(
|
|
101557
|
+
dir,
|
|
101558
|
+
testOpts.optimization,
|
|
101559
|
+
testOpts.fastMath,
|
|
101560
|
+
testOpts.noNative
|
|
101561
|
+
);
|
|
101234
101562
|
break;
|
|
101235
101563
|
}
|
|
101236
101564
|
case "build-addon":
|