numbl 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-lib/lib.js CHANGED
@@ -287,10 +287,12 @@ var RuntimeClassInstanceArray = class extends Refcounted {
287
287
  kind = "class_instance_array";
288
288
  className;
289
289
  elements;
290
- constructor(className, elements) {
290
+ shape;
291
+ constructor(className, elements, shape) {
291
292
  super();
292
293
  this.className = className;
293
294
  this.elements = elements;
295
+ this.shape = shape ?? [1, elements.length];
294
296
  for (const el of elements) incref(el);
295
297
  }
296
298
  _destroy(rt) {
@@ -797,7 +799,7 @@ function displayValue(v) {
797
799
  case "dictionary":
798
800
  return formatDictionary(v);
799
801
  case "class_instance_array":
800
- return ` 1x${v.elements.length} ${v.className} array`;
802
+ return ` ${v.shape[0]}x${v.shape[1]} ${v.className} array`;
801
803
  }
802
804
  }
803
805
  var formatStructArray = (v) => {
@@ -20391,10 +20393,11 @@ function sparseElemMul(a, b) {
20391
20393
  }
20392
20394
  function sparseElemMulDense(S, D) {
20393
20395
  const [dRows, dCols] = tensorSize2D(D);
20394
- if (S.m !== dRows || S.n !== dCols)
20396
+ if (!(dRows === S.m || dRows === 1) || !(dCols === S.n || dCols === 1))
20395
20397
  throw new RuntimeError(
20396
20398
  `Matrix dimensions must agree: [${S.m},${S.n}] vs [${dRows},${dCols}]`
20397
20399
  );
20400
+ const denseIdx = (row, col) => (dCols === 1 ? 0 : col) * dRows + (dRows === 1 ? 0 : row);
20398
20401
  const hasImag = isComplexSparse(S) || D.imag !== void 0;
20399
20402
  const irList = [];
20400
20403
  const prList = [];
@@ -20404,7 +20407,7 @@ function sparseElemMulDense(S, D) {
20404
20407
  jc[col] = irList.length;
20405
20408
  for (let k = S.jc[col]; k < S.jc[col + 1]; k++) {
20406
20409
  const row = S.ir[k];
20407
- const idx = col * S.m + row;
20410
+ const idx = denseIdx(row, col);
20408
20411
  const aRe = S.pr[k], aIm = S.pi ? S.pi[k] : 0;
20409
20412
  const bRe = D.data[idx], bIm = D.imag ? D.imag[idx] : 0;
20410
20413
  const re = aRe * bRe - aIm * bIm;
@@ -22513,12 +22516,24 @@ function transposeCore(v, conjugate) {
22513
22516
  if (v._isLogical) t._isLogical = true;
22514
22517
  return t;
22515
22518
  }
22519
+ function transposeClassInstanceArray(v) {
22520
+ const [r, c] = v.shape;
22521
+ const out = new Array(v.elements.length);
22522
+ for (let i = 0; i < r; i++) {
22523
+ for (let j = 0; j < c; j++) {
22524
+ out[i * c + j] = v.elements[j * r + i];
22525
+ }
22526
+ }
22527
+ return new RuntimeClassInstanceArray(v.className, out, [c, r]);
22528
+ }
22516
22529
  function mTranspose(v) {
22517
22530
  if (isRuntimeSparseMatrix(v)) return sparseTranspose(v);
22518
22531
  if (isRuntimeComplexNumber(v)) return v;
22519
22532
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
22520
22533
  if (isRuntimeCell(v)) return transposeCellArray(v);
22521
22534
  if (isRuntimeChar(v)) return v;
22535
+ if (isRuntimeClassInstance(v)) return v;
22536
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
22522
22537
  if (!isRuntimeTensor(v))
22523
22538
  throw new RuntimeError("Cannot transpose non-numeric value");
22524
22539
  return transposeCore(v, false);
@@ -22529,6 +22544,8 @@ function mConjugateTranspose(v) {
22529
22544
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
22530
22545
  if (isRuntimeCell(v)) return transposeCellArray(v);
22531
22546
  if (isRuntimeChar(v)) return v;
22547
+ if (isRuntimeClassInstance(v)) return v;
22548
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
22532
22549
  if (!isRuntimeTensor(v))
22533
22550
  throw new RuntimeError("Cannot transpose non-numeric value");
22534
22551
  return transposeCore(v, true);
@@ -25213,6 +25230,60 @@ function cellCatAlongDim(values, dimIdx) {
25213
25230
  }
25214
25231
 
25215
25232
  // src/numbl-core/runtime/struct-access.ts
25233
+ function handlePropToRuntime(v) {
25234
+ if (typeof v === "number") return RTV.num(v);
25235
+ if (typeof v === "boolean") return RTV.logical(v);
25236
+ if (typeof v === "string") return RTV.char(v);
25237
+ if (Array.isArray(v)) {
25238
+ const data = allocFloat64Array(v);
25239
+ return RTV.tensor(data, [1, v.length]);
25240
+ }
25241
+ return v;
25242
+ }
25243
+ var HANDLE_DEFAULTS = {
25244
+ quiver3: {
25245
+ LineWidth: 0.5,
25246
+ LineStyle: "-",
25247
+ ShowArrowHead: true,
25248
+ AutoScale: true,
25249
+ AutoScaleFactor: 0.9,
25250
+ Marker: "none",
25251
+ Color: [0, 0.447, 0.741]
25252
+ }
25253
+ };
25254
+ function resolveHandleKey(trace, field) {
25255
+ if (field in trace) return field;
25256
+ const camel = field.charAt(0).toLowerCase() + field.slice(1);
25257
+ if (camel in trace) return camel;
25258
+ if (field.endsWith("Data")) {
25259
+ const base = field.slice(0, -4);
25260
+ const short = base.charAt(0).toLowerCase() + base.slice(1);
25261
+ if (short in trace) return short;
25262
+ }
25263
+ return null;
25264
+ }
25265
+ function runtimeToHandleValue(value, current) {
25266
+ if (typeof current === "boolean") {
25267
+ if (typeof value === "boolean") return value;
25268
+ if (typeof value === "number") return value !== 0;
25269
+ if (isRuntimeString(value) || isRuntimeChar(value)) {
25270
+ const s = (isRuntimeChar(value) ? value.value : value).toLowerCase();
25271
+ return !(s === "off" || s === "false" || s === "0");
25272
+ }
25273
+ return true;
25274
+ }
25275
+ if (typeof current === "string") {
25276
+ return isRuntimeChar(value) ? value.value : isRuntimeString(value) ? value : String(value);
25277
+ }
25278
+ if (typeof current === "number" || current === void 0) {
25279
+ if (typeof value === "number") return value;
25280
+ if (isRuntimeNumber(value)) return value;
25281
+ }
25282
+ if (Array.isArray(current) && isRuntimeTensor(value)) {
25283
+ return Array.from(value.data);
25284
+ }
25285
+ return value;
25286
+ }
25216
25287
  function getRTValueField(base, field) {
25217
25288
  if (isRuntimeStruct(base) || isRuntimeClassInstance(base)) {
25218
25289
  const val = base.fields.get(field);
@@ -25236,6 +25307,15 @@ function getRTValueField(base, field) {
25236
25307
  });
25237
25308
  return horzcat(...values);
25238
25309
  }
25310
+ if (isRuntimeGraphicsHandle(base)) {
25311
+ const key = resolveHandleKey(base._trace, field);
25312
+ if (key !== null) return handlePropToRuntime(base._trace[key]);
25313
+ const dflt = HANDLE_DEFAULTS[base._traceType]?.[field];
25314
+ if (dflt !== void 0) return handlePropToRuntime(dflt);
25315
+ throw new RuntimeError(
25316
+ `No property '${field}' on ${base._traceType} handle`
25317
+ );
25318
+ }
25239
25319
  throw new RuntimeError(`Cannot access field ${field} on ${kstr(base)}`);
25240
25320
  }
25241
25321
  function setRTValueField(base, field, value, rt) {
@@ -25280,6 +25360,12 @@ function setRTValueField(base, field, value, rt) {
25280
25360
  `Cannot assign field '${field}' on a non-scalar struct array without indexing`
25281
25361
  );
25282
25362
  }
25363
+ if (isRuntimeGraphicsHandle(base)) {
25364
+ const key = resolveHandleKey(base._trace, field) ?? field.charAt(0).toLowerCase() + field.slice(1);
25365
+ const current = base._trace[key] ?? HANDLE_DEFAULTS[base._traceType]?.[field];
25366
+ base._trace[key] = runtimeToHandleValue(value, current);
25367
+ return base;
25368
+ }
25283
25369
  if (isRuntimeNumber(base) && base === 0) {
25284
25370
  return RTV.struct(/* @__PURE__ */ new Map([[field, value]]));
25285
25371
  }
@@ -30124,6 +30210,27 @@ function toNumArray(v, name) {
30124
30210
  throw new RuntimeError(`${name}: arguments must be numeric arrays`);
30125
30211
  }
30126
30212
 
30213
+ // src/numbl-core/helpers/effectively-real.ts
30214
+ function imagAllZero(imag2) {
30215
+ if (!imag2) return true;
30216
+ for (let i = 0; i < imag2.length; i++) {
30217
+ if (imag2[i] !== 0) return false;
30218
+ }
30219
+ return true;
30220
+ }
30221
+ function stripZeroImagTensor(t) {
30222
+ if (t.imag && imagAllZero(t.imag)) {
30223
+ const out = RTV.tensor(t.data, t.shape);
30224
+ if (t._isLogical) out._isLogical = true;
30225
+ return out;
30226
+ }
30227
+ return t;
30228
+ }
30229
+ function stripZeroImagValue(v) {
30230
+ if (isRuntimeTensor(v)) return stripZeroImagTensor(v);
30231
+ return v;
30232
+ }
30233
+
30127
30234
  // src/numbl-core/helpers/reduction/min-max.ts
30128
30235
  function minMaxScan(data, imag2, indices, initial, isBetter, complexIsBetter) {
30129
30236
  let mRe = initial, mIm = 0, mIdx = 0;
@@ -30308,6 +30415,7 @@ function minMaxImpl(name, args, nargout, initial, isBetter, twoArgFn) {
30308
30415
  return isBetter(Math.atan2(imA, reA), Math.atan2(imB, reB));
30309
30416
  };
30310
30417
  args = args.map((a) => isRuntimeSparseMatrix(a) ? sparseToDense(a) : a);
30418
+ args = args.map(stripZeroImagValue);
30311
30419
  if (args.length === 1) {
30312
30420
  const v = args[0];
30313
30421
  if (isRuntimeNumber(v) || isRuntimeLogical(v) || isRuntimeComplexNumber(v)) {
@@ -30795,8 +30903,8 @@ defineBuiltin({
30795
30903
  if (typeof v === "number") return true;
30796
30904
  if (typeof v === "boolean") return true;
30797
30905
  if (isRuntimeComplexNumber(v)) return v.im === 0;
30798
- if (isRuntimeTensor(v)) return !v.imag;
30799
- if (isRuntimeSparseMatrix(v)) return !v.pi;
30906
+ if (isRuntimeTensor(v)) return imagAllZero(v.imag);
30907
+ if (isRuntimeSparseMatrix(v)) return !v.pi || imagAllZero(v.pi);
30800
30908
  return true;
30801
30909
  }
30802
30910
  }
@@ -31218,7 +31326,7 @@ function getShape(v) {
31218
31326
  if (isRuntimeSparseMatrix(v)) return [v.m, v.n];
31219
31327
  if (isRuntimeCell(v)) return v.shape;
31220
31328
  if (isRuntimeStructArray(v)) return [1, v.elements.length];
31221
- if (isRuntimeClassInstanceArray(v)) return [1, v.elements.length];
31329
+ if (isRuntimeClassInstanceArray(v)) return [...v.shape];
31222
31330
  return [1, 1];
31223
31331
  }
31224
31332
  defineBuiltin({
@@ -31396,7 +31504,8 @@ defineBuiltin({
31396
31504
  }
31397
31505
  if (isRuntimeString(v)) return 1;
31398
31506
  if (isRuntimeStructArray(v)) return v.elements.length;
31399
- if (isRuntimeClassInstanceArray(v)) return v.elements.length;
31507
+ if (isRuntimeClassInstanceArray(v))
31508
+ return v.elements.length === 0 ? 0 : Math.max(...v.shape);
31400
31509
  return 1;
31401
31510
  }
31402
31511
  }
@@ -31509,8 +31618,15 @@ defineBuiltin({
31509
31618
  if (isRuntimeClassInstanceArray(v)) return mkChar(v.className);
31510
31619
  if (isRuntimeFunction(v)) return mkChar("function_handle");
31511
31620
  if (isRuntimeDummyHandle(v)) return mkChar("dummy_handle");
31512
- if (isRuntimeGraphicsHandle(v))
31513
- return mkChar("matlab.graphics.primitive.Surface");
31621
+ if (isRuntimeGraphicsHandle(v)) {
31622
+ const handleClass = {
31623
+ contour: "matlab.graphics.chart.primitive.Contour",
31624
+ quiver3: "matlab.graphics.chart.primitive.Quiver"
31625
+ };
31626
+ return mkChar(
31627
+ handleClass[v._traceType] ?? "matlab.graphics.primitive.Surface"
31628
+ );
31629
+ }
31514
31630
  return mkChar("unknown");
31515
31631
  }
31516
31632
  }
@@ -33915,7 +34031,7 @@ defineBuiltin({
33915
34031
  return v;
33916
34032
  }
33917
34033
  if (isRuntimeTensor(v)) {
33918
- return sortTensor(v, dim, descend, nargout);
34034
+ return sortTensor(stripZeroImagTensor(v), dim, descend, nargout);
33919
34035
  }
33920
34036
  if (isRuntimeCell(v)) {
33921
34037
  return sortCell(v, descend, nargout);
@@ -36551,6 +36667,7 @@ defineBuiltin({
36551
36667
  const blocks = args.map((a) => {
36552
36668
  if (isRuntimeNumber(a))
36553
36669
  return RTV.tensor(allocFloat64Array([a]), [1, 1]);
36670
+ if (isRuntimeSparseMatrix(a)) return sparseToDense(a);
36554
36671
  if (!isRuntimeTensor(a))
36555
36672
  throw new RuntimeError("blkdiag: arguments must be numeric");
36556
36673
  return a;
@@ -37919,6 +38036,21 @@ defineBuiltin({
37919
38036
  const shape = reps.length >= 2 ? reps : [reps[0], reps[0]];
37920
38037
  return RTV.tensor(data, shape, imag2);
37921
38038
  }
38039
+ if (isRuntimeCell(v)) {
38040
+ const srcShape2 = v.shape.length >= 2 ? v.shape : [1, v.shape[0] ?? v.data.length];
38041
+ const [sm, sn] = [srcShape2[0], srcShape2[1]];
38042
+ const r0 = reps[0] ?? 1;
38043
+ const r1 = reps.length >= 2 ? reps[1] : reps[0] ?? 1;
38044
+ const om = sm * r0;
38045
+ const on = sn * r1;
38046
+ const out2 = new Array(om * on);
38047
+ for (let J = 0; J < on; J++) {
38048
+ for (let I = 0; I < om; I++) {
38049
+ out2[I + J * om] = v.data[I % sm + J % sn * sm];
38050
+ }
38051
+ }
38052
+ return RTV.cell(out2, [om, on]);
38053
+ }
37922
38054
  if (!isRuntimeTensor(v))
37923
38055
  throw new RuntimeError("repmat: first argument must be numeric");
37924
38056
  if (reps.every((r) => r === 1)) {
@@ -43992,6 +44124,13 @@ for (const name of ["groot", "gcf", "gca", "shg", "newplot"]) {
43992
44124
  })
43993
44125
  });
43994
44126
  }
44127
+ registerIBuiltin({
44128
+ name: "camlight",
44129
+ resolve: () => ({
44130
+ outputTypes: [{ kind: "unknown" }],
44131
+ apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
44132
+ })
44133
+ });
43995
44134
  registerIBuiltin({
43996
44135
  name: "get",
43997
44136
  resolve: () => ({
@@ -44146,7 +44285,6 @@ registerIBuiltin({
44146
44285
  })
44147
44286
  });
44148
44287
  for (const cm of [
44149
- "parula",
44150
44288
  "jet",
44151
44289
  "hsv",
44152
44290
  "hot",
@@ -44168,6 +44306,44 @@ for (const cm of [
44168
44306
  })
44169
44307
  });
44170
44308
  }
44309
+ var PARULA_ANCHORS = [
44310
+ [0.2422, 0.1504, 0.6603],
44311
+ [0.278, 0.3556, 0.9777],
44312
+ [0.1085, 0.5267, 0.8943],
44313
+ [0.0469, 0.6353, 0.7861],
44314
+ [0.2161, 0.7269, 0.6499],
44315
+ [0.5044, 0.7993, 0.4519],
44316
+ [0.8328, 0.8056, 0.2008],
44317
+ [0.9871, 0.8092, 0.1432],
44318
+ [0.9763, 0.9831, 0.0538]
44319
+ ];
44320
+ function parulaColormap(m) {
44321
+ const data = allocFloat64Array(m * 3);
44322
+ const last = PARULA_ANCHORS.length - 1;
44323
+ for (let i = 0; i < m; i++) {
44324
+ const t = m <= 1 ? 0 : i / (m - 1) * last;
44325
+ const k = Math.min(last - 1, Math.floor(t));
44326
+ const f = t - k;
44327
+ const a = PARULA_ANCHORS[k];
44328
+ const b = PARULA_ANCHORS[Math.min(last, k + 1)];
44329
+ for (let c = 0; c < 3; c++) {
44330
+ data[c * m + i] = a[c] + f * (b[c] - a[c]);
44331
+ }
44332
+ }
44333
+ return RTV.tensor(data, [m, 3]);
44334
+ }
44335
+ registerIBuiltin({
44336
+ name: "parula",
44337
+ resolve: () => ({
44338
+ outputTypes: [{ kind: "unknown" }],
44339
+ apply: (args) => {
44340
+ if (args.length >= 1 && (isRuntimeNumber(args[0]) || isRuntimeTensor(args[0]))) {
44341
+ return parulaColormap(Math.max(0, Math.round(toNumber(args[0]))));
44342
+ }
44343
+ return RTV.char("parula");
44344
+ }
44345
+ })
44346
+ });
44171
44347
  var appdataStore = /* @__PURE__ */ new Map();
44172
44348
  function resetAppdataStore() {
44173
44349
  const rt = getCurrentRuntime();
@@ -46222,6 +46398,10 @@ var H = {
46222
46398
  signatures: ["nexttile", "nexttile(IDX)"],
46223
46399
  description: "Advance to the next tile in the current tiled layout, or move to tile IDX. Auto-creates a flow layout if none exists."
46224
46400
  },
46401
+ surface: {
46402
+ signatures: ["surface(X,Y,Z)", "surface(Z)", "surface(X,Y,Z,C)"],
46403
+ description: "Primitive surface plot. Like surf, but adds to the current axes without clearing existing objects (does not respect hold)."
46404
+ },
46225
46405
  title: {
46226
46406
  signatures: ["title(TXT)"],
46227
46407
  description: "Set title of current axes."
@@ -46258,6 +46438,10 @@ var H = {
46258
46438
  signatures: ["clf"],
46259
46439
  description: "Clear current figure."
46260
46440
  },
46441
+ cla: {
46442
+ signatures: ["cla", "cla(ax)", "cla reset", "cla(ax,'reset')"],
46443
+ description: "Clear current axes. With 'reset', also reset axes properties to defaults."
46444
+ },
46261
46445
  sgtitle: {
46262
46446
  signatures: ["sgtitle(TXT)"],
46263
46447
  description: "Set super-title for subplot grid."
@@ -46310,6 +46494,10 @@ var H = {
46310
46494
  signatures: ["shg"],
46311
46495
  description: "Show current figure."
46312
46496
  },
46497
+ camlight: {
46498
+ signatures: ["camlight", "camlight(position)", "cl = camlight(___)"],
46499
+ description: "Create a light in camera coordinates. Stub in numbl (no lighting model): accepts all arguments and returns a placeholder handle."
46500
+ },
46313
46501
  newplot: {
46314
46502
  signatures: ["newplot"],
46315
46503
  description: "Prepare axes for new plot."
@@ -47434,6 +47622,12 @@ function parseContourArgs(args, filled) {
47434
47622
  let rows;
47435
47623
  let cols;
47436
47624
  let nLevels = 10;
47625
+ let levels;
47626
+ const applyLevelArg = (arg) => {
47627
+ const vals = toNumberArray(arg);
47628
+ if (vals.length <= 1) nLevels = vals.length === 1 ? vals[0] : nLevels;
47629
+ else levels = vals;
47630
+ };
47437
47631
  if (numericCount === 1) {
47438
47632
  const info = getMatrixInfo(args[pos++]);
47439
47633
  rows = info.rows;
@@ -47447,8 +47641,7 @@ function parseContourArgs(args, filled) {
47447
47641
  rows = info.rows;
47448
47642
  cols = info.cols;
47449
47643
  zData = info.data;
47450
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
47451
- pos++;
47644
+ applyLevelArg(args[pos++]);
47452
47645
  const gen = generateMeshgrid(rows, cols);
47453
47646
  xData = gen.x;
47454
47647
  yData = gen.y;
@@ -47469,15 +47662,144 @@ function parseContourArgs(args, filled) {
47469
47662
  rows = zInfo.rows;
47470
47663
  cols = zInfo.cols;
47471
47664
  zData = zInfo.data;
47472
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
47473
- pos++;
47665
+ applyLevelArg(args[pos++]);
47474
47666
  const expanded = expandXY(x, y, rows, cols);
47475
47667
  xData = expanded.x;
47476
47668
  yData = expanded.y;
47477
47669
  } else {
47478
47670
  throw new Error("contour requires at least 1 argument");
47479
47671
  }
47480
- return { x: xData, y: yData, z: zData, rows, cols, nLevels, filled };
47672
+ let lineWidth;
47673
+ let lineStyle;
47674
+ let lineColor;
47675
+ for (; pos + 1 < args.length; pos += 2) {
47676
+ if (!isStringArg(args[pos])) break;
47677
+ const name = getStringValue(args[pos]).toLowerCase();
47678
+ const val = args[pos + 1];
47679
+ switch (name) {
47680
+ case "linewidth":
47681
+ lineWidth = toNumber(val);
47682
+ break;
47683
+ case "linestyle":
47684
+ lineStyle = getStringValue(val);
47685
+ break;
47686
+ case "linecolor":
47687
+ case "color":
47688
+ lineColor = isStringArg(val) ? getStringValue(val) : toNumberArray(val);
47689
+ break;
47690
+ case "levellist":
47691
+ case "levels": {
47692
+ const vals = toNumberArray(val);
47693
+ if (vals.length >= 1) levels = vals;
47694
+ break;
47695
+ }
47696
+ }
47697
+ }
47698
+ if (levels) nLevels = levels.length;
47699
+ return {
47700
+ x: xData,
47701
+ y: yData,
47702
+ z: zData,
47703
+ rows,
47704
+ cols,
47705
+ nLevels,
47706
+ ...levels ? { levels } : {},
47707
+ ...lineWidth !== void 0 ? { lineWidth } : {},
47708
+ ...lineStyle !== void 0 ? { lineStyle } : {},
47709
+ ...lineColor !== void 0 ? { lineColor } : {},
47710
+ filled
47711
+ };
47712
+ }
47713
+ function marchingSquaresCell(z00, z10, z01, z11, x00, y00, x10, y10, x01, y01, x11, y11, level) {
47714
+ const code = (z00 >= level ? 1 : 0) | (z10 >= level ? 2 : 0) | (z01 >= level ? 4 : 0) | (z11 >= level ? 8 : 0);
47715
+ if (code === 0 || code === 15) return [];
47716
+ const lerp = (a, b, za, zb) => a + (level - za) / (zb - za || 1) * (b - a);
47717
+ const bx = lerp(x00, x10, z00, z10), by = lerp(y00, y10, z00, z10);
47718
+ const rx = lerp(x10, x11, z10, z11), ry = lerp(y10, y11, z10, z11);
47719
+ const tx = lerp(x01, x11, z01, z11), ty = lerp(y01, y11, z01, z11);
47720
+ const lx = lerp(x00, x01, z00, z01), ly = lerp(y00, y01, z00, z01);
47721
+ const segs = [];
47722
+ switch (code) {
47723
+ case 1:
47724
+ case 14:
47725
+ segs.push([bx, by, lx, ly]);
47726
+ break;
47727
+ case 2:
47728
+ case 13:
47729
+ segs.push([bx, by, rx, ry]);
47730
+ break;
47731
+ case 3:
47732
+ case 12:
47733
+ segs.push([lx, ly, rx, ry]);
47734
+ break;
47735
+ case 4:
47736
+ case 11:
47737
+ segs.push([lx, ly, tx, ty]);
47738
+ break;
47739
+ case 5:
47740
+ case 10:
47741
+ segs.push([bx, by, tx, ty]);
47742
+ break;
47743
+ case 6:
47744
+ case 9:
47745
+ segs.push([bx, by, lx, ly]);
47746
+ segs.push([tx, ty, rx, ry]);
47747
+ break;
47748
+ case 7:
47749
+ case 8:
47750
+ segs.push([tx, ty, rx, ry]);
47751
+ break;
47752
+ }
47753
+ return segs;
47754
+ }
47755
+ function computeContourMatrix(trace) {
47756
+ const { x, y, z, rows, cols } = trace;
47757
+ const at = (a, i, j) => a[j * rows + i];
47758
+ let zMin = Infinity;
47759
+ let zMax = -Infinity;
47760
+ for (const v of z) {
47761
+ if (Number.isFinite(v)) {
47762
+ if (v < zMin) zMin = v;
47763
+ if (v > zMax) zMax = v;
47764
+ }
47765
+ }
47766
+ let levelList;
47767
+ if (trace.levels && trace.levels.length > 0) {
47768
+ levelList = trace.levels.filter((v) => Number.isFinite(v));
47769
+ } else {
47770
+ const n = Math.max(1, Math.round(trace.nLevels));
47771
+ levelList = [];
47772
+ if (Number.isFinite(zMin) && Number.isFinite(zMax) && zMax > zMin) {
47773
+ const step = (zMax - zMin) / (n + 1);
47774
+ for (let k = 1; k <= n; k++) levelList.push(zMin + k * step);
47775
+ }
47776
+ }
47777
+ const data = [];
47778
+ for (const level of levelList) {
47779
+ for (let j = 0; j < cols - 1; j++) {
47780
+ for (let i = 0; i < rows - 1; i++) {
47781
+ const segs = marchingSquaresCell(
47782
+ at(z, i, j),
47783
+ at(z, i + 1, j),
47784
+ at(z, i, j + 1),
47785
+ at(z, i + 1, j + 1),
47786
+ at(x, i, j),
47787
+ at(y, i, j),
47788
+ at(x, i + 1, j),
47789
+ at(y, i + 1, j),
47790
+ at(x, i, j + 1),
47791
+ at(y, i, j + 1),
47792
+ at(x, i + 1, j + 1),
47793
+ at(y, i + 1, j + 1),
47794
+ level
47795
+ );
47796
+ for (const [x1, y1, x2, y2] of segs) {
47797
+ data.push(level, 2, x1, y1, x2, y2);
47798
+ }
47799
+ }
47800
+ }
47801
+ }
47802
+ return { data, n: data.length / 2, levelList };
47481
47803
  }
47482
47804
  function parseMeshArgs(args) {
47483
47805
  const trace = parseSurfArgs(args);
@@ -48425,6 +48747,213 @@ function computeQuiverAutoScale(x, y, u, v, rows, cols, factor) {
48425
48747
  if (maxMag === 0) return 1;
48426
48748
  return factor * spacing / maxMag;
48427
48749
  }
48750
+ function parseQuiver3Args(args) {
48751
+ if (args.length < 4) throw new Error("quiver3 requires at least 4 arguments");
48752
+ let numericCount = 0;
48753
+ for (let i = 0; i < args.length; i++) {
48754
+ if (isNumericArg(args[i])) numericCount++;
48755
+ else break;
48756
+ }
48757
+ let pos = 0;
48758
+ let xData;
48759
+ let yData;
48760
+ let zData;
48761
+ let uData;
48762
+ let vData;
48763
+ let wData;
48764
+ let rows;
48765
+ let cols;
48766
+ const arity = numericCount >= 6 ? 6 : 4;
48767
+ if (arity === 4) {
48768
+ const zInfo = getMatrixInfo(args[pos++]);
48769
+ rows = zInfo.rows;
48770
+ cols = zInfo.cols;
48771
+ zData = zInfo.data;
48772
+ uData = getMatrixInfo(args[pos++]).data;
48773
+ vData = getMatrixInfo(args[pos++]).data;
48774
+ wData = getMatrixInfo(args[pos++]).data;
48775
+ const n = zData.length;
48776
+ if (rows === 1 || cols === 1) {
48777
+ xData = new Array(n);
48778
+ yData = new Array(n);
48779
+ for (let i = 0; i < n; i++) {
48780
+ xData[i] = i + 1;
48781
+ yData[i] = 1;
48782
+ }
48783
+ } else {
48784
+ const gen = generateMeshgrid(rows, n / rows);
48785
+ xData = gen.x;
48786
+ yData = gen.y;
48787
+ }
48788
+ } else {
48789
+ const X = args[pos++];
48790
+ const Y = args[pos++];
48791
+ const zInfo = getMatrixInfo(args[pos++]);
48792
+ rows = zInfo.rows;
48793
+ cols = zInfo.cols;
48794
+ zData = zInfo.data;
48795
+ uData = getMatrixInfo(args[pos++]).data;
48796
+ vData = getMatrixInfo(args[pos++]).data;
48797
+ wData = getMatrixInfo(args[pos++]).data;
48798
+ const n = uData.length;
48799
+ const xFlat = toNumberArray(X);
48800
+ const yFlat = toNumberArray(Y);
48801
+ if (xFlat.length === n && yFlat.length === n) {
48802
+ xData = xFlat;
48803
+ yData = yFlat;
48804
+ } else {
48805
+ const expanded = expandXY(X, Y, rows, cols);
48806
+ xData = expanded.x;
48807
+ yData = expanded.y;
48808
+ }
48809
+ }
48810
+ let autoScale = true;
48811
+ let autoScaleFactor = 0.9;
48812
+ let reportedASF = 0.9;
48813
+ if (pos < args.length && isNumericArg(args[pos]) && !isStringArg(args[pos])) {
48814
+ const s = toNumber(args[pos]);
48815
+ if (s === 0) {
48816
+ autoScale = false;
48817
+ } else {
48818
+ autoScaleFactor = s * 0.9;
48819
+ reportedASF = s;
48820
+ }
48821
+ pos++;
48822
+ }
48823
+ const trace = {
48824
+ x: xData,
48825
+ y: yData,
48826
+ z: zData,
48827
+ u: uData,
48828
+ v: vData,
48829
+ w: wData,
48830
+ showArrowHead: true
48831
+ };
48832
+ while (pos < args.length && isStringArg(args[pos]) && !isQuiverNameValueKey(args[pos])) {
48833
+ const s = getStringIfString(args[pos]);
48834
+ if (s === void 0) break;
48835
+ if (s === "off") {
48836
+ autoScale = false;
48837
+ pos++;
48838
+ continue;
48839
+ }
48840
+ if (s === "filled") {
48841
+ trace.markerFilled = true;
48842
+ pos++;
48843
+ continue;
48844
+ }
48845
+ const spec = parseLineSpec(s);
48846
+ if (spec) {
48847
+ if (spec.color) trace.color = COLOR_SHORT[spec.color];
48848
+ if (spec.lineStyle) trace.lineStyle = spec.lineStyle;
48849
+ if (spec.marker) {
48850
+ trace.marker = spec.marker;
48851
+ trace.showArrowHead = false;
48852
+ }
48853
+ pos++;
48854
+ continue;
48855
+ }
48856
+ const c = resolveColor(s);
48857
+ if (c) {
48858
+ trace.color = c;
48859
+ pos++;
48860
+ continue;
48861
+ }
48862
+ break;
48863
+ }
48864
+ while (pos < args.length) {
48865
+ const key = isQuiverNameValueKey(args[pos]);
48866
+ if (!key) break;
48867
+ pos++;
48868
+ if (pos >= args.length) break;
48869
+ const value = args[pos++];
48870
+ switch (key) {
48871
+ case "color": {
48872
+ const c = resolveColor(value);
48873
+ if (c) trace.color = c;
48874
+ break;
48875
+ }
48876
+ case "linestyle":
48877
+ trace.lineStyle = getStringValue(value);
48878
+ break;
48879
+ case "linewidth":
48880
+ trace.lineWidth = typeof value === "number" ? value : toNumber(value);
48881
+ break;
48882
+ case "marker": {
48883
+ const s = getStringValue(value);
48884
+ trace.marker = s === "none" ? void 0 : s;
48885
+ break;
48886
+ }
48887
+ case "showarrowhead": {
48888
+ const s = getStringValue(value).toLowerCase();
48889
+ trace.showArrowHead = !(s === "off" || s === "false" || s === "0");
48890
+ break;
48891
+ }
48892
+ case "autoscale": {
48893
+ const s = getStringValue(value).toLowerCase();
48894
+ autoScale = !(s === "off" || s === "false" || s === "0");
48895
+ break;
48896
+ }
48897
+ case "autoscalefactor": {
48898
+ const n = typeof value === "number" ? value : toNumber(value);
48899
+ autoScaleFactor = n * 0.9;
48900
+ reportedASF = n;
48901
+ break;
48902
+ }
48903
+ }
48904
+ }
48905
+ trace.autoScale = autoScale;
48906
+ trace.autoScaleFactor = reportedASF;
48907
+ if (autoScale) {
48908
+ const factor = computeQuiver3AutoScale(
48909
+ xData,
48910
+ yData,
48911
+ zData,
48912
+ uData,
48913
+ vData,
48914
+ wData,
48915
+ autoScaleFactor
48916
+ );
48917
+ if (factor !== 1) {
48918
+ trace.u = uData.map((x) => x * factor);
48919
+ trace.v = vData.map((x) => x * factor);
48920
+ trace.w = wData.map((x) => x * factor);
48921
+ }
48922
+ }
48923
+ return trace;
48924
+ }
48925
+ function computeQuiver3AutoScale(x, y, z, u, v, w, factor) {
48926
+ const n = u.length;
48927
+ if (n === 0) return 1;
48928
+ const range2 = (a) => {
48929
+ let lo = Infinity;
48930
+ let hi = -Infinity;
48931
+ for (const t of a) {
48932
+ if (isFinite(t)) {
48933
+ if (t < lo) lo = t;
48934
+ if (t > hi) hi = t;
48935
+ }
48936
+ }
48937
+ return hi > lo ? hi - lo : 0;
48938
+ };
48939
+ const xr = range2(x);
48940
+ const yr = range2(y);
48941
+ const zr = range2(z);
48942
+ const dims = [xr, yr, zr].filter((d) => d > 0);
48943
+ let spacing;
48944
+ if (dims.length === 3) spacing = Math.cbrt(xr * yr * zr / n);
48945
+ else if (dims.length === 2) spacing = Math.sqrt(dims[0] * dims[1] / n);
48946
+ else if (dims.length === 1) spacing = dims[0] / Math.max(1, n - 1);
48947
+ else spacing = 1;
48948
+ spacing = spacing || 1;
48949
+ let maxMag = 0;
48950
+ for (let i = 0; i < n; i++) {
48951
+ const m = Math.sqrt(u[i] * u[i] + v[i] * v[i] + w[i] * w[i]);
48952
+ if (isFinite(m) && m > maxMag) maxMag = m;
48953
+ }
48954
+ if (maxMag === 0) return 1;
48955
+ return factor * spacing / maxMag;
48956
+ }
48428
48957
 
48429
48958
  // src/numbl-core/runtime/syncChannel.ts
48430
48959
  var syncSleepWarned = false;
@@ -48516,6 +49045,9 @@ function plotInstr(plotInstructions, instr) {
48516
49045
  case "clf":
48517
49046
  plotInstructions.push({ type: instr.type });
48518
49047
  break;
49048
+ case "cla":
49049
+ plotInstructions.push({ type: "cla", reset: instr.reset });
49050
+ break;
48519
49051
  case "set_subplot":
48520
49052
  plotInstructions.push({
48521
49053
  type: "set_subplot",
@@ -48600,6 +49132,10 @@ function surfCall(plotInstructions, args) {
48600
49132
  const trace = parseSurfArgs(args);
48601
49133
  plotInstructions.push({ type: "surf", trace });
48602
49134
  }
49135
+ function surfaceCall(plotInstructions, args) {
49136
+ const trace = parseSurfArgs(args);
49137
+ plotInstructions.push({ type: "surface", trace });
49138
+ }
48603
49139
  function imagescCall(plotInstructions, args) {
48604
49140
  const trace = parseImagescArgs(args);
48605
49141
  plotInstructions.push({ type: "imagesc", trace });
@@ -48852,6 +49388,10 @@ function quiverCall(plotInstructions, args) {
48852
49388
  plotInstructions.push({ type: "quiver", traces });
48853
49389
  }
48854
49390
  }
49391
+ function quiver3Call(plotInstructions, args) {
49392
+ const trace = parseQuiver3Args(args);
49393
+ plotInstructions.push({ type: "quiver3", trace });
49394
+ }
48855
49395
  function legendCall(plotInstructions, args) {
48856
49396
  const labels = [];
48857
49397
  for (let i = 0; i < args.length; i++) {
@@ -49100,6 +49640,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49100
49640
  case "surf":
49101
49641
  surfCall(instructions, args);
49102
49642
  return true;
49643
+ case "surface":
49644
+ surfaceCall(instructions, args);
49645
+ return true;
49103
49646
  case "scatter":
49104
49647
  scatterCall(instructions, args);
49105
49648
  return true;
@@ -49179,6 +49722,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49179
49722
  case "quiver":
49180
49723
  quiverCall(instructions, args);
49181
49724
  return true;
49725
+ case "quiver3":
49726
+ quiver3Call(instructions, args);
49727
+ return true;
49182
49728
  case "view":
49183
49729
  viewCall(instructions, args);
49184
49730
  return true;
@@ -49244,6 +49790,18 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
49244
49790
  case "clf":
49245
49791
  plotInstr(instructions, { type: "clf" });
49246
49792
  return true;
49793
+ case "cla": {
49794
+ let reset = false;
49795
+ for (const a of args) {
49796
+ try {
49797
+ if (toString(a).toLowerCase().replace(/^["']|["']$/g, "") === "reset")
49798
+ reset = true;
49799
+ } catch {
49800
+ }
49801
+ }
49802
+ plotInstr(instructions, { type: "cla", reset });
49803
+ return true;
49804
+ }
49247
49805
  case "shading": {
49248
49806
  if (args.length > 0) {
49249
49807
  plotInstr(instructions, { type: "set_shading", shading: args[0] });
@@ -49421,6 +49979,7 @@ var PLOT_DISPATCH_NAMES = [
49421
49979
  "plot",
49422
49980
  "plot3",
49423
49981
  "surf",
49982
+ "surface",
49424
49983
  "scatter",
49425
49984
  "imagesc",
49426
49985
  "pcolor",
@@ -49448,6 +50007,7 @@ var PLOT_DISPATCH_NAMES = [
49448
50007
  "donutchart",
49449
50008
  "heatmap",
49450
50009
  "quiver",
50010
+ "quiver3",
49451
50011
  "view",
49452
50012
  "legend",
49453
50013
  "figure",
@@ -49463,6 +50023,7 @@ var PLOT_DISPATCH_NAMES = [
49463
50023
  "grid",
49464
50024
  "close",
49465
50025
  "clf",
50026
+ "cla",
49466
50027
  "shading",
49467
50028
  "colorbar",
49468
50029
  "colormap",
@@ -49986,6 +50547,7 @@ var SPECIAL_BUILTIN_NAMES = [
49986
50547
  "plot",
49987
50548
  "plot3",
49988
50549
  "surf",
50550
+ "surface",
49989
50551
  "scatter",
49990
50552
  "imagesc",
49991
50553
  "pcolor",
@@ -50015,6 +50577,7 @@ var SPECIAL_BUILTIN_NAMES = [
50015
50577
  "donutchart",
50016
50578
  "heatmap",
50017
50579
  "quiver",
50580
+ "quiver3",
50018
50581
  "streamline",
50019
50582
  "stream2",
50020
50583
  "ishold",
@@ -50032,6 +50595,7 @@ var SPECIAL_BUILTIN_NAMES = [
50032
50595
  "sgtitle",
50033
50596
  "shading",
50034
50597
  "clf",
50598
+ "cla",
50035
50599
  "colormap",
50036
50600
  "view",
50037
50601
  "zlabel",
@@ -50924,13 +51488,21 @@ function registerSpecialBuiltins(rt) {
50924
51488
  }
50925
51489
  });
50926
51490
  registerSpecialVoid("delete", (args) => {
50927
- const io = requireFileIO();
50928
- if (!io.deleteFile)
50929
- throw new RuntimeError("delete is not available in this environment");
50930
51491
  const margs = args.map((a) => ensureRuntimeValue(a));
50931
51492
  if (margs.length < 1)
50932
51493
  throw new RuntimeError("delete requires at least 1 argument");
50933
51494
  for (const arg of margs) {
51495
+ if (isRuntimeGraphicsHandle(arg)) {
51496
+ const instr = arg._trace.__instruction;
51497
+ if (instr) {
51498
+ const idx = rt.plotInstructions.indexOf(instr);
51499
+ if (idx >= 0) rt.plotInstructions.splice(idx, 1);
51500
+ }
51501
+ continue;
51502
+ }
51503
+ const io = requireFileIO();
51504
+ if (!io.deleteFile)
51505
+ throw new RuntimeError("delete is not available in this environment");
50934
51506
  io.deleteFile(toString(arg));
50935
51507
  }
50936
51508
  });
@@ -51399,7 +51971,7 @@ function registerSpecialBuiltins(rt) {
51399
51971
  return nargout >= 1 ? RTV.num(1) : void 0;
51400
51972
  });
51401
51973
  }
51402
- const PLOT_VOID = ["hold", "grid", "shading"];
51974
+ const PLOT_VOID = ["hold", "grid", "shading", "cla"];
51403
51975
  for (const name of PLOT_VOID) {
51404
51976
  registerSpecialVoid(name, (args) => {
51405
51977
  dispatchPlotBuiltin(
@@ -52240,7 +52812,8 @@ var PLOT_DISPATCH_NAMES_JIT = /* @__PURE__ */ new Set([
52240
52812
  "piechart",
52241
52813
  "donutchart",
52242
52814
  "heatmap",
52243
- "quiver"
52815
+ "quiver",
52816
+ "quiver3"
52244
52817
  ]);
52245
52818
  function dispatchPlotCall(rt, name, args) {
52246
52819
  if (PLOT_DISPATCH_NAMES_JIT.has(name)) {
@@ -53223,45 +53796,70 @@ function classInstanceParenIndex(rt, mv, base, indices, nargout, skipSubsref) {
53223
53796
  }
53224
53797
  throw new RuntimeError(`Index exceeds class instance dimensions`);
53225
53798
  }
53799
+ function resolveObjSubscript(raw, dimSize) {
53800
+ if (raw === COLON_SENTINEL) {
53801
+ return Array.from({ length: dimSize }, (_, i) => i);
53802
+ }
53803
+ if (typeof raw === "number") return [Math.round(raw) - 1];
53804
+ const rv = ensureRuntimeValue(raw);
53805
+ if (isRuntimeNumber(rv)) return [Math.round(toNumber(rv)) - 1];
53806
+ if (isRuntimeLogical(rv)) return rv ? [0] : [];
53807
+ if (isRuntimeTensor(rv)) {
53808
+ if (rv._isLogical) {
53809
+ const out = [];
53810
+ for (let k = 0; k < rv.data.length; k++) if (rv.data[k]) out.push(k);
53811
+ return out;
53812
+ }
53813
+ return Array.from(rv.data, (x) => Math.round(x) - 1);
53814
+ }
53815
+ throw new RuntimeError("Invalid index type for class instance array");
53816
+ }
53817
+ function makeObjResult(className, elements, shape) {
53818
+ if (elements.length === 1) return elements[0];
53819
+ return {
53820
+ kind: "class_instance_array",
53821
+ className,
53822
+ elements,
53823
+ shape
53824
+ };
53825
+ }
53226
53826
  function classInstanceArrayParenIndex(mv, indices) {
53827
+ const [rows, cols] = mv.shape;
53828
+ const total = mv.elements.length;
53829
+ if (indices.length === 2) {
53830
+ const ri = resolveObjSubscript(indices[0], rows);
53831
+ const ci = resolveObjSubscript(indices[1], cols);
53832
+ const selected2 = [];
53833
+ for (const j of ci) {
53834
+ if (j < 0 || j >= cols)
53835
+ throw new RuntimeError("Index exceeds array bounds");
53836
+ for (const i of ri) {
53837
+ if (i < 0 || i >= rows)
53838
+ throw new RuntimeError("Index exceeds array bounds");
53839
+ selected2.push(mv.elements[j * rows + i]);
53840
+ }
53841
+ }
53842
+ return makeObjResult(mv.className, selected2, [ri.length, ci.length]);
53843
+ }
53227
53844
  if (indices.length !== 1) {
53228
53845
  throw new RuntimeError(
53229
- "Class instance arrays only support single-subscript indexing"
53846
+ "Class instance arrays support one- or two-subscript indexing"
53230
53847
  );
53231
53848
  }
53232
53849
  const idx = indices[0];
53233
53850
  if (idx === COLON_SENTINEL) {
53234
- return mv;
53235
- }
53236
- if (typeof idx === "number") {
53237
- const i = Math.round(idx) - 1;
53238
- if (i < 0 || i >= mv.elements.length)
53239
- throw new RuntimeError("Index exceeds array bounds");
53240
- return mv.elements[i];
53851
+ return makeObjResult(mv.className, mv.elements.slice(), [total, 1]);
53241
53852
  }
53242
- const rv = ensureRuntimeValue(idx);
53243
- if (isRuntimeLogical(rv)) {
53244
- const i = rv ? 0 : -1;
53245
- if (i < 0 || i >= mv.elements.length)
53853
+ const linear = resolveObjSubscript(idx, total);
53854
+ const selected = [];
53855
+ for (const i of linear) {
53856
+ if (i < 0 || i >= total)
53246
53857
  throw new RuntimeError("Index exceeds array bounds");
53247
- return mv.elements[i];
53858
+ selected.push(mv.elements[i]);
53248
53859
  }
53249
- if (isRuntimeTensor(rv)) {
53250
- const selected = [];
53251
- for (let k = 0; k < rv.data.length; k++) {
53252
- const i = Math.round(rv.data[k]) - 1;
53253
- if (i < 0 || i >= mv.elements.length)
53254
- throw new RuntimeError("Index exceeds array bounds");
53255
- selected.push(mv.elements[i]);
53256
- }
53257
- if (selected.length === 1) return selected[0];
53258
- return {
53259
- kind: "class_instance_array",
53260
- className: mv.className,
53261
- elements: selected
53262
- };
53263
- }
53264
- throw new RuntimeError("Invalid index type for class instance array");
53860
+ const isColumn = cols === 1 && rows !== 1;
53861
+ const shape = isColumn ? [selected.length, 1] : [1, selected.length];
53862
+ return makeObjResult(mv.className, selected, shape);
53265
53863
  }
53266
53864
  function resolveIndicesForClassInstance(rt, mv, base, indices) {
53267
53865
  const numIndices = indices.length;
@@ -53958,6 +54556,43 @@ var Runtime = class _Runtime {
53958
54556
  return RTV.dummyHandle();
53959
54557
  }
53960
54558
  };
54559
+ const contourOverride = (filled) => (_nargout, args) => {
54560
+ const margs = args.map((a) => ensureRuntimeValue(a));
54561
+ contourCall(this.plotInstructions, margs, filled);
54562
+ if (_nargout < 1) return void 0;
54563
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
54564
+ if (!last || last.type !== "contour") return RTV.dummyHandle();
54565
+ const trace = last.trace;
54566
+ const cm = computeContourMatrix(trace);
54567
+ const C = RTV.tensor(allocFloat64Array(cm.data), [2, cm.n]);
54568
+ if (_nargout < 2) return C;
54569
+ const H2 = RTV.graphicsHandle(
54570
+ {
54571
+ LineWidth: trace.lineWidth ?? 0.5,
54572
+ LineStyle: trace.lineStyle ?? "-",
54573
+ LineColor: trace.lineColor ?? "flat",
54574
+ LevelList: cm.levelList,
54575
+ __instruction: last
54576
+ },
54577
+ "contour"
54578
+ );
54579
+ return [C, H2];
54580
+ };
54581
+ this.builtins["contour"] = contourOverride(false);
54582
+ this.builtins["contourf"] = contourOverride(true);
54583
+ this.builtins["quiver3"] = (_nargout, args) => {
54584
+ const margs = args.map((a) => ensureRuntimeValue(a));
54585
+ quiver3Call(this.plotInstructions, margs);
54586
+ if (_nargout < 1) return void 0;
54587
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
54588
+ if (last && last.type === "quiver3") {
54589
+ return RTV.graphicsHandle(
54590
+ last.trace,
54591
+ "quiver3"
54592
+ );
54593
+ }
54594
+ return RTV.dummyHandle();
54595
+ };
53961
54596
  this.builtins["fplot"] = (_nargout, args) => {
53962
54597
  fplotCall(this, this.plotInstructions, args.map(ensureRuntimeValue));
53963
54598
  };
@@ -54505,14 +55140,15 @@ var Runtime = class _Runtime {
54505
55140
  transpose(v) {
54506
55141
  if (typeof v !== "number") {
54507
55142
  const mv = ensureRuntimeValue(v);
54508
- if (isRuntimeClassInstance(mv)) return this.dispatch("transpose", 1, [v]);
55143
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
55144
+ return this.dispatch("transpose", 1, [v]);
54509
55145
  }
54510
55146
  return transpose(v);
54511
55147
  }
54512
55148
  ctranspose(v) {
54513
55149
  if (typeof v !== "number") {
54514
55150
  const mv = ensureRuntimeValue(v);
54515
- if (isRuntimeClassInstance(mv))
55151
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
54516
55152
  return this.dispatch("ctranspose", 1, [v]);
54517
55153
  }
54518
55154
  return ctranspose(v);
@@ -54596,7 +55232,9 @@ var Runtime = class _Runtime {
54596
55232
  const mvals = flat.map(
54597
55233
  (e) => typeof e === "number" ? null : ensureRuntimeValue(e)
54598
55234
  );
54599
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
55235
+ if (mvals.some(
55236
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
55237
+ )) {
54600
55238
  if (this._classHasMethod(mvals, "horzcat")) {
54601
55239
  return this.dispatch("horzcat", 1, flat);
54602
55240
  }
@@ -54609,7 +55247,9 @@ var Runtime = class _Runtime {
54609
55247
  const mvals = rows.map(
54610
55248
  (r) => typeof r === "number" ? null : ensureRuntimeValue(r)
54611
55249
  );
54612
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
55250
+ if (mvals.some(
55251
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
55252
+ )) {
54613
55253
  if (this._classHasMethod(mvals, "vertcat")) {
54614
55254
  return this.dispatch("vertcat", 1, rows);
54615
55255
  }
@@ -54621,7 +55261,7 @@ var Runtime = class _Runtime {
54621
55261
  _classHasMethod(mvals, methodName) {
54622
55262
  if (!this.resolveClassMethod) return false;
54623
55263
  for (const v of mvals) {
54624
- if (v && isRuntimeClassInstance(v)) {
55264
+ if (v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))) {
54625
55265
  if (this.resolveClassMethod(v.className, methodName) !== null) {
54626
55266
  return true;
54627
55267
  }
@@ -54932,31 +55572,66 @@ function buildStackField(e) {
54932
55572
  }
54933
55573
  return RTV.structArray(fieldNames, elements);
54934
55574
  }
54935
- function collectClassInstances(items) {
54936
- const out = [];
54937
- for (const item of items) {
54938
- const rv = ensureRuntimeValue(item);
54939
- if (isRuntimeClassInstance(rv)) {
54940
- out.push(rv);
54941
- } else if (isRuntimeClassInstanceArray(rv)) {
54942
- out.push(...rv.elements);
54943
- } else {
55575
+ function asObjArrayParts(item) {
55576
+ const rv = ensureRuntimeValue(item);
55577
+ if (isRuntimeClassInstance(rv))
55578
+ return { rows: 1, cols: 1, elements: [rv], className: rv.className };
55579
+ if (isRuntimeClassInstanceArray(rv))
55580
+ return {
55581
+ rows: rv.shape[0],
55582
+ cols: rv.shape[1],
55583
+ elements: rv.elements,
55584
+ className: rv.className
55585
+ };
55586
+ if (isRuntimeTensor(rv) && rv.data.length === 0) return null;
55587
+ throw new RuntimeError(`Cannot concatenate ${kstr(rv)} with class instances`);
55588
+ }
55589
+ function objColumn(p2, j) {
55590
+ return p2.elements.slice(j * p2.rows, j * p2.rows + p2.rows);
55591
+ }
55592
+ function defaultClassInstanceHorzcat(items) {
55593
+ const parts = items.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
55594
+ if (parts.length === 0)
55595
+ throw new RuntimeError("Cannot concatenate empty class instances");
55596
+ const rows = parts[0].rows;
55597
+ let cols = 0;
55598
+ const elements = [];
55599
+ for (const p2 of parts) {
55600
+ if (p2.rows !== rows)
54944
55601
  throw new RuntimeError(
54945
- `Cannot concatenate ${kstr(rv)} with class instances`
55602
+ "Dimensions of arrays being concatenated are not consistent"
54946
55603
  );
54947
- }
55604
+ elements.push(...p2.elements);
55605
+ cols += p2.cols;
54948
55606
  }
54949
- return out;
54950
- }
54951
- function defaultClassInstanceHorzcat(items) {
54952
- const elements = collectClassInstances(items);
54953
55607
  if (elements.length === 1) return elements[0];
54954
- return new RuntimeClassInstanceArray(elements[0].className, elements);
55608
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
55609
+ rows,
55610
+ cols
55611
+ ]);
54955
55612
  }
54956
55613
  function defaultClassInstanceVertcat(rows) {
54957
- const elements = collectClassInstances(rows);
55614
+ const parts = rows.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
55615
+ if (parts.length === 0)
55616
+ throw new RuntimeError("Cannot concatenate empty class instances");
55617
+ const cols = parts[0].cols;
55618
+ let totalRows = 0;
55619
+ for (const p2 of parts) {
55620
+ if (p2.cols !== cols)
55621
+ throw new RuntimeError(
55622
+ "Dimensions of arrays being concatenated are not consistent"
55623
+ );
55624
+ totalRows += p2.rows;
55625
+ }
55626
+ const elements = [];
55627
+ for (let j = 0; j < cols; j++) {
55628
+ for (const p2 of parts) elements.push(...objColumn(p2, j));
55629
+ }
54958
55630
  if (elements.length === 1) return elements[0];
54959
- return new RuntimeClassInstanceArray(elements[0].className, elements);
55631
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
55632
+ totalRows,
55633
+ cols
55634
+ ]);
54960
55635
  }
54961
55636
 
54962
55637
  // src/numbl-core/jsUserFunctions.ts
@@ -58647,16 +59322,27 @@ function callUserFunction(fn, args, nargout, narginOverride) {
58647
59322
  const fnEnv = new Environment();
58648
59323
  fnEnv.rt = this.rt;
58649
59324
  fnEnv.persistentFuncId = `${this.currentFile}:${fn.name}`;
58650
- const processedArgs = this.processArgumentsBlocks(fn, sharedArgs);
58651
59325
  const hasVarargin = fn.params.length > 0 && fn.params[fn.params.length - 1] === "varargin";
58652
59326
  const regularParams = hasVarargin ? fn.params.slice(0, -1) : fn.params;
58653
- for (let i = 0; i < regularParams.length; i++) {
58654
- if (i < processedArgs.length) {
58655
- fnEnv.set(regularParams[i], ensureRuntimeValue(processedArgs[i]));
59327
+ let numPositional = regularParams.length;
59328
+ const inputBlocks = (fn.argumentsBlocks ?? []).filter(
59329
+ (b) => b.kind !== "Output"
59330
+ );
59331
+ for (const block of inputBlocks) {
59332
+ for (const e of block.entries) {
59333
+ const dot2 = e.name.indexOf(".");
59334
+ if (dot2 < 0) continue;
59335
+ const idx = fn.params.indexOf(e.name.slice(0, dot2));
59336
+ if (idx >= 0 && idx < numPositional) numPositional = idx;
59337
+ }
59338
+ }
59339
+ for (let i = 0; i < numPositional && i < regularParams.length; i++) {
59340
+ if (i < sharedArgs.length && sharedArgs[i] !== void 0) {
59341
+ fnEnv.set(regularParams[i], ensureRuntimeValue(sharedArgs[i]));
58656
59342
  }
58657
59343
  }
58658
59344
  if (hasVarargin) {
58659
- const extraArgs = processedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
59345
+ const extraArgs = sharedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
58660
59346
  fnEnv.set("varargin", RTV.cell(extraArgs, [1, extraArgs.length]));
58661
59347
  }
58662
59348
  fnEnv.set("$nargin", narginOverride ?? args.length);
@@ -58676,6 +59362,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
58676
59362
  this.rt.pushCallFrame(fn.name);
58677
59363
  this.rt.pushCleanupScope();
58678
59364
  try {
59365
+ this.processArgumentsBlocks(fn, sharedArgs);
58679
59366
  this.execStmts(fn.body);
58680
59367
  if (fnEnv.persistentFuncId) {
58681
59368
  for (const name of fnEnv.persistentNames) {
@@ -58919,12 +59606,13 @@ function findExternalMethod(classInfo, methodName) {
58919
59606
  return null;
58920
59607
  }
58921
59608
  const ast = this.ctx.getCachedAST(mf.fileName);
59609
+ let primary = null;
58922
59610
  for (const stmt of ast.body) {
58923
- if (stmt.type === "Function" && stmt.name === methodName) {
58924
- return funcDefFromStmt(stmt);
58925
- }
59611
+ if (stmt.type !== "Function") continue;
59612
+ if (stmt.name === methodName) return funcDefFromStmt(stmt);
59613
+ if (!primary) primary = funcDefFromStmt(stmt);
58926
59614
  }
58927
- return null;
59615
+ return primary;
58928
59616
  }
58929
59617
  function collectClassProperties(classInfo) {
58930
59618
  const propertyNames = [...classInfo.propertyNames];
@@ -58998,7 +59686,7 @@ function evalInLocalScope(codeArg, fileName) {
58998
59686
  }
58999
59687
  function processArgumentsBlocks(fn, args) {
59000
59688
  const argBlocks = fn.argumentsBlocks;
59001
- if (!argBlocks || argBlocks.length === 0) return args;
59689
+ if (!argBlocks || argBlocks.length === 0) return;
59002
59690
  for (const block of argBlocks) {
59003
59691
  if (block.kind === "Output") continue;
59004
59692
  const entries = block.entries;
@@ -59019,48 +59707,27 @@ function processArgumentsBlocks(fn, args) {
59019
59707
  }
59020
59708
  return true;
59021
59709
  });
59022
- if (nvGroups.size > 0) {
59023
- const processedArgs2 = [...args];
59024
- const nvParamIndex = regularEntries.length;
59025
- for (const [paramName, fields] of nvGroups) {
59026
- const nvArgs = args.slice(nvParamIndex);
59027
- const defaults = {};
59028
- for (const { field, defaultExpr } of fields) {
59029
- if (defaultExpr) {
59030
- try {
59031
- defaults[field] = this.evalExpr(defaultExpr);
59032
- } catch {
59033
- }
59034
- }
59035
- }
59036
- const struct = this.rt.buildNameValueStruct(nvArgs, defaults);
59037
- const paramIdx = fn.params.indexOf(paramName);
59038
- const targetIdx = paramIdx >= 0 ? paramIdx : nvParamIndex;
59039
- processedArgs2.length = Math.max(processedArgs2.length, targetIdx + 1);
59040
- processedArgs2[targetIdx] = struct;
59041
- }
59042
- for (let i = 0; i < regularEntries.length; i++) {
59043
- if (processedArgs2[i] === void 0 && regularEntries[i].defaultValue) {
59044
- try {
59045
- processedArgs2[i] = this.evalExpr(regularEntries[i].defaultValue);
59046
- } catch {
59047
- }
59048
- }
59710
+ for (const entry of regularEntries) {
59711
+ const pIdx = fn.params.indexOf(entry.name);
59712
+ if (pIdx < 0) continue;
59713
+ const provided = pIdx < args.length && args[pIdx] !== void 0;
59714
+ if (!provided && entry.defaultValue) {
59715
+ this.env.set(
59716
+ entry.name,
59717
+ ensureRuntimeValue(this.evalExpr(entry.defaultValue))
59718
+ );
59049
59719
  }
59050
- return processedArgs2;
59051
59720
  }
59052
- const processedArgs = [...args];
59053
- for (let i = 0; i < entries.length; i++) {
59054
- if (processedArgs[i] === void 0 && entries[i].defaultValue) {
59055
- try {
59056
- processedArgs[i] = this.evalExpr(entries[i].defaultValue);
59057
- } catch {
59058
- }
59721
+ for (const [paramName, fields] of nvGroups) {
59722
+ const pIdx = fn.params.indexOf(paramName);
59723
+ const nvArgs = args.slice(pIdx >= 0 ? pIdx : args.length);
59724
+ const defaults = {};
59725
+ for (const { field, defaultExpr } of fields) {
59726
+ if (defaultExpr) defaults[field] = this.evalExpr(defaultExpr);
59059
59727
  }
59728
+ this.env.set(paramName, this.rt.buildNameValueStruct(nvArgs, defaults));
59060
59729
  }
59061
- return processedArgs;
59062
59730
  }
59063
- return args;
59064
59731
  }
59065
59732
 
59066
59733
  // src/numbl-core/interpreter/interpreter.ts
@@ -63176,6 +63843,15 @@ function mtoc2_tensor_flip_complex(a, dimIdx) {
63176
63843
  return r;
63177
63844
  }
63178
63845
 
63846
+ // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_imag_all_zero.js
63847
+ function mtoc2_tensor_imag_all_zero(a) {
63848
+ if (a.imag === void 0) return true;
63849
+ for (let i = 0; i < a.imag.length; i++) {
63850
+ if (a.imag[i] !== 0) return false;
63851
+ }
63852
+ return true;
63853
+ }
63854
+
63179
63855
  // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_linspace.js
63180
63856
  function mtoc2_tensor_linspace(a, b, n) {
63181
63857
  if (n < 0) n = 0;
@@ -63477,6 +64153,13 @@ function cSqueezeTrailing(dims) {
63477
64153
  function cReduceLaneIm(t, i) {
63478
64154
  return t.imag !== void 0 ? t.imag[i] : 0;
63479
64155
  }
64156
+ function cReduceAllImagZero(t) {
64157
+ if (t.imag === void 0) return true;
64158
+ for (let i = 0; i < t.imag.length; i++) {
64159
+ if (t.imag[i] !== 0) return false;
64160
+ }
64161
+ return true;
64162
+ }
63480
64163
  function complexAccumAll(t, init, accum, finalize) {
63481
64164
  let acc = { ...init };
63482
64165
  for (let i = 0; i < t.data.length; i++) {
@@ -63534,6 +64217,7 @@ var mtoc2_prod_complex_dim = (t, d) => complexAccumDim(t, d, cProdInit, cProdAcc
63534
64217
  var mtoc2_mean_complex_all = (t) => complexAccumAll(t, cSumInit, cSumAccum, cMeanFinalize);
63535
64218
  var mtoc2_mean_complex_dim = (t, d) => complexAccumDim(t, d, cSumInit, cSumAccum, cMeanFinalize);
63536
64219
  function complexMinmaxAll(t, cmp) {
64220
+ const realMode = cReduceAllImagZero(t);
63537
64221
  let found = false;
63538
64222
  let mRe = NaN;
63539
64223
  let mIm = 0;
@@ -63541,7 +64225,8 @@ function complexMinmaxAll(t, cmp) {
63541
64225
  const xr = t.data[i];
63542
64226
  const xi = cReduceLaneIm(t, i);
63543
64227
  if (xr !== xr || xi !== xi) continue;
63544
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
64228
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
64229
+ if (!found || better) {
63545
64230
  mRe = xr;
63546
64231
  mIm = xi;
63547
64232
  found = true;
@@ -63564,6 +64249,7 @@ function complexMinmaxDim(t, dim, cmp) {
63564
64249
  return out2;
63565
64250
  }
63566
64251
  const dimIdx = dim - 1;
64252
+ const realMode = cReduceAllImagZero(t);
63567
64253
  const axis = t.shape[dimIdx];
63568
64254
  let before = 1;
63569
64255
  for (let i = 0; i < dimIdx; i++) before *= t.shape[i];
@@ -63584,7 +64270,8 @@ function complexMinmaxDim(t, dim, cmp) {
63584
64270
  const xr = t.data[off];
63585
64271
  const xi = cReduceLaneIm(t, off);
63586
64272
  if (xr !== xr || xi !== xi) continue;
63587
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
64273
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
64274
+ if (!found || better) {
63588
64275
  mRe = xr;
63589
64276
  mIm = xi;
63590
64277
  found = true;
@@ -64064,6 +64751,35 @@ function complex_sort_indices(a, descending) {
64064
64751
  const n = a.data.length;
64065
64752
  const im = a.imag;
64066
64753
  const idx = new Array(n);
64754
+ for (let i = 0; i < n; i++) idx[i] = i;
64755
+ let realMode = true;
64756
+ if (im !== void 0) {
64757
+ for (let i = 0; i < n; i++) {
64758
+ if (im[i] !== 0) {
64759
+ realMode = false;
64760
+ break;
64761
+ }
64762
+ }
64763
+ }
64764
+ if (realMode) {
64765
+ const re = a.data;
64766
+ idx.sort((p2, q) => {
64767
+ const rp = re[p2];
64768
+ const rq = re[q];
64769
+ const pNaN = rp !== rp;
64770
+ const qNaN = rq !== rq;
64771
+ if (pNaN && qNaN) return 0;
64772
+ if (descending) {
64773
+ if (pNaN) return -1;
64774
+ if (qNaN) return 1;
64775
+ return rp < rq ? 1 : rp > rq ? -1 : 0;
64776
+ }
64777
+ if (pNaN) return 1;
64778
+ if (qNaN) return -1;
64779
+ return rp < rq ? -1 : rp > rq ? 1 : 0;
64780
+ });
64781
+ return idx;
64782
+ }
64067
64783
  const mag = new Float64Array(n);
64068
64784
  const ph = new Float64Array(n);
64069
64785
  for (let i = 0; i < n; i++) {
@@ -64071,7 +64787,6 @@ function complex_sort_indices(a, descending) {
64071
64787
  const xi = im !== void 0 ? im[i] : 0;
64072
64788
  mag[i] = Math.hypot(re, xi);
64073
64789
  ph[i] = Math.atan2(xi, re);
64074
- idx[i] = i;
64075
64790
  }
64076
64791
  idx.sort((p2, q) => {
64077
64792
  if (mag[p2] < mag[q]) return descending ? 1 : -1;
@@ -65341,6 +66056,7 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
65341
66056
  "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",
65342
66057
  "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",
65343
66058
  "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",
66059
+ "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",
65344
66060
  "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",
65345
66061
  "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',
65346
66062
  "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',
@@ -65352,13 +66068,13 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
65352
66068
  "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",
65353
66069
  "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",
65354
66070
  "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",
65355
- "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',
66071
+ "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',
65356
66072
  "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',
65357
66073
  "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',
65358
66074
  "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',
65359
66075
  "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',
65360
66076
  "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",
65361
- "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',
66077
+ "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",
65362
66078
  "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",
65363
66079
  "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",
65364
66080
  "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",
@@ -65430,6 +66146,7 @@ var JS_SNIPPETS = {
65430
66146
  "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",
65431
66147
  "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",
65432
66148
  "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",
66149
+ "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",
65433
66150
  "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",
65434
66151
  "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',
65435
66152
  "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",
@@ -65440,13 +66157,13 @@ var JS_SNIPPETS = {
65440
66157
  "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",
65441
66158
  "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",
65442
66159
  "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',
65443
- "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',
66160
+ "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',
65444
66161
  "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',
65445
66162
  "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",
65446
66163
  "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",
65447
66164
  "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",
65448
66165
  "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",
65449
- "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",
66166
+ "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",
65450
66167
  "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",
65451
66168
  "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",
65452
66169
  "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",
@@ -65509,6 +66226,7 @@ var JS_IMPORTS = {
65509
66226
  "tensor_fill_nd.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
65510
66227
  "tensor_fill_square.js": ["tensor_fill_nd.js"],
65511
66228
  "tensor_flip.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
66229
+ "tensor_imag_all_zero.js": [],
65512
66230
  "tensor_linspace.js": ["tensor_alloc.js"],
65513
66231
  "tensor_logical_real.js": ["tensor_alloc_nd.js"],
65514
66232
  "tensor_logspace.js": ["tensor_alloc.js"],
@@ -73131,7 +73849,7 @@ function checkArity2(argTypes, nargout) {
73131
73849
  function staticVerdict(t) {
73132
73850
  if (!isNumeric2(t)) return true;
73133
73851
  if (!t.isComplex) return true;
73134
- if (!isScalar(t)) return false;
73852
+ if (!isScalar(t)) return "runtime";
73135
73853
  const ex = t.exact;
73136
73854
  if (ex !== void 0 && typeof ex === "object" && "im" in ex) {
73137
73855
  const im = ex.im;
@@ -73148,23 +73866,33 @@ var isreal = {
73148
73866
  return [scalarLogical(v)];
73149
73867
  },
73150
73868
  emitC({ argTypes, argsC, useRuntime }) {
73151
- const v = staticVerdict(argTypes[0]);
73869
+ const t = argTypes[0];
73870
+ const v = staticVerdict(t);
73152
73871
  if (v === true) return "1.0";
73153
73872
  if (v === false) return "0.0";
73873
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
73874
+ useRuntime("mtoc2_tensor_imag_all_zero");
73875
+ return `mtoc2_tensor_imag_all_zero(${argsC[0]})`;
73876
+ }
73154
73877
  useRuntime("mtoc2_cscalar");
73155
73878
  return `(cimag(${argsC[0]}) == 0.0)`;
73156
73879
  },
73157
- emitJs({ argTypes, argsJs }) {
73158
- const v = staticVerdict(argTypes[0]);
73880
+ emitJs({ argTypes, argsJs, useRuntime }) {
73881
+ const t = argTypes[0];
73882
+ const v = staticVerdict(t);
73159
73883
  if (v === true) return "true";
73160
73884
  if (v === false) return "false";
73885
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
73886
+ useRuntime("mtoc2_tensor_imag_all_zero");
73887
+ return `mtoc2_tensor_imag_all_zero(${argsJs[0]})`;
73888
+ }
73161
73889
  return `(${argsJs[0]}.im === 0)`;
73162
73890
  },
73163
73891
  call({ args }) {
73164
73892
  const v = args[0];
73165
73893
  if (typeof v === "number" || typeof v === "boolean") return [true];
73166
73894
  if (isComplexValue(v)) return [v.im === 0];
73167
- if (isTensor(v)) return [!v.imag];
73895
+ if (isTensor(v)) return [mtoc2_tensor_imag_all_zero(v)];
73168
73896
  return [true];
73169
73897
  }
73170
73898
  };
@@ -81324,6 +82052,13 @@ var REGISTRY2 = /* @__PURE__ */ new Map([
81324
82052
  "mtoc2_cdiv"
81325
82053
  ])
81326
82054
  ],
82055
+ // `isreal` on a complex-typed tensor: scan the imag lane (true iff all
82056
+ // zero). Lets the JIT report realness by value, matching the
82057
+ // interpreter on tensors the type system could not prove real.
82058
+ [
82059
+ "mtoc2_tensor_imag_all_zero",
82060
+ loadSnippet("tensor_imag_all_zero.h", ["mtoc2_tensor_t"])
82061
+ ],
81327
82062
  // ── Elementwise binary/unary on real tensors ──────────────────────
81328
82063
  // One snippet covers all 11 funcs (4×_tt, 4×_ts, 2×_st, 1×uminus).
81329
82064
  // Builtins activate by op-specific synthetic name; all map to the
@@ -86363,15 +87098,18 @@ var Workspace = class _Workspace {
86363
87098
  );
86364
87099
  }
86365
87100
  let primary = null;
87101
+ let firstFn = null;
86366
87102
  for (const stmt of ast.body) {
86367
87103
  if (stmt.type !== "Function") continue;
87104
+ if (!firstFn) firstFn = stmt;
86368
87105
  if (stmt.name === methodName) {
86369
87106
  primary = stmt;
86370
87107
  }
86371
87108
  }
87109
+ primary ??= firstFn;
86372
87110
  if (!primary) {
86373
87111
  throw new UnsupportedConstruct(
86374
- `external method file '${mf.fileName}' has no function named '${methodName}'`,
87112
+ `external method file '${mf.fileName}' has no function`,
86375
87113
  info.ast.span
86376
87114
  );
86377
87115
  }