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 +873 -135
- package/dist-lib/graphics/types.d.ts +40 -1
- package/dist-lib/lib.js +872 -134
- package/dist-lib/numbl-core/helpers/effectively-real.d.ts +25 -0
- package/dist-lib/numbl-core/interpreter/interpreter.d.ts +1 -1
- package/dist-lib/numbl-core/interpreter/interpreterFunctions.d.ts +13 -1
- package/dist-lib/numbl-core/jit/builtins/runtime/snippets.gen.d.ts +1 -0
- package/dist-lib/numbl-core/jit/builtins/runtime/tensor_ops/tensor_imag_all_zero.d.ts +1 -0
- package/dist-lib/numbl-core/runtime/plotUtils.d.ts +28 -2
- package/dist-lib/numbl-core/runtime/runtimePlot.d.ts +5 -0
- package/dist-lib/numbl-core/runtime/types.d.ts +6 -3
- package/dist-lib/numbl-core/version.d.ts +1 -1
- package/dist-plot-viewer/assets/index-DfxsWeyf.js +4426 -0
- package/dist-plot-viewer/index.html +1 -1
- package/dist-site-viewer/assets/index-D0XGPdHU.js +4748 -0
- package/dist-site-viewer/assets/{numbl-worker-VkVtodCX.js → numbl-worker-B18l6dfh.js} +293 -136
- package/dist-site-viewer/index.html +1 -1
- package/package.json +1 -1
- package/dist-plot-viewer/assets/index-COAM8o1E.js +0 -4426
- package/dist-site-viewer/assets/index-CgBUy7v7.js +0 -4748
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
|
-
|
|
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
|
|
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
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
34517
|
-
|
|
34518
|
-
|
|
34519
|
-
if (i < 0 || i >=
|
|
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
|
-
|
|
35019
|
+
selected.push(mv.elements[i]);
|
|
34522
35020
|
}
|
|
34523
|
-
|
|
34524
|
-
|
|
34525
|
-
|
|
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)
|
|
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(
|
|
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(
|
|
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
|
|
40066
|
-
const
|
|
40067
|
-
|
|
40068
|
-
|
|
40069
|
-
|
|
40070
|
-
|
|
40071
|
-
|
|
40072
|
-
|
|
40073
|
-
|
|
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
|
-
|
|
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(
|
|
40680
|
+
return new RuntimeClassInstanceArray(parts[0].className, elements, [
|
|
40681
|
+
rows,
|
|
40682
|
+
cols
|
|
40683
|
+
]);
|
|
40085
40684
|
}
|
|
40086
40685
|
function defaultClassInstanceVertcat(rows) {
|
|
40087
|
-
const
|
|
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(
|
|
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
|
|
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 [
|
|
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))
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
59399
|
-
|
|
59400
|
-
|
|
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 =
|
|
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
|
|
59669
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
59768
|
-
const
|
|
59769
|
-
|
|
59770
|
-
|
|
59771
|
-
|
|
59772
|
-
|
|
59773
|
-
|
|
59774
|
-
|
|
59775
|
-
|
|
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
|
|
59798
|
-
|
|
59799
|
-
|
|
59800
|
-
|
|
59801
|
-
|
|
59802
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 [
|
|
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
|
|
87857
|
+
`external method file '${mf.fileName}' has no function`,
|
|
87120
87858
|
info.ast.span
|
|
87121
87859
|
);
|
|
87122
87860
|
}
|