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-cli/cli.js CHANGED
@@ -786,10 +786,12 @@ var RuntimeClassInstanceArray = class extends Refcounted {
786
786
  kind = "class_instance_array";
787
787
  className;
788
788
  elements;
789
- constructor(className, elements) {
789
+ shape;
790
+ constructor(className, elements, shape) {
790
791
  super();
791
792
  this.className = className;
792
793
  this.elements = elements;
794
+ this.shape = shape ?? [1, elements.length];
793
795
  for (const el of elements) incref(el);
794
796
  }
795
797
  _destroy(rt) {
@@ -3547,10 +3549,11 @@ function sparseElemMul(a, b) {
3547
3549
  }
3548
3550
  function sparseElemMulDense(S, D) {
3549
3551
  const [dRows, dCols] = tensorSize2D(D);
3550
- if (S.m !== dRows || S.n !== dCols)
3552
+ if (!(dRows === S.m || dRows === 1) || !(dCols === S.n || dCols === 1))
3551
3553
  throw new RuntimeError(
3552
3554
  `Matrix dimensions must agree: [${S.m},${S.n}] vs [${dRows},${dCols}]`
3553
3555
  );
3556
+ const denseIdx = (row, col) => (dCols === 1 ? 0 : col) * dRows + (dRows === 1 ? 0 : row);
3554
3557
  const hasImag = isComplexSparse(S) || D.imag !== void 0;
3555
3558
  const irList = [];
3556
3559
  const prList = [];
@@ -3560,7 +3563,7 @@ function sparseElemMulDense(S, D) {
3560
3563
  jc[col] = irList.length;
3561
3564
  for (let k = S.jc[col]; k < S.jc[col + 1]; k++) {
3562
3565
  const row = S.ir[k];
3563
- const idx = col * S.m + row;
3566
+ const idx = denseIdx(row, col);
3564
3567
  const aRe = S.pr[k], aIm = S.pi ? S.pi[k] : 0;
3565
3568
  const bRe = D.data[idx], bIm = D.imag ? D.imag[idx] : 0;
3566
3569
  const re = aRe * bRe - aIm * bIm;
@@ -20924,7 +20927,7 @@ function displayValue(v) {
20924
20927
  case "dictionary":
20925
20928
  return formatDictionary(v);
20926
20929
  case "class_instance_array":
20927
- return ` 1x${v.elements.length} ${v.className} array`;
20930
+ return ` ${v.shape[0]}x${v.shape[1]} ${v.className} array`;
20928
20931
  }
20929
20932
  }
20930
20933
  var formatStructArray = (v) => {
@@ -23337,6 +23340,60 @@ function cellCatAlongDim(values, dimIdx) {
23337
23340
  }
23338
23341
 
23339
23342
  // src/numbl-core/runtime/struct-access.ts
23343
+ function handlePropToRuntime(v) {
23344
+ if (typeof v === "number") return RTV.num(v);
23345
+ if (typeof v === "boolean") return RTV.logical(v);
23346
+ if (typeof v === "string") return RTV.char(v);
23347
+ if (Array.isArray(v)) {
23348
+ const data = allocFloat64Array(v);
23349
+ return RTV.tensor(data, [1, v.length]);
23350
+ }
23351
+ return v;
23352
+ }
23353
+ var HANDLE_DEFAULTS = {
23354
+ quiver3: {
23355
+ LineWidth: 0.5,
23356
+ LineStyle: "-",
23357
+ ShowArrowHead: true,
23358
+ AutoScale: true,
23359
+ AutoScaleFactor: 0.9,
23360
+ Marker: "none",
23361
+ Color: [0, 0.447, 0.741]
23362
+ }
23363
+ };
23364
+ function resolveHandleKey(trace, field) {
23365
+ if (field in trace) return field;
23366
+ const camel = field.charAt(0).toLowerCase() + field.slice(1);
23367
+ if (camel in trace) return camel;
23368
+ if (field.endsWith("Data")) {
23369
+ const base = field.slice(0, -4);
23370
+ const short = base.charAt(0).toLowerCase() + base.slice(1);
23371
+ if (short in trace) return short;
23372
+ }
23373
+ return null;
23374
+ }
23375
+ function runtimeToHandleValue(value, current) {
23376
+ if (typeof current === "boolean") {
23377
+ if (typeof value === "boolean") return value;
23378
+ if (typeof value === "number") return value !== 0;
23379
+ if (isRuntimeString(value) || isRuntimeChar(value)) {
23380
+ const s = (isRuntimeChar(value) ? value.value : value).toLowerCase();
23381
+ return !(s === "off" || s === "false" || s === "0");
23382
+ }
23383
+ return true;
23384
+ }
23385
+ if (typeof current === "string") {
23386
+ return isRuntimeChar(value) ? value.value : isRuntimeString(value) ? value : String(value);
23387
+ }
23388
+ if (typeof current === "number" || current === void 0) {
23389
+ if (typeof value === "number") return value;
23390
+ if (isRuntimeNumber(value)) return value;
23391
+ }
23392
+ if (Array.isArray(current) && isRuntimeTensor(value)) {
23393
+ return Array.from(value.data);
23394
+ }
23395
+ return value;
23396
+ }
23340
23397
  function getRTValueField(base, field) {
23341
23398
  if (isRuntimeStruct(base) || isRuntimeClassInstance(base)) {
23342
23399
  const val = base.fields.get(field);
@@ -23360,6 +23417,15 @@ function getRTValueField(base, field) {
23360
23417
  });
23361
23418
  return horzcat(...values);
23362
23419
  }
23420
+ if (isRuntimeGraphicsHandle(base)) {
23421
+ const key = resolveHandleKey(base._trace, field);
23422
+ if (key !== null) return handlePropToRuntime(base._trace[key]);
23423
+ const dflt = HANDLE_DEFAULTS[base._traceType]?.[field];
23424
+ if (dflt !== void 0) return handlePropToRuntime(dflt);
23425
+ throw new RuntimeError(
23426
+ `No property '${field}' on ${base._traceType} handle`
23427
+ );
23428
+ }
23363
23429
  throw new RuntimeError(`Cannot access field ${field} on ${kstr(base)}`);
23364
23430
  }
23365
23431
  function setRTValueField(base, field, value, rt) {
@@ -23404,6 +23470,12 @@ function setRTValueField(base, field, value, rt) {
23404
23470
  `Cannot assign field '${field}' on a non-scalar struct array without indexing`
23405
23471
  );
23406
23472
  }
23473
+ if (isRuntimeGraphicsHandle(base)) {
23474
+ const key = resolveHandleKey(base._trace, field) ?? field.charAt(0).toLowerCase() + field.slice(1);
23475
+ const current = base._trace[key] ?? HANDLE_DEFAULTS[base._traceType]?.[field];
23476
+ base._trace[key] = runtimeToHandleValue(value, current);
23477
+ return base;
23478
+ }
23407
23479
  if (isRuntimeNumber(base) && base === 0) {
23408
23480
  return RTV.struct(/* @__PURE__ */ new Map([[field, value]]));
23409
23481
  }
@@ -24669,12 +24741,24 @@ function transposeCore(v, conjugate) {
24669
24741
  if (v._isLogical) t._isLogical = true;
24670
24742
  return t;
24671
24743
  }
24744
+ function transposeClassInstanceArray(v) {
24745
+ const [r, c] = v.shape;
24746
+ const out = new Array(v.elements.length);
24747
+ for (let i = 0; i < r; i++) {
24748
+ for (let j = 0; j < c; j++) {
24749
+ out[i * c + j] = v.elements[j * r + i];
24750
+ }
24751
+ }
24752
+ return new RuntimeClassInstanceArray(v.className, out, [c, r]);
24753
+ }
24672
24754
  function mTranspose(v) {
24673
24755
  if (isRuntimeSparseMatrix(v)) return sparseTranspose(v);
24674
24756
  if (isRuntimeComplexNumber(v)) return v;
24675
24757
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
24676
24758
  if (isRuntimeCell(v)) return transposeCellArray(v);
24677
24759
  if (isRuntimeChar(v)) return v;
24760
+ if (isRuntimeClassInstance(v)) return v;
24761
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
24678
24762
  if (!isRuntimeTensor(v))
24679
24763
  throw new RuntimeError("Cannot transpose non-numeric value");
24680
24764
  return transposeCore(v, false);
@@ -24685,6 +24769,8 @@ function mConjugateTranspose(v) {
24685
24769
  if (isRuntimeNumber(v) || isRuntimeLogical(v)) return v;
24686
24770
  if (isRuntimeCell(v)) return transposeCellArray(v);
24687
24771
  if (isRuntimeChar(v)) return v;
24772
+ if (isRuntimeClassInstance(v)) return v;
24773
+ if (isRuntimeClassInstanceArray(v)) return transposeClassInstanceArray(v);
24688
24774
  if (!isRuntimeTensor(v))
24689
24775
  throw new RuntimeError("Cannot transpose non-numeric value");
24690
24776
  return transposeCore(v, true);
@@ -31042,6 +31128,12 @@ function parseContourArgs(args, filled) {
31042
31128
  let rows;
31043
31129
  let cols;
31044
31130
  let nLevels = 10;
31131
+ let levels;
31132
+ const applyLevelArg = (arg) => {
31133
+ const vals = toNumberArray(arg);
31134
+ if (vals.length <= 1) nLevels = vals.length === 1 ? vals[0] : nLevels;
31135
+ else levels = vals;
31136
+ };
31045
31137
  if (numericCount === 1) {
31046
31138
  const info = getMatrixInfo(args[pos++]);
31047
31139
  rows = info.rows;
@@ -31055,8 +31147,7 @@ function parseContourArgs(args, filled) {
31055
31147
  rows = info.rows;
31056
31148
  cols = info.cols;
31057
31149
  zData = info.data;
31058
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
31059
- pos++;
31150
+ applyLevelArg(args[pos++]);
31060
31151
  const gen = generateMeshgrid(rows, cols);
31061
31152
  xData = gen.x;
31062
31153
  yData = gen.y;
@@ -31077,15 +31168,144 @@ function parseContourArgs(args, filled) {
31077
31168
  rows = zInfo.rows;
31078
31169
  cols = zInfo.cols;
31079
31170
  zData = zInfo.data;
31080
- nLevels = typeof args[pos] === "number" ? args[pos] : toNumber(args[pos]);
31081
- pos++;
31171
+ applyLevelArg(args[pos++]);
31082
31172
  const expanded = expandXY(x, y, rows, cols);
31083
31173
  xData = expanded.x;
31084
31174
  yData = expanded.y;
31085
31175
  } else {
31086
31176
  throw new Error("contour requires at least 1 argument");
31087
31177
  }
31088
- return { x: xData, y: yData, z: zData, rows, cols, nLevels, filled };
31178
+ let lineWidth;
31179
+ let lineStyle;
31180
+ let lineColor;
31181
+ for (; pos + 1 < args.length; pos += 2) {
31182
+ if (!isStringArg(args[pos])) break;
31183
+ const name = getStringValue(args[pos]).toLowerCase();
31184
+ const val = args[pos + 1];
31185
+ switch (name) {
31186
+ case "linewidth":
31187
+ lineWidth = toNumber(val);
31188
+ break;
31189
+ case "linestyle":
31190
+ lineStyle = getStringValue(val);
31191
+ break;
31192
+ case "linecolor":
31193
+ case "color":
31194
+ lineColor = isStringArg(val) ? getStringValue(val) : toNumberArray(val);
31195
+ break;
31196
+ case "levellist":
31197
+ case "levels": {
31198
+ const vals = toNumberArray(val);
31199
+ if (vals.length >= 1) levels = vals;
31200
+ break;
31201
+ }
31202
+ }
31203
+ }
31204
+ if (levels) nLevels = levels.length;
31205
+ return {
31206
+ x: xData,
31207
+ y: yData,
31208
+ z: zData,
31209
+ rows,
31210
+ cols,
31211
+ nLevels,
31212
+ ...levels ? { levels } : {},
31213
+ ...lineWidth !== void 0 ? { lineWidth } : {},
31214
+ ...lineStyle !== void 0 ? { lineStyle } : {},
31215
+ ...lineColor !== void 0 ? { lineColor } : {},
31216
+ filled
31217
+ };
31218
+ }
31219
+ function marchingSquaresCell(z00, z10, z01, z11, x00, y00, x10, y10, x01, y01, x11, y11, level) {
31220
+ const code = (z00 >= level ? 1 : 0) | (z10 >= level ? 2 : 0) | (z01 >= level ? 4 : 0) | (z11 >= level ? 8 : 0);
31221
+ if (code === 0 || code === 15) return [];
31222
+ const lerp = (a, b, za, zb) => a + (level - za) / (zb - za || 1) * (b - a);
31223
+ const bx = lerp(x00, x10, z00, z10), by = lerp(y00, y10, z00, z10);
31224
+ const rx = lerp(x10, x11, z10, z11), ry = lerp(y10, y11, z10, z11);
31225
+ const tx = lerp(x01, x11, z01, z11), ty = lerp(y01, y11, z01, z11);
31226
+ const lx = lerp(x00, x01, z00, z01), ly = lerp(y00, y01, z00, z01);
31227
+ const segs = [];
31228
+ switch (code) {
31229
+ case 1:
31230
+ case 14:
31231
+ segs.push([bx, by, lx, ly]);
31232
+ break;
31233
+ case 2:
31234
+ case 13:
31235
+ segs.push([bx, by, rx, ry]);
31236
+ break;
31237
+ case 3:
31238
+ case 12:
31239
+ segs.push([lx, ly, rx, ry]);
31240
+ break;
31241
+ case 4:
31242
+ case 11:
31243
+ segs.push([lx, ly, tx, ty]);
31244
+ break;
31245
+ case 5:
31246
+ case 10:
31247
+ segs.push([bx, by, tx, ty]);
31248
+ break;
31249
+ case 6:
31250
+ case 9:
31251
+ segs.push([bx, by, lx, ly]);
31252
+ segs.push([tx, ty, rx, ry]);
31253
+ break;
31254
+ case 7:
31255
+ case 8:
31256
+ segs.push([tx, ty, rx, ry]);
31257
+ break;
31258
+ }
31259
+ return segs;
31260
+ }
31261
+ function computeContourMatrix(trace) {
31262
+ const { x, y, z, rows, cols } = trace;
31263
+ const at = (a, i, j) => a[j * rows + i];
31264
+ let zMin = Infinity;
31265
+ let zMax = -Infinity;
31266
+ for (const v of z) {
31267
+ if (Number.isFinite(v)) {
31268
+ if (v < zMin) zMin = v;
31269
+ if (v > zMax) zMax = v;
31270
+ }
31271
+ }
31272
+ let levelList;
31273
+ if (trace.levels && trace.levels.length > 0) {
31274
+ levelList = trace.levels.filter((v) => Number.isFinite(v));
31275
+ } else {
31276
+ const n = Math.max(1, Math.round(trace.nLevels));
31277
+ levelList = [];
31278
+ if (Number.isFinite(zMin) && Number.isFinite(zMax) && zMax > zMin) {
31279
+ const step = (zMax - zMin) / (n + 1);
31280
+ for (let k = 1; k <= n; k++) levelList.push(zMin + k * step);
31281
+ }
31282
+ }
31283
+ const data = [];
31284
+ for (const level of levelList) {
31285
+ for (let j = 0; j < cols - 1; j++) {
31286
+ for (let i = 0; i < rows - 1; i++) {
31287
+ const segs = marchingSquaresCell(
31288
+ at(z, i, j),
31289
+ at(z, i + 1, j),
31290
+ at(z, i, j + 1),
31291
+ at(z, i + 1, j + 1),
31292
+ at(x, i, j),
31293
+ at(y, i, j),
31294
+ at(x, i + 1, j),
31295
+ at(y, i + 1, j),
31296
+ at(x, i, j + 1),
31297
+ at(y, i, j + 1),
31298
+ at(x, i + 1, j + 1),
31299
+ at(y, i + 1, j + 1),
31300
+ level
31301
+ );
31302
+ for (const [x1, y1, x2, y2] of segs) {
31303
+ data.push(level, 2, x1, y1, x2, y2);
31304
+ }
31305
+ }
31306
+ }
31307
+ }
31308
+ return { data, n: data.length / 2, levelList };
31089
31309
  }
31090
31310
  function parseMeshArgs(args) {
31091
31311
  const trace = parseSurfArgs(args);
@@ -32033,6 +32253,213 @@ function computeQuiverAutoScale(x, y, u, v, rows, cols, factor) {
32033
32253
  if (maxMag === 0) return 1;
32034
32254
  return factor * spacing / maxMag;
32035
32255
  }
32256
+ function parseQuiver3Args(args) {
32257
+ if (args.length < 4) throw new Error("quiver3 requires at least 4 arguments");
32258
+ let numericCount = 0;
32259
+ for (let i = 0; i < args.length; i++) {
32260
+ if (isNumericArg(args[i])) numericCount++;
32261
+ else break;
32262
+ }
32263
+ let pos = 0;
32264
+ let xData;
32265
+ let yData;
32266
+ let zData;
32267
+ let uData;
32268
+ let vData;
32269
+ let wData;
32270
+ let rows;
32271
+ let cols;
32272
+ const arity = numericCount >= 6 ? 6 : 4;
32273
+ if (arity === 4) {
32274
+ const zInfo = getMatrixInfo(args[pos++]);
32275
+ rows = zInfo.rows;
32276
+ cols = zInfo.cols;
32277
+ zData = zInfo.data;
32278
+ uData = getMatrixInfo(args[pos++]).data;
32279
+ vData = getMatrixInfo(args[pos++]).data;
32280
+ wData = getMatrixInfo(args[pos++]).data;
32281
+ const n = zData.length;
32282
+ if (rows === 1 || cols === 1) {
32283
+ xData = new Array(n);
32284
+ yData = new Array(n);
32285
+ for (let i = 0; i < n; i++) {
32286
+ xData[i] = i + 1;
32287
+ yData[i] = 1;
32288
+ }
32289
+ } else {
32290
+ const gen = generateMeshgrid(rows, n / rows);
32291
+ xData = gen.x;
32292
+ yData = gen.y;
32293
+ }
32294
+ } else {
32295
+ const X = args[pos++];
32296
+ const Y = args[pos++];
32297
+ const zInfo = getMatrixInfo(args[pos++]);
32298
+ rows = zInfo.rows;
32299
+ cols = zInfo.cols;
32300
+ zData = zInfo.data;
32301
+ uData = getMatrixInfo(args[pos++]).data;
32302
+ vData = getMatrixInfo(args[pos++]).data;
32303
+ wData = getMatrixInfo(args[pos++]).data;
32304
+ const n = uData.length;
32305
+ const xFlat = toNumberArray(X);
32306
+ const yFlat = toNumberArray(Y);
32307
+ if (xFlat.length === n && yFlat.length === n) {
32308
+ xData = xFlat;
32309
+ yData = yFlat;
32310
+ } else {
32311
+ const expanded = expandXY(X, Y, rows, cols);
32312
+ xData = expanded.x;
32313
+ yData = expanded.y;
32314
+ }
32315
+ }
32316
+ let autoScale = true;
32317
+ let autoScaleFactor = 0.9;
32318
+ let reportedASF = 0.9;
32319
+ if (pos < args.length && isNumericArg(args[pos]) && !isStringArg(args[pos])) {
32320
+ const s = toNumber(args[pos]);
32321
+ if (s === 0) {
32322
+ autoScale = false;
32323
+ } else {
32324
+ autoScaleFactor = s * 0.9;
32325
+ reportedASF = s;
32326
+ }
32327
+ pos++;
32328
+ }
32329
+ const trace = {
32330
+ x: xData,
32331
+ y: yData,
32332
+ z: zData,
32333
+ u: uData,
32334
+ v: vData,
32335
+ w: wData,
32336
+ showArrowHead: true
32337
+ };
32338
+ while (pos < args.length && isStringArg(args[pos]) && !isQuiverNameValueKey(args[pos])) {
32339
+ const s = getStringIfString(args[pos]);
32340
+ if (s === void 0) break;
32341
+ if (s === "off") {
32342
+ autoScale = false;
32343
+ pos++;
32344
+ continue;
32345
+ }
32346
+ if (s === "filled") {
32347
+ trace.markerFilled = true;
32348
+ pos++;
32349
+ continue;
32350
+ }
32351
+ const spec = parseLineSpec(s);
32352
+ if (spec) {
32353
+ if (spec.color) trace.color = COLOR_SHORT[spec.color];
32354
+ if (spec.lineStyle) trace.lineStyle = spec.lineStyle;
32355
+ if (spec.marker) {
32356
+ trace.marker = spec.marker;
32357
+ trace.showArrowHead = false;
32358
+ }
32359
+ pos++;
32360
+ continue;
32361
+ }
32362
+ const c = resolveColor(s);
32363
+ if (c) {
32364
+ trace.color = c;
32365
+ pos++;
32366
+ continue;
32367
+ }
32368
+ break;
32369
+ }
32370
+ while (pos < args.length) {
32371
+ const key = isQuiverNameValueKey(args[pos]);
32372
+ if (!key) break;
32373
+ pos++;
32374
+ if (pos >= args.length) break;
32375
+ const value = args[pos++];
32376
+ switch (key) {
32377
+ case "color": {
32378
+ const c = resolveColor(value);
32379
+ if (c) trace.color = c;
32380
+ break;
32381
+ }
32382
+ case "linestyle":
32383
+ trace.lineStyle = getStringValue(value);
32384
+ break;
32385
+ case "linewidth":
32386
+ trace.lineWidth = typeof value === "number" ? value : toNumber(value);
32387
+ break;
32388
+ case "marker": {
32389
+ const s = getStringValue(value);
32390
+ trace.marker = s === "none" ? void 0 : s;
32391
+ break;
32392
+ }
32393
+ case "showarrowhead": {
32394
+ const s = getStringValue(value).toLowerCase();
32395
+ trace.showArrowHead = !(s === "off" || s === "false" || s === "0");
32396
+ break;
32397
+ }
32398
+ case "autoscale": {
32399
+ const s = getStringValue(value).toLowerCase();
32400
+ autoScale = !(s === "off" || s === "false" || s === "0");
32401
+ break;
32402
+ }
32403
+ case "autoscalefactor": {
32404
+ const n = typeof value === "number" ? value : toNumber(value);
32405
+ autoScaleFactor = n * 0.9;
32406
+ reportedASF = n;
32407
+ break;
32408
+ }
32409
+ }
32410
+ }
32411
+ trace.autoScale = autoScale;
32412
+ trace.autoScaleFactor = reportedASF;
32413
+ if (autoScale) {
32414
+ const factor = computeQuiver3AutoScale(
32415
+ xData,
32416
+ yData,
32417
+ zData,
32418
+ uData,
32419
+ vData,
32420
+ wData,
32421
+ autoScaleFactor
32422
+ );
32423
+ if (factor !== 1) {
32424
+ trace.u = uData.map((x) => x * factor);
32425
+ trace.v = vData.map((x) => x * factor);
32426
+ trace.w = wData.map((x) => x * factor);
32427
+ }
32428
+ }
32429
+ return trace;
32430
+ }
32431
+ function computeQuiver3AutoScale(x, y, z, u, v, w, factor) {
32432
+ const n = u.length;
32433
+ if (n === 0) return 1;
32434
+ const range2 = (a) => {
32435
+ let lo = Infinity;
32436
+ let hi = -Infinity;
32437
+ for (const t of a) {
32438
+ if (isFinite(t)) {
32439
+ if (t < lo) lo = t;
32440
+ if (t > hi) hi = t;
32441
+ }
32442
+ }
32443
+ return hi > lo ? hi - lo : 0;
32444
+ };
32445
+ const xr = range2(x);
32446
+ const yr = range2(y);
32447
+ const zr = range2(z);
32448
+ const dims = [xr, yr, zr].filter((d) => d > 0);
32449
+ let spacing;
32450
+ if (dims.length === 3) spacing = Math.cbrt(xr * yr * zr / n);
32451
+ else if (dims.length === 2) spacing = Math.sqrt(dims[0] * dims[1] / n);
32452
+ else if (dims.length === 1) spacing = dims[0] / Math.max(1, n - 1);
32453
+ else spacing = 1;
32454
+ spacing = spacing || 1;
32455
+ let maxMag = 0;
32456
+ for (let i = 0; i < n; i++) {
32457
+ const m = Math.sqrt(u[i] * u[i] + v[i] * v[i] + w[i] * w[i]);
32458
+ if (isFinite(m) && m > maxMag) maxMag = m;
32459
+ }
32460
+ if (maxMag === 0) return 1;
32461
+ return factor * spacing / maxMag;
32462
+ }
32036
32463
 
32037
32464
  // src/numbl-core/runtime/syncChannel.ts
32038
32465
  var syncSleepWarned = false;
@@ -32124,6 +32551,9 @@ function plotInstr(plotInstructions, instr) {
32124
32551
  case "clf":
32125
32552
  plotInstructions.push({ type: instr.type });
32126
32553
  break;
32554
+ case "cla":
32555
+ plotInstructions.push({ type: "cla", reset: instr.reset });
32556
+ break;
32127
32557
  case "set_subplot":
32128
32558
  plotInstructions.push({
32129
32559
  type: "set_subplot",
@@ -32208,6 +32638,10 @@ function surfCall(plotInstructions, args) {
32208
32638
  const trace = parseSurfArgs(args);
32209
32639
  plotInstructions.push({ type: "surf", trace });
32210
32640
  }
32641
+ function surfaceCall(plotInstructions, args) {
32642
+ const trace = parseSurfArgs(args);
32643
+ plotInstructions.push({ type: "surface", trace });
32644
+ }
32211
32645
  function imagescCall(plotInstructions, args) {
32212
32646
  const trace = parseImagescArgs(args);
32213
32647
  plotInstructions.push({ type: "imagesc", trace });
@@ -32460,6 +32894,10 @@ function quiverCall(plotInstructions, args) {
32460
32894
  plotInstructions.push({ type: "quiver", traces });
32461
32895
  }
32462
32896
  }
32897
+ function quiver3Call(plotInstructions, args) {
32898
+ const trace = parseQuiver3Args(args);
32899
+ plotInstructions.push({ type: "quiver3", trace });
32900
+ }
32463
32901
  function legendCall(plotInstructions, args) {
32464
32902
  const labels = [];
32465
32903
  for (let i = 0; i < args.length; i++) {
@@ -32708,6 +33146,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
32708
33146
  case "surf":
32709
33147
  surfCall(instructions, args);
32710
33148
  return true;
33149
+ case "surface":
33150
+ surfaceCall(instructions, args);
33151
+ return true;
32711
33152
  case "scatter":
32712
33153
  scatterCall(instructions, args);
32713
33154
  return true;
@@ -32787,6 +33228,9 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
32787
33228
  case "quiver":
32788
33229
  quiverCall(instructions, args);
32789
33230
  return true;
33231
+ case "quiver3":
33232
+ quiver3Call(instructions, args);
33233
+ return true;
32790
33234
  case "view":
32791
33235
  viewCall(instructions, args);
32792
33236
  return true;
@@ -32852,6 +33296,18 @@ function dispatchPlotBuiltin(name, args, instructions, state) {
32852
33296
  case "clf":
32853
33297
  plotInstr(instructions, { type: "clf" });
32854
33298
  return true;
33299
+ case "cla": {
33300
+ let reset = false;
33301
+ for (const a of args) {
33302
+ try {
33303
+ if (toString(a).toLowerCase().replace(/^["']|["']$/g, "") === "reset")
33304
+ reset = true;
33305
+ } catch {
33306
+ }
33307
+ }
33308
+ plotInstr(instructions, { type: "cla", reset });
33309
+ return true;
33310
+ }
32855
33311
  case "shading": {
32856
33312
  if (args.length > 0) {
32857
33313
  plotInstr(instructions, { type: "set_shading", shading: args[0] });
@@ -33029,6 +33485,7 @@ var PLOT_DISPATCH_NAMES = [
33029
33485
  "plot",
33030
33486
  "plot3",
33031
33487
  "surf",
33488
+ "surface",
33032
33489
  "scatter",
33033
33490
  "imagesc",
33034
33491
  "pcolor",
@@ -33056,6 +33513,7 @@ var PLOT_DISPATCH_NAMES = [
33056
33513
  "donutchart",
33057
33514
  "heatmap",
33058
33515
  "quiver",
33516
+ "quiver3",
33059
33517
  "view",
33060
33518
  "legend",
33061
33519
  "figure",
@@ -33071,6 +33529,7 @@ var PLOT_DISPATCH_NAMES = [
33071
33529
  "grid",
33072
33530
  "close",
33073
33531
  "clf",
33532
+ "cla",
33074
33533
  "shading",
33075
33534
  "colorbar",
33076
33535
  "colormap",
@@ -33128,7 +33587,8 @@ var PLOT_DISPATCH_NAMES_JIT = /* @__PURE__ */ new Set([
33128
33587
  "piechart",
33129
33588
  "donutchart",
33130
33589
  "heatmap",
33131
- "quiver"
33590
+ "quiver",
33591
+ "quiver3"
33132
33592
  ]);
33133
33593
  function dispatchPlotCall(rt, name, args) {
33134
33594
  if (PLOT_DISPATCH_NAMES_JIT.has(name)) {
@@ -34497,45 +34957,70 @@ function classInstanceParenIndex(rt, mv, base, indices, nargout, skipSubsref) {
34497
34957
  }
34498
34958
  throw new RuntimeError(`Index exceeds class instance dimensions`);
34499
34959
  }
34960
+ function resolveObjSubscript(raw, dimSize) {
34961
+ if (raw === COLON_SENTINEL) {
34962
+ return Array.from({ length: dimSize }, (_, i) => i);
34963
+ }
34964
+ if (typeof raw === "number") return [Math.round(raw) - 1];
34965
+ const rv = ensureRuntimeValue(raw);
34966
+ if (isRuntimeNumber(rv)) return [Math.round(toNumber(rv)) - 1];
34967
+ if (isRuntimeLogical(rv)) return rv ? [0] : [];
34968
+ if (isRuntimeTensor(rv)) {
34969
+ if (rv._isLogical) {
34970
+ const out = [];
34971
+ for (let k = 0; k < rv.data.length; k++) if (rv.data[k]) out.push(k);
34972
+ return out;
34973
+ }
34974
+ return Array.from(rv.data, (x) => Math.round(x) - 1);
34975
+ }
34976
+ throw new RuntimeError("Invalid index type for class instance array");
34977
+ }
34978
+ function makeObjResult(className, elements, shape) {
34979
+ if (elements.length === 1) return elements[0];
34980
+ return {
34981
+ kind: "class_instance_array",
34982
+ className,
34983
+ elements,
34984
+ shape
34985
+ };
34986
+ }
34500
34987
  function classInstanceArrayParenIndex(mv, indices) {
34988
+ const [rows, cols] = mv.shape;
34989
+ const total = mv.elements.length;
34990
+ if (indices.length === 2) {
34991
+ const ri = resolveObjSubscript(indices[0], rows);
34992
+ const ci = resolveObjSubscript(indices[1], cols);
34993
+ const selected2 = [];
34994
+ for (const j of ci) {
34995
+ if (j < 0 || j >= cols)
34996
+ throw new RuntimeError("Index exceeds array bounds");
34997
+ for (const i of ri) {
34998
+ if (i < 0 || i >= rows)
34999
+ throw new RuntimeError("Index exceeds array bounds");
35000
+ selected2.push(mv.elements[j * rows + i]);
35001
+ }
35002
+ }
35003
+ return makeObjResult(mv.className, selected2, [ri.length, ci.length]);
35004
+ }
34501
35005
  if (indices.length !== 1) {
34502
35006
  throw new RuntimeError(
34503
- "Class instance arrays only support single-subscript indexing"
35007
+ "Class instance arrays support one- or two-subscript indexing"
34504
35008
  );
34505
35009
  }
34506
35010
  const idx = indices[0];
34507
35011
  if (idx === COLON_SENTINEL) {
34508
- return mv;
34509
- }
34510
- if (typeof idx === "number") {
34511
- const i = Math.round(idx) - 1;
34512
- if (i < 0 || i >= mv.elements.length)
34513
- throw new RuntimeError("Index exceeds array bounds");
34514
- return mv.elements[i];
35012
+ return makeObjResult(mv.className, mv.elements.slice(), [total, 1]);
34515
35013
  }
34516
- const rv = ensureRuntimeValue(idx);
34517
- if (isRuntimeLogical(rv)) {
34518
- const i = rv ? 0 : -1;
34519
- if (i < 0 || i >= mv.elements.length)
35014
+ const linear = resolveObjSubscript(idx, total);
35015
+ const selected = [];
35016
+ for (const i of linear) {
35017
+ if (i < 0 || i >= total)
34520
35018
  throw new RuntimeError("Index exceeds array bounds");
34521
- return mv.elements[i];
35019
+ selected.push(mv.elements[i]);
34522
35020
  }
34523
- if (isRuntimeTensor(rv)) {
34524
- const selected = [];
34525
- for (let k = 0; k < rv.data.length; k++) {
34526
- const i = Math.round(rv.data[k]) - 1;
34527
- if (i < 0 || i >= mv.elements.length)
34528
- throw new RuntimeError("Index exceeds array bounds");
34529
- selected.push(mv.elements[i]);
34530
- }
34531
- if (selected.length === 1) return selected[0];
34532
- return {
34533
- kind: "class_instance_array",
34534
- className: mv.className,
34535
- elements: selected
34536
- };
34537
- }
34538
- throw new RuntimeError("Invalid index type for class instance array");
35021
+ const isColumn = cols === 1 && rows !== 1;
35022
+ const shape = isColumn ? [selected.length, 1] : [1, selected.length];
35023
+ return makeObjResult(mv.className, selected, shape);
34539
35024
  }
34540
35025
  function resolveIndicesForClassInstance(rt, mv, base, indices) {
34541
35026
  const numIndices = indices.length;
@@ -35479,6 +35964,13 @@ for (const name of ["groot", "gcf", "gca", "shg", "newplot"]) {
35479
35964
  })
35480
35965
  });
35481
35966
  }
35967
+ registerIBuiltin({
35968
+ name: "camlight",
35969
+ resolve: () => ({
35970
+ outputTypes: [{ kind: "unknown" }],
35971
+ apply: (_args, nargout) => nargout >= 1 ? RTV.dummyHandle() : void 0
35972
+ })
35973
+ });
35482
35974
  registerIBuiltin({
35483
35975
  name: "get",
35484
35976
  resolve: () => ({
@@ -35633,7 +36125,6 @@ registerIBuiltin({
35633
36125
  })
35634
36126
  });
35635
36127
  for (const cm of [
35636
- "parula",
35637
36128
  "jet",
35638
36129
  "hsv",
35639
36130
  "hot",
@@ -35655,6 +36146,44 @@ for (const cm of [
35655
36146
  })
35656
36147
  });
35657
36148
  }
36149
+ var PARULA_ANCHORS = [
36150
+ [0.2422, 0.1504, 0.6603],
36151
+ [0.278, 0.3556, 0.9777],
36152
+ [0.1085, 0.5267, 0.8943],
36153
+ [0.0469, 0.6353, 0.7861],
36154
+ [0.2161, 0.7269, 0.6499],
36155
+ [0.5044, 0.7993, 0.4519],
36156
+ [0.8328, 0.8056, 0.2008],
36157
+ [0.9871, 0.8092, 0.1432],
36158
+ [0.9763, 0.9831, 0.0538]
36159
+ ];
36160
+ function parulaColormap(m) {
36161
+ const data = allocFloat64Array(m * 3);
36162
+ const last = PARULA_ANCHORS.length - 1;
36163
+ for (let i = 0; i < m; i++) {
36164
+ const t = m <= 1 ? 0 : i / (m - 1) * last;
36165
+ const k = Math.min(last - 1, Math.floor(t));
36166
+ const f = t - k;
36167
+ const a = PARULA_ANCHORS[k];
36168
+ const b = PARULA_ANCHORS[Math.min(last, k + 1)];
36169
+ for (let c = 0; c < 3; c++) {
36170
+ data[c * m + i] = a[c] + f * (b[c] - a[c]);
36171
+ }
36172
+ }
36173
+ return RTV.tensor(data, [m, 3]);
36174
+ }
36175
+ registerIBuiltin({
36176
+ name: "parula",
36177
+ resolve: () => ({
36178
+ outputTypes: [{ kind: "unknown" }],
36179
+ apply: (args) => {
36180
+ if (args.length >= 1 && (isRuntimeNumber(args[0]) || isRuntimeTensor(args[0]))) {
36181
+ return parulaColormap(Math.max(0, Math.round(toNumber(args[0]))));
36182
+ }
36183
+ return RTV.char("parula");
36184
+ }
36185
+ })
36186
+ });
35658
36187
  var appdataStore = /* @__PURE__ */ new Map();
35659
36188
  function resetAppdataStore() {
35660
36189
  const rt = getCurrentRuntime();
@@ -36512,6 +37041,7 @@ var SPECIAL_BUILTIN_NAMES = [
36512
37041
  "plot",
36513
37042
  "plot3",
36514
37043
  "surf",
37044
+ "surface",
36515
37045
  "scatter",
36516
37046
  "imagesc",
36517
37047
  "pcolor",
@@ -36541,6 +37071,7 @@ var SPECIAL_BUILTIN_NAMES = [
36541
37071
  "donutchart",
36542
37072
  "heatmap",
36543
37073
  "quiver",
37074
+ "quiver3",
36544
37075
  "streamline",
36545
37076
  "stream2",
36546
37077
  "ishold",
@@ -36558,6 +37089,7 @@ var SPECIAL_BUILTIN_NAMES = [
36558
37089
  "sgtitle",
36559
37090
  "shading",
36560
37091
  "clf",
37092
+ "cla",
36561
37093
  "colormap",
36562
37094
  "view",
36563
37095
  "zlabel",
@@ -37450,13 +37982,21 @@ function registerSpecialBuiltins(rt) {
37450
37982
  }
37451
37983
  });
37452
37984
  registerSpecialVoid("delete", (args) => {
37453
- const io = requireFileIO();
37454
- if (!io.deleteFile)
37455
- throw new RuntimeError("delete is not available in this environment");
37456
37985
  const margs = args.map((a) => ensureRuntimeValue(a));
37457
37986
  if (margs.length < 1)
37458
37987
  throw new RuntimeError("delete requires at least 1 argument");
37459
37988
  for (const arg of margs) {
37989
+ if (isRuntimeGraphicsHandle(arg)) {
37990
+ const instr = arg._trace.__instruction;
37991
+ if (instr) {
37992
+ const idx = rt.plotInstructions.indexOf(instr);
37993
+ if (idx >= 0) rt.plotInstructions.splice(idx, 1);
37994
+ }
37995
+ continue;
37996
+ }
37997
+ const io = requireFileIO();
37998
+ if (!io.deleteFile)
37999
+ throw new RuntimeError("delete is not available in this environment");
37460
38000
  io.deleteFile(toString(arg));
37461
38001
  }
37462
38002
  });
@@ -37925,7 +38465,7 @@ function registerSpecialBuiltins(rt) {
37925
38465
  return nargout >= 1 ? RTV.num(1) : void 0;
37926
38466
  });
37927
38467
  }
37928
- const PLOT_VOID = ["hold", "grid", "shading"];
38468
+ const PLOT_VOID = ["hold", "grid", "shading", "cla"];
37929
38469
  for (const name of PLOT_VOID) {
37930
38470
  registerSpecialVoid(name, (args) => {
37931
38471
  dispatchPlotBuiltin(
@@ -39088,6 +39628,43 @@ var Runtime = class _Runtime {
39088
39628
  return RTV.dummyHandle();
39089
39629
  }
39090
39630
  };
39631
+ const contourOverride = (filled) => (_nargout, args) => {
39632
+ const margs = args.map((a) => ensureRuntimeValue(a));
39633
+ contourCall(this.plotInstructions, margs, filled);
39634
+ if (_nargout < 1) return void 0;
39635
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
39636
+ if (!last || last.type !== "contour") return RTV.dummyHandle();
39637
+ const trace = last.trace;
39638
+ const cm = computeContourMatrix(trace);
39639
+ const C = RTV.tensor(allocFloat64Array(cm.data), [2, cm.n]);
39640
+ if (_nargout < 2) return C;
39641
+ const H2 = RTV.graphicsHandle(
39642
+ {
39643
+ LineWidth: trace.lineWidth ?? 0.5,
39644
+ LineStyle: trace.lineStyle ?? "-",
39645
+ LineColor: trace.lineColor ?? "flat",
39646
+ LevelList: cm.levelList,
39647
+ __instruction: last
39648
+ },
39649
+ "contour"
39650
+ );
39651
+ return [C, H2];
39652
+ };
39653
+ this.builtins["contour"] = contourOverride(false);
39654
+ this.builtins["contourf"] = contourOverride(true);
39655
+ this.builtins["quiver3"] = (_nargout, args) => {
39656
+ const margs = args.map((a) => ensureRuntimeValue(a));
39657
+ quiver3Call(this.plotInstructions, margs);
39658
+ if (_nargout < 1) return void 0;
39659
+ const last = this.plotInstructions[this.plotInstructions.length - 1];
39660
+ if (last && last.type === "quiver3") {
39661
+ return RTV.graphicsHandle(
39662
+ last.trace,
39663
+ "quiver3"
39664
+ );
39665
+ }
39666
+ return RTV.dummyHandle();
39667
+ };
39091
39668
  this.builtins["fplot"] = (_nargout, args) => {
39092
39669
  fplotCall(this, this.plotInstructions, args.map(ensureRuntimeValue));
39093
39670
  };
@@ -39635,14 +40212,15 @@ var Runtime = class _Runtime {
39635
40212
  transpose(v) {
39636
40213
  if (typeof v !== "number") {
39637
40214
  const mv = ensureRuntimeValue(v);
39638
- if (isRuntimeClassInstance(mv)) return this.dispatch("transpose", 1, [v]);
40215
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
40216
+ return this.dispatch("transpose", 1, [v]);
39639
40217
  }
39640
40218
  return transpose(v);
39641
40219
  }
39642
40220
  ctranspose(v) {
39643
40221
  if (typeof v !== "number") {
39644
40222
  const mv = ensureRuntimeValue(v);
39645
- if (isRuntimeClassInstance(mv))
40223
+ if (isRuntimeClassInstance(mv) || isRuntimeClassInstanceArray(mv))
39646
40224
  return this.dispatch("ctranspose", 1, [v]);
39647
40225
  }
39648
40226
  return ctranspose(v);
@@ -39726,7 +40304,9 @@ var Runtime = class _Runtime {
39726
40304
  const mvals = flat.map(
39727
40305
  (e) => typeof e === "number" ? null : ensureRuntimeValue(e)
39728
40306
  );
39729
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
40307
+ if (mvals.some(
40308
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
40309
+ )) {
39730
40310
  if (this._classHasMethod(mvals, "horzcat")) {
39731
40311
  return this.dispatch("horzcat", 1, flat);
39732
40312
  }
@@ -39739,7 +40319,9 @@ var Runtime = class _Runtime {
39739
40319
  const mvals = rows.map(
39740
40320
  (r) => typeof r === "number" ? null : ensureRuntimeValue(r)
39741
40321
  );
39742
- if (mvals.some((v) => v && isRuntimeClassInstance(v))) {
40322
+ if (mvals.some(
40323
+ (v) => v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))
40324
+ )) {
39743
40325
  if (this._classHasMethod(mvals, "vertcat")) {
39744
40326
  return this.dispatch("vertcat", 1, rows);
39745
40327
  }
@@ -39751,7 +40333,7 @@ var Runtime = class _Runtime {
39751
40333
  _classHasMethod(mvals, methodName) {
39752
40334
  if (!this.resolveClassMethod) return false;
39753
40335
  for (const v of mvals) {
39754
- if (v && isRuntimeClassInstance(v)) {
40336
+ if (v && (isRuntimeClassInstance(v) || isRuntimeClassInstanceArray(v))) {
39755
40337
  if (this.resolveClassMethod(v.className, methodName) !== null) {
39756
40338
  return true;
39757
40339
  }
@@ -40062,31 +40644,66 @@ function buildStackField(e) {
40062
40644
  }
40063
40645
  return RTV.structArray(fieldNames, elements);
40064
40646
  }
40065
- function collectClassInstances(items) {
40066
- const out = [];
40067
- for (const item of items) {
40068
- const rv = ensureRuntimeValue(item);
40069
- if (isRuntimeClassInstance(rv)) {
40070
- out.push(rv);
40071
- } else if (isRuntimeClassInstanceArray(rv)) {
40072
- out.push(...rv.elements);
40073
- } else {
40647
+ function asObjArrayParts(item) {
40648
+ const rv = ensureRuntimeValue(item);
40649
+ if (isRuntimeClassInstance(rv))
40650
+ return { rows: 1, cols: 1, elements: [rv], className: rv.className };
40651
+ if (isRuntimeClassInstanceArray(rv))
40652
+ return {
40653
+ rows: rv.shape[0],
40654
+ cols: rv.shape[1],
40655
+ elements: rv.elements,
40656
+ className: rv.className
40657
+ };
40658
+ if (isRuntimeTensor(rv) && rv.data.length === 0) return null;
40659
+ throw new RuntimeError(`Cannot concatenate ${kstr(rv)} with class instances`);
40660
+ }
40661
+ function objColumn(p2, j) {
40662
+ return p2.elements.slice(j * p2.rows, j * p2.rows + p2.rows);
40663
+ }
40664
+ function defaultClassInstanceHorzcat(items) {
40665
+ const parts = items.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
40666
+ if (parts.length === 0)
40667
+ throw new RuntimeError("Cannot concatenate empty class instances");
40668
+ const rows = parts[0].rows;
40669
+ let cols = 0;
40670
+ const elements = [];
40671
+ for (const p2 of parts) {
40672
+ if (p2.rows !== rows)
40074
40673
  throw new RuntimeError(
40075
- `Cannot concatenate ${kstr(rv)} with class instances`
40674
+ "Dimensions of arrays being concatenated are not consistent"
40076
40675
  );
40077
- }
40676
+ elements.push(...p2.elements);
40677
+ cols += p2.cols;
40078
40678
  }
40079
- return out;
40080
- }
40081
- function defaultClassInstanceHorzcat(items) {
40082
- const elements = collectClassInstances(items);
40083
40679
  if (elements.length === 1) return elements[0];
40084
- return new RuntimeClassInstanceArray(elements[0].className, elements);
40680
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
40681
+ rows,
40682
+ cols
40683
+ ]);
40085
40684
  }
40086
40685
  function defaultClassInstanceVertcat(rows) {
40087
- const elements = collectClassInstances(rows);
40686
+ const parts = rows.map(asObjArrayParts).filter((p2) => p2 !== null && p2.elements.length > 0);
40687
+ if (parts.length === 0)
40688
+ throw new RuntimeError("Cannot concatenate empty class instances");
40689
+ const cols = parts[0].cols;
40690
+ let totalRows = 0;
40691
+ for (const p2 of parts) {
40692
+ if (p2.cols !== cols)
40693
+ throw new RuntimeError(
40694
+ "Dimensions of arrays being concatenated are not consistent"
40695
+ );
40696
+ totalRows += p2.rows;
40697
+ }
40698
+ const elements = [];
40699
+ for (let j = 0; j < cols; j++) {
40700
+ for (const p2 of parts) elements.push(...objColumn(p2, j));
40701
+ }
40088
40702
  if (elements.length === 1) return elements[0];
40089
- return new RuntimeClassInstanceArray(elements[0].className, elements);
40703
+ return new RuntimeClassInstanceArray(parts[0].className, elements, [
40704
+ totalRows,
40705
+ cols
40706
+ ]);
40090
40707
  }
40091
40708
 
40092
40709
  // src/numbl-core/helpers/reduction-helpers.ts
@@ -40504,6 +41121,27 @@ function toNumArray(v, name) {
40504
41121
  throw new RuntimeError(`${name}: arguments must be numeric arrays`);
40505
41122
  }
40506
41123
 
41124
+ // src/numbl-core/helpers/effectively-real.ts
41125
+ function imagAllZero(imag2) {
41126
+ if (!imag2) return true;
41127
+ for (let i = 0; i < imag2.length; i++) {
41128
+ if (imag2[i] !== 0) return false;
41129
+ }
41130
+ return true;
41131
+ }
41132
+ function stripZeroImagTensor(t) {
41133
+ if (t.imag && imagAllZero(t.imag)) {
41134
+ const out = RTV.tensor(t.data, t.shape);
41135
+ if (t._isLogical) out._isLogical = true;
41136
+ return out;
41137
+ }
41138
+ return t;
41139
+ }
41140
+ function stripZeroImagValue(v) {
41141
+ if (isRuntimeTensor(v)) return stripZeroImagTensor(v);
41142
+ return v;
41143
+ }
41144
+
40507
41145
  // src/numbl-core/helpers/reduction/min-max.ts
40508
41146
  function minMaxScan(data, imag2, indices, initial, isBetter, complexIsBetter) {
40509
41147
  let mRe = initial, mIm = 0, mIdx = 0;
@@ -40688,6 +41326,7 @@ function minMaxImpl(name, args, nargout, initial, isBetter, twoArgFn) {
40688
41326
  return isBetter(Math.atan2(imA, reA), Math.atan2(imB, reB));
40689
41327
  };
40690
41328
  args = args.map((a) => isRuntimeSparseMatrix(a) ? sparseToDense(a) : a);
41329
+ args = args.map(stripZeroImagValue);
40691
41330
  if (args.length === 1) {
40692
41331
  const v = args[0];
40693
41332
  if (isRuntimeNumber(v) || isRuntimeLogical(v) || isRuntimeComplexNumber(v)) {
@@ -41175,8 +41814,8 @@ defineBuiltin({
41175
41814
  if (typeof v === "number") return true;
41176
41815
  if (typeof v === "boolean") return true;
41177
41816
  if (isRuntimeComplexNumber(v)) return v.im === 0;
41178
- if (isRuntimeTensor(v)) return !v.imag;
41179
- if (isRuntimeSparseMatrix(v)) return !v.pi;
41817
+ if (isRuntimeTensor(v)) return imagAllZero(v.imag);
41818
+ if (isRuntimeSparseMatrix(v)) return !v.pi || imagAllZero(v.pi);
41180
41819
  return true;
41181
41820
  }
41182
41821
  }
@@ -41598,7 +42237,7 @@ function getShape(v) {
41598
42237
  if (isRuntimeSparseMatrix(v)) return [v.m, v.n];
41599
42238
  if (isRuntimeCell(v)) return v.shape;
41600
42239
  if (isRuntimeStructArray(v)) return [1, v.elements.length];
41601
- if (isRuntimeClassInstanceArray(v)) return [1, v.elements.length];
42240
+ if (isRuntimeClassInstanceArray(v)) return [...v.shape];
41602
42241
  return [1, 1];
41603
42242
  }
41604
42243
  defineBuiltin({
@@ -41776,7 +42415,8 @@ defineBuiltin({
41776
42415
  }
41777
42416
  if (isRuntimeString(v)) return 1;
41778
42417
  if (isRuntimeStructArray(v)) return v.elements.length;
41779
- if (isRuntimeClassInstanceArray(v)) return v.elements.length;
42418
+ if (isRuntimeClassInstanceArray(v))
42419
+ return v.elements.length === 0 ? 0 : Math.max(...v.shape);
41780
42420
  return 1;
41781
42421
  }
41782
42422
  }
@@ -41889,8 +42529,15 @@ defineBuiltin({
41889
42529
  if (isRuntimeClassInstanceArray(v)) return mkChar(v.className);
41890
42530
  if (isRuntimeFunction(v)) return mkChar("function_handle");
41891
42531
  if (isRuntimeDummyHandle(v)) return mkChar("dummy_handle");
41892
- if (isRuntimeGraphicsHandle(v))
41893
- return mkChar("matlab.graphics.primitive.Surface");
42532
+ if (isRuntimeGraphicsHandle(v)) {
42533
+ const handleClass = {
42534
+ contour: "matlab.graphics.chart.primitive.Contour",
42535
+ quiver3: "matlab.graphics.chart.primitive.Quiver"
42536
+ };
42537
+ return mkChar(
42538
+ handleClass[v._traceType] ?? "matlab.graphics.primitive.Surface"
42539
+ );
42540
+ }
41894
42541
  return mkChar("unknown");
41895
42542
  }
41896
42543
  }
@@ -44295,7 +44942,7 @@ defineBuiltin({
44295
44942
  return v;
44296
44943
  }
44297
44944
  if (isRuntimeTensor(v)) {
44298
- return sortTensor(v, dim, descend, nargout);
44945
+ return sortTensor(stripZeroImagTensor(v), dim, descend, nargout);
44299
44946
  }
44300
44947
  if (isRuntimeCell(v)) {
44301
44948
  return sortCell(v, descend, nargout);
@@ -46931,6 +47578,7 @@ defineBuiltin({
46931
47578
  const blocks = args.map((a) => {
46932
47579
  if (isRuntimeNumber(a))
46933
47580
  return RTV.tensor(allocFloat64Array([a]), [1, 1]);
47581
+ if (isRuntimeSparseMatrix(a)) return sparseToDense(a);
46934
47582
  if (!isRuntimeTensor(a))
46935
47583
  throw new RuntimeError("blkdiag: arguments must be numeric");
46936
47584
  return a;
@@ -48299,6 +48947,21 @@ defineBuiltin({
48299
48947
  const shape = reps.length >= 2 ? reps : [reps[0], reps[0]];
48300
48948
  return RTV.tensor(data, shape, imag2);
48301
48949
  }
48950
+ if (isRuntimeCell(v)) {
48951
+ const srcShape2 = v.shape.length >= 2 ? v.shape : [1, v.shape[0] ?? v.data.length];
48952
+ const [sm, sn] = [srcShape2[0], srcShape2[1]];
48953
+ const r0 = reps[0] ?? 1;
48954
+ const r1 = reps.length >= 2 ? reps[1] : reps[0] ?? 1;
48955
+ const om = sm * r0;
48956
+ const on = sn * r1;
48957
+ const out2 = new Array(om * on);
48958
+ for (let J = 0; J < on; J++) {
48959
+ for (let I = 0; I < om; I++) {
48960
+ out2[I + J * om] = v.data[I % sm + J % sn * sm];
48961
+ }
48962
+ }
48963
+ return RTV.cell(out2, [om, on]);
48964
+ }
48302
48965
  if (!isRuntimeTensor(v))
48303
48966
  throw new RuntimeError("repmat: first argument must be numeric");
48304
48967
  if (reps.every((r) => r === 1)) {
@@ -55076,6 +55739,10 @@ var H = {
55076
55739
  signatures: ["nexttile", "nexttile(IDX)"],
55077
55740
  description: "Advance to the next tile in the current tiled layout, or move to tile IDX. Auto-creates a flow layout if none exists."
55078
55741
  },
55742
+ surface: {
55743
+ signatures: ["surface(X,Y,Z)", "surface(Z)", "surface(X,Y,Z,C)"],
55744
+ description: "Primitive surface plot. Like surf, but adds to the current axes without clearing existing objects (does not respect hold)."
55745
+ },
55079
55746
  title: {
55080
55747
  signatures: ["title(TXT)"],
55081
55748
  description: "Set title of current axes."
@@ -55112,6 +55779,10 @@ var H = {
55112
55779
  signatures: ["clf"],
55113
55780
  description: "Clear current figure."
55114
55781
  },
55782
+ cla: {
55783
+ signatures: ["cla", "cla(ax)", "cla reset", "cla(ax,'reset')"],
55784
+ description: "Clear current axes. With 'reset', also reset axes properties to defaults."
55785
+ },
55115
55786
  sgtitle: {
55116
55787
  signatures: ["sgtitle(TXT)"],
55117
55788
  description: "Set super-title for subplot grid."
@@ -55164,6 +55835,10 @@ var H = {
55164
55835
  signatures: ["shg"],
55165
55836
  description: "Show current figure."
55166
55837
  },
55838
+ camlight: {
55839
+ signatures: ["camlight", "camlight(position)", "cl = camlight(___)"],
55840
+ description: "Create a light in camera coordinates. Stub in numbl (no lighting model): accepts all arguments and returns a placeholder handle."
55841
+ },
55167
55842
  newplot: {
55168
55843
  signatures: ["newplot"],
55169
55844
  description: "Prepare axes for new plot."
@@ -55689,7 +56364,7 @@ function getSourceLine(getSource, file, line) {
55689
56364
  }
55690
56365
 
55691
56366
  // src/numbl-core/version.ts
55692
- var NUMBL_VERSION = "0.4.0";
56367
+ var NUMBL_VERSION = "0.4.2";
55693
56368
 
55694
56369
  // src/cli-repl.ts
55695
56370
  import { createInterface } from "readline";
@@ -59392,16 +60067,27 @@ function callUserFunction(fn, args, nargout, narginOverride) {
59392
60067
  const fnEnv = new Environment();
59393
60068
  fnEnv.rt = this.rt;
59394
60069
  fnEnv.persistentFuncId = `${this.currentFile}:${fn.name}`;
59395
- const processedArgs = this.processArgumentsBlocks(fn, sharedArgs);
59396
60070
  const hasVarargin = fn.params.length > 0 && fn.params[fn.params.length - 1] === "varargin";
59397
60071
  const regularParams = hasVarargin ? fn.params.slice(0, -1) : fn.params;
59398
- for (let i = 0; i < regularParams.length; i++) {
59399
- if (i < processedArgs.length) {
59400
- fnEnv.set(regularParams[i], ensureRuntimeValue(processedArgs[i]));
60072
+ let numPositional = regularParams.length;
60073
+ const inputBlocks = (fn.argumentsBlocks ?? []).filter(
60074
+ (b) => b.kind !== "Output"
60075
+ );
60076
+ for (const block of inputBlocks) {
60077
+ for (const e of block.entries) {
60078
+ const dot2 = e.name.indexOf(".");
60079
+ if (dot2 < 0) continue;
60080
+ const idx = fn.params.indexOf(e.name.slice(0, dot2));
60081
+ if (idx >= 0 && idx < numPositional) numPositional = idx;
60082
+ }
60083
+ }
60084
+ for (let i = 0; i < numPositional && i < regularParams.length; i++) {
60085
+ if (i < sharedArgs.length && sharedArgs[i] !== void 0) {
60086
+ fnEnv.set(regularParams[i], ensureRuntimeValue(sharedArgs[i]));
59401
60087
  }
59402
60088
  }
59403
60089
  if (hasVarargin) {
59404
- const extraArgs = processedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
60090
+ const extraArgs = sharedArgs.slice(regularParams.length).map((a) => ensureRuntimeValue(a));
59405
60091
  fnEnv.set("varargin", RTV.cell(extraArgs, [1, extraArgs.length]));
59406
60092
  }
59407
60093
  fnEnv.set("$nargin", narginOverride ?? args.length);
@@ -59421,6 +60107,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
59421
60107
  this.rt.pushCallFrame(fn.name);
59422
60108
  this.rt.pushCleanupScope();
59423
60109
  try {
60110
+ this.processArgumentsBlocks(fn, sharedArgs);
59424
60111
  this.execStmts(fn.body);
59425
60112
  if (fnEnv.persistentFuncId) {
59426
60113
  for (const name of fnEnv.persistentNames) {
@@ -59664,12 +60351,13 @@ function findExternalMethod(classInfo, methodName) {
59664
60351
  return null;
59665
60352
  }
59666
60353
  const ast = this.ctx.getCachedAST(mf.fileName);
60354
+ let primary = null;
59667
60355
  for (const stmt of ast.body) {
59668
- if (stmt.type === "Function" && stmt.name === methodName) {
59669
- return funcDefFromStmt(stmt);
59670
- }
60356
+ if (stmt.type !== "Function") continue;
60357
+ if (stmt.name === methodName) return funcDefFromStmt(stmt);
60358
+ if (!primary) primary = funcDefFromStmt(stmt);
59671
60359
  }
59672
- return null;
60360
+ return primary;
59673
60361
  }
59674
60362
  function collectClassProperties(classInfo) {
59675
60363
  const propertyNames = [...classInfo.propertyNames];
@@ -59743,7 +60431,7 @@ function evalInLocalScope(codeArg, fileName) {
59743
60431
  }
59744
60432
  function processArgumentsBlocks(fn, args) {
59745
60433
  const argBlocks = fn.argumentsBlocks;
59746
- if (!argBlocks || argBlocks.length === 0) return args;
60434
+ if (!argBlocks || argBlocks.length === 0) return;
59747
60435
  for (const block of argBlocks) {
59748
60436
  if (block.kind === "Output") continue;
59749
60437
  const entries = block.entries;
@@ -59764,48 +60452,27 @@ function processArgumentsBlocks(fn, args) {
59764
60452
  }
59765
60453
  return true;
59766
60454
  });
59767
- if (nvGroups.size > 0) {
59768
- const processedArgs2 = [...args];
59769
- const nvParamIndex = regularEntries.length;
59770
- for (const [paramName, fields] of nvGroups) {
59771
- const nvArgs = args.slice(nvParamIndex);
59772
- const defaults = {};
59773
- for (const { field, defaultExpr } of fields) {
59774
- if (defaultExpr) {
59775
- try {
59776
- defaults[field] = this.evalExpr(defaultExpr);
59777
- } catch {
59778
- }
59779
- }
59780
- }
59781
- const struct = this.rt.buildNameValueStruct(nvArgs, defaults);
59782
- const paramIdx = fn.params.indexOf(paramName);
59783
- const targetIdx = paramIdx >= 0 ? paramIdx : nvParamIndex;
59784
- processedArgs2.length = Math.max(processedArgs2.length, targetIdx + 1);
59785
- processedArgs2[targetIdx] = struct;
59786
- }
59787
- for (let i = 0; i < regularEntries.length; i++) {
59788
- if (processedArgs2[i] === void 0 && regularEntries[i].defaultValue) {
59789
- try {
59790
- processedArgs2[i] = this.evalExpr(regularEntries[i].defaultValue);
59791
- } catch {
59792
- }
59793
- }
60455
+ for (const entry of regularEntries) {
60456
+ const pIdx = fn.params.indexOf(entry.name);
60457
+ if (pIdx < 0) continue;
60458
+ const provided = pIdx < args.length && args[pIdx] !== void 0;
60459
+ if (!provided && entry.defaultValue) {
60460
+ this.env.set(
60461
+ entry.name,
60462
+ ensureRuntimeValue(this.evalExpr(entry.defaultValue))
60463
+ );
59794
60464
  }
59795
- return processedArgs2;
59796
60465
  }
59797
- const processedArgs = [...args];
59798
- for (let i = 0; i < entries.length; i++) {
59799
- if (processedArgs[i] === void 0 && entries[i].defaultValue) {
59800
- try {
59801
- processedArgs[i] = this.evalExpr(entries[i].defaultValue);
59802
- } catch {
59803
- }
60466
+ for (const [paramName, fields] of nvGroups) {
60467
+ const pIdx = fn.params.indexOf(paramName);
60468
+ const nvArgs = args.slice(pIdx >= 0 ? pIdx : args.length);
60469
+ const defaults = {};
60470
+ for (const { field, defaultExpr } of fields) {
60471
+ if (defaultExpr) defaults[field] = this.evalExpr(defaultExpr);
59804
60472
  }
60473
+ this.env.set(paramName, this.rt.buildNameValueStruct(nvArgs, defaults));
59805
60474
  }
59806
- return processedArgs;
59807
60475
  }
59808
- return args;
59809
60476
  }
59810
60477
 
59811
60478
  // src/numbl-core/interpreter/interpreter.ts
@@ -63921,6 +64588,15 @@ function mtoc2_tensor_flip_complex(a, dimIdx) {
63921
64588
  return r;
63922
64589
  }
63923
64590
 
64591
+ // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_imag_all_zero.js
64592
+ function mtoc2_tensor_imag_all_zero(a) {
64593
+ if (a.imag === void 0) return true;
64594
+ for (let i = 0; i < a.imag.length; i++) {
64595
+ if (a.imag[i] !== 0) return false;
64596
+ }
64597
+ return true;
64598
+ }
64599
+
63924
64600
  // src/numbl-core/jit/builtins/runtime/tensor_ops/tensor_linspace.js
63925
64601
  function mtoc2_tensor_linspace(a, b, n) {
63926
64602
  if (n < 0) n = 0;
@@ -64222,6 +64898,13 @@ function cSqueezeTrailing(dims) {
64222
64898
  function cReduceLaneIm(t, i) {
64223
64899
  return t.imag !== void 0 ? t.imag[i] : 0;
64224
64900
  }
64901
+ function cReduceAllImagZero(t) {
64902
+ if (t.imag === void 0) return true;
64903
+ for (let i = 0; i < t.imag.length; i++) {
64904
+ if (t.imag[i] !== 0) return false;
64905
+ }
64906
+ return true;
64907
+ }
64225
64908
  function complexAccumAll(t, init, accum, finalize) {
64226
64909
  let acc = { ...init };
64227
64910
  for (let i = 0; i < t.data.length; i++) {
@@ -64279,6 +64962,7 @@ var mtoc2_prod_complex_dim = (t, d) => complexAccumDim(t, d, cProdInit, cProdAcc
64279
64962
  var mtoc2_mean_complex_all = (t) => complexAccumAll(t, cSumInit, cSumAccum, cMeanFinalize);
64280
64963
  var mtoc2_mean_complex_dim = (t, d) => complexAccumDim(t, d, cSumInit, cSumAccum, cMeanFinalize);
64281
64964
  function complexMinmaxAll(t, cmp) {
64965
+ const realMode = cReduceAllImagZero(t);
64282
64966
  let found = false;
64283
64967
  let mRe = NaN;
64284
64968
  let mIm = 0;
@@ -64286,7 +64970,8 @@ function complexMinmaxAll(t, cmp) {
64286
64970
  const xr = t.data[i];
64287
64971
  const xi = cReduceLaneIm(t, i);
64288
64972
  if (xr !== xr || xi !== xi) continue;
64289
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
64973
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
64974
+ if (!found || better) {
64290
64975
  mRe = xr;
64291
64976
  mIm = xi;
64292
64977
  found = true;
@@ -64309,6 +64994,7 @@ function complexMinmaxDim(t, dim, cmp) {
64309
64994
  return out2;
64310
64995
  }
64311
64996
  const dimIdx = dim - 1;
64997
+ const realMode = cReduceAllImagZero(t);
64312
64998
  const axis = t.shape[dimIdx];
64313
64999
  let before = 1;
64314
65000
  for (let i = 0; i < dimIdx; i++) before *= t.shape[i];
@@ -64329,7 +65015,8 @@ function complexMinmaxDim(t, dim, cmp) {
64329
65015
  const xr = t.data[off];
64330
65016
  const xi = cReduceLaneIm(t, off);
64331
65017
  if (xr !== xr || xi !== xi) continue;
64332
- if (!found || complexBetter(xr, xi, mRe, mIm, cmp)) {
65018
+ const better = realMode ? cmp === "<" ? xr < mRe : xr > mRe : complexBetter(xr, xi, mRe, mIm, cmp);
65019
+ if (!found || better) {
64333
65020
  mRe = xr;
64334
65021
  mIm = xi;
64335
65022
  found = true;
@@ -64809,6 +65496,35 @@ function complex_sort_indices(a, descending) {
64809
65496
  const n = a.data.length;
64810
65497
  const im = a.imag;
64811
65498
  const idx = new Array(n);
65499
+ for (let i = 0; i < n; i++) idx[i] = i;
65500
+ let realMode = true;
65501
+ if (im !== void 0) {
65502
+ for (let i = 0; i < n; i++) {
65503
+ if (im[i] !== 0) {
65504
+ realMode = false;
65505
+ break;
65506
+ }
65507
+ }
65508
+ }
65509
+ if (realMode) {
65510
+ const re = a.data;
65511
+ idx.sort((p2, q) => {
65512
+ const rp = re[p2];
65513
+ const rq = re[q];
65514
+ const pNaN = rp !== rp;
65515
+ const qNaN = rq !== rq;
65516
+ if (pNaN && qNaN) return 0;
65517
+ if (descending) {
65518
+ if (pNaN) return -1;
65519
+ if (qNaN) return 1;
65520
+ return rp < rq ? 1 : rp > rq ? -1 : 0;
65521
+ }
65522
+ if (pNaN) return 1;
65523
+ if (qNaN) return -1;
65524
+ return rp < rq ? -1 : rp > rq ? 1 : 0;
65525
+ });
65526
+ return idx;
65527
+ }
64812
65528
  const mag = new Float64Array(n);
64813
65529
  const ph = new Float64Array(n);
64814
65530
  for (let i = 0; i < n; i++) {
@@ -64816,7 +65532,6 @@ function complex_sort_indices(a, descending) {
64816
65532
  const xi = im !== void 0 ? im[i] : 0;
64817
65533
  mag[i] = Math.hypot(re, xi);
64818
65534
  ph[i] = Math.atan2(xi, re);
64819
- idx[i] = i;
64820
65535
  }
64821
65536
  idx.sort((p2, q) => {
64822
65537
  if (mag[p2] < mag[q]) return descending ? 1 : -1;
@@ -66086,6 +66801,7 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
66086
66801
  "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",
66087
66802
  "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",
66088
66803
  "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",
66804
+ "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",
66089
66805
  "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",
66090
66806
  "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',
66091
66807
  "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',
@@ -66097,13 +66813,13 @@ static void mtoc2__format_walk(mtoc2__writer_fn writer, void *ctx,
66097
66813
  "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",
66098
66814
  "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",
66099
66815
  "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",
66100
- "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',
66816
+ "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',
66101
66817
  "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',
66102
66818
  "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',
66103
66819
  "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',
66104
66820
  "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',
66105
66821
  "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",
66106
- "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',
66822
+ "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",
66107
66823
  "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",
66108
66824
  "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",
66109
66825
  "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",
@@ -66175,6 +66891,7 @@ var JS_SNIPPETS = {
66175
66891
  "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",
66176
66892
  "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",
66177
66893
  "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",
66894
+ "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",
66178
66895
  "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",
66179
66896
  "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',
66180
66897
  "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",
@@ -66185,13 +66902,13 @@ var JS_SNIPPETS = {
66185
66902
  "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",
66186
66903
  "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",
66187
66904
  "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',
66188
- "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',
66905
+ "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',
66189
66906
  "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',
66190
66907
  "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",
66191
66908
  "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",
66192
66909
  "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",
66193
66910
  "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",
66194
- "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",
66911
+ "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",
66195
66912
  "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",
66196
66913
  "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",
66197
66914
  "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",
@@ -66254,6 +66971,7 @@ var JS_IMPORTS = {
66254
66971
  "tensor_fill_nd.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
66255
66972
  "tensor_fill_square.js": ["tensor_fill_nd.js"],
66256
66973
  "tensor_flip.js": ["tensor_alloc_nd.js", "tensor_alloc_nd_complex.js"],
66974
+ "tensor_imag_all_zero.js": [],
66257
66975
  "tensor_linspace.js": ["tensor_alloc.js"],
66258
66976
  "tensor_logical_real.js": ["tensor_alloc_nd.js"],
66259
66977
  "tensor_logspace.js": ["tensor_alloc.js"],
@@ -73876,7 +74594,7 @@ function checkArity2(argTypes, nargout) {
73876
74594
  function staticVerdict(t) {
73877
74595
  if (!isNumeric2(t)) return true;
73878
74596
  if (!t.isComplex) return true;
73879
- if (!isScalar(t)) return false;
74597
+ if (!isScalar(t)) return "runtime";
73880
74598
  const ex = t.exact;
73881
74599
  if (ex !== void 0 && typeof ex === "object" && "im" in ex) {
73882
74600
  const im = ex.im;
@@ -73893,23 +74611,33 @@ var isreal = {
73893
74611
  return [scalarLogical(v)];
73894
74612
  },
73895
74613
  emitC({ argTypes, argsC, useRuntime }) {
73896
- const v = staticVerdict(argTypes[0]);
74614
+ const t = argTypes[0];
74615
+ const v = staticVerdict(t);
73897
74616
  if (v === true) return "1.0";
73898
74617
  if (v === false) return "0.0";
74618
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
74619
+ useRuntime("mtoc2_tensor_imag_all_zero");
74620
+ return `mtoc2_tensor_imag_all_zero(${argsC[0]})`;
74621
+ }
73899
74622
  useRuntime("mtoc2_cscalar");
73900
74623
  return `(cimag(${argsC[0]}) == 0.0)`;
73901
74624
  },
73902
- emitJs({ argTypes, argsJs }) {
73903
- const v = staticVerdict(argTypes[0]);
74625
+ emitJs({ argTypes, argsJs, useRuntime }) {
74626
+ const t = argTypes[0];
74627
+ const v = staticVerdict(t);
73904
74628
  if (v === true) return "true";
73905
74629
  if (v === false) return "false";
74630
+ if (isNumeric2(t) && t.isComplex && !isScalar(t)) {
74631
+ useRuntime("mtoc2_tensor_imag_all_zero");
74632
+ return `mtoc2_tensor_imag_all_zero(${argsJs[0]})`;
74633
+ }
73906
74634
  return `(${argsJs[0]}.im === 0)`;
73907
74635
  },
73908
74636
  call({ args }) {
73909
74637
  const v = args[0];
73910
74638
  if (typeof v === "number" || typeof v === "boolean") return [true];
73911
74639
  if (isComplexValue(v)) return [v.im === 0];
73912
- if (isTensor(v)) return [!v.imag];
74640
+ if (isTensor(v)) return [mtoc2_tensor_imag_all_zero(v)];
73913
74641
  return [true];
73914
74642
  }
73915
74643
  };
@@ -82069,6 +82797,13 @@ var REGISTRY2 = /* @__PURE__ */ new Map([
82069
82797
  "mtoc2_cdiv"
82070
82798
  ])
82071
82799
  ],
82800
+ // `isreal` on a complex-typed tensor: scan the imag lane (true iff all
82801
+ // zero). Lets the JIT report realness by value, matching the
82802
+ // interpreter on tensors the type system could not prove real.
82803
+ [
82804
+ "mtoc2_tensor_imag_all_zero",
82805
+ loadSnippet("tensor_imag_all_zero.h", ["mtoc2_tensor_t"])
82806
+ ],
82072
82807
  // ── Elementwise binary/unary on real tensors ──────────────────────
82073
82808
  // One snippet covers all 11 funcs (4×_tt, 4×_ts, 2×_st, 1×uminus).
82074
82809
  // Builtins activate by op-specific synthetic name; all map to the
@@ -87108,15 +87843,18 @@ var Workspace = class _Workspace {
87108
87843
  );
87109
87844
  }
87110
87845
  let primary = null;
87846
+ let firstFn = null;
87111
87847
  for (const stmt of ast.body) {
87112
87848
  if (stmt.type !== "Function") continue;
87849
+ if (!firstFn) firstFn = stmt;
87113
87850
  if (stmt.name === methodName) {
87114
87851
  primary = stmt;
87115
87852
  }
87116
87853
  }
87854
+ primary ??= firstFn;
87117
87855
  if (!primary) {
87118
87856
  throw new UnsupportedConstruct(
87119
- `external method file '${mf.fileName}' has no function named '${methodName}'`,
87857
+ `external method file '${mf.fileName}' has no function`,
87120
87858
  info.ast.span
87121
87859
  );
87122
87860
  }