numbl 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/cli.js CHANGED
@@ -2213,9 +2213,9 @@ function airyAllComplex(zr, zi) {
2213
2213
  sfpi += m3k * tfi;
2214
2214
  sgpr += (m3k + 1) * tgr;
2215
2215
  sgpi += (m3k + 1) * tgi;
2216
- const mag = Math.abs(tfr) + Math.abs(tfi) + Math.abs(tgr) + Math.abs(tgi);
2216
+ const mag2 = Math.abs(tfr) + Math.abs(tfi) + Math.abs(tgr) + Math.abs(tgi);
2217
2217
  const ref = Math.abs(sfr) + Math.abs(sfi) + Math.abs(sgr) + Math.abs(sgi) + 1e-300;
2218
- if (mag < 1e-16 * ref) break;
2218
+ if (mag2 < 1e-16 * ref) break;
2219
2219
  }
2220
2220
  const gr = zr * sgr - zi * sgi;
2221
2221
  const gi = zr * sgi + zi * sgr;
@@ -24554,8 +24554,8 @@ function sinpi(x) {
24554
24554
  return Math.sin(Math.PI * x);
24555
24555
  }
24556
24556
  function negRealPow(negBase, exponent) {
24557
- const mag = Math.pow(-negBase, exponent);
24558
- return { re: mag * cospi(exponent), im: mag * sinpi(exponent) };
24557
+ const mag2 = Math.pow(-negBase, exponent);
24558
+ return { re: mag2 * cospi(exponent), im: mag2 * sinpi(exponent) };
24559
24559
  }
24560
24560
  function mPow(a, b) {
24561
24561
  if (isComplexOrMixed(a, b)) {
@@ -26464,9 +26464,9 @@ function complexSqrt(re, im) {
26464
26464
  if (re >= 0) return { re: Math.sqrt(re), im: 0 };
26465
26465
  return { re: 0, im: Math.sqrt(-re) };
26466
26466
  }
26467
- const mag = Math.hypot(re, im);
26468
- if (mag === 0) return { re: 0, im: 0 };
26469
- const r = Math.sqrt(mag);
26467
+ const mag2 = Math.hypot(re, im);
26468
+ if (mag2 === 0) return { re: 0, im: 0 };
26469
+ const r = Math.sqrt(mag2);
26470
26470
  const angle2 = Math.atan2(im, re) / 2;
26471
26471
  return { re: r * Math.cos(angle2), im: r * Math.sin(angle2) };
26472
26472
  }
@@ -26514,9 +26514,9 @@ defineBuiltin({
26514
26514
  )
26515
26515
  });
26516
26516
  registerUnary("sign", Math.sign, (re, im) => {
26517
- const mag = Math.hypot(re, im);
26518
- if (mag === 0) return { re: 0, im: 0 };
26519
- return { re: re / mag, im: im / mag };
26517
+ const mag2 = Math.hypot(re, im);
26518
+ if (mag2 === 0) return { re: 0, im: 0 };
26519
+ return { re: re / mag2, im: im / mag2 };
26520
26520
  });
26521
26521
  function registerRounding(name, fn) {
26522
26522
  defineBuiltin({
@@ -33133,6 +33133,134 @@ function stream2Call(args) {
33133
33133
  return RTV.cell(cellData, [1, cellData.length]);
33134
33134
  }
33135
33135
 
33136
+ // src/numbl-core/runtime/axisCommand.ts
33137
+ var STYLE_WORDS = /* @__PURE__ */ new Set([
33138
+ "tight",
33139
+ "padded",
33140
+ "fill",
33141
+ "equal",
33142
+ "image",
33143
+ "square",
33144
+ "vis3d",
33145
+ "normal",
33146
+ "tickaligned"
33147
+ ]);
33148
+ var STYLE_RESETS_LIMITS = /* @__PURE__ */ new Set([
33149
+ "tight",
33150
+ "padded",
33151
+ "equal",
33152
+ "image",
33153
+ "tickaligned"
33154
+ ]);
33155
+ var AUTO_AXIS_TOKENS = {
33156
+ auto: ["x", "y", "z"],
33157
+ "auto x": ["x"],
33158
+ "auto y": ["y"],
33159
+ "auto z": ["z"],
33160
+ "auto xy": ["x", "y"],
33161
+ "auto yx": ["x", "y"],
33162
+ "auto xz": ["x", "z"],
33163
+ "auto zx": ["x", "z"],
33164
+ "auto yz": ["y", "z"],
33165
+ "auto zy": ["y", "z"]
33166
+ };
33167
+ function isHandle(v) {
33168
+ return isRuntimeDummyHandle(v) || isRuntimeGraphicsHandle(v);
33169
+ }
33170
+ function numericValues(v) {
33171
+ if (isRuntimeTensor(v)) return Array.from(v.data);
33172
+ if (isRuntimeNumber(v)) return [v];
33173
+ if (isRuntimeLogical(v)) return [v ? 1 : 0];
33174
+ return null;
33175
+ }
33176
+ function bound(v) {
33177
+ return Number.isFinite(v) ? v : null;
33178
+ }
33179
+ function clearLimits(axes) {
33180
+ const instr = {
33181
+ type: "set_axis_limits"
33182
+ };
33183
+ if (axes.includes("x")) instr.xlim = "auto";
33184
+ if (axes.includes("y")) instr.ylim = "auto";
33185
+ if (axes.includes("z")) instr.zlim = "auto";
33186
+ return instr;
33187
+ }
33188
+ function applyAxisCommand(args, instructions, freezeLimits) {
33189
+ let i = 0;
33190
+ while (i < args.length && isHandle(args[i])) i++;
33191
+ let applied = false;
33192
+ const styles = [];
33193
+ for (; i < args.length; i++) {
33194
+ const arg = args[i];
33195
+ const nums = numericValues(arg);
33196
+ if (nums !== null) {
33197
+ applied = true;
33198
+ if (nums.length === 1) {
33199
+ instructions.push({ type: "set_axis_visible", value: nums[0] !== 0 });
33200
+ continue;
33201
+ }
33202
+ if (nums.length === 4 || nums.length === 6 || nums.length === 8) {
33203
+ const instr = {
33204
+ type: "set_axis_limits",
33205
+ xlim: [bound(nums[0]), bound(nums[1])],
33206
+ ylim: [bound(nums[2]), bound(nums[3])]
33207
+ };
33208
+ if (nums.length >= 6) instr.zlim = [bound(nums[4]), bound(nums[5])];
33209
+ instructions.push(instr);
33210
+ if (nums.length === 8 && Number.isFinite(nums[6]) && Number.isFinite(nums[7])) {
33211
+ instructions.push({ type: "set_caxis", limits: [nums[6], nums[7]] });
33212
+ }
33213
+ continue;
33214
+ }
33215
+ throw new RuntimeError(
33216
+ "axis: limit vector must have 4, 6, or 8 elements"
33217
+ );
33218
+ }
33219
+ const kw = toString(arg).trim().replace(/^["']|["']$/g, "").toLowerCase();
33220
+ applied = true;
33221
+ if (STYLE_WORDS.has(kw)) {
33222
+ styles.push(kw);
33223
+ if (STYLE_RESETS_LIMITS.has(kw)) {
33224
+ instructions.push(clearLimits(["x", "y", "z"]));
33225
+ }
33226
+ continue;
33227
+ }
33228
+ if (kw === "manual") {
33229
+ const lim = freezeLimits?.();
33230
+ if (lim && lim.length >= 4) {
33231
+ const instr = {
33232
+ type: "set_axis_limits",
33233
+ xlim: [lim[0], lim[1]],
33234
+ ylim: [lim[2], lim[3]]
33235
+ };
33236
+ if (lim.length >= 6) instr.zlim = [lim[4], lim[5]];
33237
+ instructions.push(instr);
33238
+ }
33239
+ continue;
33240
+ }
33241
+ if (kw in AUTO_AXIS_TOKENS) {
33242
+ instructions.push(clearLimits(AUTO_AXIS_TOKENS[kw]));
33243
+ continue;
33244
+ }
33245
+ if (kw === "xy" || kw === "ij") {
33246
+ instructions.push({
33247
+ type: "set_axis_ydir",
33248
+ dir: kw === "ij" ? "reverse" : "normal"
33249
+ });
33250
+ continue;
33251
+ }
33252
+ if (kw === "on" || kw === "off") {
33253
+ instructions.push({ type: "set_axis_visible", value: kw === "on" });
33254
+ continue;
33255
+ }
33256
+ throw new RuntimeError(`axis: unknown option '${kw}'`);
33257
+ }
33258
+ if (styles.length > 0) {
33259
+ instructions.push({ type: "set_axis", value: styles.join(" ") });
33260
+ }
33261
+ return applied;
33262
+ }
33263
+
33136
33264
  // src/numbl-core/runtime/plotBuiltinDispatch.ts
33137
33265
  function dispatchPlotBuiltin(name, args, instructions, state) {
33138
33266
  switch (name) {
@@ -33321,10 +33449,7 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
33321
33449
  dispatchColormap(args, instructions);
33322
33450
  return true;
33323
33451
  case "axis": {
33324
- if (args.length > 0) {
33325
- const val = toString(args[0]).replace(/^"|"$/g, "");
33326
- plotInstr(instructions, { type: "set_axis", value: val });
33327
- }
33452
+ applyAxisCommand(args, instructions);
33328
33453
  return true;
33329
33454
  }
33330
33455
  case "caxis":
@@ -37007,6 +37132,85 @@ function quadgkAdaptive(integrand, a, b, opts = {}) {
37007
37132
  };
37008
37133
  }
37009
37134
 
37135
+ // src/numbl-core/helpers/eigs-select.ts
37136
+ var SIGMA_ALIASES = {
37137
+ lm: "largestabs",
37138
+ largestabs: "largestabs",
37139
+ sm: "smallestabs",
37140
+ smallestabs: "smallestabs",
37141
+ lr: "largestreal",
37142
+ la: "largestreal",
37143
+ largestreal: "largestreal",
37144
+ sr: "smallestreal",
37145
+ sa: "smallestreal",
37146
+ smallestreal: "smallestreal",
37147
+ be: "bothendsreal",
37148
+ bothendsreal: "bothendsreal",
37149
+ li: "largestimag",
37150
+ largestimag: "largestimag",
37151
+ si: "smallestimag",
37152
+ smallestimag: "smallestimag",
37153
+ bothendsimag: "bothendsimag"
37154
+ };
37155
+ function normalizeSigmaString(s) {
37156
+ return SIGMA_ALIASES[s.trim().toLowerCase()] ?? null;
37157
+ }
37158
+ var mag = (re, im) => Math.hypot(re, im);
37159
+ function sortedBy(n, key, descending) {
37160
+ const idx = Array.from({ length: n }, (_, i) => i);
37161
+ idx.sort((a, b) => {
37162
+ const ka = key(a);
37163
+ const kb = key(b);
37164
+ if (ka !== kb) return descending ? kb - ka : ka - kb;
37165
+ return a - b;
37166
+ });
37167
+ return idx;
37168
+ }
37169
+ function bothEnds(n, key, k) {
37170
+ const nHigh = Math.ceil(k / 2);
37171
+ const nLow = Math.floor(k / 2);
37172
+ const desc = sortedBy(n, key, true);
37173
+ const high = desc.slice(0, nHigh);
37174
+ const highSet = new Set(high);
37175
+ const low = [];
37176
+ for (let i = desc.length - 1; i >= 0 && low.length < nLow; i--) {
37177
+ if (!highSet.has(desc[i])) low.push(desc[i]);
37178
+ }
37179
+ return { high, low };
37180
+ }
37181
+ function selectEigsIndices(re, im, k, sigma) {
37182
+ const n = re.length;
37183
+ const kk = Math.max(0, Math.min(k, n));
37184
+ switch (sigma.kind) {
37185
+ case "largestabs":
37186
+ return sortedBy(n, (i) => mag(re[i], im[i]), true).slice(0, kk);
37187
+ case "smallestabs":
37188
+ return sortedBy(n, (i) => mag(re[i], im[i]), false).slice(0, kk);
37189
+ case "largestreal":
37190
+ return sortedBy(n, (i) => re[i], true).slice(0, kk);
37191
+ case "smallestreal":
37192
+ return sortedBy(n, (i) => re[i], false).slice(0, kk);
37193
+ case "largestimag":
37194
+ return sortedBy(n, (i) => im[i], true).slice(0, kk);
37195
+ case "smallestimag":
37196
+ return sortedBy(n, (i) => im[i], false).slice(0, kk);
37197
+ case "scalar": {
37198
+ const { re: sr, im: si } = sigma;
37199
+ return sortedBy(n, (i) => mag(re[i] - sr, im[i] - si), false).slice(0, kk);
37200
+ }
37201
+ case "bothendsreal": {
37202
+ const { high, low } = bothEnds(n, (i) => re[i], kk);
37203
+ return [...high, ...low].sort((a, b) => re[a] - re[b] || a - b);
37204
+ }
37205
+ case "bothendsimag": {
37206
+ const { high, low } = bothEnds(n, (i) => im[i], kk);
37207
+ return [...high, ...low].sort(
37208
+ (a, b) => Math.abs(im[b]) - Math.abs(im[a]) || a - b
37209
+ );
37210
+ }
37211
+ }
37212
+ }
37213
+
37010
37214
  // src/numbl-core/runtime/specialBuiltinNames.ts
37011
37215
  var SPECIAL_BUILTIN_NAMES = [
37012
37216
  "help",
@@ -37125,6 +37329,7 @@ var SPECIAL_BUILTIN_NAMES = [
37125
37329
  "toc",
37126
37330
  "quadgk",
37127
37331
  "gmres",
37332
+ "eigs",
37128
37333
  "onCleanup"
37129
37334
  ];
37130
37335
 
@@ -38502,6 +38707,9 @@ function registerSpecialBuiltins(rt) {
38502
38707
  registerSpecial("gmres", (nargout, args) => {
38503
38708
  return _gmresImpl(rt, nargout, args);
38504
38709
  });
38710
+ registerSpecial("eigs", (nargout, args) => {
38711
+ return _eigsImpl(rt, nargout, args);
38712
+ });
38505
38713
  registerSpecial("onCleanup", (_nargout, args) => {
38506
38714
  if (args.length !== 1 || !isRuntimeFunction(args[0]))
38507
38715
  throw new RuntimeError("onCleanup requires a function handle argument");
@@ -38509,6 +38717,271 @@ function registerSpecialBuiltins(rt) {
38509
38717
  return RTV.classInstance("onCleanup", [], false);
38510
38718
  });
38511
38719
  }
38720
+ function eigsReIm(v) {
38721
+ if (isRuntimeNumber(v)) return { re: [v], im: [0] };
38722
+ if (isRuntimeComplexNumber(v)) return { re: [v.re], im: [v.im] };
38723
+ if (isRuntimeTensor(v)) {
38724
+ const re = Array.from(v.data);
38725
+ const im = v.imag ? Array.from(v.imag) : re.map(() => 0);
38726
+ return { re, im };
38727
+ }
38728
+ throw new RuntimeError("eigs: expected a numeric value");
38729
+ }
38730
+ function eigsAsTensor(v) {
38731
+ const rv = ensureRuntimeValue(v);
38732
+ if (isRuntimeTensor(rv)) return rv;
38733
+ if (isRuntimeNumber(rv))
38734
+ return RTV.tensor(allocFloat64Array([rv]), [1, 1]);
38735
+ if (isRuntimeComplexNumber(rv))
38736
+ return RTV.tensor(
38737
+ allocFloat64Array([rv.re]),
38738
+ [1, 1],
38739
+ allocFloat64Array([rv.im])
38740
+ );
38741
+ throw new RuntimeError("eigs: expected a numeric matrix");
38742
+ }
38743
+ function eigsMatrixSize(A) {
38744
+ if (isRuntimeNumber(A) || isRuntimeComplexNumber(A)) return 1;
38745
+ if (isRuntimeTensor(A)) {
38746
+ const rows = A.shape[0] ?? 1;
38747
+ const cols = A.shape[1] ?? 1;
38748
+ if (rows !== cols)
38749
+ throw new RuntimeError("eigs: input matrix must be square");
38750
+ return rows;
38751
+ }
38752
+ throw new RuntimeError("eigs: input must be a numeric matrix");
38753
+ }
38754
+ function eigsIsScalar(v) {
38755
+ return isRuntimeNumber(v) || isRuntimeComplexNumber(v) || isRuntimeTensor(v) && v.data.length === 1;
38756
+ }
38757
+ function eigsIsMatrixArg(v) {
38758
+ if (!isRuntimeTensor(v)) return false;
38759
+ if (v.data.length === 0) return true;
38760
+ const rows = v.shape[0] ?? 1;
38761
+ const cols = v.shape[1] ?? 1;
38762
+ return rows > 1 || cols > 1;
38763
+ }
38764
+ function eigsToBool(v) {
38765
+ if (isRuntimeLogical(v)) return v;
38766
+ if (isRuntimeNumber(v)) return v !== 0;
38767
+ if (isRuntimeTensor(v) && v.data.length >= 1) return v.data[0] !== 0;
38768
+ return false;
38769
+ }
38770
+ function eigsParseSigma(v) {
38771
+ if (isRuntimeString(v) || isRuntimeChar(v)) {
38772
+ const s = typeof v === "string" ? v : v.value;
38773
+ const kind = normalizeSigmaString(s);
38774
+ if (!kind) throw new RuntimeError(`eigs: unknown sigma option '${s}'`);
38775
+ return { kind };
38776
+ }
38777
+ let re;
38778
+ let im = 0;
38779
+ if (isRuntimeComplexNumber(v)) {
38780
+ re = v.re;
38781
+ im = v.im;
38782
+ } else if (isRuntimeNumber(v)) {
38783
+ re = v;
38784
+ } else if (isRuntimeTensor(v) && v.data.length >= 1) {
38785
+ re = v.data[0];
38786
+ im = v.imag ? v.imag[0] : 0;
38787
+ } else {
38788
+ throw new RuntimeError("eigs: invalid sigma argument");
38789
+ }
38790
+ if (re === 0 && im === 0) return { kind: "smallestabs" };
38791
+ return { kind: "scalar", re, im };
38792
+ }
38793
+ function eigsReconstructOperator(rt, afun, n) {
38794
+ const re = allocFloat64Array(n * n);
38795
+ const im = allocFloat64Array(n * n);
38796
+ let anyImag = false;
38797
+ for (let j = 0; j < n; j++) {
38798
+ const e = allocFloat64Array(n);
38799
+ e[j] = 1;
38800
+ const col = ensureRuntimeValue(
38801
+ rt.index(afun, [RTV.tensor(e, [n, 1])], 1)
38802
+ );
38803
+ const { re: cre, im: cim } = eigsReIm(col);
38804
+ for (let i = 0; i < n; i++) {
38805
+ re[j * n + i] = cre[i] ?? 0;
38806
+ im[j * n + i] = cim[i] ?? 0;
38807
+ if (im[j * n + i] !== 0) anyImag = true;
38808
+ }
38809
+ }
38810
+ return anyImag ? RTV.tensor(re, [n, n], im) : RTV.tensor(re, [n, n]);
38811
+ }
38812
+ function eigsRecoverAfunMatrix(rt, M, sigma, B, n) {
38813
+ if (sigma.kind === "smallestabs") {
38814
+ return eigsAsTensor(rt.dispatch("inv", 1, [M]));
38815
+ }
38816
+ if (sigma.kind === "scalar") {
38817
+ const base = eigsAsTensor(rt.dispatch("inv", 1, [M]));
38818
+ const shiftRe = allocFloat64Array(n * n);
38819
+ const shiftIm = allocFloat64Array(n * n);
38820
+ if (B && isRuntimeTensor(B)) {
38821
+ for (let i = 0; i < n * n; i++) {
38822
+ shiftRe[i] = (B.data[i] ?? 0) * sigma.re - (B.imag?.[i] ?? 0) * sigma.im;
38823
+ shiftIm[i] = (B.data[i] ?? 0) * sigma.im + (B.imag?.[i] ?? 0) * sigma.re;
38824
+ }
38825
+ } else {
38826
+ for (let d = 0; d < n; d++) {
38827
+ shiftRe[d * n + d] = sigma.re;
38828
+ shiftIm[d * n + d] = sigma.im;
38829
+ }
38830
+ }
38831
+ const shift = maybeComplexTensor(shiftRe, [n, n], shiftIm);
38832
+ return eigsAsTensor(rt.dispatch("plus", 1, [base, shift]));
38833
+ }
38834
+ return M;
38835
+ }
38836
+ function eigsReconstructCholesky(R, perm, n) {
38837
+ if (!isRuntimeTensor(R))
38838
+ throw new RuntimeError("eigs: Cholesky factor must be a matrix");
38839
+ const r = R.data;
38840
+ const rtr = allocFloat64Array(n * n);
38841
+ for (let i = 0; i < n; i++) {
38842
+ for (let j = 0; j < n; j++) {
38843
+ let s = 0;
38844
+ for (let kk = 0; kk < n; kk++) s += r[i * n + kk] * r[j * n + kk];
38845
+ rtr[j * n + i] = s;
38846
+ }
38847
+ }
38848
+ if (!perm) return RTV.tensor(rtr, [n, n]);
38849
+ const b = allocFloat64Array(n * n);
38850
+ for (let i = 0; i < n; i++) {
38851
+ for (let j = 0; j < n; j++) {
38852
+ const pi2 = (perm[i] ?? i + 1) - 1;
38853
+ const pj = (perm[j] ?? j + 1) - 1;
38854
+ b[pj * n + pi2] = rtr[j * n + i];
38855
+ }
38856
+ }
38857
+ return RTV.tensor(b, [n, n]);
38858
+ }
38859
+ function _eigsImpl(rt, nargout, args) {
38860
+ const margs = args.map((a) => ensureRuntimeValue(a));
38861
+ if (margs.length < 1)
38862
+ throw new RuntimeError("eigs requires at least 1 argument");
38863
+ let afun = null;
38864
+ let A = null;
38865
+ let n;
38866
+ let pos;
38867
+ if (isRuntimeFunction(margs[0])) {
38868
+ afun = margs[0];
38869
+ if (margs.length < 2 || !eigsIsScalar(margs[1]))
38870
+ throw new RuntimeError(
38871
+ "eigs: a function handle must be followed by the matrix size n"
38872
+ );
38873
+ n = Math.floor(toNumber(margs[1]));
38874
+ pos = 2;
38875
+ } else {
38876
+ A = margs[0];
38877
+ n = eigsMatrixSize(A);
38878
+ pos = 1;
38879
+ }
38880
+ let B = null;
38881
+ if (pos < margs.length && eigsIsMatrixArg(margs[pos])) {
38882
+ const b = margs[pos];
38883
+ if (!(isRuntimeTensor(b) && b.data.length === 0)) B = b;
38884
+ pos++;
38885
+ }
38886
+ let kReq = null;
38887
+ if (pos < margs.length && eigsIsScalar(margs[pos])) {
38888
+ kReq = Math.max(1, Math.floor(toNumber(margs[pos])));
38889
+ pos++;
38890
+ }
38891
+ let sigma = { kind: "largestabs" };
38892
+ if (pos < margs.length) {
38893
+ const a = margs[pos];
38894
+ if (isRuntimeString(a) || isRuntimeChar(a)) {
38895
+ const s = typeof a === "string" ? a : a.value;
38896
+ if (normalizeSigmaString(s)) {
38897
+ sigma = eigsParseSigma(a);
38898
+ pos++;
38899
+ }
38900
+ } else if (eigsIsScalar(a)) {
38901
+ sigma = eigsParseSigma(a);
38902
+ pos++;
38903
+ }
38904
+ }
38905
+ let isCholesky = false;
38906
+ let cholPerm = null;
38907
+ const applyOption = (key, val) => {
38908
+ const lk = key.toLowerCase();
38909
+ if (lk === "ischolesky" || lk === "cholb") isCholesky = eigsToBool(val);
38910
+ else if (lk === "choleskypermutation" || lk === "permb") {
38911
+ if (isRuntimeTensor(val)) cholPerm = Array.from(val.data);
38912
+ }
38913
+ };
38914
+ while (pos < margs.length) {
38915
+ const a = margs[pos];
38916
+ if (isRuntimeStruct(a)) {
38917
+ for (const [key, val] of a.fields) applyOption(key, val);
38918
+ pos++;
38919
+ } else if (pos + 1 < margs.length) {
38920
+ applyOption(
38921
+ isRuntimeChar(margs[pos]) ? margs[pos].value : String(toString(margs[pos])),
38922
+ margs[pos + 1]
38923
+ );
38924
+ pos += 2;
38925
+ } else {
38926
+ break;
38927
+ }
38928
+ }
38929
+ const k = kReq == null ? Math.min(6, n) : Math.min(kReq, n);
38930
+ let actualA;
38931
+ if (afun) {
38932
+ const M = eigsReconstructOperator(rt, afun, n);
38933
+ actualA = eigsRecoverAfunMatrix(rt, M, sigma, B, n);
38934
+ } else {
38935
+ actualA = A;
38936
+ }
38937
+ let effective = actualA;
38938
+ if (B) {
38939
+ const Bmat = isCholesky ? eigsReconstructCholesky(B, cholPerm, n) : B;
38940
+ effective = eigsAsTensor(rt.dispatch("mldivide", 1, [Bmat, actualA]));
38941
+ }
38942
+ if (nargout <= 1) {
38943
+ const d = ensureRuntimeValue(rt.dispatch("eig", 1, [effective]));
38944
+ const { re: re2, im: im2 } = eigsReIm(d);
38945
+ const order2 = selectEigsIndices(re2, im2, k, sigma);
38946
+ const outRe = allocFloat64Array(order2.length);
38947
+ const outIm = allocFloat64Array(order2.length);
38948
+ for (let c = 0; c < order2.length; c++) {
38949
+ outRe[c] = re2[order2[c]];
38950
+ outIm[c] = im2[order2[c]];
38951
+ }
38952
+ return maybeComplexTensor(outRe, [order2.length, 1], outIm);
38953
+ }
38954
+ const res = rt.dispatch("eig", 2, [effective]);
38955
+ const Vall = eigsAsTensor(res[0]);
38956
+ const Dall = eigsAsTensor(res[1]);
38957
+ const p2 = Dall.shape[0] ?? 1;
38958
+ const vrows = Vall.shape[0] ?? p2;
38959
+ const re = [];
38960
+ const im = [];
38961
+ for (let j = 0; j < p2; j++) {
38962
+ re.push(Dall.data[j * p2 + j]);
38963
+ im.push(Dall.imag ? Dall.imag[j * p2 + j] : 0);
38964
+ }
38965
+ const order = selectEigsIndices(re, im, k, sigma);
38966
+ const kk = order.length;
38967
+ const Vre = allocFloat64Array(vrows * kk);
38968
+ const Vim = allocFloat64Array(vrows * kk);
38969
+ const Dre = allocFloat64Array(kk * kk);
38970
+ const Dim = allocFloat64Array(kk * kk);
38971
+ for (let c = 0; c < kk; c++) {
38972
+ const s = order[c];
38973
+ for (let r = 0; r < vrows; r++) {
38974
+ Vre[c * vrows + r] = Vall.data[s * vrows + r];
38975
+ if (Vall.imag) Vim[c * vrows + r] = Vall.imag[s * vrows + r];
38976
+ }
38977
+ Dre[c * kk + c] = re[s];
38978
+ Dim[c * kk + c] = im[s];
38979
+ }
38980
+ const V = maybeComplexTensor(Vre, [vrows, kk], Vim);
38981
+ const D = maybeComplexTensor(Dre, [kk, kk], Dim);
38982
+ if (nargout === 2) return [V, D];
38983
+ return [V, D, RTV.num(0)];
38984
+ }
38512
38985
  function _ode45Impl(rt, nargout, args, tableau = dormandPrince45) {
38513
38986
  const solverName = tableau.name;
38514
38987
  if (args.length < 3)
@@ -39444,6 +39917,484 @@ function memberChainAssign(rt, base, names, rhs) {
39444
39917
  return subsasgnFallback(rt, base, names, rhs);
39445
39918
  }
39446
39919
 
39920
+ // src/graphics/figuresReducer.ts
39921
+ var defaultAxes = {
39922
+ holdOn: false,
39923
+ traces: [],
39924
+ plot3Traces: [],
39925
+ surfTraces: [],
39926
+ pcolorTraces: [],
39927
+ contourTraces: [],
39928
+ barTraces: [],
39929
+ barhTraces: [],
39930
+ bar3Traces: [],
39931
+ bar3hTraces: [],
39932
+ errorBarTraces: [],
39933
+ boxTraces: [],
39934
+ quiverTraces: [],
39935
+ quiver3Traces: [],
39936
+ areaTraces: [],
39937
+ areaBaseValue: 0
39938
+ };
39939
+ function getAxes(fig) {
39940
+ return fig.axes[fig.currentAxesIndex] || { ...defaultAxes };
39941
+ }
39942
+ function setAxes(fig, axes) {
39943
+ return {
39944
+ ...fig,
39945
+ axes: { ...fig.axes, [fig.currentAxesIndex]: axes }
39946
+ };
39947
+ }
39948
+ var initialFiguresState = {
39949
+ currentHandle: 1,
39950
+ figs: {}
39951
+ };
39952
+ var defaultFigure = {
39953
+ currentAxesIndex: 1,
39954
+ axes: {}
39955
+ };
39956
+ function ensureFig(state) {
39957
+ return state.figs[state.currentHandle] || { ...defaultFigure };
39958
+ }
39959
+ function updateAxes(state, update) {
39960
+ const fig = ensureFig(state);
39961
+ const axes = getAxes(fig);
39962
+ return {
39963
+ ...state,
39964
+ figs: {
39965
+ ...state.figs,
39966
+ [state.currentHandle]: setAxes(fig, { ...axes, ...update })
39967
+ }
39968
+ };
39969
+ }
39970
+ function addTraces(state, update) {
39971
+ const fig = ensureFig(state);
39972
+ const axes = getAxes(fig);
39973
+ const hold = axes.holdOn;
39974
+ return {
39975
+ ...state,
39976
+ figs: {
39977
+ ...state.figs,
39978
+ [state.currentHandle]: setAxes(fig, {
39979
+ ...axes,
39980
+ traces: update.traces ?? (hold ? axes.traces : []),
39981
+ plot3Traces: update.plot3Traces ?? (hold ? axes.plot3Traces : []),
39982
+ surfTraces: update.surfTraces ?? (hold ? axes.surfTraces : []),
39983
+ pcolorTraces: update.pcolorTraces ?? (hold ? axes.pcolorTraces : []),
39984
+ contourTraces: update.contourTraces ?? (hold ? axes.contourTraces : []),
39985
+ barTraces: update.barTraces ?? (hold ? axes.barTraces : []),
39986
+ barhTraces: update.barhTraces ?? (hold ? axes.barhTraces : []),
39987
+ bar3Traces: update.bar3Traces ?? (hold ? axes.bar3Traces : []),
39988
+ bar3hTraces: update.bar3hTraces ?? (hold ? axes.bar3hTraces : []),
39989
+ errorBarTraces: update.errorBarTraces ?? (hold ? axes.errorBarTraces : []),
39990
+ boxTraces: update.boxTraces ?? (hold ? axes.boxTraces : []),
39991
+ pieTrace: update.pieTrace ?? (hold ? axes.pieTrace : void 0),
39992
+ heatmapTrace: update.heatmapTrace ?? (hold ? axes.heatmapTrace : void 0),
39993
+ quiverTraces: update.quiverTraces ?? (hold ? axes.quiverTraces : []),
39994
+ quiver3Traces: update.quiver3Traces ?? (hold ? axes.quiver3Traces : []),
39995
+ areaTraces: update.areaTraces ?? (hold ? axes.areaTraces : []),
39996
+ areaBaseValue: update.areaBaseValue ?? axes.areaBaseValue,
39997
+ ...update.imagescTrace !== void 0 ? { imagescTrace: update.imagescTrace } : {}
39998
+ })
39999
+ }
40000
+ };
40001
+ }
40002
+ var figuresReducer = (state, action) => {
40003
+ switch (action.type) {
40004
+ case "set_figure_handle":
40005
+ return { ...state, currentHandle: action.handle };
40006
+ case "set_hold":
40007
+ return updateAxes(state, { holdOn: action.value });
40008
+ case "plot": {
40009
+ const axes = getAxes(ensureFig(state));
40010
+ return addTraces(state, {
40011
+ traces: axes.holdOn ? [...axes.traces, ...action.traces] : [...action.traces]
40012
+ });
40013
+ }
40014
+ case "plot3": {
40015
+ const axes = getAxes(ensureFig(state));
40016
+ return addTraces(state, {
40017
+ plot3Traces: axes.holdOn ? [...axes.plot3Traces, ...action.traces] : [...action.traces]
40018
+ });
40019
+ }
40020
+ case "surf": {
40021
+ const axes = getAxes(ensureFig(state));
40022
+ return addTraces(state, {
40023
+ surfTraces: axes.holdOn ? [...axes.surfTraces, action.trace] : [action.trace]
40024
+ });
40025
+ }
40026
+ case "surface": {
40027
+ const axes = getAxes(ensureFig(state));
40028
+ return updateAxes(state, {
40029
+ surfTraces: [...axes.surfTraces, action.trace]
40030
+ });
40031
+ }
40032
+ case "imagesc":
40033
+ return addTraces(state, { imagescTrace: action.trace });
40034
+ case "pcolor": {
40035
+ const axes = getAxes(ensureFig(state));
40036
+ return addTraces(state, {
40037
+ pcolorTraces: axes.holdOn ? [...axes.pcolorTraces, action.trace] : [action.trace]
40038
+ });
40039
+ }
40040
+ case "contour": {
40041
+ const axes = getAxes(ensureFig(state));
40042
+ return addTraces(state, {
40043
+ contourTraces: axes.holdOn ? [...axes.contourTraces, action.trace] : [action.trace]
40044
+ });
40045
+ }
40046
+ case "mesh": {
40047
+ const axes = getAxes(ensureFig(state));
40048
+ return addTraces(state, {
40049
+ surfTraces: axes.holdOn ? [...axes.surfTraces, action.trace] : [action.trace]
40050
+ });
40051
+ }
40052
+ case "bar": {
40053
+ const axes = getAxes(ensureFig(state));
40054
+ return addTraces(state, {
40055
+ barTraces: axes.holdOn ? [...axes.barTraces, ...action.traces] : [...action.traces]
40056
+ });
40057
+ }
40058
+ case "barh": {
40059
+ const axes = getAxes(ensureFig(state));
40060
+ return addTraces(state, {
40061
+ barhTraces: axes.holdOn ? [...axes.barhTraces, ...action.traces] : [...action.traces]
40062
+ });
40063
+ }
40064
+ case "bar3": {
40065
+ const axes = getAxes(ensureFig(state));
40066
+ return addTraces(state, {
40067
+ bar3Traces: axes.holdOn ? [...axes.bar3Traces, action.trace] : [action.trace]
40068
+ });
40069
+ }
40070
+ case "bar3h": {
40071
+ const axes = getAxes(ensureFig(state));
40072
+ return addTraces(state, {
40073
+ bar3hTraces: axes.holdOn ? [...axes.bar3hTraces, action.trace] : [action.trace]
40074
+ });
40075
+ }
40076
+ case "errorbar": {
40077
+ const axes = getAxes(ensureFig(state));
40078
+ return addTraces(state, {
40079
+ errorBarTraces: axes.holdOn ? [...axes.errorBarTraces, ...action.traces] : [...action.traces]
40080
+ });
40081
+ }
40082
+ case "boxchart": {
40083
+ const axes = getAxes(ensureFig(state));
40084
+ return addTraces(state, {
40085
+ boxTraces: axes.holdOn ? [...axes.boxTraces, ...action.traces] : [...action.traces]
40086
+ });
40087
+ }
40088
+ case "piechart":
40089
+ return addTraces(state, { pieTrace: action.trace });
40090
+ case "heatmap":
40091
+ return addTraces(state, { heatmapTrace: action.trace });
40092
+ case "quiver": {
40093
+ const axes = getAxes(ensureFig(state));
40094
+ return addTraces(state, {
40095
+ quiverTraces: axes.holdOn ? [...axes.quiverTraces, ...action.traces] : [...action.traces]
40096
+ });
40097
+ }
40098
+ case "quiver3": {
40099
+ const axes = getAxes(ensureFig(state));
40100
+ return addTraces(state, {
40101
+ quiver3Traces: axes.holdOn ? [...axes.quiver3Traces, action.trace] : [action.trace]
40102
+ });
40103
+ }
40104
+ case "area": {
40105
+ const axes = getAxes(ensureFig(state));
40106
+ return addTraces(state, {
40107
+ areaTraces: axes.holdOn ? [...axes.areaTraces, ...action.traces] : [...action.traces],
40108
+ areaBaseValue: action.baseValue
40109
+ });
40110
+ }
40111
+ case "close": {
40112
+ const remainingFigs = Object.fromEntries(
40113
+ Object.entries(state.figs).filter(
40114
+ ([k]) => Number(k) !== state.currentHandle
40115
+ )
40116
+ );
40117
+ const handles = Object.keys(remainingFigs).map(Number).sort((a, b) => a - b);
40118
+ return {
40119
+ ...state,
40120
+ currentHandle: handles.length > 0 ? handles[handles.length - 1] : 1,
40121
+ figs: remainingFigs
40122
+ };
40123
+ }
40124
+ case "close_all":
40125
+ case "clear":
40126
+ return initialFiguresState;
40127
+ case "set_title":
40128
+ return updateAxes(state, { title: action.text });
40129
+ case "set_xlabel":
40130
+ return updateAxes(state, { xlabel: action.text });
40131
+ case "set_ylabel":
40132
+ return updateAxes(state, { ylabel: action.text });
40133
+ case "set_zlabel":
40134
+ return updateAxes(state, { zlabel: action.text });
40135
+ case "set_shading":
40136
+ return updateAxes(state, { shading: action.shading });
40137
+ case "set_legend":
40138
+ return updateAxes(state, { legend: action.labels });
40139
+ case "set_sgtitle": {
40140
+ const fig = ensureFig(state);
40141
+ return {
40142
+ ...state,
40143
+ figs: {
40144
+ ...state.figs,
40145
+ [state.currentHandle]: { ...fig, sgtitle: action.text }
40146
+ }
40147
+ };
40148
+ }
40149
+ case "set_grid":
40150
+ return updateAxes(state, { gridOn: action.value });
40151
+ case "set_colorbar":
40152
+ return updateAxes(state, {
40153
+ colorbar: action.value !== "off",
40154
+ colorbarLocation: action.location ?? "eastoutside"
40155
+ });
40156
+ case "set_colormap":
40157
+ return updateAxes(state, {
40158
+ colormap: action.name,
40159
+ colormapData: action.data
40160
+ });
40161
+ case "set_view":
40162
+ return updateAxes(state, { view: { az: action.az, el: action.el } });
40163
+ case "set_axis":
40164
+ return updateAxes(state, { axisMode: action.value });
40165
+ case "set_axis_limits": {
40166
+ const axes = getAxes(ensureFig(state));
40167
+ const resolve3 = (prev, spec) => {
40168
+ if (spec === void 0) return prev;
40169
+ if (spec === "auto") return void 0;
40170
+ return spec;
40171
+ };
40172
+ return updateAxes(state, {
40173
+ xlim: resolve3(axes.xlim, action.xlim),
40174
+ ylim: resolve3(axes.ylim, action.ylim),
40175
+ zlim: resolve3(axes.zlim, action.zlim)
40176
+ });
40177
+ }
40178
+ case "set_axis_ydir":
40179
+ return updateAxes(state, { yDir: action.dir });
40180
+ case "set_axis_visible":
40181
+ return updateAxes(state, { axisVisible: action.value });
40182
+ case "set_axis_scale":
40183
+ return updateAxes(state, { axisScale: action.value });
40184
+ case "set_caxis":
40185
+ return updateAxes(state, { caxis: action.limits });
40186
+ case "clf": {
40187
+ const fig = state.figs[state.currentHandle];
40188
+ if (!fig) return state;
40189
+ return {
40190
+ ...state,
40191
+ figs: {
40192
+ ...state.figs,
40193
+ [state.currentHandle]: { ...defaultFigure }
40194
+ }
40195
+ };
40196
+ }
40197
+ case "cla": {
40198
+ const fig = ensureFig(state);
40199
+ const prev = getAxes(fig);
40200
+ const cleared = action.reset ? { ...defaultAxes } : {
40201
+ ...defaultAxes,
40202
+ title: prev.title,
40203
+ xlabel: prev.xlabel,
40204
+ ylabel: prev.ylabel,
40205
+ zlabel: prev.zlabel,
40206
+ gridOn: prev.gridOn,
40207
+ colorbar: prev.colorbar,
40208
+ colorbarLocation: prev.colorbarLocation,
40209
+ colormap: prev.colormap,
40210
+ colormapData: prev.colormapData,
40211
+ view: prev.view,
40212
+ axisMode: prev.axisMode,
40213
+ axisScale: prev.axisScale,
40214
+ caxis: prev.caxis,
40215
+ xlim: prev.xlim,
40216
+ ylim: prev.ylim,
40217
+ zlim: prev.zlim,
40218
+ yDir: prev.yDir,
40219
+ axisVisible: prev.axisVisible,
40220
+ shading: prev.shading
40221
+ };
40222
+ return {
40223
+ ...state,
40224
+ figs: {
40225
+ ...state.figs,
40226
+ [state.currentHandle]: setAxes(fig, cleared)
40227
+ }
40228
+ };
40229
+ }
40230
+ case "set_subplot": {
40231
+ const fig = ensureFig(state);
40232
+ const newFig = {
40233
+ ...fig,
40234
+ subplotGrid: { rows: action.rows, cols: action.cols },
40235
+ currentAxesIndex: action.index
40236
+ };
40237
+ if (!newFig.axes[action.index]) {
40238
+ newFig.axes = { ...newFig.axes, [action.index]: { ...defaultAxes } };
40239
+ }
40240
+ return {
40241
+ ...state,
40242
+ figs: { ...state.figs, [state.currentHandle]: newFig }
40243
+ };
40244
+ }
40245
+ default:
40246
+ return state;
40247
+ }
40248
+ };
40249
+
40250
+ // src/graphics/axisLimits.ts
40251
+ var EMPTY = { min: Infinity, max: -Infinity };
40252
+ function include(e, v) {
40253
+ if (Number.isFinite(v)) {
40254
+ if (v < e.min) e.min = v;
40255
+ if (v > e.max) e.max = v;
40256
+ }
40257
+ }
40258
+ function includeAll(e, vs) {
40259
+ if (!vs) return;
40260
+ for (let i = 0; i < vs.length; i++) include(e, vs[i]);
40261
+ }
40262
+ function axesIs3D(axes) {
40263
+ return (axes.surfTraces?.length ?? 0) > 0 || (axes.plot3Traces?.length ?? 0) > 0 || (axes.bar3Traces?.length ?? 0) > 0 || (axes.bar3hTraces?.length ?? 0) > 0 || (axes.quiver3Traces?.length ?? 0) > 0;
40264
+ }
40265
+ function dataExtents(axes) {
40266
+ const x = { ...EMPTY };
40267
+ const y = { ...EMPTY };
40268
+ const z = { ...EMPTY };
40269
+ for (const t of axes.traces ?? []) {
40270
+ includeAll(x, t.x);
40271
+ includeAll(y, t.y);
40272
+ }
40273
+ for (const t of axes.plot3Traces ?? []) {
40274
+ includeAll(x, t.x);
40275
+ includeAll(y, t.y);
40276
+ includeAll(z, t.z);
40277
+ }
40278
+ for (const t of [...axes.surfTraces ?? []]) {
40279
+ includeAll(x, t.x);
40280
+ includeAll(y, t.y);
40281
+ includeAll(z, t.z);
40282
+ }
40283
+ if (axes.imagescTrace) {
40284
+ include(x, axes.imagescTrace.x[0]);
40285
+ include(x, axes.imagescTrace.x[1]);
40286
+ include(y, axes.imagescTrace.y[0]);
40287
+ include(y, axes.imagescTrace.y[1]);
40288
+ }
40289
+ for (const t of axes.pcolorTraces ?? []) {
40290
+ includeAll(x, t.x);
40291
+ includeAll(y, t.y);
40292
+ }
40293
+ for (const t of axes.contourTraces ?? []) {
40294
+ includeAll(x, t.x);
40295
+ includeAll(y, t.y);
40296
+ }
40297
+ for (const bt of axes.barTraces ?? []) {
40298
+ const hw = bt.width / 2;
40299
+ for (let i = 0; i < bt.x.length; i++) {
40300
+ include(x, bt.x[i] - hw);
40301
+ include(x, bt.x[i] + hw);
40302
+ include(y, bt.y[i]);
40303
+ }
40304
+ include(y, 0);
40305
+ }
40306
+ for (const bt of axes.barhTraces ?? []) {
40307
+ const hh = bt.width / 2;
40308
+ for (let i = 0; i < bt.x.length; i++) {
40309
+ include(y, bt.x[i] - hh);
40310
+ include(y, bt.x[i] + hh);
40311
+ include(x, bt.y[i]);
40312
+ }
40313
+ include(x, 0);
40314
+ }
40315
+ for (const bt of [...axes.bar3Traces ?? [], ...axes.bar3hTraces ?? []]) {
40316
+ includeAll(x, bt.x);
40317
+ includeAll(y, bt.y);
40318
+ includeAll(z, bt.z);
40319
+ include(z, 0);
40320
+ }
40321
+ for (const et of axes.errorBarTraces ?? []) {
40322
+ for (let i = 0; i < et.x.length; i++) {
40323
+ include(x, et.xNeg ? et.x[i] - et.xNeg[i] : et.x[i]);
40324
+ include(x, et.xPos ? et.x[i] + et.xPos[i] : et.x[i]);
40325
+ include(y, et.y[i] - et.yNeg[i]);
40326
+ include(y, et.y[i] + et.yPos[i]);
40327
+ }
40328
+ }
40329
+ for (const bt of axes.boxTraces ?? []) {
40330
+ const hw = bt.width / 2;
40331
+ include(x, bt.x - hw);
40332
+ include(x, bt.x + hw);
40333
+ include(y, bt.whiskerLow);
40334
+ include(y, bt.whiskerHigh);
40335
+ for (const o of bt.outliers) include(y, o);
40336
+ }
40337
+ if (axes.areaTraces && axes.areaTraces.length > 0) {
40338
+ const base = axes.areaBaseValue ?? 0;
40339
+ include(y, base);
40340
+ const n = axes.areaTraces[0].x.length;
40341
+ for (let i = 0; i < n; i++) {
40342
+ let cum = base;
40343
+ for (const t of axes.areaTraces) {
40344
+ if (i < t.x.length) {
40345
+ include(x, t.x[i]);
40346
+ cum += t.y[i] - base;
40347
+ include(y, cum);
40348
+ }
40349
+ }
40350
+ }
40351
+ }
40352
+ for (const qt of axes.quiverTraces ?? []) {
40353
+ for (let i = 0; i < qt.x.length; i++) {
40354
+ include(x, qt.x[i]);
40355
+ include(x, qt.x[i] + qt.u[i]);
40356
+ include(y, qt.y[i]);
40357
+ include(y, qt.y[i] + qt.v[i]);
40358
+ }
40359
+ }
40360
+ for (const qt of axes.quiver3Traces ?? []) {
40361
+ for (let i = 0; i < qt.x.length; i++) {
40362
+ include(x, qt.x[i]);
40363
+ include(x, qt.x[i] + qt.u[i]);
40364
+ include(y, qt.y[i]);
40365
+ include(y, qt.y[i] + qt.v[i]);
40366
+ include(z, qt.z[i]);
40367
+ include(z, qt.z[i] + qt.w[i]);
40368
+ }
40369
+ }
40370
+ return { x, y, z };
40371
+ }
40372
+ function padExtent(e, tight) {
40373
+ if (!Number.isFinite(e.min) || !Number.isFinite(e.max)) return [0, 1];
40374
+ let { min: min2, max: max2 } = e;
40375
+ if (max2 === min2) {
40376
+ min2 -= 1;
40377
+ max2 += 1;
40378
+ return [min2, max2];
40379
+ }
40380
+ if (tight) return [min2, max2];
40381
+ const pad = (max2 - min2) * 0.05;
40382
+ return [min2 - pad, max2 + pad];
40383
+ }
40384
+ function applyExplicit(auto, explicit) {
40385
+ if (!explicit) return auto;
40386
+ return [explicit[0] ?? auto[0], explicit[1] ?? auto[1]];
40387
+ }
40388
+ function computeAxisLimits(axes) {
40389
+ const tight = axes.axisMode?.includes("tight") || axes.axisMode?.includes("image") ? true : false;
40390
+ const ext = dataExtents(axes);
40391
+ const xl = applyExplicit(padExtent(ext.x, tight), axes.xlim);
40392
+ const yl = applyExplicit(padExtent(ext.y, tight), axes.ylim);
40393
+ if (!axesIs3D(axes)) return [xl[0], xl[1], yl[0], yl[1]];
40394
+ const zl = applyExplicit(padExtent(ext.z, tight), axes.zlim);
40395
+ return [xl[0], xl[1], yl[0], yl[1], zl[0], zl[1]];
40396
+ }
40397
+
39447
40398
  // src/numbl-core/runtime/runtime.ts
39448
40399
  var Runtime = class _Runtime {
39449
40400
  constructor(options, initialVariableValues) {
@@ -39665,6 +40616,18 @@ var Runtime = class _Runtime {
39665
40616
  }
39666
40617
  return RTV.dummyHandle();
39667
40618
  };
40619
+ this.builtins["axis"] = (nargout, args) => {
40620
+ const margs = args.map((a) => ensureRuntimeValue(a));
40621
+ const applied = applyAxisCommand(
40622
+ margs,
40623
+ this.plotInstructions,
40624
+ () => this.currentAxisLimits()
40625
+ );
40626
+ if (!applied && nargout >= 1) {
40627
+ return RTV.row(this.currentAxisLimits());
40628
+ }
40629
+ return void 0;
40630
+ };
39668
40631
  this.builtins["fplot"] = (_nargout, args) => {
39669
40632
  fplotCall(this, this.plotInstructions, args.map(ensureRuntimeValue));
39670
40633
  };
@@ -40568,6 +41531,21 @@ var Runtime = class _Runtime {
40568
41531
  ishold() {
40569
41532
  return RTV.logical(this.holdState);
40570
41533
  }
41534
+ /** Current axis limits of the active axes, as MATLAB's `axis` query
41535
+ * returns them: `[xmin xmax ymin ymax]` (2-D) or with `zmin zmax`
41536
+ * appended (3-D). Reduces the accumulated plot instructions into a figure
41537
+ * state and reads the current axes' explicit/data-derived limits. */
41538
+ currentAxisLimits() {
41539
+ let state = initialFiguresState;
41540
+ for (const instr of this.plotInstructions) {
41541
+ state = figuresReducer(state, instr);
41542
+ }
41543
+ const fig = state.figs[state.currentHandle];
41544
+ if (!fig) return [0, 1, 0, 1];
41545
+ const axes = fig.axes[fig.currentAxesIndex];
41546
+ if (!axes) return [0, 1, 0, 1];
41547
+ return computeAxisLimits(axes);
41548
+ }
40571
41549
  // ── Drawnow / Pause ─────────────────────────────────────────────────
40572
41550
  drawnow() {
40573
41551
  return drawnow(this.plotInstructions, this.options);
@@ -41121,6 +42099,27 @@ function toNumArray(v, name) {
41121
42099
  throw new RuntimeError(`${name}: arguments must be numeric arrays`);
41122
42100
  }
41123
42101
 
42102
+ // src/numbl-core/helpers/effectively-real.ts
42103
+ function imagAllZero(imag2) {
42104
+ if (!imag2) return true;
42105
+ for (let i = 0; i < imag2.length; i++) {
42106
+ if (imag2[i] !== 0) return false;
42107
+ }
42108
+ return true;
42109
+ }
42110
+ function stripZeroImagTensor(t) {
42111
+ if (t.imag && imagAllZero(t.imag)) {
42112
+ const out = RTV.tensor(t.data, t.shape);
42113
+ if (t._isLogical) out._isLogical = true;
42114
+ return out;
42115
+ }
42116
+ return t;
42117
+ }
42118
+ function stripZeroImagValue(v) {
42119
+ if (isRuntimeTensor(v)) return stripZeroImagTensor(v);
42120
+ return v;
42121
+ }
42122
+
41124
42123
  // src/numbl-core/helpers/reduction/min-max.ts
41125
42124
  function minMaxScan(data, imag2, indices, initial, isBetter, complexIsBetter) {
41126
42125
  let mRe = initial, mIm = 0, mIdx = 0;
@@ -41305,6 +42304,7 @@ function minMaxImpl(name, args, nargout, initial, isBetter, twoArgFn) {
41305
42304
  return isBetter(Math.atan2(imA, reA), Math.atan2(imB, reB));
41306
42305
  };
41307
42306
  args = args.map((a) => isRuntimeSparseMatrix(a) ? sparseToDense(a) : a);
42307
+ args = args.map(stripZeroImagValue);
41308
42308
  if (args.length === 1) {
41309
42309
  const v = args[0];
41310
42310
  if (isRuntimeNumber(v) || isRuntimeLogical(v) || isRuntimeComplexNumber(v)) {
@@ -41792,8 +42792,8 @@ defineBuiltin({
41792
42792
  if (typeof v === "number") return true;
41793
42793
  if (typeof v === "boolean") return true;
41794
42794
  if (isRuntimeComplexNumber(v)) return v.im === 0;
41795
- if (isRuntimeTensor(v)) return !v.imag;
41796
- if (isRuntimeSparseMatrix(v)) return !v.pi;
42795
+ if (isRuntimeTensor(v)) return imagAllZero(v.imag);
42796
+ if (isRuntimeSparseMatrix(v)) return !v.pi || imagAllZero(v.pi);
41797
42797
  return true;
41798
42798
  }
41799
42799
  }
@@ -44920,7 +45920,7 @@ defineBuiltin({
44920
45920
  return v;
44921
45921
  }
44922
45922
  if (isRuntimeTensor(v)) {
44923
- return sortTensor(v, dim, descend, nargout);
45923
+ return sortTensor(stripZeroImagTensor(v), dim, descend, nargout);
44924
45924
  }
44925
45925
  if (isRuntimeCell(v)) {
44926
45926
  return sortCell(v, descend, nargout);
@@ -44959,10 +45959,10 @@ function sortTensor(v, dim, descend, nargout) {
44959
45959
  const dimSize = shape[dimIdx];
44960
45960
  let cmpFlatIdx;
44961
45961
  if (im && !im.every((x) => x === 0)) {
44962
- const mag = (i) => Math.sqrt(re[i] * re[i] + im[i] * im[i]);
45962
+ const mag2 = (i) => Math.sqrt(re[i] * re[i] + im[i] * im[i]);
44963
45963
  const phase = (i) => Math.atan2(im[i], re[i]);
44964
45964
  cmpFlatIdx = (a, b) => {
44965
- const diff2 = mag(a) - mag(b);
45965
+ const diff2 = mag2(a) - mag2(b);
44966
45966
  if (diff2 !== 0) return descend ? -diff2 : diff2;
44967
45967
  const pDiff = phase(a) - phase(b);
44968
45968
  return descend ? -pDiff : pDiff;
@@ -46390,9 +47390,9 @@ function invComplexJS(dataRe, dataIm, n) {
46390
47390
  let maxRow = col;
46391
47391
  let maxMag = augRe[col * 2 * n + col] ** 2 + augIm[col * 2 * n + col] ** 2;
46392
47392
  for (let row = col + 1; row < n; row++) {
46393
- const mag = augRe[row * 2 * n + col] ** 2 + augIm[row * 2 * n + col] ** 2;
46394
- if (mag > maxMag) {
46395
- maxMag = mag;
47393
+ const mag2 = augRe[row * 2 * n + col] ** 2 + augIm[row * 2 * n + col] ** 2;
47394
+ if (mag2 > maxMag) {
47395
+ maxMag = mag2;
46396
47396
  maxRow = row;
46397
47397
  }
46398
47398
  }
@@ -46437,6 +47437,88 @@ function invComplexJS(dataRe, dataIm, n) {
46437
47437
  }
46438
47438
  return { re: resultRe, im: resultIm };
46439
47439
  }
47440
+ defineBuiltin({
47441
+ name: "expm",
47442
+ cases: [
47443
+ {
47444
+ match: (argTypes, nargout) => {
47445
+ if (argTypes.length !== 1 || nargout > 1) return null;
47446
+ if (!isNumericJitType(argTypes[0])) return null;
47447
+ const a = argTypes[0];
47448
+ if (a.kind === "number" || a.kind === "boolean") return [NUM];
47449
+ if (a.kind === "complex_or_number") return [COMPLEX_OR_NUM];
47450
+ if (a.kind === "tensor")
47451
+ return [{ kind: "tensor", isComplex: a.isComplex }];
47452
+ return null;
47453
+ },
47454
+ apply: (args) => expmApply(args)
47455
+ }
47456
+ ]
47457
+ });
47458
+ function expmApply(args) {
47459
+ if (args.length !== 1) throw new RuntimeError("expm requires 1 argument");
47460
+ const A = args[0];
47461
+ if (isRuntimeNumber(A)) return RTV.num(Math.exp(A));
47462
+ if (isRuntimeComplexNumber(A)) {
47463
+ const ex = Math.exp(A.re);
47464
+ return RTV.complex(ex * Math.cos(A.im), ex * Math.sin(A.im));
47465
+ }
47466
+ if (!isRuntimeTensor(A))
47467
+ throw new RuntimeError("expm: argument must be a numeric matrix");
47468
+ const [m, n] = tensorSize2D(A);
47469
+ if (m !== n) throw new RuntimeError("expm: input must be a square matrix");
47470
+ if (n === 0) return A;
47471
+ if (n === 1) {
47472
+ const ex = Math.exp(A.data[0]);
47473
+ if (A.imag)
47474
+ return RTV.tensor(
47475
+ Float64Array.of(ex * Math.cos(A.imag[0])),
47476
+ [1, 1],
47477
+ Float64Array.of(ex * Math.sin(A.imag[0]))
47478
+ );
47479
+ return RTV.tensor(Float64Array.of(ex), [1, 1]);
47480
+ }
47481
+ let s = 0;
47482
+ for (let scaled = matrixOneNorm(A, n); scaled > 0.5; scaled /= 2) s++;
47483
+ const As = mMul(A, RTV.num(Math.pow(2, -s)));
47484
+ const q = 6;
47485
+ const I = identityTensor(n);
47486
+ let c = 0.5;
47487
+ let E = mAdd(I, mMul(As, RTV.num(c)));
47488
+ let D = mSub(I, mMul(As, RTV.num(c)));
47489
+ let X = As;
47490
+ let plus2 = true;
47491
+ for (let k = 2; k <= q; k++) {
47492
+ c = c * (q - k + 1) / (k * (2 * q - k + 1));
47493
+ X = mMul(As, X);
47494
+ const cX = mMul(X, RTV.num(c));
47495
+ E = mAdd(E, cX);
47496
+ D = plus2 ? mAdd(D, cX) : mSub(D, cX);
47497
+ plus2 = !plus2;
47498
+ }
47499
+ let R = mLeftDiv(D, E);
47500
+ for (let i = 0; i < s; i++) R = mMul(R, R);
47501
+ return R;
47502
+ }
47503
+ function matrixOneNorm(A, n) {
47504
+ const re = A.data;
47505
+ const im = A.imag;
47506
+ let maxSum = 0;
47507
+ for (let j = 0; j < n; j++) {
47508
+ let colSum = 0;
47509
+ for (let i = 0; i < n; i++) {
47510
+ const idx = i + j * n;
47511
+ colSum += im ? Math.hypot(re[idx], im[idx]) : Math.abs(re[idx]);
47512
+ }
47513
+ if (colSum > maxSum) maxSum = colSum;
47514
+ }
47515
+ return maxSum;
47516
+ }
47517
+ function identityTensor(n) {
47518
+ const data = allocFloat64Array(n * n);
47519
+ for (let i = 0; i < n; i++) data[i + i * n] = 1;
47520
+ return RTV.tensor(data, [n, n]);
47521
+ }
46440
47522
  registerIBuiltin({
46441
47523
  name: "svd",
46442
47524
  resolve: (argTypes, nargout) => {
@@ -47963,8 +49045,8 @@ function isFiberConjugateSymmetric(re, im) {
47963
49045
  if (N <= 1) return Math.abs(im[0]) < 1e-14;
47964
49046
  let maxMag = 0;
47965
49047
  for (let i = 0; i < N; i++) {
47966
- const mag = Math.abs(re[i]) + Math.abs(im[i]);
47967
- if (mag > maxMag) maxMag = mag;
49048
+ const mag2 = Math.abs(re[i]) + Math.abs(im[i]);
49049
+ if (mag2 > maxMag) maxMag = mag2;
47968
49050
  }
47969
49051
  const tol = Math.max(1e-14, maxMag * 1e-12);
47970
49052
  if (Math.abs(im[0]) > tol) return false;
@@ -54964,6 +56046,10 @@ var H = {
54964
56046
  signatures: ["B = inv(A)"],
54965
56047
  description: "Matrix inverse."
54966
56048
  },
56049
+ expm: {
56050
+ signatures: ["Y = expm(X)"],
56051
+ description: "Matrix exponential of square matrix X (scaling-and-squaring Pad\xE9). Use exp for the element-wise exponential."
56052
+ },
54967
56053
  trace: {
54968
56054
  signatures: ["T = trace(A)"],
54969
56055
  description: "Sum of diagonal elements."
@@ -54972,6 +56058,17 @@ var H = {
54972
56058
  signatures: ["E = eig(A)", "[V, D] = eig(A)", "[V, D, W] = eig(A)"],
54973
56059
  description: "Eigenvalues and eigenvectors of square matrix. With three outputs, W contains left eigenvectors."
54974
56060
  },
56061
+ eigs: {
56062
+ signatures: [
56063
+ "d = eigs(A)",
56064
+ "d = eigs(A, k)",
56065
+ "d = eigs(A, k, sigma)",
56066
+ "d = eigs(A, B, ...)",
56067
+ "d = eigs(Afun, n, ...)",
56068
+ "[V, D, flag] = eigs(...)"
56069
+ ],
56070
+ description: "Subset of k eigenvalues/eigenvectors (default 6). sigma selects which: 'largestabs' (default), 'smallestabs', 'largestreal', 'smallestreal', 'bothendsreal', 'largestimag', 'smallestimag', 'bothendsimag', or a scalar (eigenvalues closest to it). Supports the generalized problem A*V = B*V*D and a function handle Afun. Computed densely via eig, so convergence options are accepted but ignored and flag is always 0."
56071
+ },
54975
56072
  svd: {
54976
56073
  signatures: [
54977
56074
  "S = svd(A)",
@@ -55773,6 +56870,17 @@ var H = {
55773
56870
  signatures: ["colorbar", "colorbar(MODE)"],
55774
56871
  description: "Display colorbar on current axes."
55775
56872
  },
56873
+ axis: {
56874
+ signatures: [
56875
+ "axis([xmin xmax ymin ymax])",
56876
+ "axis STYLE",
56877
+ "axis MODE",
56878
+ "axis ij | xy",
56879
+ "axis on | off",
56880
+ "LIM = axis"
56881
+ ],
56882
+ description: "Set or query axis limits, scaling, direction, and visibility. LIMITS is a 4-, 6-, or 8-element vector (inf bounds stay automatic). STYLE is tight | padded | fill | equal | image | square | vis3d | normal | tickaligned. MODE is manual | auto | 'auto x' | 'auto y' | 'auto z' (and xy combinations). 'ij' reverses the y-axis; 'off' hides the axes lines and background. LIM = axis returns the current limits."
56883
+ },
55776
56884
  xlim: {
55777
56885
  signatures: ["xlim(LIMITS)"],
55778
56886
  description: "Set x-axis limits."
@@ -56342,7 +57450,7 @@ function getSourceLine(getSource, file, line) {
56342
57450
  }
56343
57451
 
56344
57452
  // src/numbl-core/version.ts
56345
- var NUMBL_VERSION = "0.4.1";
57453
+ var NUMBL_VERSION = "0.4.3";
56346
57454
 
56347
57455
  // src/cli-repl.ts
56348
57456
  import { createInterface } from "readline";
@@ -59980,11 +61088,11 @@ function instantiateClass(className, args, nargout) {
59980
61088
  } catch {
59981
61089
  }
59982
61090
  }
59983
- const isHandle2 = this.isHandleClass(classInfo);
61091
+ const isHandle3 = this.isHandleClass(classInfo);
59984
61092
  const instance = RTV.classInstance(
59985
61093
  className,
59986
61094
  propertyNames,
59987
- isHandle2,
61095
+ isHandle3,
59988
61096
  defaults
59989
61097
  );
59990
61098
  if (classInfo.constructorName) {
@@ -62036,7 +63144,7 @@ function isNumeric2(t) {
62036
63144
  function isVoid(t) {
62037
63145
  return t.kind === "Void";
62038
63146
  }
62039
- function isHandle(t) {
63147
+ function isHandle2(t) {
62040
63148
  return t.kind === "Handle";
62041
63149
  }
62042
63150
  function isStruct(t) {
@@ -64566,6 +65674,15 @@ function mtoc2_tensor_flip_complex(a, dimIdx) {
64566
65674
  return r;
64567
65675
  }
64568
65676
 
65677
+ // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_imag_all_zero.js
65678
+ function mtoc2_tensor_imag_all_zero(a) {
65679
+ if (a.imag === void 0) return true;
65680
+ for (let i = 0; i < a.imag.length; i++) {
65681
+ if (a.imag[i] !== 0) return false;
65682
+ }
65683
+ return true;
65684
+ }
65685
+
64569
65686
  // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_linspace.js
64570
65687
  function mtoc2_tensor_linspace(a, b, n) {
64571
65688
  if (n < 0) n = 0;
@@ -64867,6 +65984,13 @@ function cSqueezeTrailing(dims) {
64867
65984
  function cReduceLaneIm(t, i) {
64868
65985
  return t.imag !== void 0 ? t.imag[i] : 0;
64869
65986
  }
65987
+ function cReduceAllImagZero(t) {
65988
+ if (t.imag === void 0) return true;
65989
+ for (let i = 0; i < t.imag.length; i++) {
65990
+ if (t.imag[i] !== 0) return false;
65991
+ }
65992
+ return true;
65993
+ }
64870
65994
  function complexAccumAll(t, init, accum, finalize) {
64871
65995
  let acc = { ...init };
64872
65996
  for (let i = 0; i < t.data.length; i++) {
@@ -64924,6 +66048,7 @@ var mtoc2_prod_complex_dim = (t, d) => complexAccumDim(t, d, cProdInit, cProdAcc
64924
66048
  var mtoc2_mean_complex_all = (t) => complexAccumAll(t, cSumInit, cSumAccum, cMeanFinalize);
64925
66049
  var mtoc2_mean_complex_dim = (t, d) => complexAccumDim(t, d, cSumInit, cSumAccum, cMeanFinalize);
64926
66050
  function complexMinmaxAll(t, cmp) {
66051
+ const realMode = cReduceAllImagZero(t);
64927
66052
  let found = false;
64928
66053
  let mRe = NaN;
64929
66054
  let mIm = 0;
@@ -64931,7 +66056,8 @@ function complexMinmaxAll(t, cmp) {
64931
66056
  const xr = t.data[i];
64932
66057
  const xi = cReduceLaneIm(t, i);
64933
66058
  if (xr !== xr || xi !== xi) continue;
64934
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
66059
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
66060
+ if (!found || better) {
64935
66061
  mRe = xr;
64936
66062
  mIm = xi;
64937
66063
  found = true;
@@ -64954,6 +66080,7 @@ function complexMinmaxDim(t, dim, cmp) {
64954
66080
  return out2;
64955
66081
  }
64956
66082
  const dimIdx = dim - 1;
66083
+ const realMode = cReduceAllImagZero(t);
64957
66084
  const axis = t.shape[dimIdx];
64958
66085
  let before = 1;
64959
66086
  for (let i = 0; i < dimIdx; i++) before *= t.shape[i];
@@ -64974,7 +66101,8 @@ function complexMinmaxDim(t, dim, cmp) {
64974
66101
  const xr = t.data[off];
64975
66102
  const xi = cReduceLaneIm(t, off);
64976
66103
  if (xr !== xr || xi !== xi) continue;
64977
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
66104
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
66105
+ if (!found || better) {
64978
66106
  mRe = xr;
64979
66107
  mIm = xi;
64980
66108
  found = true;
@@ -65454,18 +66582,46 @@ function complex_sort_indices(a, descending) {
65454
66582
  const n = a.data.length;
65455
66583
  const im = a.imag;
65456
66584
  const idx = new Array(n);
65457
- const mag = new Float64Array(n);
66585
+ for (let i = 0; i < n; i++) idx[i] = i;
66586
+ let realMode = true;
66587
+ if (im !== void 0) {
66588
+ for (let i = 0; i < n; i++) {
66589
+ if (im[i] !== 0) {
66590
+ realMode = false;
66591
+ break;
66592
+ }
66593
+ }
66594
+ }
66595
+ if (realMode) {
66596
+ const re = a.data;
66597
+ idx.sort((p2, q) => {
66598
+ const rp = re[p2];
66599
+ const rq = re[q];
66600
+ const pNaN = rp !== rp;
66601
+ const qNaN = rq !== rq;
66602
+ if (pNaN && qNaN) return 0;
66603
+ if (descending) {
66604
+ if (pNaN) return -1;
66605
+ if (qNaN) return 1;
66606
+ return rp < rq ? 1 : rp > rq ? -1 : 0;
66607
+ }
66608
+ if (pNaN) return 1;
66609
+ if (qNaN) return -1;
66610
+ return rp < rq ? -1 : rp > rq ? 1 : 0;
66611
+ });
66612
+ return idx;
66613
+ }
66614
+ const mag2 = new Float64Array(n);
65458
66615
  const ph = new Float64Array(n);
65459
66616
  for (let i = 0; i < n; i++) {
65460
66617
  const re = a.data[i];
65461
66618
  const xi = im !== void 0 ? im[i] : 0;
65462
- mag[i] = Math.hypot(re, xi);
66619
+ mag2[i] = Math.hypot(re, xi);
65463
66620
  ph[i] = Math.atan2(xi, re);
65464
- idx[i] = i;
65465
66621
  }
65466
66622
  idx.sort((p2, q) => {
65467
- if (mag[p2] < mag[q]) return descending ? 1 : -1;
65468
- if (mag[p2] > mag[q]) return descending ? -1 : 1;
66623
+ if (mag2[p2] < mag2[q]) return descending ? 1 : -1;
66624
+ if (mag2[p2] > mag2[q]) return descending ? -1 : 1;
65469
66625
  if (ph[p2] < ph[q]) return descending ? 1 : -1;
65470
66626
  if (ph[p2] > ph[q]) return descending ? -1 : 1;
65471
66627
  return p2 - q;
@@ -66731,6 +67887,7 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
66731
67887
  "tensor_fill_nd.h": "/* mtoc2 runtime helper: build an N-D tensor filled with `v`. Real\n * variant allocates a real tensor; complex variant takes `(re, im)`\n * and fills both lanes.\n *\n * Parameterized companion to `mtoc2_tensor_zeros_nd` / `_ones_nd`;\n * activated by the `nan` / `NaN` / `Inf` / `inf` shape-constructor\n * builtins (which would otherwise need their own per-constant fill\n * helpers) and by `repmat(scalar, ...)`. The returned tensor is\n * freshly owned.\n */\n\nstatic mtoc2_tensor_t mtoc2_tensor_fill_nd(double v, int ndim,\n const long *dims) {\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(ndim, dims);\n size_t n = 1;\n for (int i = 0; i < ndim; i++) n *= (size_t)out.dims[i];\n for (size_t i = 0; i < n; i++) out.real[i] = v;\n return out;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_fill_nd_complex(double re, double im,\n int ndim,\n const long *dims) {\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(ndim, dims);\n size_t n = 1;\n for (int i = 0; i < ndim; i++) n *= (size_t)out.dims[i];\n for (size_t i = 0; i < n; i++) {\n out.real[i] = re;\n out.imag[i] = im;\n }\n return out;\n}\n",
66732
67888
  "tensor_fill_square.h": "/* mtoc2 runtime helper: build an n\xD7n real tensor filled with `v`.\n *\n * Single-eval companion to `mtoc2_tensor_fill_nd` for the MATLAB\n * `nan(n)` / `Inf(n)` shorthand when `n` is a runtime expression.\n * See `mtoc2_tensor_zeros_square` for the rationale.\n */\n\nstatic mtoc2_tensor_t mtoc2_tensor_fill_square(double v, long n) {\n long dims[2] = {n, n};\n return mtoc2_tensor_fill_nd(v, 2, dims);\n}\n",
66733
67889
  "tensor_flip.h": "/* mtoc2 runtime helper: `flip(t, dimIdx)` \u2014 return a freshly-owned\n * tensor with `t.real` mirrored along axis `dimIdx` (0-based).\n *\n * Numbl's reference is `flipAlongDim` in\n * `interpreter/builtins/array-manipulation.ts` line ~41. Same\n * column-major slab math: stride = product of dims below the axis,\n * outer = product of dims above. For each outer slab, we walk the\n * axis backwards on the source and forward on the destination,\n * copying `strideDim`-element contiguous blocks.\n *\n * `flipud(t)` lowers to `mtoc2_tensor_flip(t, 0)`; `fliplr(t)` to\n * `mtoc2_tensor_flip(t, 1)`; `flip(t, k)` to\n * `mtoc2_tensor_flip(t, k - 1)`. The `dimIdx` is 0-based at the C\n * boundary so the runtime stays uniform across the three source-\n * level builtins. mtoc2 codegen converts MATLAB's 1-based `k` to\n * 0-based at the call site.\n *\n * Out-of-range `dimIdx` (\u2265 ndim) is a no-op flip \u2014 numbl returns\n * the input unchanged in that case (the \"axis is size 1\" rule). We\n * still allocate a fresh copy so the owned-value invariant holds.\n *\n * `mtoc2_tensor_flip_complex` is the sibling that walks both lanes;\n * it tolerates `a.imag == NULL` (a real tensor that flowed in via a\n * complex-typed route) by zero-filling the output imag lane.\n */\n\n#include <string.h>\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_flip(mtoc2_tensor_t a, long dimIdx) {\n long total = 1;\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)total * sizeof(double));\n r.imag = NULL;\n r.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i];\n\n long axisSize = (dimIdx >= 0 && dimIdx < (long)a.ndim) ? a.dims[dimIdx] : 1;\n // total==0 (empty along some axis) must short-circuit BEFORE the slab math:\n // if a dim below the axis is 0, slabSize becomes 0 and total/slabSize is a\n // 0/0 integer division (SIGILL). An empty result needs no copy anyway.\n if (axisSize <= 1 || total == 0) {\n // Nothing to flip \u2014 just deep-copy the buffer.\n if (total > 0) memcpy(r.real, a.real, (size_t)total * sizeof(double));\n return r;\n }\n\n long strideDim = 1;\n for (long d = 0; d < dimIdx; d++) strideDim *= a.dims[d];\n long slabSize = strideDim * axisSize;\n long numOuter = total / slabSize;\n\n for (long outer = 0; outer < numOuter; outer++) {\n long base = outer * slabSize;\n for (long k = 0; k < axisSize; k++) {\n long srcOff = base + k * strideDim;\n long dstOff = base + (axisSize - 1 - k) * strideDim;\n memcpy(\n r.real + dstOff,\n a.real + srcOff,\n (size_t)strideDim * sizeof(double)\n );\n }\n }\n return r;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_flip_complex(mtoc2_tensor_t a, long dimIdx) {\n long total = 1;\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i];\n mtoc2_tensor_t r = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims);\n int srcHasImag = (a.imag != NULL);\n\n long axisSize = (dimIdx >= 0 && dimIdx < (long)a.ndim) ? a.dims[dimIdx] : 1;\n // See mtoc2_tensor_flip: total==0 must short-circuit before slab math to\n // avoid a 0/0 division when a dim below the axis is 0.\n if (axisSize <= 1 || total == 0) {\n if (total > 0) {\n memcpy(r.real, a.real, (size_t)total * sizeof(double));\n if (srcHasImag) {\n memcpy(r.imag, a.imag, (size_t)total * sizeof(double));\n } else {\n memset(r.imag, 0, (size_t)total * sizeof(double));\n }\n }\n return r;\n }\n\n if (!srcHasImag && total > 0) {\n memset(r.imag, 0, (size_t)total * sizeof(double));\n }\n\n long strideDim = 1;\n for (long d = 0; d < dimIdx; d++) strideDim *= a.dims[d];\n long slabSize = strideDim * axisSize;\n long numOuter = total / slabSize;\n\n for (long outer = 0; outer < numOuter; outer++) {\n long base = outer * slabSize;\n for (long k = 0; k < axisSize; k++) {\n long srcOff = base + k * strideDim;\n long dstOff = base + (axisSize - 1 - k) * strideDim;\n memcpy(r.real + dstOff, a.real + srcOff,\n (size_t)strideDim * sizeof(double));\n if (srcHasImag) {\n memcpy(r.imag + dstOff, a.imag + srcOff,\n (size_t)strideDim * sizeof(double));\n }\n }\n }\n return r;\n}\n",
67890
+ "tensor_imag_all_zero.h": "/* mtoc2 runtime helper: true (1.0) when a tensor carries no imaginary\n * content \u2014 NULL imag lane, or every imag element exactly zero. `isreal`\n * uses this for complex-typed tensors the JIT could not prove real at\n * compile time, reporting realness by value (matching the interpreter\n * and the complex-scalar `cimag(z) == 0` rule). Returns a logical double. */\n\nstatic double mtoc2_tensor_imag_all_zero(mtoc2_tensor_t a) {\n if (a.imag == NULL) return 1.0;\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n for (long i = 0; i < n; i++) {\n if (a.imag[i] != 0.0) return 0.0;\n }\n return 1.0;\n}\n",
66734
67891
  "tensor_linspace.h": "/* mtoc2 runtime helper: build a 1\xD7n row tensor of n linearly-spaced\n * values from `a` to `b` inclusive. Matches numbl's `linspace` byte-\n * for-byte:\n *\n * - n <= 0 \u2192 1\xD70 empty tensor.\n * - n == 1 \u2192 just `[b]` (matches MATLAB; not the midpoint).\n * - n > 1 \u2192 first/last slots pinned at `a`/`b` exactly so a NaN\n * or Inf endpoint doesn't contaminate the other end;\n * inner values are `a + (b - a) * i / (n - 1)`.\n *\n * Opposite-sign infinite endpoints place 0 at the exact center for\n * odd n (e.g. `linspace(-Inf, Inf, 5)` \u2192 `[-Inf, ?, 0, ?, Inf]`).\n */\n\n#include <math.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_linspace(double a, double b, long n) {\n if (n < 0) n = 0;\n mtoc2_tensor_t out = mtoc2_tensor_alloc(1, n);\n if (n == 0) return out;\n if (n == 1) {\n out.real[0] = b;\n return out;\n }\n out.real[0] = a;\n out.real[n - 1] = b;\n for (long i = 1; i < n - 1; i++) {\n out.real[i] = a + (b - a) * (double)i / (double)(n - 1);\n }\n if ((n & 1) == 1 && !isfinite(a) && !isfinite(b)) {\n double sa = (a > 0) - (a < 0);\n double sb = (b > 0) - (b < 0);\n if (sa != sb) {\n out.real[(n - 1) / 2] = 0.0;\n }\n }\n return out;\n}\n",
66735
67892
  "tensor_logical_mask.h": '/* mtoc2 runtime helper: logical-mask indexing support.\n *\n * `mtoc2_logical_mask_indices` scans `mask` column-major and fills\n * `out_indices` with the 0-based positions where the mask is truthy.\n * Each truthy position must be less than `axis_len`; otherwise this\n * aborts with a numbl-style "Index exceeds array bounds" message via\n * `mtoc2_oob_abort`. Returns the truthy count, which is also the\n * number of entries written into `out_indices`. `out_indices` must\n * have room for at least `mask.numel()` longs.\n *\n * `axis` is the axis number for the diagnostic (0-based) when this\n * helper is called for a per-axis slot (`M(:, mask)` \u2192 axis = 1), or\n * `-1` for the linear single-slot form (`a(mask)`).\n *\n * Used by both reads (`IndexSlice` with a `LogicalMask` slot) and\n * linear-form writes (`IndexSliceStore` with a single `LogicalMask`\n * slot). The caller allocates the index buffer with `mtoc2_alloc`,\n * passes it in, and frees it after the iteration that consumes it.\n */\n\nstatic long mtoc2_logical_mask_indices(\n mtoc2_tensor_t mask, long axis_len, int axis, const char *loc,\n long *out_indices\n) {\n long mask_n = 1;\n for (int d = 0; d < mask.ndim; d++) mask_n *= mask.dims[d];\n long count = 0;\n for (long i = 0; i < mask_n; i++) {\n if (mask.real[i] != 0.0) {\n if (i >= axis_len) {\n mtoc2_oob_abort(loc, axis, i + 1, 1, axis_len);\n }\n out_indices[count++] = i;\n }\n }\n return count;\n}\n',
66736
67893
  "tensor_logical_real.h": '/* mtoc2 runtime helpers: elementwise logical ops on tensors.\n *\n * Same allocate-and-fill pattern as tensor_unary_real_math.h, but the\n * per-element kernel is a logical predicate that returns 0.0 or 1.0.\n *\n * `mtoc2_tensor_not` mirrors numbl\'s `not(v)` (runtimeOperators.ts):\n * a real-tensor input produces a freshly-owned logical-typed tensor\n * of the same shape with `out[i] = (in[i] == 0.0) ? 1.0 : 0.0`. The\n * complex sibling fires "true" iff both lanes are exactly zero. The\n * result is a real (logical-typed) tensor in both cases \u2014 the type\n * system records `elem: "logical"` so disp / downstream consumers\n * know how to interpret the doubles.\n */\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_not(mtoc2_tensor_t a) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i];\n for (long i = 0; i < n; i++) r.real[i] = (a.real[i] == 0.0) ? 1.0 : 0.0;\n return r;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_not_complex(mtoc2_tensor_t a) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i];\n int srcHasImag = (a.imag != NULL);\n for (long i = 0; i < n; i++) {\n double re = a.real[i];\n double im = srcHasImag ? a.imag[i] : 0.0;\n r.real[i] = (re == 0.0 && im == 0.0) ? 1.0 : 0.0;\n }\n return r;\n}\n',
@@ -66742,13 +67899,13 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
66742
67899
  "tensor_ones_nd.h": "/* mtoc2 runtime helper: build a real N-D tensor filled with ones.\n *\n * Allocates via `mtoc2_tensor_alloc_nd`, then fills the `real` buffer\n * with `1.0` via a plain element loop (`memset` only works for byte\n * patterns; `1.0` is not such a pattern). The returned tensor is\n * freshly owned; `imag` is NULL.\n */\n\nstatic mtoc2_tensor_t mtoc2_tensor_ones_nd(int ndim, const long *dims) {\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(ndim, dims);\n size_t n = 1;\n for (int i = 0; i < ndim; i++) n *= (size_t)out.dims[i];\n for (size_t i = 0; i < n; i++) out.real[i] = 1.0;\n return out;\n}\n",
66743
67900
  "tensor_ones_square.h": "/* mtoc2 runtime helper: build an n\xD7n real tensor filled with ones.\n *\n * Single-eval companion to `mtoc2_tensor_ones_nd` for the MATLAB\n * `ones(n)` shorthand when `n` is a runtime expression. See the\n * `mtoc2_tensor_zeros_square` header for the rationale.\n */\n\nstatic mtoc2_tensor_t mtoc2_tensor_ones_square(long n) {\n long dims[2] = {n, n};\n return mtoc2_tensor_ones_nd(2, dims);\n}\n",
66744
67901
  "tensor_predicate.h": "/* mtoc2 runtime helpers: elementwise tensor \u2192 logical-tensor\n * predicates (`isnan`, `isinf`, `isfinite`, `logical`). Same\n * allocate-and-fill shape as `tensor_unary_real_math.h`; each element\n * maps to 1.0 / 0.0. The result is logical at the source level (the\n * buffer is the usual Float64 lane, `imag == NULL`).\n *\n * The `_complex` siblings operate per-element on `(re, im)`; for\n * `isnan` / `isinf` the predicate fires if either component\n * triggers, for `isfinite` both must be finite. They tolerate\n * `a.imag == NULL` (a real tensor that flowed through a\n * complex-typed route) by treating the imag input as zero.\n */\n#include <math.h>\n#include <stdlib.h>\n\n#define MTOC2_DEFINE_UNARY_PRED(name, EXPR) \\\n static mtoc2_tensor_t name(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n mtoc2_tensor_t r; \\\n r.real = mtoc2_alloc((size_t)n * sizeof(double)); \\\n r.imag = NULL; \\\n r.ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i]; \\\n MTOC2_OMP_PARFOR_N \\\n for (long i = 0; i < n; i++) { \\\n double x = a.real[i]; \\\n r.real[i] = (EXPR) ? 1.0 : 0.0; \\\n } \\\n return r; \\\n }\n\nMTOC2_DEFINE_UNARY_PRED(mtoc2_tensor_isnan, isnan(x))\nMTOC2_DEFINE_UNARY_PRED(mtoc2_tensor_logical, x != 0.0)\nMTOC2_DEFINE_UNARY_PRED(mtoc2_tensor_isinf, isinf(x))\nMTOC2_DEFINE_UNARY_PRED(mtoc2_tensor_isfinite, isfinite(x))\n\n#undef MTOC2_DEFINE_UNARY_PRED\n\n#define MTOC2_DEFINE_UNARY_PRED_COMPLEX(name, EXPR) \\\n static mtoc2_tensor_t name(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n mtoc2_tensor_t r; \\\n r.real = mtoc2_alloc((size_t)n * sizeof(double)); \\\n r.imag = NULL; \\\n r.ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i]; \\\n MTOC2_OMP_PARFOR_N \\\n for (long i = 0; i < n; i++) { \\\n double re = a.real[i]; \\\n double im = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n r.real[i] = (EXPR) ? 1.0 : 0.0; \\\n } \\\n return r; \\\n }\n\nMTOC2_DEFINE_UNARY_PRED_COMPLEX(mtoc2_tensor_isnan_complex,\n isnan(re) || isnan(im))\nMTOC2_DEFINE_UNARY_PRED_COMPLEX(mtoc2_tensor_isinf_complex,\n isinf(re) || isinf(im))\nMTOC2_DEFINE_UNARY_PRED_COMPLEX(mtoc2_tensor_isfinite_complex,\n isfinite(re) && isfinite(im))\n\n#undef MTOC2_DEFINE_UNARY_PRED_COMPLEX\n",
66745
- "tensor_reduce_complex.h": '/* mtoc2 runtime helpers: complex-tensor reductions.\n *\n * Sibling of `tensor_reduce_real.h`. Same `_all` / `_dim` shape per\n * op; each kernel walks both lanes and builds intermediate\n * `double _Complex` values through `mtoc2_cmake` / `mtoc2_c*` so the\n * c2js backend can translate the bodies straight (no bare\n * `<complex.h>` operators).\n *\n * Result types per op:\n * sum / prod / mean \u2192 complex (lane-pair accumulator)\n * min / max \u2192 complex (magnitude compare, atan2\n * tiebreak \u2014 matches numbl\'s\n * `complexIsBetter`)\n * any / all \u2192 real (toBool: `re != 0 || im != 0`,\n * then aggregate via OR/AND)\n *\n * Input tolerance: `imag == NULL` (a real tensor flowing in through\n * a complex-typed route) is treated as zero on every cell.\n *\n * `_all` returns a `double _Complex` for the numeric reducers and a\n * `double` for the logical reducers. `_dim` returns a freshly-owned\n * complex tensor (numeric reducers) or a real tensor (logical\n * reducers) of the reduced shape \u2014 same trailing-singleton squeeze\n * rule as the real helper.\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Shared with the real helper; defined inline here so this file is\n * standalone-includable (the runtime activator may pull the complex\n * snippet in independently of the real one). */\nstatic void mtoc2__squeeze_trailing_c(int *ndim, long *dims) {\n while (*ndim > 2 && dims[*ndim - 1] == 1) {\n (*ndim)--;\n }\n}\n\n/* Numeric (sum/prod/mean) reduction template \u2014 complex accumulator. */\n#define MTOC2_DEFINE_CACCUM_REDUCTION(name, INIT, ACCUM, FINALIZE) \\\n static double _Complex mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n double _Complex acc = (INIT); \\\n for (long i = 0; i < n; i++) { \\\n double aim = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n double _Complex x = mtoc2_cmake(a.real[i], aim); \\\n acc = ACCUM(acc, x); \\\n } \\\n return FINALIZE(acc, n); \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims); \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n if (a.imag != NULL) { \\\n memcpy(out.imag, a.imag, (size_t)total * sizeof(double)); \\\n } else { \\\n memset(out.imag, 0, (size_t)total * sizeof(double)); \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double _Complex acc = (INIT); \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double aim = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n double _Complex x = mtoc2_cmake(a.real[off], aim); \\\n acc = ACCUM(acc, x); \\\n } \\\n double _Complex fin = FINALIZE(acc, axis); \\\n long dst = outer * before + inner; \\\n out.real[dst] = mtoc2_creal(fin); \\\n out.imag[dst] = mtoc2_cimag(fin); \\\n } \\\n } \\\n return out; \\\n }\n\n/* min/max template \u2014 complex compare via magnitude + atan2 tiebreak.\n * NaN-skip on either lane. Accumulator seed is NaN+NaN; first non-\n * NaN element captures, later non-NaN elements compare. */\n#define MTOC2_DEFINE_CMINMAX_REDUCTION(name, CMP) \\\n static int mtoc2__##name##_complex_better( \\\n double aRe, double aIm, double bRe, double bIm) { \\\n double absA = hypot(aRe, aIm); \\\n double absB = hypot(bRe, bIm); \\\n if (absA != absB) return absA CMP absB; \\\n return atan2(aIm, aRe) CMP atan2(bIm, bRe); \\\n } \\\n static double _Complex mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n int found = 0; \\\n double mRe = NAN, mIm = 0.0; \\\n for (long i = 0; i < n; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n if (xr != xr || xi != xi) continue; \\\n if (!found || mtoc2__##name##_complex_better(xr, xi, mRe, mIm)) { \\\n mRe = xr; \\\n mIm = xi; \\\n found = 1; \\\n } \\\n } \\\n return mtoc2_cmake(mRe, mIm); \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims); \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n if (a.imag != NULL) { \\\n memcpy(out.imag, a.imag, (size_t)total * sizeof(double)); \\\n } else { \\\n memset(out.imag, 0, (size_t)total * sizeof(double)); \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n int found = 0; \\\n double mRe = NAN, mIm = 0.0; \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double xr = a.real[off]; \\\n double xi = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n if (xr != xr || xi != xi) continue; \\\n if (!found || mtoc2__##name##_complex_better(xr, xi, mRe, mIm)) { \\\n mRe = xr; \\\n mIm = xi; \\\n found = 1; \\\n } \\\n } \\\n long dst = outer * before + inner; \\\n out.real[dst] = mRe; \\\n out.imag[dst] = mIm; \\\n } \\\n } \\\n return out; \\\n }\n\n/* any/all template \u2014 real result; toBool per element (either lane\n * nonzero). Mirrors `MTOC2_DEFINE_LOGICAL_REDUCTION` shape. */\n#define MTOC2_DEFINE_CLOGICAL_REDUCTION(name, EMPTY_RESULT, SHORT_BODY) \\\n static double mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n if (n == 0) return (double)(EMPTY_RESULT); \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long i = 0; i < n; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n int x = (xr != 0.0 || xi != 0.0); \\\n SHORT_BODY; \\\n } \\\n return acc; \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n for (long i = 0; i < total; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n out.real[i] = (xr != 0.0 || xi != 0.0) ? 1.0 : 0.0; \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double xr = a.real[off]; \\\n double xi = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n int x = (xr != 0.0 || xi != 0.0); \\\n SHORT_BODY; \\\n } \\\n out.real[outer * before + inner] = acc; \\\n } \\\n } \\\n return out; \\\n }\n\n/* Accumulator-statement macros. */\n#define MTOC2_CACC_SUM(acc, x) mtoc2_cadd((acc), (x))\n#define MTOC2_CACC_PROD(acc, x) mtoc2_cmul((acc), (x))\n#define MTOC2_CFIN_ID(acc, n) (acc)\n#define MTOC2_CFIN_MEAN(acc, n) mtoc2_cdiv((acc), mtoc2_cmake((double)(n), 0.0))\n\nMTOC2_DEFINE_CACCUM_REDUCTION(sum, mtoc2_cmake(0.0, 0.0), MTOC2_CACC_SUM, MTOC2_CFIN_ID)\nMTOC2_DEFINE_CACCUM_REDUCTION(prod, mtoc2_cmake(1.0, 0.0), MTOC2_CACC_PROD, MTOC2_CFIN_ID)\nMTOC2_DEFINE_CACCUM_REDUCTION(mean, mtoc2_cmake(0.0, 0.0), MTOC2_CACC_SUM, MTOC2_CFIN_MEAN)\n\nMTOC2_DEFINE_CMINMAX_REDUCTION(min, <)\nMTOC2_DEFINE_CMINMAX_REDUCTION(max, >)\n\nMTOC2_DEFINE_CLOGICAL_REDUCTION(any, 0,\n if (x) { acc = 1.0; break; })\nMTOC2_DEFINE_CLOGICAL_REDUCTION(all, 1,\n if (!x) { acc = 0.0; break; })\n',
67902
+ "tensor_reduce_complex.h": '/* mtoc2 runtime helpers: complex-tensor reductions.\n *\n * Sibling of `tensor_reduce_real.h`. Same `_all` / `_dim` shape per\n * op; each kernel walks both lanes and builds intermediate\n * `double _Complex` values through `mtoc2_cmake` / `mtoc2_c*` so the\n * c2js backend can translate the bodies straight (no bare\n * `<complex.h>` operators).\n *\n * Result types per op:\n * sum / prod / mean \u2192 complex (lane-pair accumulator)\n * min / max \u2192 complex (magnitude compare, atan2\n * tiebreak \u2014 matches numbl\'s\n * `complexIsBetter`)\n * any / all \u2192 real (toBool: `re != 0 || im != 0`,\n * then aggregate via OR/AND)\n *\n * Input tolerance: `imag == NULL` (a real tensor flowing in through\n * a complex-typed route) is treated as zero on every cell.\n *\n * `_all` returns a `double _Complex` for the numeric reducers and a\n * `double` for the logical reducers. `_dim` returns a freshly-owned\n * complex tensor (numeric reducers) or a real tensor (logical\n * reducers) of the reduced shape \u2014 same trailing-singleton squeeze\n * rule as the real helper.\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Shared with the real helper; defined inline here so this file is\n * standalone-includable (the runtime activator may pull the complex\n * snippet in independently of the real one). */\nstatic void mtoc2__squeeze_trailing_c(int *ndim, long *dims) {\n while (*ndim > 2 && dims[*ndim - 1] == 1) {\n (*ndim)--;\n }\n}\n\n/* Numeric (sum/prod/mean) reduction template \u2014 complex accumulator. */\n#define MTOC2_DEFINE_CACCUM_REDUCTION(name, INIT, ACCUM, FINALIZE) \\\n static double _Complex mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n double _Complex acc = (INIT); \\\n for (long i = 0; i < n; i++) { \\\n double aim = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n double _Complex x = mtoc2_cmake(a.real[i], aim); \\\n acc = ACCUM(acc, x); \\\n } \\\n return FINALIZE(acc, n); \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims); \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n if (a.imag != NULL) { \\\n memcpy(out.imag, a.imag, (size_t)total * sizeof(double)); \\\n } else { \\\n memset(out.imag, 0, (size_t)total * sizeof(double)); \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double _Complex acc = (INIT); \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double aim = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n double _Complex x = mtoc2_cmake(a.real[off], aim); \\\n acc = ACCUM(acc, x); \\\n } \\\n double _Complex fin = FINALIZE(acc, axis); \\\n long dst = outer * before + inner; \\\n out.real[dst] = mtoc2_creal(fin); \\\n out.imag[dst] = mtoc2_cimag(fin); \\\n } \\\n } \\\n return out; \\\n }\n\n/* True when the tensor carries no imaginary content (NULL lane or every\n * element zero). Such a tensor is real in value \u2014 min/max must order by\n * value, not magnitude, to match the interpreter and MATLAB on real data. */\nstatic int mtoc2__creduce_all_imag_zero(mtoc2_tensor_t a) {\n if (a.imag == NULL) return 1;\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n for (long i = 0; i < n; i++) {\n if (a.imag[i] != 0.0) return 0;\n }\n return 1;\n}\n\n/* min/max template \u2014 complex compare via magnitude + atan2 tiebreak.\n * When the whole tensor is real (all-zero imag) we order by value\n * instead. NaN-skip on either lane. Accumulator seed is NaN+NaN; first\n * non-NaN element captures, later non-NaN elements compare. */\n#define MTOC2_DEFINE_CMINMAX_REDUCTION(name, CMP) \\\n static int mtoc2__##name##_complex_better( \\\n double aRe, double aIm, double bRe, double bIm) { \\\n double absA = hypot(aRe, aIm); \\\n double absB = hypot(bRe, bIm); \\\n if (absA != absB) return absA CMP absB; \\\n return atan2(aIm, aRe) CMP atan2(bIm, bRe); \\\n } \\\n static double _Complex mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n int realMode = mtoc2__creduce_all_imag_zero(a); \\\n int found = 0; \\\n double mRe = NAN, mIm = 0.0; \\\n for (long i = 0; i < n; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n if (xr != xr || xi != xi) continue; \\\n int better = realMode ? (xr CMP mRe) \\\n : mtoc2__##name##_complex_better(xr, xi, mRe, mIm); \\\n if (!found || better) { \\\n mRe = xr; \\\n mIm = xi; \\\n found = 1; \\\n } \\\n } \\\n return mtoc2_cmake(mRe, mIm); \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims); \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n if (a.imag != NULL) { \\\n memcpy(out.imag, a.imag, (size_t)total * sizeof(double)); \\\n } else { \\\n memset(out.imag, 0, (size_t)total * sizeof(double)); \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(out_ndim, out_dims); \\\n long slab = before * axis; \\\n int realMode = mtoc2__creduce_all_imag_zero(a); \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n int found = 0; \\\n double mRe = NAN, mIm = 0.0; \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double xr = a.real[off]; \\\n double xi = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n if (xr != xr || xi != xi) continue; \\\n int better = realMode ? (xr CMP mRe) \\\n : mtoc2__##name##_complex_better(xr, xi, mRe, mIm); \\\n if (!found || better) { \\\n mRe = xr; \\\n mIm = xi; \\\n found = 1; \\\n } \\\n } \\\n long dst = outer * before + inner; \\\n out.real[dst] = mRe; \\\n out.imag[dst] = mIm; \\\n } \\\n } \\\n return out; \\\n }\n\n/* any/all template \u2014 real result; toBool per element (either lane\n * nonzero). Mirrors `MTOC2_DEFINE_LOGICAL_REDUCTION` shape. */\n#define MTOC2_DEFINE_CLOGICAL_REDUCTION(name, EMPTY_RESULT, SHORT_BODY) \\\n static double mtoc2_##name##_complex_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n if (n == 0) return (double)(EMPTY_RESULT); \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long i = 0; i < n; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n int x = (xr != 0.0 || xi != 0.0); \\\n SHORT_BODY; \\\n } \\\n return acc; \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_complex_dim( \\\n mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_complex_dim: dim must be >= 1 (got %d)\\n", dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n for (long i = 0; i < total; i++) { \\\n double xr = a.real[i]; \\\n double xi = (a.imag != NULL) ? a.imag[i] : 0.0; \\\n out.real[i] = (xr != 0.0 || xi != 0.0) ? 1.0 : 0.0; \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing_c(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long k = 0; k < axis; k++) { \\\n long off = slabBase + inner + k * before; \\\n double xr = a.real[off]; \\\n double xi = (a.imag != NULL) ? a.imag[off] : 0.0; \\\n int x = (xr != 0.0 || xi != 0.0); \\\n SHORT_BODY; \\\n } \\\n out.real[outer * before + inner] = acc; \\\n } \\\n } \\\n return out; \\\n }\n\n/* Accumulator-statement macros. */\n#define MTOC2_CACC_SUM(acc, x) mtoc2_cadd((acc), (x))\n#define MTOC2_CACC_PROD(acc, x) mtoc2_cmul((acc), (x))\n#define MTOC2_CFIN_ID(acc, n) (acc)\n#define MTOC2_CFIN_MEAN(acc, n) mtoc2_cdiv((acc), mtoc2_cmake((double)(n), 0.0))\n\nMTOC2_DEFINE_CACCUM_REDUCTION(sum, mtoc2_cmake(0.0, 0.0), MTOC2_CACC_SUM, MTOC2_CFIN_ID)\nMTOC2_DEFINE_CACCUM_REDUCTION(prod, mtoc2_cmake(1.0, 0.0), MTOC2_CACC_PROD, MTOC2_CFIN_ID)\nMTOC2_DEFINE_CACCUM_REDUCTION(mean, mtoc2_cmake(0.0, 0.0), MTOC2_CACC_SUM, MTOC2_CFIN_MEAN)\n\nMTOC2_DEFINE_CMINMAX_REDUCTION(min, <)\nMTOC2_DEFINE_CMINMAX_REDUCTION(max, >)\n\nMTOC2_DEFINE_CLOGICAL_REDUCTION(any, 0,\n if (x) { acc = 1.0; break; })\nMTOC2_DEFINE_CLOGICAL_REDUCTION(all, 1,\n if (!x) { acc = 0.0; break; })\n',
66746
67903
  "tensor_reduce_real.h": '/* mtoc2 runtime helpers: real-tensor reductions.\n *\n * One macro per op generates two helpers:\n *\n * mtoc2_<name>_all(a) \u2014 reduce every element to a scalar.\n * mtoc2_<name>_dim(a, dim) \u2014 reduce along the 1-based axis `dim`,\n * returning a freshly-owned tensor.\n *\n * The `_dim` template mirrors numbl\'s `forEachSlice`: compute\n * `before = prod(dims[0..dim-2])`, `axis = dims[dim-1]`,\n * `after = prod(dims[dim..ndim-1])`. Walk the column-major buffer in\n * `(after \xD7 before)` fiber order with stride `before` between\n * elements along the reduced axis. Output dims are the input dims\n * with `dims[dim-1] = 1`, then trailing singletons stripped subject\n * to a 2-axis floor (matches the type system\'s\n * `tensorDoubleFromDims` rule).\n *\n * If `dim > a.ndim` the runtime emits a per-op no-op: every reducer\n * (sum/prod/mean/min/max) copies `a` as-is; the logical reducers\n * (any/all) emit an elementwise cast to {0, 1}. The transfer step\n * already proved the output shape, so this branch only fires when\n * the type-side dim/shape analysis can\'t fold to AxisAll.\n *\n * Real-only \u2014 complex is out of scope. Same-shape and column-major\n * conventions match the rest of mtoc2\'s tensor runtime.\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\n/* Strip trailing singleton axes down to a 2-axis floor. Updates\n * `*ndim` in place; `dims` is the row buffer. */\nstatic void mtoc2__squeeze_trailing(int *ndim, long *dims) {\n while (*ndim > 2 && dims[*ndim - 1] == 1) {\n (*ndim)--;\n }\n}\n\n/* Helper: reduce-all loop for accumulator-based reducers\n * (sum, prod, mean). `INIT` seeds the accumulator; `ACCUM(acc, x)`\n * is a C statement updating `acc`; `FINALIZE(acc, n)` is the final\n * transformation given the count. */\n#define MTOC2_DEFINE_ACCUM_REDUCTION(name, INIT, ACCUM, FINALIZE) \\\n static double mtoc2_##name##_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n double acc = (INIT); \\\n for (long i = 0; i < n; i++) { \\\n double x = a.real[i]; \\\n ACCUM(acc, x); \\\n } \\\n return FINALIZE(acc, n); \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_dim(mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_dim: dim must be >= 1 (got %d)\\n", \\\n dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n /* No-op axis: output is same shape/data as input (fresh copy). */ \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out; \\\n out.ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out.dims[i] = a.dims[i]; \\\n out.real = mtoc2_alloc((size_t)total * sizeof(double)); \\\n out.imag = NULL; \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_total = before * after; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double acc = (INIT); \\\n for (long k = 0; k < axis; k++) { \\\n double x = a.real[slabBase + inner + k * before]; \\\n ACCUM(acc, x); \\\n } \\\n out.real[outer * before + inner] = FINALIZE(acc, axis); \\\n } \\\n } \\\n (void)out_total; \\\n return out; \\\n }\n\n/* Helper: reduce-all loop for min/max. Seed is NaN; first non-NaN\n * element captures, later non-NaN elements compare via CMP.\n * Mirrors numbl\'s NaN-skip convention. */\n#define MTOC2_DEFINE_MINMAX_REDUCTION(name, CMP) \\\n static double mtoc2_##name##_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n double acc = NAN; \\\n for (long i = 0; i < n; i++) { \\\n double x = a.real[i]; \\\n if (x != x) continue; /* skip NaN */ \\\n if (acc != acc || (x CMP acc)) acc = x; \\\n } \\\n return acc; \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_dim(mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_dim: dim must be >= 1 (got %d)\\n", \\\n dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n mtoc2_tensor_t out; \\\n out.ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out.dims[i] = a.dims[i]; \\\n out.real = mtoc2_alloc((size_t)total * sizeof(double)); \\\n out.imag = NULL; \\\n memcpy(out.real, a.real, (size_t)total * sizeof(double)); \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double acc = NAN; \\\n for (long k = 0; k < axis; k++) { \\\n double x = a.real[slabBase + inner + k * before]; \\\n if (x != x) continue; \\\n if (acc != acc || (x CMP acc)) acc = x; \\\n } \\\n out.real[outer * before + inner] = acc; \\\n } \\\n } \\\n return out; \\\n }\n\n/* Helper: any/all reduction. Short-circuits per fiber.\n * `EMPTY_RESULT` is the value when the reduced fiber is empty:\n * - any: 0 (no element is nonzero in an empty set)\n * - all: 1 (vacuously true)\n * `SHORT(acc, x)` updates `acc` if `x` triggers the short-circuit;\n * `done` short-circuits the inner loop once `acc` settles. */\n#define MTOC2_DEFINE_LOGICAL_REDUCTION(name, EMPTY_RESULT, SHORT_BODY) \\\n static double mtoc2_##name##_all(mtoc2_tensor_t a) { \\\n long n = 1; \\\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i]; \\\n if (n == 0) return (double)(EMPTY_RESULT); \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long i = 0; i < n; i++) { \\\n double x = a.real[i]; \\\n SHORT_BODY; \\\n } \\\n return acc; \\\n } \\\n \\\n static mtoc2_tensor_t mtoc2_##name##_dim(mtoc2_tensor_t a, int dim) { \\\n if (dim < 1) { \\\n fprintf(stderr, "mtoc2: " #name "_dim: dim must be >= 1 (got %d)\\n", \\\n dim); \\\n abort(); \\\n } \\\n if (dim > a.ndim) { \\\n /* Numbl\'s `logicalAlongDim` with `dim > ndims` does an elementwise \\\n * cast to logical: each element becomes 1.0 if nonzero else 0.0. */ \\\n long total = 1; \\\n for (int i = 0; i < a.ndim; i++) total *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n for (long i = 0; i < total; i++) { \\\n out.real[i] = (a.real[i] != 0.0) ? 1.0 : 0.0; \\\n } \\\n return out; \\\n } \\\n int dimIdx = dim - 1; \\\n long axis = a.dims[dimIdx]; \\\n long before = 1; \\\n for (int i = 0; i < dimIdx; i++) before *= a.dims[i]; \\\n long after = 1; \\\n for (int i = dimIdx + 1; i < a.ndim; i++) after *= a.dims[i]; \\\n long out_dims[MTOC2_MAX_NDIM]; \\\n int out_ndim = a.ndim; \\\n for (int i = 0; i < a.ndim; i++) out_dims[i] = a.dims[i]; \\\n out_dims[dimIdx] = 1; \\\n mtoc2__squeeze_trailing(&out_ndim, out_dims); \\\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, out_dims); \\\n long slab = before * axis; \\\n for (long outer = 0; outer < after; outer++) { \\\n long slabBase = outer * slab; \\\n for (long inner = 0; inner < before; inner++) { \\\n double acc = (double)(EMPTY_RESULT); \\\n for (long k = 0; k < axis; k++) { \\\n double x = a.real[slabBase + inner + k * before]; \\\n SHORT_BODY; \\\n } \\\n out.real[outer * before + inner] = acc; \\\n } \\\n } \\\n return out; \\\n }\n\n/* Identity finalizer (sum, prod): pass the accumulator through. */\n#define MTOC2_FIN_ID(acc, n) (acc)\n/* Mean finalizer: divide by element count. Empty fiber \u2192 0/0 = NaN. */\n#define MTOC2_FIN_MEAN(acc, n) ((double)(acc) / (double)(n))\n\n/* Accumulator-statement macros. Wrapped in `do {} while(0)` to keep\n * them safe inside any single-statement context the templates use. */\n#define MTOC2_ACC_SUM(acc, x) do { (acc) += (x); } while (0)\n#define MTOC2_ACC_PROD(acc, x) do { (acc) *= (x); } while (0)\n\nMTOC2_DEFINE_ACCUM_REDUCTION(sum, 0.0, MTOC2_ACC_SUM, MTOC2_FIN_ID)\nMTOC2_DEFINE_ACCUM_REDUCTION(prod, 1.0, MTOC2_ACC_PROD, MTOC2_FIN_ID)\nMTOC2_DEFINE_ACCUM_REDUCTION(mean, 0.0, MTOC2_ACC_SUM, MTOC2_FIN_MEAN)\n\nMTOC2_DEFINE_MINMAX_REDUCTION(min, <)\nMTOC2_DEFINE_MINMAX_REDUCTION(max, >)\n\n/* `any` ignores NaN (MATLAB: any(NaN) is 0); `x == x` excludes it so a\n * NaN doesn\'t wrongly short-circuit to true. `all` tests `x == 0.0`,\n * which NaN already fails, so it needs no guard. */\nMTOC2_DEFINE_LOGICAL_REDUCTION(any, 0,\n if (x != 0.0 && x == x) { acc = 1.0; break; })\nMTOC2_DEFINE_LOGICAL_REDUCTION(all, 1,\n if (x == 0.0) { acc = 0.0; break; })\n',
66747
67904
  "tensor_repmat.h": '/* mtoc2 runtime helper: `repmat(A, reps)` \u2014 tile a tensor by\n * replicating it along each axis.\n *\n * Numbl\'s reference is the `repmat` builtin in\n * `interpreter/builtins/array-manipulation.ts` (the tensor branch).\n *\n * Contract:\n * - `in` is the source tensor (real, owned-value invariant unchanged).\n * - `nreps` is the number of replication factors supplied (1..MTOC2_MAX_NDIM).\n * - `reps_in[i]` is the per-axis replication factor; negative values\n * clamp to 0 (yielding an empty axis), matching numbl/MATLAB.\n *\n * Output shape is `padShape[i] * padReps[i]` where the input\'s shape\n * and the reps vector are both right-padded with 1s to a common rank\n * `max(in.ndim, nreps)`. Result is freshly owned; `imag` is NULL.\n *\n * Algorithm: copy the input data into the start of the output buffer\n * (column-major flat layout is preserved when trailing dims are 1),\n * then iteratively expand along each axis. For axis `d` with rep > 1,\n * we walk the existing blocks of size `blockSize = prod(curShape[0..d])`\n * in reverse order and replicate each block `rep` times consecutively.\n * Reverse order avoids overwriting source data; `memmove` covers the\n * b=0 in-place case where the block stays at its original offset.\n */\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_repmat(mtoc2_tensor_t in, int nreps,\n const long *reps_in) {\n if (nreps < 1 || nreps > MTOC2_MAX_NDIM) {\n fprintf(stderr,\n "mtoc2: repmat nreps %d out of range [1, %d]\\n", nreps, MTOC2_MAX_NDIM);\n abort();\n }\n long reps[MTOC2_MAX_NDIM];\n for (int i = 0; i < nreps; i++) reps[i] = reps_in[i] < 0 ? 0 : reps_in[i];\n\n int in_ndim = in.ndim;\n int out_ndim = nreps > in_ndim ? nreps : in_ndim;\n if (out_ndim > MTOC2_MAX_NDIM) {\n fprintf(stderr,\n "mtoc2: repmat output ndim %d exceeds %d\\n", out_ndim, MTOC2_MAX_NDIM);\n abort();\n }\n\n long padShape[MTOC2_MAX_NDIM];\n long padReps[MTOC2_MAX_NDIM];\n long outDims[MTOC2_MAX_NDIM];\n for (int i = 0; i < out_ndim; i++) {\n padShape[i] = i < in_ndim ? in.dims[i] : 1;\n padReps[i] = i < nreps ? reps[i] : 1;\n outDims[i] = padShape[i] * padReps[i];\n }\n\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(out_ndim, outDims);\n\n size_t outTotal = 1;\n for (int i = 0; i < out_ndim; i++) outTotal *= (size_t)outDims[i];\n if (outTotal == 0) return out;\n\n size_t inTotal = 1;\n for (int i = 0; i < in_ndim; i++) inTotal *= (size_t)in.dims[i];\n if (inTotal == 0) return out;\n\n /* Initial copy: input\'s data laid out in column-major with shape\n * `in.dims` matches the same flat layout under `padShape` (trailing\n * 1s don\'t change flat indexing). */\n memcpy(out.real, in.real, inTotal * sizeof(double));\n\n long curShape[MTOC2_MAX_NDIM];\n for (int i = 0; i < out_ndim; i++) curShape[i] = padShape[i];\n size_t curTotal = inTotal;\n\n for (int d = 0; d < out_ndim; d++) {\n long rep = padReps[d];\n if (rep == 1) continue;\n\n size_t blockSize = 1;\n for (int i = 0; i <= d; i++) blockSize *= (size_t)curShape[i];\n\n if (rep == 0 || blockSize == 0) {\n curShape[d] *= rep;\n curTotal = 0;\n /* Once curTotal is 0, no further work needed \u2014 outTotal is also\n * 0 (because outDims[d] = padShape[d] * 0 = 0). The alloc above\n * already produced a zero-element tensor; bail out. */\n return out;\n }\n\n size_t numBlocks = curTotal / blockSize;\n /* Walk blocks in reverse so writes don\'t clobber as-yet-unread\n * source blocks. Each block of `blockSize` doubles to `blockSize\n * * rep` consecutive slots at offset `b * blockSize * rep`. */\n for (size_t b = numBlocks; b > 0;) {\n b--;\n size_t srcOff = b * blockSize;\n size_t dstBase = b * blockSize * (size_t)rep;\n if (dstBase != srcOff) {\n memmove(out.real + dstBase, out.real + srcOff,\n blockSize * sizeof(double));\n }\n for (long r = 1; r < rep; r++) {\n memcpy(out.real + dstBase + (size_t)r * blockSize,\n out.real + dstBase,\n blockSize * sizeof(double));\n }\n }\n\n curShape[d] *= rep;\n curTotal *= (size_t)rep;\n }\n\n return out;\n}\n\n/* Complex-input sibling: tiles both lanes. Tolerates `in.imag == NULL`\n * (a real tensor that flowed in via a complex-typed route) by zero-\n * filling the output imag lane. */\nstatic mtoc2_tensor_t mtoc2_tensor_repmat_complex(mtoc2_tensor_t in,\n int nreps,\n const long *reps_in) {\n if (nreps < 1 || nreps > MTOC2_MAX_NDIM) {\n fprintf(stderr,\n "mtoc2: repmat_complex nreps %d out of range [1, %d]\\n",\n nreps, MTOC2_MAX_NDIM);\n abort();\n }\n long reps[MTOC2_MAX_NDIM];\n for (int i = 0; i < nreps; i++) reps[i] = reps_in[i] < 0 ? 0 : reps_in[i];\n\n int in_ndim = in.ndim;\n int out_ndim = nreps > in_ndim ? nreps : in_ndim;\n if (out_ndim > MTOC2_MAX_NDIM) {\n fprintf(stderr,\n "mtoc2: repmat_complex output ndim %d exceeds %d\\n",\n out_ndim, MTOC2_MAX_NDIM);\n abort();\n }\n\n long padShape[MTOC2_MAX_NDIM];\n long padReps[MTOC2_MAX_NDIM];\n long outDims[MTOC2_MAX_NDIM];\n for (int i = 0; i < out_ndim; i++) {\n padShape[i] = i < in_ndim ? in.dims[i] : 1;\n padReps[i] = i < nreps ? reps[i] : 1;\n outDims[i] = padShape[i] * padReps[i];\n }\n\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(out_ndim, outDims);\n\n size_t outTotal = 1;\n for (int i = 0; i < out_ndim; i++) outTotal *= (size_t)outDims[i];\n if (outTotal == 0) return out;\n\n size_t inTotal = 1;\n for (int i = 0; i < in_ndim; i++) inTotal *= (size_t)in.dims[i];\n if (inTotal == 0) {\n memset(out.imag, 0, outTotal * sizeof(double));\n return out;\n }\n\n int srcHasImag = (in.imag != NULL);\n\n memcpy(out.real, in.real, inTotal * sizeof(double));\n if (srcHasImag) {\n memcpy(out.imag, in.imag, inTotal * sizeof(double));\n } else {\n memset(out.imag, 0, inTotal * sizeof(double));\n }\n\n long curShape[MTOC2_MAX_NDIM];\n for (int i = 0; i < out_ndim; i++) curShape[i] = padShape[i];\n size_t curTotal = inTotal;\n\n for (int d = 0; d < out_ndim; d++) {\n long rep = padReps[d];\n if (rep == 1) continue;\n\n size_t blockSize = 1;\n for (int i = 0; i <= d; i++) blockSize *= (size_t)curShape[i];\n\n if (rep == 0 || blockSize == 0) {\n curShape[d] *= rep;\n curTotal = 0;\n return out;\n }\n\n size_t numBlocks = curTotal / blockSize;\n for (size_t b = numBlocks; b > 0;) {\n b--;\n size_t srcOff = b * blockSize;\n size_t dstBase = b * blockSize * (size_t)rep;\n if (dstBase != srcOff) {\n memmove(out.real + dstBase, out.real + srcOff,\n blockSize * sizeof(double));\n memmove(out.imag + dstBase, out.imag + srcOff,\n blockSize * sizeof(double));\n }\n for (long r = 1; r < rep; r++) {\n memcpy(out.real + dstBase + (size_t)r * blockSize,\n out.real + dstBase,\n blockSize * sizeof(double));\n memcpy(out.imag + dstBase + (size_t)r * blockSize,\n out.imag + dstBase,\n blockSize * sizeof(double));\n }\n }\n\n curShape[d] *= rep;\n curTotal *= (size_t)rep;\n }\n\n return out;\n}\n',
66748
67905
  "tensor_reshape_nd.h": '/* mtoc2 runtime helper: reshape a real tensor to an N-D shape.\n *\n * Receives the input tensor by value (`mtoc2_tensor_t`) plus a\n * caller-supplied dim list (`ndim`, `dims`). Allocates a fresh\n * output tensor via `mtoc2_tensor_alloc_nd` and copies the input\'s\n * column-major buffer wholesale \u2014 reshape is a layout reinterpret,\n * so the linear element order is unchanged.\n *\n * `dims[i] == -1` is the MATLAB `[]` auto-infer slot: the helper\n * scans for a single -1 and fills it from `in_total / prod(others)`.\n * Two or more sentinels, or an `in_total` not divisible by the\n * explicit dims, abort with a clear message.\n *\n * Element-count check: the lowerer enforces `prod(input.dims) ==\n * prod(dims)` at translate time when the input shape is statically\n * known. This helper is the fallback when the input shape only\n * appears at runtime (e.g. a tensor function param whose\n * specialization arg type came in without a concrete shape). On\n * mismatch it prints to stderr and aborts, matching numbl\'s\n * runtime-error surface.\n *\n * Real-only \u2014 mtoc2\'s tensor side is real-only today; the type\n * lattice rejects complex inputs at lowering. The output\'s `imag`\n * is NULL, set by `mtoc2_tensor_alloc_nd`.\n */\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_reshape_nd(\n mtoc2_tensor_t in, int ndim, const long *dims) {\n size_t in_total = 1;\n for (int i = 0; i < in.ndim; i++) in_total *= (size_t)in.dims[i];\n /* Scan for at most one `-1` infer slot and the product of the\n * remaining explicit dims. */\n int infer_idx = -1;\n size_t explicit_prod = 1;\n for (int i = 0; i < ndim; i++) {\n if (dims[i] == -1) {\n if (infer_idx != -1) {\n fprintf(stderr,\n "mtoc2: reshape: at most one \'[]\' auto-infer slot allowed\\n");\n abort();\n }\n infer_idx = i;\n } else if (dims[i] < 0) {\n fprintf(stderr,\n "mtoc2: reshape: dim %d must be a non-negative integer "\n "(got %ld)\\n", i + 1, dims[i]);\n abort();\n } else {\n explicit_prod *= (size_t)dims[i];\n }\n }\n long resolved_dims[MTOC2_MAX_NDIM];\n for (int i = 0; i < ndim; i++) resolved_dims[i] = dims[i];\n size_t out_total;\n if (infer_idx != -1) {\n if (explicit_prod == 0 && in_total != 0) {\n fprintf(stderr,\n "mtoc2: reshape: input has %zu elements but the explicit dims "\n "around \'[]\' multiply to 0\\n", in_total);\n abort();\n }\n if (explicit_prod > 0 && in_total % explicit_prod != 0) {\n fprintf(stderr,\n "mtoc2: reshape: input has %zu elements, not divisible by %zu "\n "(the explicit dims around \'[]\')\\n", in_total, explicit_prod);\n abort();\n }\n resolved_dims[infer_idx] =\n (explicit_prod == 0) ? 0 : (long)(in_total / explicit_prod);\n out_total = in_total;\n } else {\n out_total = explicit_prod;\n if (in_total != out_total) {\n fprintf(stderr,\n "mtoc2: reshape: number of elements must not change "\n "(in=%zu, out=%zu)\\n", in_total, out_total);\n abort();\n }\n }\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd(ndim, resolved_dims);\n if (out_total > 0)\n memcpy(out.real, in.real, out_total * sizeof(double));\n return out;\n}\n',
66749
67906
  "tensor_reshape_nd_complex.h": '/* mtoc2 runtime helper: reshape a complex tensor to an N-D shape.\n *\n * Sibling of `mtoc2_reshape_nd`. Same `-1` auto-infer slot, same\n * element-count check, same runtime-error surface \u2014 the only\n * difference is the output is allocated via\n * `mtoc2_tensor_alloc_nd_complex` (both lanes) and both lanes get\n * memcpy\'d from the input. Reshape is a layout reinterpret, so the\n * linear element order is unchanged on either lane.\n *\n * Tolerates `in.imag == NULL` (a real tensor flowing through a\n * complex-typed reshape route) by zeroing the output imag lane.\n */\n\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_reshape_nd_complex(\n mtoc2_tensor_t in, int ndim, const long *dims) {\n size_t in_total = 1;\n for (int i = 0; i < in.ndim; i++) in_total *= (size_t)in.dims[i];\n int infer_idx = -1;\n size_t explicit_prod = 1;\n for (int i = 0; i < ndim; i++) {\n if (dims[i] == -1) {\n if (infer_idx != -1) {\n fprintf(stderr,\n "mtoc2: reshape: at most one \'[]\' auto-infer slot allowed\\n");\n abort();\n }\n infer_idx = i;\n } else if (dims[i] < 0) {\n fprintf(stderr,\n "mtoc2: reshape: dim %d must be a non-negative integer "\n "(got %ld)\\n", i + 1, dims[i]);\n abort();\n } else {\n explicit_prod *= (size_t)dims[i];\n }\n }\n long resolved_dims[MTOC2_MAX_NDIM];\n for (int i = 0; i < ndim; i++) resolved_dims[i] = dims[i];\n size_t out_total;\n if (infer_idx != -1) {\n if (explicit_prod == 0 && in_total != 0) {\n fprintf(stderr,\n "mtoc2: reshape: input has %zu elements but the explicit dims "\n "around \'[]\' multiply to 0\\n", in_total);\n abort();\n }\n if (explicit_prod > 0 && in_total % explicit_prod != 0) {\n fprintf(stderr,\n "mtoc2: reshape: input has %zu elements, not divisible by %zu "\n "(the explicit dims around \'[]\')\\n", in_total, explicit_prod);\n abort();\n }\n resolved_dims[infer_idx] =\n (explicit_prod == 0) ? 0 : (long)(in_total / explicit_prod);\n out_total = in_total;\n } else {\n out_total = explicit_prod;\n if (in_total != out_total) {\n fprintf(stderr,\n "mtoc2: reshape: number of elements must not change "\n "(in=%zu, out=%zu)\\n", in_total, out_total);\n abort();\n }\n }\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(ndim, resolved_dims);\n if (out_total > 0) {\n memcpy(out.real, in.real, out_total * sizeof(double));\n if (in.imag != NULL) {\n memcpy(out.imag, in.imag, out_total * sizeof(double));\n } else {\n memset(out.imag, 0, out_total * sizeof(double));\n }\n }\n return out;\n}\n',
66750
67907
  "tensor_size.h": "/* mtoc2 runtime helper: `size(t)` \u2014 returns a freshly-owned 1\xD7ndim\n * row tensor whose elements are the input's dim sizes as doubles.\n * MATLAB / numbl semantics: scalars and vectors return at least a\n * 2-element row (the type system already pads to ndim \u2265 2; this\n * helper just copies dims[] into a row vector).\n *\n * For `size(t, k)` mtoc2 emits a scalar `(double)t.dims[k-1]` inline\n * \u2014 no runtime helper needed for that form.\n */\n\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_size_row(mtoc2_tensor_t a) {\n long n = a.ndim;\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = 2;\n r.dims[0] = 1;\n r.dims[1] = n;\n for (long i = 0; i < n; i++) r.real[i] = (double)a.dims[i];\n return r;\n}\n",
66751
- "tensor_sort_real.h": '/* mtoc2 runtime helper: stable sort on a tensor.\n *\n * mtoc2_sort_real(a, descending)\n * `b = sort(a)` / `sort(a, \'ascend\'|\'descend\')` \u2014 returns a\n * freshly-owned tensor of the same shape as `a`, with the flat\n * (column-major) entries sorted in the requested direction.\n *\n * mtoc2_sort_real_2(a, descending, &out_v, &out_i)\n * `[v, i] = sort(...)` \u2014 fills `*out_v` with the sorted values\n * and `*out_i` with 1-based original positions.\n *\n * mtoc2_sort_complex / mtoc2_sort_complex_2\n * Complex-input siblings. Numbl / MATLAB sort complex by\n * magnitude (hypot), tiebreak by phase (atan2). Tolerates\n * `a.imag == NULL` (real-input flowed through a complex route)\n * by treating imag as zero.\n *\n * Sort is stable in both directions: ties resolve by ascending\n * original index, matching numbl\'s behaviour (verified against\n * `sort([5 2 8 1 2], \'descend\')` \u2192 indices `3 1 2 5 4`).\n *\n * The lowering layer restricts the input to a 1\xD7N row vector or N\xD71\n * column vector for v1; the helper itself walks the column-major\n * flat buffer and would handle any rank, but the type system rejects\n * the higher-rank cases until the per-axis form is plumbed through.\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\ntypedef struct {\n double v;\n long ix;\n} mtoc2_sort_pair_t;\n\n/* NaN ranks as the maximum (MATLAB): last when ascending, first when\n * descending. Without this, NaN compares false both ways and falls to\n * the index tie-break, leaving the comparator non-transitive \u2014 which is\n * undefined behavior for qsort and corrupts the array. */\nstatic int mtoc2_sort_cmp_asc(const void *pa, const void *pb) {\n const mtoc2_sort_pair_t *a = (const mtoc2_sort_pair_t *)pa;\n const mtoc2_sort_pair_t *b = (const mtoc2_sort_pair_t *)pb;\n int an = a->v != a->v, bn = b->v != b->v;\n if (an || bn) {\n if (!(an && bn)) return an ? 1 : -1; /* NaN sorts last */\n } else {\n if (a->v < b->v) return -1;\n if (a->v > b->v) return 1;\n }\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic int mtoc2_sort_cmp_desc(const void *pa, const void *pb) {\n const mtoc2_sort_pair_t *a = (const mtoc2_sort_pair_t *)pa;\n const mtoc2_sort_pair_t *b = (const mtoc2_sort_pair_t *)pb;\n int an = a->v != a->v, bn = b->v != b->v;\n if (an || bn) {\n if (!(an && bn)) return an ? -1 : 1; /* NaN sorts first */\n } else {\n if (a->v > b->v) return -1;\n if (a->v < b->v) return 1;\n }\n /* Tie-break still by ascending original index \u2014 both numbl and\n * MATLAB keep ties in original order in either direction. */\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic mtoc2_tensor_t mtoc2_sort_real(mtoc2_tensor_t a, int descending) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i];\n if (n == 0) return r;\n mtoc2_sort_pair_t *buf =\n (mtoc2_sort_pair_t *)malloc((size_t)n * sizeof(mtoc2_sort_pair_t));\n if (!buf) {\n fprintf(stderr, "mtoc2: out of memory (sort buffer)\\n");\n abort();\n }\n for (long i = 0; i < n; i++) {\n buf[i].v = a.real[i];\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_pair_t),\n descending ? mtoc2_sort_cmp_desc : mtoc2_sort_cmp_asc);\n for (long i = 0; i < n; i++) r.real[i] = buf[i].v;\n free(buf);\n return r;\n}\n\nstatic void mtoc2_sort_real_2(mtoc2_tensor_t a, int descending,\n mtoc2_tensor_t *out_v, mtoc2_tensor_t *out_i) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t v;\n mtoc2_tensor_t ix;\n v.real = mtoc2_alloc((size_t)n * sizeof(double));\n v.imag = NULL;\n v.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) v.dims[i] = a.dims[i];\n ix.real = mtoc2_alloc((size_t)n * sizeof(double));\n ix.imag = NULL;\n ix.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) ix.dims[i] = a.dims[i];\n if (n > 0) {\n mtoc2_sort_pair_t *buf =\n (mtoc2_sort_pair_t *)malloc((size_t)n * sizeof(mtoc2_sort_pair_t));\n if (!buf) {\n fprintf(stderr, "mtoc2: out of memory (sort buffer)\\n");\n abort();\n }\n for (long i = 0; i < n; i++) {\n buf[i].v = a.real[i];\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_pair_t),\n descending ? mtoc2_sort_cmp_desc : mtoc2_sort_cmp_asc);\n for (long i = 0; i < n; i++) {\n v.real[i] = buf[i].v;\n ix.real[i] = (double)(buf[i].ix + 1);\n }\n free(buf);\n }\n mtoc2_tensor_assign(out_v, v);\n mtoc2_tensor_assign(out_i, ix);\n}\n\ntypedef struct {\n double mag;\n double phase;\n long ix;\n} mtoc2_sort_complex_pair_t;\n\nstatic int mtoc2_sort_cmp_complex_asc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n if (a->mag < b->mag) return -1;\n if (a->mag > b->mag) return 1;\n if (a->phase < b->phase) return -1;\n if (a->phase > b->phase) return 1;\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic int mtoc2_sort_cmp_complex_desc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n if (a->mag > b->mag) return -1;\n if (a->mag < b->mag) return 1;\n if (a->phase > b->phase) return -1;\n if (a->phase < b->phase) return 1;\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic mtoc2_tensor_t mtoc2_sort_complex(mtoc2_tensor_t a, int descending) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims);\n if (n == 0) return r;\n int srcHasImag = (a.imag != NULL);\n if (!srcHasImag) memset(r.imag, 0, (size_t)n * sizeof(double));\n mtoc2_sort_complex_pair_t *buf =\n (mtoc2_sort_complex_pair_t *)malloc(\n (size_t)n * sizeof(mtoc2_sort_complex_pair_t));\n if (!buf) {\n fprintf(stderr, "mtoc2: out of memory (sort complex buffer)\\n");\n abort();\n }\n for (long i = 0; i < n; i++) {\n double re = a.real[i];\n double im = srcHasImag ? a.imag[i] : 0.0;\n buf[i].mag = hypot(re, im);\n buf[i].phase = atan2(im, re);\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_complex_pair_t),\n descending ? mtoc2_sort_cmp_complex_desc\n : mtoc2_sort_cmp_complex_asc);\n for (long i = 0; i < n; i++) {\n r.real[i] = a.real[buf[i].ix];\n r.imag[i] = srcHasImag ? a.imag[buf[i].ix] : 0.0;\n }\n free(buf);\n return r;\n}\n\nstatic void mtoc2_sort_complex_2(mtoc2_tensor_t a, int descending,\n mtoc2_tensor_t *out_v, mtoc2_tensor_t *out_i) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t v = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims);\n mtoc2_tensor_t ix;\n ix.real = mtoc2_alloc((size_t)n * sizeof(double));\n ix.imag = NULL;\n ix.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) ix.dims[i] = a.dims[i];\n if (n > 0) {\n int srcHasImag = (a.imag != NULL);\n if (!srcHasImag) memset(v.imag, 0, (size_t)n * sizeof(double));\n mtoc2_sort_complex_pair_t *buf =\n (mtoc2_sort_complex_pair_t *)malloc(\n (size_t)n * sizeof(mtoc2_sort_complex_pair_t));\n if (!buf) {\n fprintf(stderr, "mtoc2: out of memory (sort complex buffer)\\n");\n abort();\n }\n for (long i = 0; i < n; i++) {\n double re = a.real[i];\n double im = srcHasImag ? a.imag[i] : 0.0;\n buf[i].mag = hypot(re, im);\n buf[i].phase = atan2(im, re);\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_complex_pair_t),\n descending ? mtoc2_sort_cmp_complex_desc\n : mtoc2_sort_cmp_complex_asc);\n for (long i = 0; i < n; i++) {\n v.real[i] = a.real[buf[i].ix];\n v.imag[i] = srcHasImag ? a.imag[buf[i].ix] : 0.0;\n ix.real[i] = (double)(buf[i].ix + 1);\n }\n free(buf);\n }\n mtoc2_tensor_assign(out_v, v);\n mtoc2_tensor_assign(out_i, ix);\n}\n',
67908
+ "tensor_sort_real.h": "/* mtoc2 runtime helper: stable sort on a tensor.\n *\n * mtoc2_sort_real(a, descending)\n * `b = sort(a)` / `sort(a, 'ascend'|'descend')` \u2014 returns a\n * freshly-owned tensor of the same shape as `a`, with the flat\n * (column-major) entries sorted in the requested direction.\n *\n * mtoc2_sort_real_2(a, descending, &out_v, &out_i)\n * `[v, i] = sort(...)` \u2014 fills `*out_v` with the sorted values\n * and `*out_i` with 1-based original positions.\n *\n * mtoc2_sort_complex / mtoc2_sort_complex_2\n * Complex-input siblings. Numbl / MATLAB sort complex by\n * magnitude (hypot), tiebreak by phase (atan2). Tolerates\n * `a.imag == NULL` (real-input flowed through a complex route)\n * by treating imag as zero.\n *\n * Sort is stable in both directions: ties resolve by ascending\n * original index, matching numbl's behaviour (verified against\n * `sort([5 2 8 1 2], 'descend')` \u2192 indices `3 1 2 5 4`).\n *\n * The lowering layer restricts the input to a 1\xD7N row vector or N\xD71\n * column vector for v1; the helper itself walks the column-major\n * flat buffer and would handle any rank, but the type system rejects\n * the higher-rank cases until the per-axis form is plumbed through.\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n\ntypedef struct {\n double v;\n long ix;\n} mtoc2_sort_pair_t;\n\n/* NaN ranks as the maximum (MATLAB): last when ascending, first when\n * descending. Without this, NaN compares false both ways and falls to\n * the index tie-break, leaving the comparator non-transitive \u2014 which is\n * undefined behavior for qsort and corrupts the array. */\nstatic int mtoc2_sort_cmp_asc(const void *pa, const void *pb) {\n const mtoc2_sort_pair_t *a = (const mtoc2_sort_pair_t *)pa;\n const mtoc2_sort_pair_t *b = (const mtoc2_sort_pair_t *)pb;\n int an = a->v != a->v, bn = b->v != b->v;\n if (an || bn) {\n if (!(an && bn)) return an ? 1 : -1; /* NaN sorts last */\n } else {\n if (a->v < b->v) return -1;\n if (a->v > b->v) return 1;\n }\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic int mtoc2_sort_cmp_desc(const void *pa, const void *pb) {\n const mtoc2_sort_pair_t *a = (const mtoc2_sort_pair_t *)pa;\n const mtoc2_sort_pair_t *b = (const mtoc2_sort_pair_t *)pb;\n int an = a->v != a->v, bn = b->v != b->v;\n if (an || bn) {\n if (!(an && bn)) return an ? -1 : 1; /* NaN sorts first */\n } else {\n if (a->v > b->v) return -1;\n if (a->v < b->v) return 1;\n }\n /* Tie-break still by ascending original index \u2014 both numbl and\n * MATLAB keep ties in original order in either direction. */\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic mtoc2_tensor_t mtoc2_sort_real(mtoc2_tensor_t a, int descending) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) r.dims[i] = a.dims[i];\n if (n == 0) return r;\n mtoc2_sort_pair_t *buf =\n (mtoc2_sort_pair_t *)malloc((size_t)n * sizeof(mtoc2_sort_pair_t));\n if (!buf) {\n fprintf(stderr, \"mtoc2: out of memory (sort buffer)\\n\");\n abort();\n }\n for (long i = 0; i < n; i++) {\n buf[i].v = a.real[i];\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_pair_t),\n descending ? mtoc2_sort_cmp_desc : mtoc2_sort_cmp_asc);\n for (long i = 0; i < n; i++) r.real[i] = buf[i].v;\n free(buf);\n return r;\n}\n\nstatic void mtoc2_sort_real_2(mtoc2_tensor_t a, int descending,\n mtoc2_tensor_t *out_v, mtoc2_tensor_t *out_i) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t v;\n mtoc2_tensor_t ix;\n v.real = mtoc2_alloc((size_t)n * sizeof(double));\n v.imag = NULL;\n v.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) v.dims[i] = a.dims[i];\n ix.real = mtoc2_alloc((size_t)n * sizeof(double));\n ix.imag = NULL;\n ix.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) ix.dims[i] = a.dims[i];\n if (n > 0) {\n mtoc2_sort_pair_t *buf =\n (mtoc2_sort_pair_t *)malloc((size_t)n * sizeof(mtoc2_sort_pair_t));\n if (!buf) {\n fprintf(stderr, \"mtoc2: out of memory (sort buffer)\\n\");\n abort();\n }\n for (long i = 0; i < n; i++) {\n buf[i].v = a.real[i];\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_pair_t),\n descending ? mtoc2_sort_cmp_desc : mtoc2_sort_cmp_asc);\n for (long i = 0; i < n; i++) {\n v.real[i] = buf[i].v;\n ix.real[i] = (double)(buf[i].ix + 1);\n }\n free(buf);\n }\n mtoc2_tensor_assign(out_v, v);\n mtoc2_tensor_assign(out_i, ix);\n}\n\ntypedef struct {\n double mag;\n double phase;\n long ix;\n} mtoc2_sort_complex_pair_t;\n\nstatic int mtoc2_sort_cmp_complex_asc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n if (a->mag < b->mag) return -1;\n if (a->mag > b->mag) return 1;\n if (a->phase < b->phase) return -1;\n if (a->phase > b->phase) return 1;\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic int mtoc2_sort_cmp_complex_desc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n if (a->mag > b->mag) return -1;\n if (a->mag < b->mag) return 1;\n if (a->phase > b->phase) return -1;\n if (a->phase < b->phase) return 1;\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\n/* Real-value comparators used when the input's imaginary lane is all\n * zero: order by signed value (the `mag` field holds the signed real\n * part in that mode), NaNs last (asc) / first (desc), tiebreak by index.\n * Matches the interpreter and MATLAB on real data. */\nstatic int mtoc2_sort_cmp_real_asc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n int aNaN = a->mag != a->mag;\n int bNaN = b->mag != b->mag;\n if (!(aNaN && bNaN)) {\n if (aNaN) return 1;\n if (bNaN) return -1;\n if (a->mag < b->mag) return -1;\n if (a->mag > b->mag) return 1;\n }\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\nstatic int mtoc2_sort_cmp_real_desc(const void *pa, const void *pb) {\n const mtoc2_sort_complex_pair_t *a = (const mtoc2_sort_complex_pair_t *)pa;\n const mtoc2_sort_complex_pair_t *b = (const mtoc2_sort_complex_pair_t *)pb;\n int aNaN = a->mag != a->mag;\n int bNaN = b->mag != b->mag;\n if (!(aNaN && bNaN)) {\n if (aNaN) return -1;\n if (bNaN) return 1;\n if (a->mag > b->mag) return -1;\n if (a->mag < b->mag) return 1;\n }\n if (a->ix < b->ix) return -1;\n if (a->ix > b->ix) return 1;\n return 0;\n}\n\n/* True when the tensor carries no imaginary content (NULL lane or all\n * elements zero) \u2014 then sort orders by signed real value. */\nstatic int mtoc2_sort_all_imag_zero(mtoc2_tensor_t a) {\n if (a.imag == NULL) return 1;\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n for (long i = 0; i < n; i++) {\n if (a.imag[i] != 0.0) return 0;\n }\n return 1;\n}\n\nstatic mtoc2_tensor_t mtoc2_sort_complex(mtoc2_tensor_t a, int descending) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t r = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims);\n if (n == 0) return r;\n int srcHasImag = (a.imag != NULL);\n if (!srcHasImag) memset(r.imag, 0, (size_t)n * sizeof(double));\n int realMode = mtoc2_sort_all_imag_zero(a);\n mtoc2_sort_complex_pair_t *buf =\n (mtoc2_sort_complex_pair_t *)malloc(\n (size_t)n * sizeof(mtoc2_sort_complex_pair_t));\n if (!buf) {\n fprintf(stderr, \"mtoc2: out of memory (sort complex buffer)\\n\");\n abort();\n }\n for (long i = 0; i < n; i++) {\n double re = a.real[i];\n double im = srcHasImag ? a.imag[i] : 0.0;\n buf[i].mag = realMode ? re : hypot(re, im);\n buf[i].phase = realMode ? 0.0 : atan2(im, re);\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_complex_pair_t),\n realMode ? (descending ? mtoc2_sort_cmp_real_desc\n : mtoc2_sort_cmp_real_asc)\n : (descending ? mtoc2_sort_cmp_complex_desc\n : mtoc2_sort_cmp_complex_asc));\n for (long i = 0; i < n; i++) {\n r.real[i] = a.real[buf[i].ix];\n r.imag[i] = srcHasImag ? a.imag[buf[i].ix] : 0.0;\n }\n free(buf);\n return r;\n}\n\nstatic void mtoc2_sort_complex_2(mtoc2_tensor_t a, int descending,\n mtoc2_tensor_t *out_v, mtoc2_tensor_t *out_i) {\n long n = 1;\n for (int i = 0; i < a.ndim; i++) n *= a.dims[i];\n mtoc2_tensor_t v = mtoc2_tensor_alloc_nd_complex(a.ndim, a.dims);\n mtoc2_tensor_t ix;\n ix.real = mtoc2_alloc((size_t)n * sizeof(double));\n ix.imag = NULL;\n ix.ndim = a.ndim;\n for (int i = 0; i < a.ndim; i++) ix.dims[i] = a.dims[i];\n if (n > 0) {\n int srcHasImag = (a.imag != NULL);\n if (!srcHasImag) memset(v.imag, 0, (size_t)n * sizeof(double));\n int realMode = mtoc2_sort_all_imag_zero(a);\n mtoc2_sort_complex_pair_t *buf =\n (mtoc2_sort_complex_pair_t *)malloc(\n (size_t)n * sizeof(mtoc2_sort_complex_pair_t));\n if (!buf) {\n fprintf(stderr, \"mtoc2: out of memory (sort complex buffer)\\n\");\n abort();\n }\n for (long i = 0; i < n; i++) {\n double re = a.real[i];\n double im = srcHasImag ? a.imag[i] : 0.0;\n buf[i].mag = realMode ? re : hypot(re, im);\n buf[i].phase = realMode ? 0.0 : atan2(im, re);\n buf[i].ix = i;\n }\n qsort(buf, (size_t)n, sizeof(mtoc2_sort_complex_pair_t),\n realMode ? (descending ? mtoc2_sort_cmp_real_desc\n : mtoc2_sort_cmp_real_asc)\n : (descending ? mtoc2_sort_cmp_complex_desc\n : mtoc2_sort_cmp_complex_asc));\n for (long i = 0; i < n; i++) {\n v.real[i] = a.real[buf[i].ix];\n v.imag[i] = srcHasImag ? a.imag[buf[i].ix] : 0.0;\n ix.real[i] = (double)(buf[i].ix + 1);\n }\n free(buf);\n }\n mtoc2_tensor_assign(out_v, v);\n mtoc2_tensor_assign(out_i, ix);\n}\n",
66752
67909
  "tensor_transpose.h": "/* mtoc2 runtime helper: real-tensor non-conjugate transpose for 2-D\n * inputs. Returns a freshly-owned tensor with `dims` swapped. The\n * 2-D restriction is enforced at lowering; by the time this helper\n * runs, `a.ndim` is always 2.\n *\n * Column-major in, column-major out. Source `a` has shape (m \xD7 n);\n * destination has shape (n \xD7 m). Source element at (sr, sc) lives at\n * `a.real[sr + sc*m]`; destination element at (sc, sr) \u2014 the transpose\n * mapping \u2014 lives at `out.real[sc + sr*n]`. The inner loop walks the\n * source's column-major buffer linearly to keep the read stride\n * unit-stride.\n *\n * For complex support (not yet a thing in mtoc2), the conjugate\n * variant would negate `a.imag` while copying; the non-conjugate\n * variant just copies. Mirrors numbl's `transposeCore` in\n * `helpers/arithmetic.ts`.\n */\n\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_transpose(mtoc2_tensor_t a) {\n long m = a.dims[0];\n long n = a.dims[1];\n mtoc2_tensor_t r;\n r.real = mtoc2_alloc((size_t)m * (size_t)n * sizeof(double));\n r.imag = NULL;\n r.ndim = 2;\n r.dims[0] = n;\n r.dims[1] = m;\n for (long sc = 0; sc < n; sc++) {\n for (long sr = 0; sr < m; sr++) {\n r.real[sc + sr * n] = a.real[sr + sc * m];\n }\n }\n return r;\n}\n",
66753
67910
  "tensor_transpose_complex.h": "/* mtoc2 runtime helper: complex-tensor non-conjugate transpose for\n * 2-D inputs. Sibling of `mtoc2_tensor_transpose` \u2014 same shape\n * permutation, but copies BOTH lanes (no conjugation, the `.'`\n * operator). The `'` (conjugate transpose) operator lowers to\n * `transpose(conj(z))` at the lowering layer, so this helper only\n * sees the non-conjugating case.\n *\n * Tolerates `a.imag == NULL` (real-tensor flowing through a\n * complex-typed transpose route) by zeroing the result's imag lane.\n */\n\n#include <stdlib.h>\n#include <string.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_transpose_complex(mtoc2_tensor_t a) {\n long m = a.dims[0];\n long n = a.dims[1];\n long dims[2];\n dims[0] = n;\n dims[1] = m;\n mtoc2_tensor_t r = mtoc2_tensor_alloc_nd_complex(2, dims);\n if (a.imag == NULL) {\n /* Defensive: zero the imag lane so the transposed result is a\n * well-formed complex tensor with re-only content. */\n memset(r.imag, 0, (size_t)m * (size_t)n * sizeof(double));\n }\n for (long sc = 0; sc < n; sc++) {\n for (long sr = 0; sr < m; sr++) {\n r.real[sc + sr * n] = a.real[sr + sc * m];\n if (a.imag != NULL) {\n r.imag[sc + sr * n] = a.imag[sr + sc * m];\n }\n }\n }\n return r;\n}\n",
66754
67911
  "tensor_triangular.h": "/* mtoc2 runtime helper: `triu` / `tril` \u2014 extract upper / lower\n * triangular part of a 2-D matrix around the k-th diagonal. Mirrors\n * numbl's `triPart` in `interpreter/builtins/array-extras.ts`.\n *\n * - `mtoc2_tensor_triu(A, k)` returns a fresh `rows \xD7 cols` tensor\n * equal to `A` where `j - i >= k` (column - row), zero elsewhere.\n * `k = 0` is the main diagonal; `k > 0` selects a super-diagonal;\n * `k < 0` selects a sub-diagonal.\n * - `mtoc2_tensor_tril(A, k)` is the mirror: keep entries where\n * `i - j >= -k` (equivalently `j - i <= k`), zero elsewhere.\n * - `*_complex` siblings walk both lanes; tolerate `a.imag == NULL`\n * (real-input flowed through a complex route).\n *\n * Storage column-major to match `mtoc2_tensor_t`. Result is freshly\n * owned.\n */\n\n#include <string.h>\n#include <stdlib.h>\n\nstatic mtoc2_tensor_t mtoc2_tensor_triu(mtoc2_tensor_t a, long k) {\n long rows = a.dims[0];\n long cols = a.dims[1];\n mtoc2_tensor_t out = mtoc2_tensor_alloc(rows, cols);\n if (rows > 0 && cols > 0)\n memset(out.real, 0, (size_t)rows * (size_t)cols * sizeof(double));\n for (long j = 0; j < cols; j++) {\n for (long i = 0; i < rows; i++) {\n if (j - i >= k) {\n long idx = i + j * rows;\n out.real[idx] = a.real[idx];\n }\n }\n }\n return out;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_tril(mtoc2_tensor_t a, long k) {\n long rows = a.dims[0];\n long cols = a.dims[1];\n mtoc2_tensor_t out = mtoc2_tensor_alloc(rows, cols);\n if (rows > 0 && cols > 0)\n memset(out.real, 0, (size_t)rows * (size_t)cols * sizeof(double));\n for (long j = 0; j < cols; j++) {\n for (long i = 0; i < rows; i++) {\n if (i - j >= -k) {\n long idx = i + j * rows;\n out.real[idx] = a.real[idx];\n }\n }\n }\n return out;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_triu_complex(mtoc2_tensor_t a, long k) {\n long rows = a.dims[0];\n long cols = a.dims[1];\n long dims2[2] = {rows, cols};\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(2, dims2);\n if (rows > 0 && cols > 0) {\n memset(out.real, 0, (size_t)rows * (size_t)cols * sizeof(double));\n memset(out.imag, 0, (size_t)rows * (size_t)cols * sizeof(double));\n }\n int srcHasImag = (a.imag != NULL);\n for (long j = 0; j < cols; j++) {\n for (long i = 0; i < rows; i++) {\n if (j - i >= k) {\n long idx = i + j * rows;\n out.real[idx] = a.real[idx];\n if (srcHasImag) out.imag[idx] = a.imag[idx];\n }\n }\n }\n return out;\n}\n\nstatic mtoc2_tensor_t mtoc2_tensor_tril_complex(mtoc2_tensor_t a, long k) {\n long rows = a.dims[0];\n long cols = a.dims[1];\n long dims2[2] = {rows, cols};\n mtoc2_tensor_t out = mtoc2_tensor_alloc_nd_complex(2, dims2);\n if (rows > 0 && cols > 0) {\n memset(out.real, 0, (size_t)rows * (size_t)cols * sizeof(double));\n memset(out.imag, 0, (size_t)rows * (size_t)cols * sizeof(double));\n }\n int srcHasImag = (a.imag != NULL);\n for (long j = 0; j < cols; j++) {\n for (long i = 0; i < rows; i++) {\n if (i - j >= -k) {\n long idx = i + j * rows;\n out.real[idx] = a.real[idx];\n if (srcHasImag) out.imag[idx] = a.imag[idx];\n }\n }\n }\n return out;\n}\n",
@@ -66820,6 +67977,7 @@ var JS_SNIPPETS = {
66820
67977
  "tensor_fill_nd.js": "// JS sibling of `tensor_fill_nd.h`. Like zeros/ones but takes the\n// fill value as a leading argument \u2014 used by the `nan` / `Inf` shape-\n// constructor branches and by `repmat(scalar, ...)`. Complex variant\n// takes `(re, im)` and fills both lanes.\n\n\n\nfunction mtoc2_tensor_fill_nd(value, ndim, dims) {\n const t = mtoc2_tensor_alloc_nd(ndim, dims);\n t.data.fill(value);\n return t;\n}\n\nfunction mtoc2_tensor_fill_nd_complex(re, im, ndim, dims) {\n const t = mtoc2_tensor_alloc_nd_complex(ndim, dims);\n t.data.fill(re);\n t.imag.fill(im);\n return t;\n}\n",
66821
67978
  "tensor_fill_square.js": "// JS sibling of `tensor_fill_square.h`. Single-eval helper for\n// `nan(n)` / `Inf(n)` style square-fill constructors.\n\n\nfunction mtoc2_tensor_fill_square(value, n) {\n return mtoc2_tensor_fill_nd(value, 2, [n, n]);\n}\n",
66822
67979
  "tensor_flip.js": "// JS sibling of `tensor_flip.h`. Two helpers:\n// - `mtoc2_tensor_flip(t, dimIdx)` \u2014 real-input variant.\n// - `mtoc2_tensor_flip_complex(t, dimIdx)` \u2014 walks both lanes;\n// if `imag` is undefined the output imag stays zero.\n// Out-of-range axis acts as a deep-copy no-op in both.\n\n\n\nfunction mtoc2_tensor_flip(a, dimIdx) {\n const r = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n const axisSize = dimIdx >= 0 && dimIdx < a.shape.length ? a.shape[dimIdx] : 1;\n if (axisSize <= 1) {\n r.data.set(a.data);\n return r;\n }\n let strideDim = 1;\n for (let d = 0; d < dimIdx; d++) strideDim *= a.shape[d];\n const slabSize = strideDim * axisSize;\n const total = r.data.length;\n const numOuter = total / slabSize;\n for (let outer = 0; outer < numOuter; outer++) {\n const base = outer * slabSize;\n for (let k = 0; k < axisSize; k++) {\n const srcOff = base + k * strideDim;\n const dstOff = base + (axisSize - 1 - k) * strideDim;\n for (let i = 0; i < strideDim; i++) {\n r.data[dstOff + i] = a.data[srcOff + i];\n }\n }\n }\n return r;\n}\n\nfunction mtoc2_tensor_flip_complex(a, dimIdx) {\n const r = mtoc2_tensor_alloc_nd_complex(a.shape.length, a.shape);\n const im = a.imag;\n const axisSize = dimIdx >= 0 && dimIdx < a.shape.length ? a.shape[dimIdx] : 1;\n if (axisSize <= 1) {\n r.data.set(a.data);\n if (im !== undefined) r.imag.set(im);\n return r;\n }\n let strideDim = 1;\n for (let d = 0; d < dimIdx; d++) strideDim *= a.shape[d];\n const slabSize = strideDim * axisSize;\n const total = r.data.length;\n const numOuter = total / slabSize;\n for (let outer = 0; outer < numOuter; outer++) {\n const base = outer * slabSize;\n for (let k = 0; k < axisSize; k++) {\n const srcOff = base + k * strideDim;\n const dstOff = base + (axisSize - 1 - k) * strideDim;\n for (let i = 0; i < strideDim; i++) {\n r.data[dstOff + i] = a.data[srcOff + i];\n if (im !== undefined) r.imag[dstOff + i] = im[srcOff + i];\n }\n }\n }\n return r;\n}\n",
67980
+ "tensor_imag_all_zero.js": "// JS sibling of `tensor_imag_all_zero.h`. True when a tensor carries no\n// imaginary content (no imag lane, or every imag element exactly zero).\n// `isreal` uses this for complex-typed tensors the JIT could not prove\n// real at compile time, so it reports realness by value \u2014 matching the\n// interpreter and the complex-scalar `v.im === 0` rule.\nfunction mtoc2_tensor_imag_all_zero(a) {\n if (a.imag === undefined) return true;\n for (let i = 0; i < a.imag.length; i++) {\n if (a.imag[i] !== 0) return false;\n }\n return true;\n}\n",
66823
67981
  "tensor_linspace.js": "// JS sibling of `tensor_linspace.h`. Build a 1\xD7n row tensor of n\n// linearly-spaced values from `a` to `b`.\n\n\nfunction mtoc2_tensor_linspace(a, b, n) {\n if (n < 0) n = 0;\n const out = mtoc2_tensor_alloc(1, n);\n if (n === 0) return out;\n if (n === 1) {\n out.data[0] = b;\n return out;\n }\n out.data[0] = a;\n out.data[n - 1] = b;\n for (let i = 1; i < n - 1; i++) {\n out.data[i] = a + ((b - a) * i) / (n - 1);\n }\n if ((n & 1) === 1 && !Number.isFinite(a) && !Number.isFinite(b)) {\n const sa = Math.sign(a);\n const sb = Math.sign(b);\n if (sa !== sb) out.data[(n - 1) / 2] = 0;\n }\n return out;\n}\n",
66824
67982
  "tensor_logical_real.js": '// JS sibling of `tensor_logical_real.h`. Elementwise logical NOT on\n// real and complex tensors. Real input: `out[i] = (in[i] == 0) ? 1 : 0`.\n// Complex input: fires "true" iff both lanes are exactly zero.\n// Result is logical-tagged in both cases.\n\n\nfunction mtoc2_tensor_not(a) {\n const r = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n for (let i = 0; i < r.data.length; i++) {\n r.data[i] = a.data[i] === 0 ? 1 : 0;\n }\n // Tag as logical so `a(mask)` / `M(:, mask)` etc. take the mask\n // path in the interpreter (and js-aot, when wired). The tensor\n // alloc helpers return plain numeric tensors; we mutate the field\n // here rather than threading a parameter through every allocator.\n r.isLogical = true;\n return r;\n}\n\nfunction mtoc2_tensor_not_complex(a) {\n const r = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n const im = a.imag;\n for (let i = 0; i < r.data.length; i++) {\n const re = a.data[i];\n const v = im !== undefined ? im[i] : 0;\n r.data[i] = re === 0 && v === 0 ? 1 : 0;\n }\n r.isLogical = true;\n return r;\n}\n',
66825
67983
  "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",
@@ -66830,13 +67988,13 @@ var JS_SNIPPETS = {
66830
67988
  "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",
66831
67989
  "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",
66832
67990
  "tensor_predicate.js": '// JS sibling of `tensor_predicate.h`. Real-tensor \u2192 logical-tensor\n// predicate kernels for the js-aot backend, plus their `_complex`\n// siblings (each reads `imag[i]` when defined, treats it as 0\n// otherwise). Result carries `isLogical: true` so downstream index-\n// slot resolution treats it as a mask.\n\nfunction pred_kernel(a, fn) {\n const out = new Float64Array(a.data.length);\n for (let i = 0; i < a.data.length; i++) out[i] = fn(a.data[i]) ? 1 : 0;\n return {\n mtoc2Tag: "tensor",\n shape: a.shape.slice(),\n data: out,\n isLogical: true,\n };\n}\n\nfunction pred_kernel_complex(a, fn) {\n const out = new Float64Array(a.data.length);\n const im = a.imag;\n for (let i = 0; i < a.data.length; i++) {\n out[i] = fn(a.data[i], im !== undefined ? im[i] : 0) ? 1 : 0;\n }\n return {\n mtoc2Tag: "tensor",\n shape: a.shape.slice(),\n data: out,\n isLogical: true,\n };\n}\n\nfunction mtoc2_tensor_isnan(a) {\n return pred_kernel(a, Number.isNaN);\n}\n\nfunction mtoc2_tensor_logical(a) {\n return pred_kernel(a, x => x !== 0);\n}\n\nfunction mtoc2_tensor_isinf(a) {\n return pred_kernel(a, x => x === Infinity || x === -Infinity);\n}\n\nfunction mtoc2_tensor_isfinite(a) {\n return pred_kernel(a, Number.isFinite);\n}\n\nfunction mtoc2_tensor_isnan_complex(a) {\n return pred_kernel_complex(\n a,\n (re, im) => Number.isNaN(re) || Number.isNaN(im)\n );\n}\n\nfunction mtoc2_tensor_isinf_complex(a) {\n const isInf = x => x === Infinity || x === -Infinity;\n return pred_kernel_complex(a, (re, im) => isInf(re) || isInf(im));\n}\n\nfunction mtoc2_tensor_isfinite_complex(a) {\n return pred_kernel_complex(\n a,\n (re, im) => Number.isFinite(re) && Number.isFinite(im)\n );\n}\n',
66833
- "tensor_reduce_complex.js": '// JS sibling of `tensor_reduce_complex.h`. Complex-tensor reductions.\n// Mirrors the real reducer shape (sum/prod/mean \u2192 complex; min/max\n// \u2192 complex via magnitude+atan2 tiebreak; any/all \u2192 real).\n\n\n\nfunction cSqueezeTrailing(dims) {\n while (dims.length > 2 && dims[dims.length - 1] === 1) dims.pop();\n return dims;\n}\n\nfunction cReduceLaneIm(t, i) {\n return t.imag !== undefined ? t.imag[i] : 0;\n}\n\n// Numeric (sum/prod/mean) \u2014 complex accumulator { re, im }.\nfunction complexAccumAll(t, init, accum, finalize) {\n let acc = { ...init };\n for (let i = 0; i < t.data.length; i++) {\n acc = accum(acc, { re: t.data[i], im: cReduceLaneIm(t, i) });\n }\n return finalize(acc, t.data.length);\n}\n\nfunction complexAccumDim(t, dim, init, accum, finalize) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n // No-op axis: return a fresh complex copy.\n const out = mtoc2_tensor_alloc_nd_complex(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n if (t.imag !== undefined) out.imag.set(t.imag);\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd_complex(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let acc = { ...init };\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n acc = accum(acc, { re: t.data[off], im: cReduceLaneIm(t, off) });\n }\n const fin = finalize(acc, axis);\n const dst = aft * before + bef;\n out.data[dst] = fin.re;\n out.imag[dst] = fin.im;\n }\n }\n return out;\n}\n\nconst cSumInit = { re: 0, im: 0 };\nconst cProdInit = { re: 1, im: 0 };\nconst cSumAccum = (a, x) => ({ re: a.re + x.re, im: a.im + x.im });\nconst cProdAccum = (a, x) => ({\n re: a.re * x.re - a.im * x.im,\n im: a.re * x.im + a.im * x.re,\n});\nconst cIdFinalize = a => a;\nconst cMeanFinalize = (a, n) =>\n n === 0 ? { re: NaN, im: NaN } : { re: a.re / n, im: a.im / n };\n\nconst mtoc2_sum_complex_all = t =>\n complexAccumAll(t, cSumInit, cSumAccum, cIdFinalize);\nconst mtoc2_sum_complex_dim = (t, d) =>\n complexAccumDim(t, d, cSumInit, cSumAccum, cIdFinalize);\nconst mtoc2_prod_complex_all = t =>\n complexAccumAll(t, cProdInit, cProdAccum, cIdFinalize);\nconst mtoc2_prod_complex_dim = (t, d) =>\n complexAccumDim(t, d, cProdInit, cProdAccum, cIdFinalize);\nconst mtoc2_mean_complex_all = t =>\n complexAccumAll(t, cSumInit, cSumAccum, cMeanFinalize);\nconst mtoc2_mean_complex_dim = (t, d) =>\n complexAccumDim(t, d, cSumInit, cSumAccum, cMeanFinalize);\n\n// Min / max \u2014 magnitude compare with atan2 tiebreak (numbl\'s\n// complexIsBetter). Skip NaN-lane elements; result is complex.\nfunction complexMinmaxAll(t, cmp) {\n let found = false;\n let mRe = NaN;\n let mIm = 0;\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n if (xr !== xr || xi !== xi) continue;\n if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {\n mRe = xr;\n mIm = xi;\n found = true;\n }\n }\n return { re: mRe, im: mIm };\n}\n\nfunction complexBetter(aRe, aIm, bRe, bIm, cmp) {\n const absA = Math.hypot(aRe, aIm);\n const absB = Math.hypot(bRe, bIm);\n if (absA !== absB) return cmp === "<" ? absA < absB : absA > absB;\n return cmp === "<"\n ? Math.atan2(aIm, aRe) < Math.atan2(bIm, bRe)\n : Math.atan2(aIm, aRe) > Math.atan2(bIm, bRe);\n}\n\nfunction complexMinmaxDim(t, dim, cmp) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n const out = mtoc2_tensor_alloc_nd_complex(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n if (t.imag !== undefined) out.imag.set(t.imag);\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd_complex(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let found = false;\n let mRe = NaN;\n let mIm = 0;\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n const xr = t.data[off];\n const xi = cReduceLaneIm(t, off);\n if (xr !== xr || xi !== xi) continue;\n if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {\n mRe = xr;\n mIm = xi;\n found = true;\n }\n }\n const dst = aft * before + bef;\n out.data[dst] = mRe;\n out.imag[dst] = mIm;\n }\n }\n return out;\n}\n\nconst mtoc2_min_complex_all = t => complexMinmaxAll(t, "<");\nconst mtoc2_min_complex_dim = (t, d) => complexMinmaxDim(t, d, "<");\nconst mtoc2_max_complex_all = t => complexMinmaxAll(t, ">");\nconst mtoc2_max_complex_dim = (t, d) => complexMinmaxDim(t, d, ">");\n\n// any / all \u2014 real result; toBool per element (either lane nonzero).\nfunction complexLogicalAll(t, emptyResult, shortPredicate) {\n if (t.data.length === 0) return emptyResult;\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n const x = xr !== 0 || xi !== 0;\n if (shortPredicate(x)) return emptyResult === 1 ? 0 : 1;\n }\n return emptyResult;\n}\n\nfunction complexLogicalDim(t, dim, emptyResult, shortPredicate) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n const out = mtoc2_tensor_alloc_nd(t.shape.length, t.shape.slice());\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n out.data[i] = xr !== 0 || xi !== 0 ? 1 : 0;\n }\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let res = emptyResult;\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n const x = t.data[off] !== 0 || cReduceLaneIm(t, off) !== 0;\n if (shortPredicate(x)) {\n res = emptyResult === 1 ? 0 : 1;\n break;\n }\n }\n out.data[aft * before + bef] = res;\n }\n }\n return out;\n}\n\nconst cAnyShort = x => x;\nconst cAllShort = x => !x;\nconst mtoc2_any_complex_all = t => complexLogicalAll(t, 0, cAnyShort);\nconst mtoc2_any_complex_dim = (t, d) =>\n complexLogicalDim(t, d, 0, cAnyShort);\nconst mtoc2_all_complex_all = t => complexLogicalAll(t, 1, cAllShort);\nconst mtoc2_all_complex_dim = (t, d) =>\n complexLogicalDim(t, d, 1, cAllShort);\n',
67991
+ "tensor_reduce_complex.js": '// JS sibling of `tensor_reduce_complex.h`. Complex-tensor reductions.\n// Mirrors the real reducer shape (sum/prod/mean \u2192 complex; min/max\n// \u2192 complex via magnitude+atan2 tiebreak; any/all \u2192 real).\n\n\n\nfunction cSqueezeTrailing(dims) {\n while (dims.length > 2 && dims[dims.length - 1] === 1) dims.pop();\n return dims;\n}\n\nfunction cReduceLaneIm(t, i) {\n return t.imag !== undefined ? t.imag[i] : 0;\n}\n\n// True when the tensor carries no imaginary content (no lane, or every\n// element zero). Such a tensor is real in value \u2014 min/max must order by\n// value, not magnitude, to match the interpreter and MATLAB on real data.\nfunction cReduceAllImagZero(t) {\n if (t.imag === undefined) return true;\n for (let i = 0; i < t.imag.length; i++) {\n if (t.imag[i] !== 0) return false;\n }\n return true;\n}\n\n// Numeric (sum/prod/mean) \u2014 complex accumulator { re, im }.\nfunction complexAccumAll(t, init, accum, finalize) {\n let acc = { ...init };\n for (let i = 0; i < t.data.length; i++) {\n acc = accum(acc, { re: t.data[i], im: cReduceLaneIm(t, i) });\n }\n return finalize(acc, t.data.length);\n}\n\nfunction complexAccumDim(t, dim, init, accum, finalize) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n // No-op axis: return a fresh complex copy.\n const out = mtoc2_tensor_alloc_nd_complex(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n if (t.imag !== undefined) out.imag.set(t.imag);\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd_complex(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let acc = { ...init };\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n acc = accum(acc, { re: t.data[off], im: cReduceLaneIm(t, off) });\n }\n const fin = finalize(acc, axis);\n const dst = aft * before + bef;\n out.data[dst] = fin.re;\n out.imag[dst] = fin.im;\n }\n }\n return out;\n}\n\nconst cSumInit = { re: 0, im: 0 };\nconst cProdInit = { re: 1, im: 0 };\nconst cSumAccum = (a, x) => ({ re: a.re + x.re, im: a.im + x.im });\nconst cProdAccum = (a, x) => ({\n re: a.re * x.re - a.im * x.im,\n im: a.re * x.im + a.im * x.re,\n});\nconst cIdFinalize = a => a;\nconst cMeanFinalize = (a, n) =>\n n === 0 ? { re: NaN, im: NaN } : { re: a.re / n, im: a.im / n };\n\nconst mtoc2_sum_complex_all = t =>\n complexAccumAll(t, cSumInit, cSumAccum, cIdFinalize);\nconst mtoc2_sum_complex_dim = (t, d) =>\n complexAccumDim(t, d, cSumInit, cSumAccum, cIdFinalize);\nconst mtoc2_prod_complex_all = t =>\n complexAccumAll(t, cProdInit, cProdAccum, cIdFinalize);\nconst mtoc2_prod_complex_dim = (t, d) =>\n complexAccumDim(t, d, cProdInit, cProdAccum, cIdFinalize);\nconst mtoc2_mean_complex_all = t =>\n complexAccumAll(t, cSumInit, cSumAccum, cMeanFinalize);\nconst mtoc2_mean_complex_dim = (t, d) =>\n complexAccumDim(t, d, cSumInit, cSumAccum, cMeanFinalize);\n\n// Min / max \u2014 magnitude compare with atan2 tiebreak (numbl\'s\n// complexIsBetter). Skip NaN-lane elements; result is complex.\nfunction complexMinmaxAll(t, cmp) {\n const realMode = cReduceAllImagZero(t);\n let found = false;\n let mRe = NaN;\n let mIm = 0;\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n if (xr !== xr || xi !== xi) continue;\n const better = realMode\n ? cmp === "<"\n ? xr < mRe\n : xr > mRe\n : complexBetter(xr, xi, mRe, mIm, cmp);\n if (!found || better) {\n mRe = xr;\n mIm = xi;\n found = true;\n }\n }\n return { re: mRe, im: mIm };\n}\n\nfunction complexBetter(aRe, aIm, bRe, bIm, cmp) {\n const absA = Math.hypot(aRe, aIm);\n const absB = Math.hypot(bRe, bIm);\n if (absA !== absB) return cmp === "<" ? absA < absB : absA > absB;\n return cmp === "<"\n ? Math.atan2(aIm, aRe) < Math.atan2(bIm, bRe)\n : Math.atan2(aIm, aRe) > Math.atan2(bIm, bRe);\n}\n\nfunction complexMinmaxDim(t, dim, cmp) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n const out = mtoc2_tensor_alloc_nd_complex(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n if (t.imag !== undefined) out.imag.set(t.imag);\n return out;\n }\n const dimIdx = dim - 1;\n const realMode = cReduceAllImagZero(t);\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd_complex(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let found = false;\n let mRe = NaN;\n let mIm = 0;\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n const xr = t.data[off];\n const xi = cReduceLaneIm(t, off);\n if (xr !== xr || xi !== xi) continue;\n const better = realMode\n ? cmp === "<"\n ? xr < mRe\n : xr > mRe\n : complexBetter(xr, xi, mRe, mIm, cmp);\n if (!found || better) {\n mRe = xr;\n mIm = xi;\n found = true;\n }\n }\n const dst = aft * before + bef;\n out.data[dst] = mRe;\n out.imag[dst] = mIm;\n }\n }\n return out;\n}\n\nconst mtoc2_min_complex_all = t => complexMinmaxAll(t, "<");\nconst mtoc2_min_complex_dim = (t, d) => complexMinmaxDim(t, d, "<");\nconst mtoc2_max_complex_all = t => complexMinmaxAll(t, ">");\nconst mtoc2_max_complex_dim = (t, d) => complexMinmaxDim(t, d, ">");\n\n// any / all \u2014 real result; toBool per element (either lane nonzero).\nfunction complexLogicalAll(t, emptyResult, shortPredicate) {\n if (t.data.length === 0) return emptyResult;\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n const x = xr !== 0 || xi !== 0;\n if (shortPredicate(x)) return emptyResult === 1 ? 0 : 1;\n }\n return emptyResult;\n}\n\nfunction complexLogicalDim(t, dim, emptyResult, shortPredicate) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n const out = mtoc2_tensor_alloc_nd(t.shape.length, t.shape.slice());\n for (let i = 0; i < t.data.length; i++) {\n const xr = t.data[i];\n const xi = cReduceLaneIm(t, i);\n out.data[i] = xr !== 0 || xi !== 0 ? 1 : 0;\n }\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n cSqueezeTrailing(outDims);\n const out = mtoc2_tensor_alloc_nd(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let res = emptyResult;\n for (let k = 0; k < axis; k++) {\n const off = base + k * before;\n const x = t.data[off] !== 0 || cReduceLaneIm(t, off) !== 0;\n if (shortPredicate(x)) {\n res = emptyResult === 1 ? 0 : 1;\n break;\n }\n }\n out.data[aft * before + bef] = res;\n }\n }\n return out;\n}\n\nconst cAnyShort = x => x;\nconst cAllShort = x => !x;\nconst mtoc2_any_complex_all = t => complexLogicalAll(t, 0, cAnyShort);\nconst mtoc2_any_complex_dim = (t, d) =>\n complexLogicalDim(t, d, 0, cAnyShort);\nconst mtoc2_all_complex_all = t => complexLogicalAll(t, 1, cAllShort);\nconst mtoc2_all_complex_dim = (t, d) =>\n complexLogicalDim(t, d, 1, cAllShort);\n',
66834
67992
  "tensor_reduce_real.js": '// JS sibling of `tensor_reduce_real.h`. Real-tensor reductions \u2014\n// `_all` returns a scalar; `_dim` returns a freshly-allocated tensor\n// reduced along the 1-based `dim` axis. Mirrors numbl\'s\n// `forEachSlice` semantics with column-major (before \xD7 axis \xD7 after)\n// traversal.\n//\n// Output shape rule for `_dim`: input dims with `dims[dim-1] = 1`,\n// then trailing singletons stripped subject to a 2-axis floor.\n\n\nfunction squeeze_trailing(dims) {\n while (dims.length > 2 && dims[dims.length - 1] === 1) dims.pop();\n return dims;\n}\n\n// Accumulator-based reducer (`sum`, `prod`, `mean`). `init` seeds\n// the running value; `accum(a, x)` is the per-element step;\n// `finalize(a, n)` is the post-loop transform.\nfunction accum_all(t, init, accum, finalize) {\n let acc = init;\n for (let i = 0; i < t.data.length; i++) acc = accum(acc, t.data[i]);\n return finalize(acc, t.data.length);\n}\n\nfunction accum_dim(t, dim, init, accum, finalize) {\n if (dim < 1) {\n throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n }\n if (dim > t.shape.length) {\n // Reducing along a trailing singleton axis is the identity: every\n // fiber has length 1, so sum/prod/mean each yield that single\n // element unchanged. Return a same-shape COPY of the data (the C\n // kernel memcpy\'s here too); allocating without copying left a\n // zero-filled tensor \u2014 the original opt1 bug.\n const out = mtoc2_tensor_alloc_nd(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = squeeze_trailing(t.shape.slice());\n outDims[dimIdx] = 1;\n // Re-squeeze after the in-place axis update (the original `out_dims\n // = a.shape.slice(); out_dims[dimIdx] = 1` then squeeze pattern).\n squeeze_trailing(outDims);\n const out = mtoc2_tensor_alloc_nd(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let acc = init;\n for (let k = 0; k < axis; k++) {\n acc = accum(acc, t.data[base + k * before]);\n }\n out.data[aft * before + bef] = finalize(acc, axis);\n }\n }\n return out;\n}\n\n// Min/max reducer. Ignores NaN like numbl/MATLAB: NaN entries are\n// skipped, and the result is NaN only when every element is NaN.\n// Mirrors the interpreter\'s `minMaxScan` (helpers/reduction/min-max.ts)\n// and the C kernel \u2014 seeding with data[0] would let a *leading* NaN\n// poison the result (`x < NaN` / `x > NaN` are always false).\nfunction minmax_all(t, op /* "min" | "max" */) {\n if (t.data.length === 0) return op === "min" ? Infinity : -Infinity;\n let best = NaN;\n let found = false;\n for (let i = 0; i < t.data.length; i++) {\n const x = t.data[i];\n if (x !== x) continue; // skip NaN\n if (!found || (op === "min" ? x < best : x > best)) {\n best = x;\n found = true;\n }\n }\n return found ? best : NaN;\n}\n\nfunction minmax_dim(t, dim, op) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n const out = mtoc2_tensor_alloc_nd(t.shape.length, t.shape.slice());\n out.data.set(t.data);\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n squeeze_trailing(outDims);\n const out = mtoc2_tensor_alloc_nd(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let best = NaN;\n let found = false;\n for (let k = 0; k < axis; k++) {\n const x = t.data[base + k * before];\n if (x !== x) continue; // skip NaN\n if (!found || (op === "min" ? x < best : x > best)) {\n best = x;\n found = true;\n }\n }\n out.data[aft * before + bef] = found ? best : NaN;\n }\n }\n return out;\n}\n\n// Logical reducer (`any`, `all`). `emptyResult` is the value for a\n// 0-element reduction; `short` is the early-exit predicate.\nfunction logical_all(t, emptyResult, shortPredicate) {\n if (t.data.length === 0) return emptyResult;\n for (let i = 0; i < t.data.length; i++) {\n if (shortPredicate(t.data[i])) return emptyResult === 1 ? 0 : 1;\n }\n return emptyResult;\n}\n\nfunction logical_dim(t, dim, emptyResult, shortPredicate) {\n if (dim < 1) throw new Error(`reducer _dim: dim must be >= 1 (got ${dim})`);\n if (dim > t.shape.length) {\n // No-op axis: emit a logical cast of the input (each element \u2192\n // 1 if nonzero, 0 otherwise).\n const out = mtoc2_tensor_alloc_nd(t.shape.length, t.shape.slice());\n for (let i = 0; i < t.data.length; i++) {\n out.data[i] = t.data[i] !== 0 ? 1 : 0;\n }\n return out;\n }\n const dimIdx = dim - 1;\n const axis = t.shape[dimIdx];\n let before = 1;\n for (let i = 0; i < dimIdx; i++) before *= t.shape[i];\n let after = 1;\n for (let i = dimIdx + 1; i < t.shape.length; i++) after *= t.shape[i];\n const outDims = t.shape.slice();\n outDims[dimIdx] = 1;\n squeeze_trailing(outDims);\n const out = mtoc2_tensor_alloc_nd(outDims.length, outDims);\n for (let aft = 0; aft < after; aft++) {\n for (let bef = 0; bef < before; bef++) {\n const base = aft * before * axis + bef;\n let res = emptyResult;\n for (let k = 0; k < axis; k++) {\n if (shortPredicate(t.data[base + k * before])) {\n res = emptyResult === 1 ? 0 : 1;\n break;\n }\n }\n out.data[aft * before + bef] = res;\n }\n }\n return out;\n}\n\n// \u2500\u2500 Sum \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst sumInit = 0;\nconst sumAccum = (a, x) => a + x;\nconst idFinalize = a => a;\nconst mtoc2_sum_all = t => accum_all(t, sumInit, sumAccum, idFinalize);\nconst mtoc2_sum_dim = (t, d) =>\n accum_dim(t, d, sumInit, sumAccum, idFinalize);\n\n// \u2500\u2500 Prod \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst prodInit = 1;\nconst prodAccum = (a, x) => a * x;\nconst mtoc2_prod_all = t =>\n accum_all(t, prodInit, prodAccum, idFinalize);\nconst mtoc2_prod_dim = (t, d) =>\n accum_dim(t, d, prodInit, prodAccum, idFinalize);\n\n// \u2500\u2500 Mean \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst meanFinalize = (a, n) => (n === 0 ? NaN : a / n);\nconst mtoc2_mean_all = t =>\n accum_all(t, sumInit, sumAccum, meanFinalize);\nconst mtoc2_mean_dim = (t, d) =>\n accum_dim(t, d, sumInit, sumAccum, meanFinalize);\n\n// \u2500\u2500 Min / max \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst mtoc2_min_all = t => minmax_all(t, "min");\nconst mtoc2_min_dim = (t, d) => minmax_dim(t, d, "min");\nconst mtoc2_max_all = t => minmax_all(t, "max");\nconst mtoc2_max_dim = (t, d) => minmax_dim(t, d, "max");\n\n// \u2500\u2500 Any / all \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// any: short-circuits on a non-NaN nonzero; emptyResult = 0. NaN is\n// ignored (MATLAB: any(NaN) is 0, any([0 NaN]) is 0), so `x === x`\n// excludes it \u2014 without that, NaN wrongly short-circuited to true.\n// (`all` needs no such guard: allShort tests `x === 0`, which NaN\n// already fails, so a NaN simply doesn\'t force all to false.)\nconst anyShort = x => x !== 0 && x === x;\nconst mtoc2_any_all = t => logical_all(t, 0, anyShort);\nconst mtoc2_any_dim = (t, d) => logical_dim(t, d, 0, anyShort);\n// all: short-circuits on zero; emptyResult = 1.\nconst allShort = x => x === 0;\nconst mtoc2_all_all = t => logical_all(t, 1, allShort);\nconst mtoc2_all_dim = (t, d) => logical_dim(t, d, 1, allShort);\n',
66835
67993
  "tensor_repmat.js": "// JS sibling of `tensor_repmat.h`. Two helpers:\n// - `mtoc2_tensor_repmat(in, nreps, reps)` \u2014 tile a real tensor.\n// - `mtoc2_tensor_repmat_complex(in, nreps, reps)` \u2014 tile both\n// lanes of a complex tensor (zero imag when input is real).\n// Negative reps clamp to 0; input shape and reps are right-padded\n// with 1s to a common rank.\n\n\n\nfunction mtoc2_tensor_repmat(input, nreps, repsIn) {\n const reps = [];\n for (let i = 0; i < nreps; i++) {\n const r = repsIn[i] < 0 ? 0 : repsIn[i];\n reps.push(r);\n }\n const inShape = input.shape;\n const inNdim = inShape.length;\n const outNdim = Math.max(nreps, inNdim);\n const padShape = [];\n const padReps = [];\n const outDims = [];\n for (let i = 0; i < outNdim; i++) {\n padShape.push(i < inNdim ? inShape[i] : 1);\n padReps.push(i < nreps ? reps[i] : 1);\n outDims.push(padShape[i] * padReps[i]);\n }\n const out = mtoc2_tensor_alloc_nd(outNdim, outDims);\n let outTotal = 1;\n for (const d of outDims) outTotal *= d;\n if (outTotal === 0) return out;\n let inTotal = 1;\n for (const d of inShape) inTotal *= d;\n if (inTotal === 0) return out;\n\n // Initial copy: trailing-1 padding doesn't change column-major layout.\n out.data.set(input.data.subarray(0, inTotal), 0);\n\n const curShape = padShape.slice();\n let curTotal = inTotal;\n\n for (let d = 0; d < outNdim; d++) {\n const rep = padReps[d];\n if (rep === 1) continue;\n let blockSize = 1;\n for (let i = 0; i <= d; i++) blockSize *= curShape[i];\n if (rep === 0 || blockSize === 0) {\n // outTotal will be 0; the alloc above already produced an empty\n // tensor. Done.\n return out;\n }\n const numBlocks = curTotal / blockSize;\n // Walk blocks in reverse to avoid overwriting source data.\n for (let b = numBlocks - 1; b >= 0; b--) {\n const srcOff = b * blockSize;\n const dstBase = b * blockSize * rep;\n if (dstBase !== srcOff) {\n // copyWithin handles overlapping moves correctly.\n out.data.copyWithin(dstBase, srcOff, srcOff + blockSize);\n }\n for (let r = 1; r < rep; r++) {\n out.data.copyWithin(\n dstBase + r * blockSize,\n dstBase,\n dstBase + blockSize\n );\n }\n }\n curShape[d] *= rep;\n curTotal *= rep;\n }\n return out;\n}\n\nfunction mtoc2_tensor_repmat_complex(input, nreps, repsIn) {\n const reps = [];\n for (let i = 0; i < nreps; i++) {\n const r = repsIn[i] < 0 ? 0 : repsIn[i];\n reps.push(r);\n }\n const inShape = input.shape;\n const inNdim = inShape.length;\n const outNdim = Math.max(nreps, inNdim);\n const padShape = [];\n const padReps = [];\n const outDims = [];\n for (let i = 0; i < outNdim; i++) {\n padShape.push(i < inNdim ? inShape[i] : 1);\n padReps.push(i < nreps ? reps[i] : 1);\n outDims.push(padShape[i] * padReps[i]);\n }\n const out = mtoc2_tensor_alloc_nd_complex(outNdim, outDims);\n let outTotal = 1;\n for (const d of outDims) outTotal *= d;\n if (outTotal === 0) return out;\n let inTotal = 1;\n for (const d of inShape) inTotal *= d;\n if (inTotal === 0) return out;\n\n const im = input.imag;\n out.data.set(input.data.subarray(0, inTotal), 0);\n if (im !== undefined) out.imag.set(im.subarray(0, inTotal), 0);\n\n const curShape = padShape.slice();\n let curTotal = inTotal;\n\n for (let d = 0; d < outNdim; d++) {\n const rep = padReps[d];\n if (rep === 1) continue;\n let blockSize = 1;\n for (let i = 0; i <= d; i++) blockSize *= curShape[i];\n if (rep === 0 || blockSize === 0) return out;\n const numBlocks = curTotal / blockSize;\n for (let b = numBlocks - 1; b >= 0; b--) {\n const srcOff = b * blockSize;\n const dstBase = b * blockSize * rep;\n if (dstBase !== srcOff) {\n out.data.copyWithin(dstBase, srcOff, srcOff + blockSize);\n out.imag.copyWithin(dstBase, srcOff, srcOff + blockSize);\n }\n for (let r = 1; r < rep; r++) {\n out.data.copyWithin(\n dstBase + r * blockSize,\n dstBase,\n dstBase + blockSize\n );\n out.imag.copyWithin(\n dstBase + r * blockSize,\n dstBase,\n dstBase + blockSize\n );\n }\n }\n curShape[d] *= rep;\n curTotal *= rep;\n }\n return out;\n}\n",
66836
67994
  "tensor_reshape_nd.js": "// JS sibling of `tensor_reshape_nd.h`. Reshape a real tensor to an\n// N-D shape, supporting one `-1` auto-infer slot. Same error\n// behaviour as the C side: throws (instead of `abort()`) on bad\n// inputs.\n\n\nfunction mtoc2_reshape_nd(input, ndim, dims) {\n let inTotal = 1;\n for (const d of input.shape) inTotal *= d;\n let inferIdx = -1;\n let explicitProd = 1;\n for (let i = 0; i < ndim; i++) {\n if (dims[i] === -1) {\n if (inferIdx !== -1) {\n throw new Error(\"reshape: at most one '[]' auto-infer slot allowed\");\n }\n inferIdx = i;\n } else if (dims[i] < 0) {\n throw new Error(\n `reshape: dim ${i + 1} must be a non-negative integer (got ${dims[i]})`\n );\n } else {\n explicitProd *= dims[i];\n }\n }\n const resolved = new Array(ndim);\n for (let i = 0; i < ndim; i++) resolved[i] = dims[i];\n let outTotal;\n if (inferIdx !== -1) {\n if (explicitProd === 0 && inTotal !== 0) {\n throw new Error(\n `reshape: input has ${inTotal} elements but explicit dims around '[]' multiply to 0`\n );\n }\n if (explicitProd > 0 && inTotal % explicitProd !== 0) {\n throw new Error(\n `reshape: input has ${inTotal} elements, not divisible by ${explicitProd}`\n );\n }\n resolved[inferIdx] = explicitProd === 0 ? 0 : inTotal / explicitProd;\n outTotal = inTotal;\n } else {\n outTotal = explicitProd;\n if (inTotal !== outTotal) {\n throw new Error(\n `reshape: number of elements must not change (in=${inTotal}, out=${outTotal})`\n );\n }\n }\n const out = mtoc2_tensor_alloc_nd(ndim, resolved);\n if (outTotal > 0) out.data.set(input.data.subarray(0, outTotal));\n return out;\n}\n",
66837
67995
  "tensor_reshape_nd_complex.js": "// JS sibling of `tensor_reshape_nd_complex.h`. Reshape a complex\n// tensor to an N-D shape. Same auto-infer / element-count rules as\n// the real reshape; both lanes are reinterpreted (no rearrangement).\n\n\nfunction mtoc2_reshape_nd_complex(input, ndim, dims) {\n let inTotal = 1;\n for (const d of input.shape) inTotal *= d;\n let inferIdx = -1;\n let explicitProd = 1;\n for (let i = 0; i < ndim; i++) {\n if (dims[i] === -1) {\n if (inferIdx !== -1) {\n throw new Error(\"reshape: at most one '[]' auto-infer slot allowed\");\n }\n inferIdx = i;\n } else if (dims[i] < 0) {\n throw new Error(\n `reshape: dim ${i + 1} must be a non-negative integer (got ${dims[i]})`\n );\n } else {\n explicitProd *= dims[i];\n }\n }\n const resolved = new Array(ndim);\n for (let i = 0; i < ndim; i++) resolved[i] = dims[i];\n let outTotal;\n if (inferIdx !== -1) {\n if (explicitProd === 0 && inTotal !== 0) {\n throw new Error(\n `reshape: input has ${inTotal} elements but explicit dims around '[]' multiply to 0`\n );\n }\n if (explicitProd > 0 && inTotal % explicitProd !== 0) {\n throw new Error(\n `reshape: input has ${inTotal} elements, not divisible by ${explicitProd}`\n );\n }\n resolved[inferIdx] = explicitProd === 0 ? 0 : inTotal / explicitProd;\n outTotal = inTotal;\n } else {\n outTotal = explicitProd;\n if (inTotal !== outTotal) {\n throw new Error(\n `reshape: number of elements must not change (in=${inTotal}, out=${outTotal})`\n );\n }\n }\n const out = mtoc2_tensor_alloc_nd_complex(ndim, resolved);\n if (outTotal > 0) {\n out.data.set(input.data.subarray(0, outTotal));\n if (input.imag !== undefined) {\n out.imag.set(input.imag.subarray(0, outTotal));\n }\n }\n return out;\n}\n",
66838
67996
  "tensor_size.js": "// JS sibling of `tensor_size.h`. Build a 1\xD7ndim row tensor whose\n// elements are the input's dim sizes.\n\n\nfunction mtoc2_tensor_size_row(a) {\n const n = a.shape.length;\n const r = mtoc2_tensor_alloc(1, n);\n for (let i = 0; i < n; i++) r.data[i] = a.shape[i];\n return r;\n}\n",
66839
- "tensor_sort_real.js": "// JS sibling of `tensor_sort_real.h`. Stable sort on real and\n// complex tensors. The descending flag flips the comparator while\n// keeping the tie-break on ascending original index. Complex sort\n// orders by magnitude then phase (matches numbl).\n\n\n\nfunction pair_sort_indices(a, descending) {\n const n = a.data.length;\n const idx = new Array(n);\n for (let i = 0; i < n; i++) idx[i] = i;\n idx.sort((p, q) => {\n const av = a.data[p];\n const bv = a.data[q];\n // NaN ranks as the maximum (MATLAB): last when ascending, first\n // when descending. Without this, NaN compares false both ways and\n // falls through to the index tie-break, making the comparator\n // non-transitive and corrupting the whole array.\n const an = av !== av;\n const bn = bv !== bv;\n if (an || bn) {\n if (an && bn) return p - q;\n if (an) return descending ? -1 : 1;\n return descending ? 1 : -1;\n }\n if (av < bv) return descending ? 1 : -1;\n if (av > bv) return descending ? -1 : 1;\n return p - q;\n });\n return idx;\n}\n\nfunction complex_sort_indices(a, descending) {\n const n = a.data.length;\n const im = a.imag;\n const idx = new Array(n);\n const mag = new Float64Array(n);\n const ph = new Float64Array(n);\n for (let i = 0; i < n; i++) {\n const re = a.data[i];\n const xi = im !== undefined ? im[i] : 0;\n mag[i] = Math.hypot(re, xi);\n ph[i] = Math.atan2(xi, re);\n idx[i] = i;\n }\n idx.sort((p, q) => {\n if (mag[p] < mag[q]) return descending ? 1 : -1;\n if (mag[p] > mag[q]) return descending ? -1 : 1;\n if (ph[p] < ph[q]) return descending ? 1 : -1;\n if (ph[p] > ph[q]) return descending ? -1 : 1;\n return p - q;\n });\n return idx;\n}\n\nfunction mtoc2_sort_real(a, descending) {\n const v = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return v;\n const sorted = pair_sort_indices(a, descending);\n for (let i = 0; i < sorted.length; i++) v.data[i] = a.data[sorted[i]];\n return v;\n}\n\nfunction mtoc2_sort_real_2(a, descending) {\n const v = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n const ix = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return { v, ix };\n const sorted = pair_sort_indices(a, descending);\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n ix.data[i] = sorted[i] + 1;\n }\n return { v, ix };\n}\n\nfunction mtoc2_sort_complex(a, descending) {\n const v = mtoc2_tensor_alloc_nd_complex(a.shape.length, a.shape);\n if (a.data.length === 0) return v;\n const sorted = complex_sort_indices(a, descending);\n const im = a.imag;\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n if (im !== undefined) v.imag[i] = im[sorted[i]];\n }\n return v;\n}\n\nfunction mtoc2_sort_complex_2(a, descending) {\n const v = mtoc2_tensor_alloc_nd_complex(a.shape.length, a.shape);\n const ix = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return { v, ix };\n const sorted = complex_sort_indices(a, descending);\n const im = a.imag;\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n if (im !== undefined) v.imag[i] = im[sorted[i]];\n ix.data[i] = sorted[i] + 1;\n }\n return { v, ix };\n}\n",
67997
+ "tensor_sort_real.js": "// JS sibling of `tensor_sort_real.h`. Stable sort on real and\n// complex tensors. The descending flag flips the comparator while\n// keeping the tie-break on ascending original index. Complex sort\n// orders by magnitude then phase (matches numbl).\n\n\n\nfunction pair_sort_indices(a, descending) {\n const n = a.data.length;\n const idx = new Array(n);\n for (let i = 0; i < n; i++) idx[i] = i;\n idx.sort((p, q) => {\n const av = a.data[p];\n const bv = a.data[q];\n // NaN ranks as the maximum (MATLAB): last when ascending, first\n // when descending. Without this, NaN compares false both ways and\n // falls through to the index tie-break, making the comparator\n // non-transitive and corrupting the whole array.\n const an = av !== av;\n const bn = bv !== bv;\n if (an || bn) {\n if (an && bn) return p - q;\n if (an) return descending ? -1 : 1;\n return descending ? 1 : -1;\n }\n if (av < bv) return descending ? 1 : -1;\n if (av > bv) return descending ? -1 : 1;\n return p - q;\n });\n return idx;\n}\n\nfunction complex_sort_indices(a, descending) {\n const n = a.data.length;\n const im = a.imag;\n const idx = new Array(n);\n for (let i = 0; i < n; i++) idx[i] = i;\n\n // All-zero imaginary lane \u2192 order by signed real value (matches the\n // interpreter and MATLAB on real data), not by magnitude. NaNs sort\n // last when ascending, first when descending.\n let realMode = true;\n if (im !== undefined) {\n for (let i = 0; i < n; i++) {\n if (im[i] !== 0) {\n realMode = false;\n break;\n }\n }\n }\n if (realMode) {\n const re = a.data;\n idx.sort((p, q) => {\n const rp = re[p];\n const rq = re[q];\n const pNaN = rp !== rp;\n const qNaN = rq !== rq;\n if (pNaN && qNaN) return 0;\n if (descending) {\n if (pNaN) return -1;\n if (qNaN) return 1;\n return rp < rq ? 1 : rp > rq ? -1 : 0;\n }\n if (pNaN) return 1;\n if (qNaN) return -1;\n return rp < rq ? -1 : rp > rq ? 1 : 0;\n });\n return idx;\n }\n\n const mag = new Float64Array(n);\n const ph = new Float64Array(n);\n for (let i = 0; i < n; i++) {\n const re = a.data[i];\n const xi = im !== undefined ? im[i] : 0;\n mag[i] = Math.hypot(re, xi);\n ph[i] = Math.atan2(xi, re);\n }\n idx.sort((p, q) => {\n if (mag[p] < mag[q]) return descending ? 1 : -1;\n if (mag[p] > mag[q]) return descending ? -1 : 1;\n if (ph[p] < ph[q]) return descending ? 1 : -1;\n if (ph[p] > ph[q]) return descending ? -1 : 1;\n return p - q;\n });\n return idx;\n}\n\nfunction mtoc2_sort_real(a, descending) {\n const v = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return v;\n const sorted = pair_sort_indices(a, descending);\n for (let i = 0; i < sorted.length; i++) v.data[i] = a.data[sorted[i]];\n return v;\n}\n\nfunction mtoc2_sort_real_2(a, descending) {\n const v = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n const ix = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return { v, ix };\n const sorted = pair_sort_indices(a, descending);\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n ix.data[i] = sorted[i] + 1;\n }\n return { v, ix };\n}\n\nfunction mtoc2_sort_complex(a, descending) {\n const v = mtoc2_tensor_alloc_nd_complex(a.shape.length, a.shape);\n if (a.data.length === 0) return v;\n const sorted = complex_sort_indices(a, descending);\n const im = a.imag;\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n if (im !== undefined) v.imag[i] = im[sorted[i]];\n }\n return v;\n}\n\nfunction mtoc2_sort_complex_2(a, descending) {\n const v = mtoc2_tensor_alloc_nd_complex(a.shape.length, a.shape);\n const ix = mtoc2_tensor_alloc_nd(a.shape.length, a.shape);\n if (a.data.length === 0) return { v, ix };\n const sorted = complex_sort_indices(a, descending);\n const im = a.imag;\n for (let i = 0; i < sorted.length; i++) {\n v.data[i] = a.data[sorted[i]];\n if (im !== undefined) v.imag[i] = im[sorted[i]];\n ix.data[i] = sorted[i] + 1;\n }\n return { v, ix };\n}\n",
66840
67998
  "tensor_transpose.js": "// JS sibling of `tensor_transpose.h`. Real 2-D non-conjugate\n// transpose. Mirrors `transposeCore` semantics: column-major in,\n// column-major out.\n\n\nfunction mtoc2_tensor_transpose(a) {\n const m = a.shape[0];\n const n = a.shape[1];\n const r = mtoc2_tensor_alloc(n, m);\n for (let sc = 0; sc < n; sc++) {\n for (let sr = 0; sr < m; sr++) {\n r.data[sc + sr * n] = a.data[sr + sc * m];\n }\n }\n return r;\n}\n",
66841
67999
  "tensor_transpose_complex.js": "// JS sibling of `tensor_transpose_complex.h`. Real 2-D non-conjugate\n// transpose for a complex tensor \u2014 both lanes get the same index\n// permutation. `'` (conjugate transpose) lowers to\n// `transpose(conj(z))` upstream, so this helper isn't responsible\n// for negating the imag lane.\n\n\nfunction mtoc2_tensor_transpose_complex(a) {\n const m = a.shape[0];\n const n = a.shape[1];\n const r = mtoc2_tensor_alloc_complex(n, m);\n const aim = a.imag;\n for (let sc = 0; sc < n; sc++) {\n for (let sr = 0; sr < m; sr++) {\n r.data[sc + sr * n] = a.data[sr + sc * m];\n r.imag[sc + sr * n] = aim !== undefined ? aim[sr + sc * m] : 0;\n }\n }\n return r;\n}\n",
66842
68000
  "tensor_triangular.js": "// JS sibling of `tensor_triangular.h`. Four helpers: `triu` / `tril`\n// keep entries where `j - i >= k` / `i - j >= -k`; their `_complex`\n// siblings walk both lanes. Mirrors `triPart` in numbl's\n// `interpreter/builtins/array-extras.ts`.\n\n\n\nfunction mtoc2_tensor_triu(a, k) {\n const rows = a.shape[0];\n const cols = a.shape[1];\n const out = mtoc2_tensor_alloc(rows, cols);\n for (let j = 0; j < cols; j++) {\n for (let i = 0; i < rows; i++) {\n if (j - i >= k) {\n const idx = i + j * rows;\n out.data[idx] = a.data[idx];\n }\n }\n }\n return out;\n}\n\nfunction mtoc2_tensor_tril(a, k) {\n const rows = a.shape[0];\n const cols = a.shape[1];\n const out = mtoc2_tensor_alloc(rows, cols);\n for (let j = 0; j < cols; j++) {\n for (let i = 0; i < rows; i++) {\n if (i - j >= -k) {\n const idx = i + j * rows;\n out.data[idx] = a.data[idx];\n }\n }\n }\n return out;\n}\n\nfunction mtoc2_tensor_triu_complex(a, k) {\n const rows = a.shape[0];\n const cols = a.shape[1];\n const out = mtoc2_tensor_alloc_nd_complex(2, [rows, cols]);\n const im = a.imag;\n for (let j = 0; j < cols; j++) {\n for (let i = 0; i < rows; i++) {\n if (j - i >= k) {\n const idx = i + j * rows;\n out.data[idx] = a.data[idx];\n if (im !== undefined) out.imag[idx] = im[idx];\n }\n }\n }\n return out;\n}\n\nfunction mtoc2_tensor_tril_complex(a, k) {\n const rows = a.shape[0];\n const cols = a.shape[1];\n const out = mtoc2_tensor_alloc_nd_complex(2, [rows, cols]);\n const im = a.imag;\n for (let j = 0; j < cols; j++) {\n for (let i = 0; i < rows; i++) {\n if (i - j >= -k) {\n const idx = i + j * rows;\n out.data[idx] = a.data[idx];\n if (im !== undefined) out.imag[idx] = im[idx];\n }\n }\n }\n return out;\n}\n",
@@ -66899,6 +68057,7 @@ var JS_IMPORTS = {
66899
68057
  "tensor_fill_nd.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
66900
68058
  "tensor_fill_square.js": ["tensor_fill_nd.js"],
66901
68059
  "tensor_flip.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
68060
+ "tensor_imag_all_zero.js": [],
66902
68061
  "tensor_linspace.js": ["tensor_alloc.js"],
66903
68062
  "tensor_logical_real.js": ["tensor_alloc_nd.js"],
66904
68063
  "tensor_logspace.js": ["tensor_alloc.js"],
@@ -68283,7 +69442,7 @@ var ge = defineCompare("ge", ">=", (a, b) => a >= b);
68283
69442
  // src/numbl-core/jit/codegen/cHelpers.ts
68284
69443
  function cTypeFor(t) {
68285
69444
  if (isMultiElement(t)) return "mtoc2_tensor_t";
68286
- if (isHandle(t)) return handleTypedefName(t);
69445
+ if (isHandle2(t)) return handleTypedefName(t);
68287
69446
  if (t.kind === "Struct") return structTypedefName(t);
68288
69447
  if (t.kind === "Class") return classTypedefName(t);
68289
69448
  if (t.kind === "Cell") return cellTypedefName(t);
@@ -73919,7 +75078,7 @@ function staticAnswer(t) {
73919
75078
  }
73920
75079
  if (isString(t)) return true;
73921
75080
  if (isChar(t)) return false;
73922
- if (isStruct(t) || isClass(t) || isHandle(t)) return false;
75081
+ if (isStruct(t) || isClass(t) || isHandle2(t)) return false;
73923
75082
  return void 0;
73924
75083
  }
73925
75084
  function requireKnown(t) {
@@ -74038,7 +75197,7 @@ var isstruct = {
74038
75197
  function staticClassNameOf(t) {
74039
75198
  if (isClass(t)) return t.className;
74040
75199
  if (isStruct(t)) return "struct";
74041
- if (isHandle(t)) return "function_handle";
75200
+ if (isHandle2(t)) return "function_handle";
74042
75201
  if (isChar(t)) return "char";
74043
75202
  if (isString(t)) return "string";
74044
75203
  if (isCell(t)) return "cell";
@@ -74521,7 +75680,7 @@ function checkArity2(argTypes, nargout) {
74521
75680
  function staticVerdict(t) {
74522
75681
  if (!isNumeric2(t)) return true;
74523
75682
  if (!t.isComplex) return true;
74524
- if (!isScalar(t)) return false;
75683
+ if (!isScalar(t)) return "runtime";
74525
75684
  const ex = t.exact;
74526
75685
  if (ex !== void 0 && typeof ex === "object" && "im" in ex) {
74527
75686
  const im = ex.im;
@@ -74538,23 +75697,33 @@ var isreal = {
74538
75697
  return [scalarLogical(v)];
74539
75698
  },
74540
75699
  emitC({ argTypes, argsC, useRuntime }) {
74541
- const v = staticVerdict(argTypes[0]);
75700
+ const t = argTypes[0];
75701
+ const v = staticVerdict(t);
74542
75702
  if (v === true) return "1.0";
74543
75703
  if (v === false) return "0.0";
75704
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
75705
+ useRuntime("mtoc2_tensor_imag_all_zero");
75706
+ return `mtoc2_tensor_imag_all_zero(${argsC[0]})`;
75707
+ }
74544
75708
  useRuntime("mtoc2_cscalar");
74545
75709
  return `(cimag(${argsC[0]}) == 0.0)`;
74546
75710
  },
74547
- emitJs({ argTypes, argsJs }) {
74548
- const v = staticVerdict(argTypes[0]);
75711
+ emitJs({ argTypes, argsJs, useRuntime }) {
75712
+ const t = argTypes[0];
75713
+ const v = staticVerdict(t);
74549
75714
  if (v === true) return "true";
74550
75715
  if (v === false) return "false";
75716
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
75717
+ useRuntime("mtoc2_tensor_imag_all_zero");
75718
+ return `mtoc2_tensor_imag_all_zero(${argsJs[0]})`;
75719
+ }
74551
75720
  return `(${argsJs[0]}.im === 0)`;
74552
75721
  },
74553
75722
  call({ args }) {
74554
75723
  const v = args[0];
74555
75724
  if (typeof v === "number" || typeof v === "boolean") return [true];
74556
75725
  if (isComplexValue(v)) return [v.im === 0];
74557
- if (isTensor(v)) return [!v.imag];
75726
+ if (isTensor(v)) return [mtoc2_tensor_imag_all_zero(v)];
74558
75727
  return [true];
74559
75728
  }
74560
75729
  };
@@ -79108,7 +80277,7 @@ function lowerFuncCall(e) {
79108
80277
  if (envEntry === void 0 && e.name === "feval") {
79109
80278
  return lowerFevalCall.call(this, e);
79110
80279
  }
79111
- if (envEntry !== void 0 && isHandle(envEntry.ty)) {
80280
+ if (envEntry !== void 0 && isHandle2(envEntry.ty)) {
79112
80281
  return dispatchHandleCall.call(this, e.name, envEntry, e.args, e.span);
79113
80282
  }
79114
80283
  if (envEntry === void 0 && this.workspace.isClass(e.name)) {
@@ -79555,7 +80724,7 @@ function lowerMultiAssign(s) {
79555
80724
  s = { ...s, lvalues: expandedLvalues };
79556
80725
  if (s.expr.type === "FuncCall") {
79557
80726
  const envEntry = this.env.get(callName);
79558
- if (envEntry !== void 0 && isHandle(envEntry.ty)) {
80727
+ if (envEntry !== void 0 && isHandle2(envEntry.ty)) {
79559
80728
  return dispatchHandleMultiAssign.call(
79560
80729
  this,
79561
80730
  callName,
@@ -82714,6 +83883,13 @@ var REGISTRY2 = /* @__PURE__ */ new Map([
82714
83883
  "mtoc2_cdiv"
82715
83884
  ])
82716
83885
  ],
83886
+ // `isreal` on a complex-typed tensor: scan the imag lane (true iff all
83887
+ // zero). Lets the JIT report realness by value, matching the
83888
+ // interpreter on tensors the type system could not prove real.
83889
+ [
83890
+ "mtoc2_tensor_imag_all_zero",
83891
+ loadSnippet("tensor_imag_all_zero.h", ["mtoc2_tensor_t"])
83892
+ ],
82717
83893
  // ── Elementwise binary/unary on real tensors ──────────────────────
82718
83894
  // One snippet covers all 11 funcs (4×_tt, 4×_ts, 2×_st, 1×uminus).
82719
83895
  // Builtins activate by op-specific synthetic name; all map to the