numbl 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cli/cli.js CHANGED
@@ -196,7 +196,7 @@ var kstr = (value) => {
196
196
  };
197
197
 
198
198
  // src/numbl-core/native/lapack-bridge.ts
199
- var NATIVE_ADDON_EXPECTED_VERSION = 1;
199
+ var NATIVE_ADDON_EXPECTED_VERSION = 2;
200
200
  var _bridge = null;
201
201
  function setLapackBridge(bridge) {
202
202
  _bridge = bridge;
@@ -678,6 +678,9 @@ var RuntimeError = class extends Error {
678
678
  * Format error with location and snippet context.
679
679
  */
680
680
  toString() {
681
+ if (this.callStack != null && this.callStack.length > 0) {
682
+ return this._formatWithCallStack();
683
+ }
681
684
  let result = this.name;
682
685
  if (this.file && this.line !== null) {
683
686
  result += ` at ${this.file}:${this.line}`;
@@ -692,58 +695,51 @@ var RuntimeError = class extends Error {
692
695
  result += `
693
696
  ${this.snippet}`;
694
697
  }
695
- if (this.callStack != null && this.callStack.length > 0) {
696
- result += `
697
- Call stack (most recent call first):`;
698
- const N = this.callStack.length;
699
- for (let i = N - 1; i >= 0; i--) {
700
- const name = this.callStack[i].name;
701
- let loc;
702
- let frameFile = null;
703
- let frameLine = 0;
704
- if (i === N - 1) {
705
- frameFile = this.file;
706
- frameLine = this.line ?? 0;
707
- if (frameFile && frameLine > 0) {
708
- loc = `${frameFile}:${frameLine}`;
709
- } else if (frameLine > 0) {
710
- loc = `line ${frameLine}`;
711
- } else {
712
- loc = "unknown";
713
- }
714
- } else {
715
- const callerFrame = this.callStack[i + 1];
716
- frameFile = callerFrame.callerFile;
717
- frameLine = callerFrame.callerLine;
718
- if (frameFile && frameLine > 0) {
719
- loc = `${frameFile}:${frameLine}`;
720
- } else if (frameLine > 0) {
721
- loc = `line ${frameLine}`;
722
- } else {
723
- loc = "unknown";
724
- }
725
- }
726
- result += `
727
- at ${name} (${loc})`;
728
- if (frameFile && frameLine > 0) {
729
- const srcLine = this._getSourceLine(frameFile, frameLine);
730
- if (srcLine) result += `
731
- ${srcLine}`;
732
- }
733
- }
734
- const outermost = this.callStack[0];
735
- if (outermost.callerFile && outermost.callerLine > 0) {
736
- result += `
737
- at ${outermost.callerFile}:${outermost.callerLine}`;
738
- const srcLine = this._getSourceLine(
739
- outermost.callerFile,
740
- outermost.callerLine
741
- );
742
- if (srcLine) result += `
743
- ${srcLine}`;
698
+ return result;
699
+ }
700
+ /** Format error with MATLAB-style call stack. */
701
+ _formatWithCallStack() {
702
+ const stack = this.callStack;
703
+ const N = stack.length;
704
+ const parts = [];
705
+ const innerName = stack[N - 1].name;
706
+ const innerFile = this.file;
707
+ const innerLine = this.line ?? 0;
708
+ if (innerFile && innerLine > 0) {
709
+ parts.push(`Error using ${innerName} (${innerFile}:${innerLine})`);
710
+ } else if (innerLine > 0) {
711
+ parts.push(`Error using ${innerName} (line ${innerLine})`);
712
+ } else {
713
+ parts.push(`Error using ${innerName}`);
714
+ }
715
+ parts.push(this.message);
716
+ for (let i = N - 2; i >= 0; i--) {
717
+ const name = stack[i].name;
718
+ const callerFrame = stack[i + 1];
719
+ const file = callerFrame.callerFile;
720
+ const line = callerFrame.callerLine;
721
+ parts.push("");
722
+ if (file && line > 0) {
723
+ parts.push(`Error in ${name} (${file}:${line})`);
724
+ const srcLine = this._getSourceLine(file, line);
725
+ if (srcLine) parts.push(` ${srcLine}`);
726
+ } else if (line > 0) {
727
+ parts.push(`Error in ${name} (line ${line})`);
728
+ } else {
729
+ parts.push(`Error in ${name}`);
744
730
  }
745
731
  }
746
- return result;
732
+ const outermost = stack[0];
733
+ if (outermost.callerFile && outermost.callerLine > 0) {
734
+ parts.push("");
735
+ parts.push(`Error in ${outermost.callerFile}:${outermost.callerLine}`);
736
+ const srcLine = this._getSourceLine(
737
+ outermost.callerFile,
738
+ outermost.callerLine
739
+ );
740
+ if (srcLine) parts.push(` ${srcLine}`);
741
+ }
742
+ return parts.join("\n");
747
743
  }
748
744
  /** Look up a single trimmed source line from fileSources. */
749
745
  _getSourceLine(file, line) {
@@ -1584,9 +1580,6 @@ function registerDynamicIBuiltin(b) {
1584
1580
  registry.set(b.name, b);
1585
1581
  _onDynamicRegister?.(b);
1586
1582
  }
1587
- function unregisterIBuiltin(name) {
1588
- registry.delete(name);
1589
- }
1590
1583
  function getAllIBuiltinNames() {
1591
1584
  return Array.from(registry.keys());
1592
1585
  }
@@ -4379,6 +4372,435 @@ function dgetrf(m, n, a, lda, ipiv) {
4379
4372
  return info;
4380
4373
  }
4381
4374
 
4375
+ // src/numbl-core/helpers/gmres.ts
4376
+ function gmresCore(matvec, precSolve, b, n, restart, tol, maxit, x0) {
4377
+ const x = x0 ? new Float64Array(x0) : new Float64Array(n);
4378
+ if (restart <= 0 || restart > n) restart = n;
4379
+ let r = sub(b, matvec(x), n);
4380
+ if (precSolve) r = precSolve(r);
4381
+ let beta = nrm2(r, n);
4382
+ let normMb;
4383
+ if (precSolve) {
4384
+ normMb = nrm2(precSolve(new Float64Array(b)), n);
4385
+ } else {
4386
+ normMb = nrm2(b, n);
4387
+ }
4388
+ if (normMb === 0) normMb = 1;
4389
+ const resvecList = [beta];
4390
+ if (beta / normMb <= tol) {
4391
+ return {
4392
+ x,
4393
+ flag: 0,
4394
+ relres: beta / normMb,
4395
+ iter: [0, 0],
4396
+ resvec: new Float64Array(resvecList)
4397
+ };
4398
+ }
4399
+ let flag = 1;
4400
+ let outerIter = 0;
4401
+ let innerIter = 0;
4402
+ for (let outer = 1; outer <= maxit; outer++) {
4403
+ outerIter = outer;
4404
+ const V = new Float64Array(n * (restart + 1));
4405
+ for (let i = 0; i < n; i++) V[i] = r[i] / beta;
4406
+ const H2 = new Float64Array((restart + 1) * restart);
4407
+ const cs = new Float64Array(restart);
4408
+ const sn = new Float64Array(restart);
4409
+ const g = new Float64Array(restart + 1);
4410
+ g[0] = beta;
4411
+ let converged = false;
4412
+ for (let j = 0; j < restart; j++) {
4413
+ innerIter = j + 1;
4414
+ let w = matvec(V.subarray(j * n, (j + 1) * n));
4415
+ if (precSolve) w = precSolve(w);
4416
+ const ldh = restart + 1;
4417
+ for (let i = 0; i <= j; i++) {
4418
+ const viOff = i * n;
4419
+ let hij = 0;
4420
+ for (let k = 0; k < n; k++) hij += w[k] * V[viOff + k];
4421
+ H2[i + j * ldh] = hij;
4422
+ for (let k = 0; k < n; k++) w[k] -= hij * V[viOff + k];
4423
+ }
4424
+ const wnorm = nrm2(w, n);
4425
+ H2[j + 1 + j * ldh] = wnorm;
4426
+ if (wnorm > 1e-300) {
4427
+ const vOff = (j + 1) * n;
4428
+ for (let k = 0; k < n; k++) V[vOff + k] = w[k] / wnorm;
4429
+ }
4430
+ for (let i = 0; i < j; i++) {
4431
+ const hi = H2[i + j * ldh];
4432
+ const hi1 = H2[i + 1 + j * ldh];
4433
+ H2[i + j * ldh] = cs[i] * hi + sn[i] * hi1;
4434
+ H2[i + 1 + j * ldh] = -sn[i] * hi + cs[i] * hi1;
4435
+ }
4436
+ const [c, s] = givensRotation(H2[j + j * ldh], H2[j + 1 + j * ldh]);
4437
+ cs[j] = c;
4438
+ sn[j] = s;
4439
+ H2[j + j * ldh] = c * H2[j + j * ldh] + s * H2[j + 1 + j * ldh];
4440
+ H2[j + 1 + j * ldh] = 0;
4441
+ const gj = g[j];
4442
+ g[j] = c * gj;
4443
+ g[j + 1] = -s * gj;
4444
+ const residNorm = Math.abs(g[j + 1]);
4445
+ resvecList.push(residNorm);
4446
+ if (residNorm / normMb <= tol) {
4447
+ const y2 = backSolve(H2, g, j + 1, ldh);
4448
+ for (let k = 0; k < n; k++) {
4449
+ for (let l = 0; l <= j; l++) x[k] += V[k + l * n] * y2[l];
4450
+ }
4451
+ flag = 0;
4452
+ converged = true;
4453
+ break;
4454
+ }
4455
+ }
4456
+ if (converged) break;
4457
+ const y = backSolve(H2, g, restart, restart + 1);
4458
+ for (let k = 0; k < n; k++) {
4459
+ for (let l = 0; l < restart; l++) x[k] += V[k + l * n] * y[l];
4460
+ }
4461
+ r = sub(b, matvec(x), n);
4462
+ if (precSolve) r = precSolve(r);
4463
+ beta = nrm2(r, n);
4464
+ if (beta / normMb <= tol) {
4465
+ flag = 0;
4466
+ innerIter = 0;
4467
+ break;
4468
+ }
4469
+ }
4470
+ let relres;
4471
+ if (flag === 0) {
4472
+ let finalR = sub(b, matvec(x), n);
4473
+ if (precSolve) finalR = precSolve(finalR);
4474
+ relres = nrm2(finalR, n) / normMb;
4475
+ } else {
4476
+ relres = beta / normMb;
4477
+ }
4478
+ return {
4479
+ x,
4480
+ flag,
4481
+ relres,
4482
+ iter: [outerIter, innerIter],
4483
+ resvec: new Float64Array(resvecList)
4484
+ };
4485
+ }
4486
+ function nrm2(a, n) {
4487
+ let s = 0;
4488
+ for (let i = 0; i < n; i++) s += a[i] * a[i];
4489
+ return Math.sqrt(s);
4490
+ }
4491
+ function sub(a, b, n) {
4492
+ const r = new Float64Array(n);
4493
+ for (let i = 0; i < n; i++) r[i] = a[i] - b[i];
4494
+ return r;
4495
+ }
4496
+ function givensRotation(a, b) {
4497
+ if (b === 0) return [1, 0];
4498
+ if (Math.abs(b) > Math.abs(a)) {
4499
+ const t2 = a / b;
4500
+ const s = 1 / Math.sqrt(1 + t2 * t2);
4501
+ return [s * t2, s];
4502
+ }
4503
+ const t = b / a;
4504
+ const c = 1 / Math.sqrt(1 + t * t);
4505
+ return [c, c * t];
4506
+ }
4507
+ function backSolve(H2, g, m, ldh) {
4508
+ const y = new Float64Array(m);
4509
+ for (let i = 0; i < m; i++) y[i] = g[i];
4510
+ for (let i = m - 1; i >= 0; i--) {
4511
+ for (let j = i + 1; j < m; j++) y[i] -= H2[i + j * ldh] * y[j];
4512
+ y[i] /= H2[i + i * ldh];
4513
+ }
4514
+ return y;
4515
+ }
4516
+ function gmresCoreComplex(matvec, precSolve, b, n, restart, tol, maxit, x0) {
4517
+ const xRe = x0 ? new Float64Array(x0.re) : new Float64Array(n);
4518
+ const xIm = x0 ? new Float64Array(x0.im) : new Float64Array(n);
4519
+ if (restart <= 0 || restart > n) restart = n;
4520
+ const ax = matvec({ re: xRe, im: xIm });
4521
+ let rRe = new Float64Array(n);
4522
+ let rIm = new Float64Array(n);
4523
+ for (let i = 0; i < n; i++) {
4524
+ rRe[i] = b.re[i] - ax.re[i];
4525
+ rIm[i] = b.im[i] - ax.im[i];
4526
+ }
4527
+ if (precSolve) {
4528
+ const pr = precSolve({ re: rRe, im: rIm });
4529
+ rRe = new Float64Array(pr.re);
4530
+ rIm = new Float64Array(pr.im);
4531
+ }
4532
+ let beta = cnrm2(rRe, rIm, n);
4533
+ let normMb;
4534
+ if (precSolve) {
4535
+ const mb = precSolve({
4536
+ re: new Float64Array(b.re),
4537
+ im: new Float64Array(b.im)
4538
+ });
4539
+ normMb = cnrm2(mb.re, mb.im, n);
4540
+ } else {
4541
+ normMb = cnrm2(b.re, b.im, n);
4542
+ }
4543
+ if (normMb === 0) normMb = 1;
4544
+ const resvecList = [beta];
4545
+ if (beta / normMb <= tol) {
4546
+ return {
4547
+ x: { re: xRe, im: xIm },
4548
+ flag: 0,
4549
+ relres: beta / normMb,
4550
+ iter: [0, 0],
4551
+ resvec: new Float64Array(resvecList)
4552
+ };
4553
+ }
4554
+ let flag = 1;
4555
+ let outerIter = 0;
4556
+ let innerIter = 0;
4557
+ for (let outer = 1; outer <= maxit; outer++) {
4558
+ outerIter = outer;
4559
+ const VRe = new Float64Array(n * (restart + 1));
4560
+ const VIm = new Float64Array(n * (restart + 1));
4561
+ for (let i = 0; i < n; i++) {
4562
+ VRe[i] = rRe[i] / beta;
4563
+ VIm[i] = rIm[i] / beta;
4564
+ }
4565
+ const ldh = restart + 1;
4566
+ const HRe = new Float64Array(ldh * restart);
4567
+ const HIm = new Float64Array(ldh * restart);
4568
+ const cs = new Float64Array(restart);
4569
+ const snRe = new Float64Array(restart);
4570
+ const snIm = new Float64Array(restart);
4571
+ const gRe = new Float64Array(restart + 1);
4572
+ const gIm = new Float64Array(restart + 1);
4573
+ gRe[0] = beta;
4574
+ let converged = false;
4575
+ for (let j = 0; j < restart; j++) {
4576
+ innerIter = j + 1;
4577
+ const vjOff = j * n;
4578
+ let wRe, wIm;
4579
+ const mv = matvec({
4580
+ re: VRe.subarray(vjOff, vjOff + n),
4581
+ im: VIm.subarray(vjOff, vjOff + n)
4582
+ });
4583
+ wRe = mv.re;
4584
+ wIm = mv.im;
4585
+ if (precSolve) {
4586
+ const pw = precSolve({ re: wRe, im: wIm });
4587
+ wRe = pw.re;
4588
+ wIm = pw.im;
4589
+ }
4590
+ if (wRe.length !== n) wRe = new Float64Array(wRe);
4591
+ if (wIm.length !== n) wIm = new Float64Array(wIm);
4592
+ for (let i = 0; i <= j; i++) {
4593
+ const viOff = i * n;
4594
+ let dRe = 0, dIm = 0;
4595
+ for (let k = 0; k < n; k++) {
4596
+ dRe += VRe[viOff + k] * wRe[k] + VIm[viOff + k] * wIm[k];
4597
+ dIm += VRe[viOff + k] * wIm[k] - VIm[viOff + k] * wRe[k];
4598
+ }
4599
+ HRe[i + j * ldh] = dRe;
4600
+ HIm[i + j * ldh] = dIm;
4601
+ for (let k = 0; k < n; k++) {
4602
+ wRe[k] -= dRe * VRe[viOff + k] - dIm * VIm[viOff + k];
4603
+ wIm[k] -= dRe * VIm[viOff + k] + dIm * VRe[viOff + k];
4604
+ }
4605
+ }
4606
+ const wnorm = cnrm2(wRe, wIm, n);
4607
+ HRe[j + 1 + j * ldh] = wnorm;
4608
+ if (wnorm > 1e-300) {
4609
+ const vOff = (j + 1) * n;
4610
+ for (let k = 0; k < n; k++) {
4611
+ VRe[vOff + k] = wRe[k] / wnorm;
4612
+ VIm[vOff + k] = wIm[k] / wnorm;
4613
+ }
4614
+ }
4615
+ for (let i = 0; i < j; i++) {
4616
+ const c2 = cs[i], sR2 = snRe[i], sI2 = snIm[i];
4617
+ const hiR = HRe[i + j * ldh], hiI = HIm[i + j * ldh];
4618
+ const hi1R = HRe[i + 1 + j * ldh], hi1I = HIm[i + 1 + j * ldh];
4619
+ HRe[i + j * ldh] = c2 * hiR + (sR2 * hi1R - sI2 * hi1I);
4620
+ HIm[i + j * ldh] = c2 * hiI + (sR2 * hi1I + sI2 * hi1R);
4621
+ HRe[i + 1 + j * ldh] = -(sR2 * hiR + sI2 * hiI) + c2 * hi1R;
4622
+ HIm[i + 1 + j * ldh] = -(-sI2 * hiR + sR2 * hiI) + c2 * hi1I;
4623
+ }
4624
+ const aR = HRe[j + j * ldh], aI = HIm[j + j * ldh];
4625
+ const bR2 = HRe[j + 1 + j * ldh], bI2 = HIm[j + 1 + j * ldh];
4626
+ const {
4627
+ c,
4628
+ sRe: sR,
4629
+ sIm: sI,
4630
+ rRe: rrR,
4631
+ rIm: rrI
4632
+ } = complexGivens(aR, aI, bR2, bI2);
4633
+ cs[j] = c;
4634
+ snRe[j] = sR;
4635
+ snIm[j] = sI;
4636
+ HRe[j + j * ldh] = rrR;
4637
+ HIm[j + j * ldh] = rrI;
4638
+ HRe[j + 1 + j * ldh] = 0;
4639
+ HIm[j + 1 + j * ldh] = 0;
4640
+ const gjR = gRe[j], gjI = gIm[j];
4641
+ const gj1R = gRe[j + 1], gj1I = gIm[j + 1];
4642
+ gRe[j] = c * gjR + (sR * gj1R - sI * gj1I);
4643
+ gIm[j] = c * gjI + (sR * gj1I + sI * gj1R);
4644
+ gRe[j + 1] = -(sR * gjR + sI * gjI) + c * gj1R;
4645
+ gIm[j + 1] = -(-sI * gjR + sR * gjI) + c * gj1I;
4646
+ const residNorm = Math.sqrt(
4647
+ gRe[j + 1] * gRe[j + 1] + gIm[j + 1] * gIm[j + 1]
4648
+ );
4649
+ resvecList.push(residNorm);
4650
+ if (residNorm / normMb <= tol) {
4651
+ const { yRe: yRe2, yIm: yIm2 } = complexBackSolve(HRe, HIm, gRe, gIm, j + 1, ldh);
4652
+ for (let k = 0; k < n; k++) {
4653
+ for (let l = 0; l <= j; l++) {
4654
+ xRe[k] += VRe[k + l * n] * yRe2[l] - VIm[k + l * n] * yIm2[l];
4655
+ xIm[k] += VRe[k + l * n] * yIm2[l] + VIm[k + l * n] * yRe2[l];
4656
+ }
4657
+ }
4658
+ flag = 0;
4659
+ converged = true;
4660
+ break;
4661
+ }
4662
+ }
4663
+ if (converged) break;
4664
+ const { yRe, yIm } = complexBackSolve(HRe, HIm, gRe, gIm, restart, ldh);
4665
+ for (let k = 0; k < n; k++) {
4666
+ for (let l = 0; l < restart; l++) {
4667
+ xRe[k] += VRe[k + l * n] * yRe[l] - VIm[k + l * n] * yIm[l];
4668
+ xIm[k] += VRe[k + l * n] * yIm[l] + VIm[k + l * n] * yRe[l];
4669
+ }
4670
+ }
4671
+ const ax2 = matvec({ re: xRe, im: xIm });
4672
+ rRe = new Float64Array(n);
4673
+ rIm = new Float64Array(n);
4674
+ for (let i = 0; i < n; i++) {
4675
+ rRe[i] = b.re[i] - ax2.re[i];
4676
+ rIm[i] = b.im[i] - ax2.im[i];
4677
+ }
4678
+ if (precSolve) {
4679
+ const pr = precSolve({ re: rRe, im: rIm });
4680
+ rRe = new Float64Array(pr.re);
4681
+ rIm = new Float64Array(pr.im);
4682
+ }
4683
+ beta = cnrm2(rRe, rIm, n);
4684
+ if (beta / normMb <= tol) {
4685
+ flag = 0;
4686
+ innerIter = 0;
4687
+ break;
4688
+ }
4689
+ }
4690
+ let relres;
4691
+ if (flag === 0) {
4692
+ const ax3 = matvec({ re: xRe, im: xIm });
4693
+ let fRe = new Float64Array(n), fIm = new Float64Array(n);
4694
+ for (let i = 0; i < n; i++) {
4695
+ fRe[i] = b.re[i] - ax3.re[i];
4696
+ fIm[i] = b.im[i] - ax3.im[i];
4697
+ }
4698
+ if (precSolve) {
4699
+ const pr = precSolve({ re: fRe, im: fIm });
4700
+ fRe = new Float64Array(pr.re);
4701
+ fIm = new Float64Array(pr.im);
4702
+ }
4703
+ relres = cnrm2(fRe, fIm, n) / normMb;
4704
+ } else {
4705
+ relres = beta / normMb;
4706
+ }
4707
+ return {
4708
+ x: { re: xRe, im: xIm },
4709
+ flag,
4710
+ relres,
4711
+ iter: [outerIter, innerIter],
4712
+ resvec: new Float64Array(resvecList)
4713
+ };
4714
+ }
4715
+ function cnrm2(re2, im2, n) {
4716
+ let s = 0;
4717
+ for (let i = 0; i < n; i++) s += re2[i] * re2[i] + im2[i] * im2[i];
4718
+ return Math.sqrt(s);
4719
+ }
4720
+ function complexGivens(aRe, aIm, bRe, bIm) {
4721
+ const absB = Math.sqrt(bRe * bRe + bIm * bIm);
4722
+ if (absB === 0) return { c: 1, sRe: 0, sIm: 0, rRe: aRe, rIm: aIm };
4723
+ const absA = Math.sqrt(aRe * aRe + aIm * aIm);
4724
+ if (absA === 0)
4725
+ return { c: 0, sRe: bRe / absB, sIm: -bIm / absB, rRe: absB, rIm: 0 };
4726
+ const norm = Math.sqrt(absA * absA + absB * absB);
4727
+ const c = absA / norm;
4728
+ const alpRe = aRe / absA, alpIm = aIm / absA;
4729
+ const sRe2 = (alpRe * bRe + alpIm * bIm) / norm;
4730
+ const sIm2 = (alpIm * bRe - alpRe * bIm) / norm;
4731
+ return { c, sRe: sRe2, sIm: sIm2, rRe: alpRe * norm, rIm: alpIm * norm };
4732
+ }
4733
+ function complexBackSolve(HRe, HIm, gRe, gIm, m, ldh) {
4734
+ const yRe = new Float64Array(m);
4735
+ const yIm = new Float64Array(m);
4736
+ for (let i = 0; i < m; i++) {
4737
+ yRe[i] = gRe[i];
4738
+ yIm[i] = gIm[i];
4739
+ }
4740
+ for (let i = m - 1; i >= 0; i--) {
4741
+ for (let j = i + 1; j < m; j++) {
4742
+ const hR = HRe[i + j * ldh], hI = HIm[i + j * ldh];
4743
+ yRe[i] -= hR * yRe[j] - hI * yIm[j];
4744
+ yIm[i] -= hR * yIm[j] + hI * yRe[j];
4745
+ }
4746
+ const dR = HRe[i + i * ldh], dI = HIm[i + i * ldh];
4747
+ const dAbs2 = dR * dR + dI * dI;
4748
+ const tmpR = yRe[i], tmpI = yIm[i];
4749
+ yRe[i] = (tmpR * dR + tmpI * dI) / dAbs2;
4750
+ yIm[i] = (tmpI * dR - tmpR * dI) / dAbs2;
4751
+ }
4752
+ return { yRe, yIm };
4753
+ }
4754
+ function complexLuSolveInPlace(n, LURe, LUIm, ipiv, rhsRe, rhsIm) {
4755
+ for (let i = 0; i < n; i++) {
4756
+ const pi = ipiv[i] - 1;
4757
+ if (pi !== i) {
4758
+ let tmp = rhsRe[i];
4759
+ rhsRe[i] = rhsRe[pi];
4760
+ rhsRe[pi] = tmp;
4761
+ tmp = rhsIm[i];
4762
+ rhsIm[i] = rhsIm[pi];
4763
+ rhsIm[pi] = tmp;
4764
+ }
4765
+ }
4766
+ for (let i = 1; i < n; i++) {
4767
+ for (let j = 0; j < i; j++) {
4768
+ const lR = LURe[i + j * n], lI = LUIm[i + j * n];
4769
+ rhsRe[i] -= lR * rhsRe[j] - lI * rhsIm[j];
4770
+ rhsIm[i] -= lR * rhsIm[j] + lI * rhsRe[j];
4771
+ }
4772
+ }
4773
+ for (let i = n - 1; i >= 0; i--) {
4774
+ for (let j = i + 1; j < n; j++) {
4775
+ const uR = LURe[i + j * n], uI = LUIm[i + j * n];
4776
+ rhsRe[i] -= uR * rhsRe[j] - uI * rhsIm[j];
4777
+ rhsIm[i] -= uR * rhsIm[j] + uI * rhsRe[j];
4778
+ }
4779
+ const dR = LURe[i + i * n], dI = LUIm[i + i * n];
4780
+ const dAbs2 = dR * dR + dI * dI;
4781
+ const tmpR = rhsRe[i], tmpI = rhsIm[i];
4782
+ rhsRe[i] = (tmpR * dR + tmpI * dI) / dAbs2;
4783
+ rhsIm[i] = (tmpI * dR - tmpR * dI) / dAbs2;
4784
+ }
4785
+ }
4786
+ function luSolveInPlace(n, LU, ipiv, rhs) {
4787
+ for (let i = 0; i < n; i++) {
4788
+ const pi = ipiv[i] - 1;
4789
+ if (pi !== i) {
4790
+ const tmp = rhs[i];
4791
+ rhs[i] = rhs[pi];
4792
+ rhs[pi] = tmp;
4793
+ }
4794
+ }
4795
+ for (let i = 1; i < n; i++) {
4796
+ for (let j = 0; j < i; j++) rhs[i] -= LU[i + j * n] * rhs[j];
4797
+ }
4798
+ for (let i = n - 1; i >= 0; i--) {
4799
+ for (let j = i + 1; j < n; j++) rhs[i] -= LU[i + j * n] * rhs[j];
4800
+ rhs[i] /= LU[i + i * n];
4801
+ }
4802
+ }
4803
+
4382
4804
  // src/ts-lapack/src/BLAS/dgemv.ts
4383
4805
  function dgemv(trans, m, n, alpha, a, aOff, lda, x, xOff, incx, beta, y, yOff, incy) {
4384
4806
  const notrans = trans === NOTRANS;
@@ -19940,6 +20362,160 @@ function cholComplex(dataRe, dataIm, n, upper) {
19940
20362
  }
19941
20363
  return { RRe: re2, RIm: im2, info };
19942
20364
  }
20365
+ function gmres(A, n, b, restart, tol, maxit, M1, M2, x0) {
20366
+ const matvecFn = (x) => {
20367
+ const y = new Float64Array(n);
20368
+ for (let i = 0; i < n; i++) {
20369
+ let s = 0;
20370
+ for (let j = 0; j < n; j++) s += A[i + j * n] * x[j];
20371
+ y[i] = s;
20372
+ }
20373
+ return y;
20374
+ };
20375
+ let precSolveFn = null;
20376
+ if (M1 || M2) {
20377
+ const m1lu = M1 ? new Float64Array(M1) : null;
20378
+ const m1ipiv = M1 ? new Int32Array(n) : null;
20379
+ if (m1lu && m1ipiv) {
20380
+ const info = dgetrf(n, n, m1lu, n, m1ipiv);
20381
+ if (info > 0) throw new Error("gmres: preconditioner M1 is singular");
20382
+ }
20383
+ const m2lu = M2 ? new Float64Array(M2) : null;
20384
+ const m2ipiv = M2 ? new Int32Array(n) : null;
20385
+ if (m2lu && m2ipiv) {
20386
+ const info = dgetrf(n, n, m2lu, n, m2ipiv);
20387
+ if (info > 0) throw new Error("gmres: preconditioner M2 is singular");
20388
+ }
20389
+ precSolveFn = (r) => {
20390
+ const z = new Float64Array(r);
20391
+ if (m1lu && m1ipiv) luSolveInPlace(n, m1lu, m1ipiv, z);
20392
+ if (m2lu && m2ipiv) luSolveInPlace(n, m2lu, m2ipiv, z);
20393
+ return z;
20394
+ };
20395
+ }
20396
+ const result = gmresCore(
20397
+ matvecFn,
20398
+ precSolveFn,
20399
+ b,
20400
+ n,
20401
+ restart,
20402
+ tol,
20403
+ maxit,
20404
+ x0
20405
+ );
20406
+ const iterArr = new Int32Array(2);
20407
+ iterArr[0] = result.iter[0];
20408
+ iterArr[1] = result.iter[1];
20409
+ return {
20410
+ x: result.x,
20411
+ flag: result.flag,
20412
+ relres: result.relres,
20413
+ iter: iterArr,
20414
+ resvec: result.resvec
20415
+ };
20416
+ }
20417
+ function gmresComplex(ARe, AIm, n, bRe, bIm, restart, tol, maxit, M1Re, M1Im, M2Re, M2Im, x0Re, x0Im) {
20418
+ const matvecFn = (x) => {
20419
+ const yRe = new Float64Array(n);
20420
+ const yIm = new Float64Array(n);
20421
+ for (let i = 0; i < n; i++) {
20422
+ let sRe2 = 0, sIm2 = 0;
20423
+ for (let j = 0; j < n; j++) {
20424
+ const aR = ARe[i + j * n], aI = AIm[i + j * n];
20425
+ sRe2 += aR * x.re[j] - aI * x.im[j];
20426
+ sIm2 += aR * x.im[j] + aI * x.re[j];
20427
+ }
20428
+ yRe[i] = sRe2;
20429
+ yIm[i] = sIm2;
20430
+ }
20431
+ return { re: yRe, im: yIm };
20432
+ };
20433
+ let precSolveFn = null;
20434
+ if (M1Re || M2Re) {
20435
+ const m1luRe = M1Re ? new Float64Array(M1Re) : null;
20436
+ const m1luIm = M1Im ? new Float64Array(M1Im) : null;
20437
+ const m1ipiv = M1Re ? new Int32Array(n) : null;
20438
+ if (m1luRe && m1luIm && m1ipiv) {
20439
+ complexLuFactor(n, m1luRe, m1luIm, m1ipiv);
20440
+ }
20441
+ const m2luRe = M2Re ? new Float64Array(M2Re) : null;
20442
+ const m2luIm = M2Im ? new Float64Array(M2Im) : null;
20443
+ const m2ipiv = M2Re ? new Int32Array(n) : null;
20444
+ if (m2luRe && m2luIm && m2ipiv) {
20445
+ complexLuFactor(n, m2luRe, m2luIm, m2ipiv);
20446
+ }
20447
+ precSolveFn = (r) => {
20448
+ const zRe = new Float64Array(r.re);
20449
+ const zIm = new Float64Array(r.im);
20450
+ if (m1luRe && m1luIm && m1ipiv)
20451
+ complexLuSolveInPlace(n, m1luRe, m1luIm, m1ipiv, zRe, zIm);
20452
+ if (m2luRe && m2luIm && m2ipiv)
20453
+ complexLuSolveInPlace(n, m2luRe, m2luIm, m2ipiv, zRe, zIm);
20454
+ return { re: zRe, im: zIm };
20455
+ };
20456
+ }
20457
+ const result = gmresCoreComplex(
20458
+ matvecFn,
20459
+ precSolveFn,
20460
+ { re: bRe, im: bIm },
20461
+ n,
20462
+ restart,
20463
+ tol,
20464
+ maxit,
20465
+ x0Re && x0Im ? { re: x0Re, im: x0Im } : null
20466
+ );
20467
+ const iterArr = new Int32Array(2);
20468
+ iterArr[0] = result.iter[0];
20469
+ iterArr[1] = result.iter[1];
20470
+ return {
20471
+ xRe: result.x.re,
20472
+ xIm: result.x.im,
20473
+ flag: result.flag,
20474
+ relres: result.relres,
20475
+ iter: iterArr,
20476
+ resvec: result.resvec
20477
+ };
20478
+ }
20479
+ function complexLuFactor(n, re2, im2, ipiv) {
20480
+ for (let k = 0; k < n; k++) {
20481
+ let maxVal = -1;
20482
+ let maxIdx = k;
20483
+ for (let i = k; i < n; i++) {
20484
+ const v = Math.sqrt(
20485
+ re2[i + k * n] * re2[i + k * n] + im2[i + k * n] * im2[i + k * n]
20486
+ );
20487
+ if (v > maxVal) {
20488
+ maxVal = v;
20489
+ maxIdx = i;
20490
+ }
20491
+ }
20492
+ ipiv[k] = maxIdx + 1;
20493
+ if (maxIdx !== k) {
20494
+ for (let j = 0; j < n; j++) {
20495
+ let tmp = re2[k + j * n];
20496
+ re2[k + j * n] = re2[maxIdx + j * n];
20497
+ re2[maxIdx + j * n] = tmp;
20498
+ tmp = im2[k + j * n];
20499
+ im2[k + j * n] = im2[maxIdx + j * n];
20500
+ im2[maxIdx + j * n] = tmp;
20501
+ }
20502
+ }
20503
+ const dR = re2[k + k * n], dI = im2[k + k * n];
20504
+ const dAbs2 = dR * dR + dI * dI;
20505
+ if (dAbs2 === 0) continue;
20506
+ for (let i = k + 1; i < n; i++) {
20507
+ const aR = re2[i + k * n], aI = im2[i + k * n];
20508
+ const lR = (aR * dR + aI * dI) / dAbs2;
20509
+ const lI = (aI * dR - aR * dI) / dAbs2;
20510
+ re2[i + k * n] = lR;
20511
+ im2[i + k * n] = lI;
20512
+ for (let j = k + 1; j < n; j++) {
20513
+ re2[i + j * n] -= lR * re2[k + j * n] - lI * im2[k + j * n];
20514
+ im2[i + j * n] -= lR * im2[k + j * n] + lI * re2[k + j * n];
20515
+ }
20516
+ }
20517
+ }
20518
+ }
19943
20519
  var _bridge2 = {
19944
20520
  inv,
19945
20521
  matmul,
@@ -19950,7 +20526,9 @@ var _bridge2 = {
19950
20526
  lu,
19951
20527
  svd,
19952
20528
  chol,
19953
- cholComplex
20529
+ cholComplex,
20530
+ gmres,
20531
+ gmresComplex
19954
20532
  };
19955
20533
  function getTsLapackBridge() {
19956
20534
  return _bridge2;
@@ -22141,11 +22719,27 @@ function catAlongDim(values, dimIdx) {
22141
22719
  if (tensors.length === 0) return RTV.tensor(new FloatXArray(0), [0, 0]);
22142
22720
  if (tensors.length === 1) return tensors[0];
22143
22721
  const ndim = Math.max(2, ...tensors.map((t) => t.shape.length));
22144
- const shapes = tensors.map((t) => {
22722
+ let shapes = tensors.map((t) => {
22145
22723
  const s = [...t.shape];
22146
22724
  while (s.length < ndim) s.push(1);
22147
22725
  return s;
22148
22726
  });
22727
+ const refIdx = tensors.findIndex((t) => t.data.length > 0);
22728
+ if (refIdx >= 0) {
22729
+ const ref = shapes[refIdx];
22730
+ const keep = tensors.map((t, i) => {
22731
+ if (t.data.length > 0) return true;
22732
+ for (let d = 0; d < ndim; d++) {
22733
+ if (d === dimIdx) continue;
22734
+ if (shapes[i][d] !== ref[d]) return false;
22735
+ }
22736
+ return true;
22737
+ });
22738
+ tensors = tensors.filter((_, i) => keep[i]);
22739
+ shapes = shapes.filter((_, i) => keep[i]);
22740
+ if (tensors.length === 0) return RTV.tensor(new FloatXArray(0), [0, 0]);
22741
+ if (tensors.length === 1) return tensors[0];
22742
+ }
22149
22743
  const refShape = shapes[0];
22150
22744
  for (let i = 1; i < shapes.length; i++) {
22151
22745
  for (let d = 0; d < ndim; d++) {
@@ -27104,6 +27698,8 @@ function dispatchPlotCall(rt, name, args) {
27104
27698
  return rt.scatter_call(args.map((a) => ensureRuntimeValue(a)));
27105
27699
  case "imagesc":
27106
27700
  return rt.imagesc_call(args.map((a) => ensureRuntimeValue(a)));
27701
+ case "pcolor":
27702
+ return rt.pcolor_call(args.map((a) => ensureRuntimeValue(a)));
27107
27703
  case "contour":
27108
27704
  return rt.contour_call(
27109
27705
  args.map((a) => ensureRuntimeValue(a)),
@@ -28412,10 +29008,10 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
28412
29008
  let stride = 1;
28413
29009
  for (let k = 0; k < nIdx; k++) {
28414
29010
  const dimSize = k < s.length ? s[k] : 1;
28415
- const sub = Math.round(indices[k]) - 1;
28416
- if (sub < 0 || sub >= dimSize)
29011
+ const sub2 = Math.round(indices[k]) - 1;
29012
+ if (sub2 < 0 || sub2 >= dimSize)
28417
29013
  throw new RuntimeError("Index exceeds array bounds");
28418
- lin += sub * stride;
29014
+ lin += sub2 * stride;
28419
29015
  stride *= dimSize;
28420
29016
  }
28421
29017
  if (t.imag !== void 0) {
@@ -28612,16 +29208,24 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
28612
29208
  }
28613
29209
  return dictInsertSingle(mv, key, rhsMv2);
28614
29210
  }
28615
- if (indices.length === 1 && (isRuntimeStructArray(mv) || isRuntimeTensor(mv) && mv.data.length === 0)) {
29211
+ if (indices.length === 1 && (isRuntimeStructArray(mv) || isRuntimeStruct(mv) || isRuntimeTensor(mv) && mv.data.length === 0)) {
28616
29212
  const rhsMv2 = ensureRuntimeValue(rhs);
28617
29213
  if (isRuntimeStruct(rhsMv2)) {
28618
29214
  const idxVal = ensureRuntimeValue(indices[0]);
28619
29215
  const k = Math.round(toNumber(idxVal)) - 1;
28620
- const existingElements = isRuntimeStructArray(mv) ? [...mv.elements] : [];
28621
- const existingFieldNames = isRuntimeStructArray(mv) ? mv.fieldNames : [];
29216
+ const existingElements = isRuntimeStructArray(mv) ? [...mv.elements] : isRuntimeStruct(mv) ? [mv] : [];
29217
+ const existingFieldNames = isRuntimeStructArray(mv) ? mv.fieldNames : isRuntimeStruct(mv) ? [...mv.fields.keys()] : [];
28622
29218
  const allFieldNames = [
28623
29219
  .../* @__PURE__ */ new Set([...existingFieldNames, ...[...rhsMv2.fields.keys()]])
28624
29220
  ];
29221
+ for (let ei = 0; ei < existingElements.length; ei++) {
29222
+ const el = existingElements[ei];
29223
+ for (const f of allFieldNames) {
29224
+ if (!el.fields.has(f)) {
29225
+ el.fields.set(f, RTV.tensor(new FloatXArray(0), [0, 0]));
29226
+ }
29227
+ }
29228
+ }
28625
29229
  while (existingElements.length <= k) {
28626
29230
  existingElements.push(
28627
29231
  RTV.struct(
@@ -29123,7 +29727,7 @@ registerIBuiltin({
29123
29727
  };
29124
29728
  }
29125
29729
  });
29126
- for (const name of ["groot", "gcf", "gca", "shg", "newplot", "caxis"]) {
29730
+ for (const name of ["groot", "gcf", "gca", "shg", "newplot"]) {
29127
29731
  registerIBuiltin({
29128
29732
  name,
29129
29733
  resolve: () => ({
@@ -29136,7 +29740,32 @@ registerIBuiltin({
29136
29740
  name: "get",
29137
29741
  resolve: () => ({
29138
29742
  outputTypes: [{ kind: "unknown" }],
29139
- apply: () => RTV.dummyHandle()
29743
+ apply: (args) => {
29744
+ if (args.length >= 2) {
29745
+ const prop = args[1];
29746
+ const propStr = isRuntimeChar(prop) ? prop.value : isRuntimeString(prop) ? prop : "";
29747
+ if (propStr.toLowerCase() === "colormap") {
29748
+ const n = 64;
29749
+ const data = new Float64Array(n * 3);
29750
+ for (let i = 0; i < n; i++) {
29751
+ const t = i / (n - 1);
29752
+ if (t < 0.5) {
29753
+ const s = t * 2;
29754
+ data[i] = 0.2 * (1 - s);
29755
+ data[n + i] = 0.1 + 0.6 * s;
29756
+ data[2 * n + i] = 0.9 - 0.3 * s;
29757
+ } else {
29758
+ const s = (t - 0.5) * 2;
29759
+ data[i] = 0.2 + 0.8 * s;
29760
+ data[n + i] = 0.7 + 0.3 * s;
29761
+ data[2 * n + i] = 0.6 - 0.5 * s;
29762
+ }
29763
+ }
29764
+ return RTV.tensor(data, [n, 3]);
29765
+ }
29766
+ }
29767
+ return RTV.dummyHandle();
29768
+ }
29140
29769
  })
29141
29770
  });
29142
29771
  registerIBuiltin({
@@ -29562,13 +30191,7 @@ registerIBuiltin({
29562
30191
  })
29563
30192
  });
29564
30193
  function getMexExt() {
29565
- if (typeof process === "undefined") return "";
29566
- const platform = process.platform;
29567
- const cpuArch = process.arch;
29568
- if (platform === "win32") return "mexw64";
29569
- if (platform === "darwin")
29570
- return cpuArch === "arm64" ? "mexmaca64" : "mexmaci64";
29571
- return "mexa64";
30194
+ return "numbl.js";
29572
30195
  }
29573
30196
  defineBuiltin({
29574
30197
  name: "mexext",
@@ -30115,6 +30738,84 @@ function expandXY(xVal, yVal, rows, cols) {
30115
30738
  }
30116
30739
  return { x, y };
30117
30740
  }
30741
+ var PCOLOR_NAME_VALUE_KEYS = /* @__PURE__ */ new Set(["edgecolor", "facealpha"]);
30742
+ function isPcolorNameValueKey(v) {
30743
+ if (!isRuntimeString(v) && !isRuntimeChar(v)) return null;
30744
+ const lower = isRuntimeString(v) ? v.toLocaleLowerCase() : v.value.toLowerCase();
30745
+ if (PCOLOR_NAME_VALUE_KEYS.has(lower)) return lower;
30746
+ return null;
30747
+ }
30748
+ function applyPcolorNameValue(trace, key, value) {
30749
+ switch (key) {
30750
+ case "edgecolor": {
30751
+ const s = getStringValueIfString(value);
30752
+ if (s !== void 0 && s.toLowerCase() === "none") {
30753
+ trace.edgeColor = "none";
30754
+ break;
30755
+ }
30756
+ const c = resolveColor(value);
30757
+ if (c) trace.edgeColor = c;
30758
+ break;
30759
+ }
30760
+ case "facealpha": {
30761
+ const n = typeof value === "number" ? value : toNumber(value);
30762
+ trace.faceAlpha = n;
30763
+ break;
30764
+ }
30765
+ }
30766
+ }
30767
+ function parsePcolorArgs(args) {
30768
+ let pos = 0;
30769
+ let xData;
30770
+ let yData;
30771
+ let cData;
30772
+ let rows;
30773
+ let cols;
30774
+ let numericCount = 0;
30775
+ for (let i = pos; i < args.length; i++) {
30776
+ if (isNumericArg(args[i])) numericCount++;
30777
+ else break;
30778
+ }
30779
+ if (numericCount === 1) {
30780
+ const c = args[pos++];
30781
+ const info = getMatrixInfo(c);
30782
+ rows = info.rows;
30783
+ cols = info.cols;
30784
+ cData = info.data;
30785
+ const gen = generateMeshgrid(rows, cols);
30786
+ xData = gen.x;
30787
+ yData = gen.y;
30788
+ } else if (numericCount >= 3) {
30789
+ const x = args[pos++];
30790
+ const y = args[pos++];
30791
+ const c = args[pos++];
30792
+ const cInfo = getMatrixInfo(c);
30793
+ rows = cInfo.rows;
30794
+ cols = cInfo.cols;
30795
+ cData = cInfo.data;
30796
+ const expanded = expandXY(x, y, rows, cols);
30797
+ xData = expanded.x;
30798
+ yData = expanded.y;
30799
+ } else {
30800
+ throw new Error("pcolor requires 1 or 3 numeric input arguments");
30801
+ }
30802
+ const trace = {
30803
+ x: xData,
30804
+ y: yData,
30805
+ c: cData,
30806
+ rows,
30807
+ cols
30808
+ };
30809
+ while (pos < args.length) {
30810
+ const key = isPcolorNameValueKey(args[pos]);
30811
+ if (!key) break;
30812
+ pos++;
30813
+ if (pos >= args.length) break;
30814
+ const value = args[pos++];
30815
+ applyPcolorNameValue(trace, key, value);
30816
+ }
30817
+ return trace;
30818
+ }
30118
30819
  function applySurfNameValue(trace, key, value) {
30119
30820
  switch (key) {
30120
30821
  case "edgecolor": {
@@ -31150,24 +31851,38 @@ function plotInstr(plotInstructions, instr) {
31150
31851
  value: resolveOnOff(instr.value)
31151
31852
  });
31152
31853
  break;
31153
- case "set_colorbar":
31154
- plotInstructions.push({
31854
+ case "set_colorbar": {
31855
+ const cbInstr = {
31155
31856
  type: "set_colorbar",
31156
31857
  value: resolveStr(instr.value)
31157
- });
31858
+ };
31859
+ if (instr.location !== void 0) {
31860
+ cbInstr.location = resolveStr(instr.location);
31861
+ }
31862
+ plotInstructions.push(cbInstr);
31158
31863
  break;
31159
- case "set_colormap":
31160
- plotInstructions.push({
31864
+ }
31865
+ case "set_colormap": {
31866
+ const cmInstr = {
31161
31867
  type: "set_colormap",
31162
31868
  name: resolveStr(instr.name).replace(/^"|"$/g, "")
31163
- });
31869
+ };
31870
+ if (instr.data) cmInstr.data = instr.data;
31871
+ plotInstructions.push(cmInstr);
31164
31872
  break;
31873
+ }
31165
31874
  case "set_axis":
31166
31875
  plotInstructions.push({
31167
31876
  type: "set_axis",
31168
31877
  value: resolveStr(instr.value).replace(/^"|"$/g, "")
31169
31878
  });
31170
31879
  break;
31880
+ case "set_caxis":
31881
+ plotInstructions.push({
31882
+ type: "set_caxis",
31883
+ limits: instr.limits
31884
+ });
31885
+ break;
31171
31886
  }
31172
31887
  }
31173
31888
  function viewCall(plotInstructions, args) {
@@ -31210,6 +31925,10 @@ function imagescCall(plotInstructions, args) {
31210
31925
  const trace = parseImagescArgs(args);
31211
31926
  plotInstructions.push({ type: "imagesc", trace });
31212
31927
  }
31928
+ function pcolorCall(plotInstructions, args) {
31929
+ const trace = parsePcolorArgs(args);
31930
+ plotInstructions.push({ type: "pcolor", trace });
31931
+ }
31213
31932
  function contourCall(plotInstructions, args, filled) {
31214
31933
  const trace = parseContourArgs(args, filled);
31215
31934
  plotInstructions.push({ type: "contour", trace });
@@ -31972,6 +32691,7 @@ var SPECIAL_BUILTIN_NAMES = [
31972
32691
  "surf",
31973
32692
  "scatter",
31974
32693
  "imagesc",
32694
+ "pcolor",
31975
32695
  "contour",
31976
32696
  "contourf",
31977
32697
  "mesh",
@@ -32014,6 +32734,7 @@ var SPECIAL_BUILTIN_NAMES = [
32014
32734
  "zlabel",
32015
32735
  "colorbar",
32016
32736
  "axis",
32737
+ "caxis",
32017
32738
  "mfilename",
32018
32739
  "addpath",
32019
32740
  "rmpath",
@@ -32039,7 +32760,8 @@ var SPECIAL_BUILTIN_NAMES = [
32039
32760
  "ode23",
32040
32761
  "deval",
32041
32762
  "toc",
32042
- "quadgk"
32763
+ "quadgk",
32764
+ "gmres"
32043
32765
  ];
32044
32766
 
32045
32767
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -33379,6 +34101,53 @@ function registerSpecialBuiltins(rt) {
33379
34101
  plotInstr(rt.plotInstructions, { type: "clf" });
33380
34102
  return nargout >= 1 ? RTV.num(1) : void 0;
33381
34103
  });
34104
+ registerSpecial("colorbar", (nargout, args) => {
34105
+ const LOCATIONS = /* @__PURE__ */ new Set([
34106
+ "east",
34107
+ "west",
34108
+ "north",
34109
+ "south",
34110
+ "eastoutside",
34111
+ "westoutside",
34112
+ "northoutside",
34113
+ "southoutside"
34114
+ ]);
34115
+ let value = "on";
34116
+ let location;
34117
+ let i = 0;
34118
+ while (i < args.length) {
34119
+ let s;
34120
+ try {
34121
+ s = toString(args[i]);
34122
+ } catch {
34123
+ break;
34124
+ }
34125
+ const lower = s.toLowerCase();
34126
+ if (lower === "off") {
34127
+ value = "off";
34128
+ i++;
34129
+ continue;
34130
+ }
34131
+ if (LOCATIONS.has(lower)) {
34132
+ location = lower;
34133
+ i++;
34134
+ continue;
34135
+ }
34136
+ i += 2;
34137
+ }
34138
+ plotInstr(rt.plotInstructions, {
34139
+ type: "set_colorbar",
34140
+ value,
34141
+ location
34142
+ });
34143
+ if (nargout >= 1) {
34144
+ return RTV.struct({
34145
+ Location: RTV.char(location ?? "eastoutside"),
34146
+ Visible: RTV.char(value === "off" ? "off" : "on")
34147
+ });
34148
+ }
34149
+ return void 0;
34150
+ });
33382
34151
  registerSpecial("ode45", (nargout, args) => {
33383
34152
  return _ode45Impl(rt, nargout, args, dormandPrince45);
33384
34153
  });
@@ -33388,6 +34157,9 @@ function registerSpecialBuiltins(rt) {
33388
34157
  registerSpecial("deval", (_nargout, args) => {
33389
34158
  return _devalImpl(args);
33390
34159
  });
34160
+ registerSpecial("gmres", (nargout, args) => {
34161
+ return _gmresImpl(rt, nargout, args);
34162
+ });
33391
34163
  }
33392
34164
  function _ode45Impl(rt, nargout, args, tableau = dormandPrince45) {
33393
34165
  const solverName = tableau.name;
@@ -33616,6 +34388,530 @@ function _devalImpl(args) {
33616
34388
  }
33617
34389
  return RTV.tensor(yData, [neq, nPts]);
33618
34390
  }
34391
+ function _gmresImpl(rt, nargout, args) {
34392
+ if (args.length < 2)
34393
+ throw new RuntimeError("gmres requires at least 2 arguments (A, b)");
34394
+ const Aarg = ensureRuntimeValue(args[0]);
34395
+ const bArg = ensureRuntimeValue(args[1]);
34396
+ if (!isRuntimeTensor(bArg) && !isRuntimeNumber(bArg) && !isRuntimeComplexNumber(bArg))
34397
+ throw new RuntimeError("gmres: b must be a numeric vector");
34398
+ let n;
34399
+ if (isRuntimeTensor(bArg)) {
34400
+ n = bArg.data.length;
34401
+ } else {
34402
+ n = 1;
34403
+ }
34404
+ const restartArg = args.length >= 3 ? ensureRuntimeValue(args[2]) : null;
34405
+ const tolArg = args.length >= 4 ? ensureRuntimeValue(args[3]) : null;
34406
+ const maxitArg = args.length >= 5 ? ensureRuntimeValue(args[4]) : null;
34407
+ const M1arg = args.length >= 6 ? ensureRuntimeValue(args[5]) : null;
34408
+ const M2arg = args.length >= 7 ? ensureRuntimeValue(args[6]) : null;
34409
+ const x0Arg = args.length >= 8 ? ensureRuntimeValue(args[7]) : null;
34410
+ let restart = n;
34411
+ if (restartArg !== null && !_isEmpty(restartArg)) {
34412
+ restart = _toNum(restartArg, "gmres: restart");
34413
+ }
34414
+ if (restart <= 0 || restart > n) restart = n;
34415
+ const noRestart = restart === n;
34416
+ const tol = tolArg !== null && !_isEmpty(tolArg) ? _toNum(tolArg, "gmres: tol") : 1e-6;
34417
+ let maxit;
34418
+ if (maxitArg !== null && !_isEmpty(maxitArg)) {
34419
+ maxit = _toNum(maxitArg, "gmres: maxit");
34420
+ } else {
34421
+ maxit = noRestart ? 1 : Math.max(1, Math.min(Math.ceil(n / restart), 10));
34422
+ }
34423
+ if (noRestart && maxitArg === null) restart = Math.min(n, 10);
34424
+ const isComplex2 = _isComplexArg(Aarg) || _isComplexArg(bArg) || M1arg !== null && _isComplexArg(M1arg) || M2arg !== null && _isComplexArg(M2arg) || x0Arg !== null && _isComplexArg(x0Arg);
34425
+ let flag;
34426
+ let relres;
34427
+ let iter;
34428
+ let resvec;
34429
+ let xTensor;
34430
+ if (isComplex2) {
34431
+ const bCV = _toComplexVec(bArg, n);
34432
+ const x0CV = x0Arg !== null && !_isEmpty(x0Arg) ? _toComplexVec(x0Arg, n) : null;
34433
+ const allMatrices = _isMatrixArg(Aarg) && _isMatrixArg(M1arg) && _isMatrixArg(M2arg);
34434
+ if (allMatrices) {
34435
+ const { re: ARe, im: AIm } = _toComplexMatrix(Aarg, n * n);
34436
+ const M1cv = _extractComplexMatrix(M1arg, n * n);
34437
+ const M2cv = _extractComplexMatrix(M2arg, n * n);
34438
+ const bridge = getEffectiveBridge("gmresComplex", "gmresComplex");
34439
+ if (bridge?.gmresComplex) {
34440
+ const r = bridge.gmresComplex(
34441
+ ARe,
34442
+ AIm,
34443
+ n,
34444
+ bCV.re,
34445
+ bCV.im,
34446
+ restart,
34447
+ tol,
34448
+ maxit,
34449
+ M1cv?.re ?? null,
34450
+ M1cv?.im ?? null,
34451
+ M2cv?.re ?? null,
34452
+ M2cv?.im ?? null,
34453
+ x0CV?.re ?? null,
34454
+ x0CV?.im ?? null
34455
+ );
34456
+ flag = r.flag;
34457
+ relres = r.relres;
34458
+ iter = [r.iter[0], r.iter[1]];
34459
+ resvec = r.resvec;
34460
+ xTensor = RTV.tensor(
34461
+ new FloatXArray(r.xRe),
34462
+ [n, 1],
34463
+ new FloatXArray(r.xIm)
34464
+ );
34465
+ } else {
34466
+ const matvec = _makeComplexMatvec(rt, Aarg, n);
34467
+ const precSolve = _makeComplexPrecSolve(rt, M1arg, M2arg, n);
34468
+ const r = gmresCoreComplex(
34469
+ matvec,
34470
+ precSolve,
34471
+ bCV,
34472
+ n,
34473
+ restart,
34474
+ tol,
34475
+ maxit,
34476
+ x0CV
34477
+ );
34478
+ flag = r.flag;
34479
+ relres = r.relres;
34480
+ iter = r.iter;
34481
+ resvec = r.resvec;
34482
+ xTensor = RTV.tensor(
34483
+ new FloatXArray(r.x.re),
34484
+ [n, 1],
34485
+ new FloatXArray(r.x.im)
34486
+ );
34487
+ }
34488
+ } else {
34489
+ const matvec = _makeComplexMatvec(rt, Aarg, n);
34490
+ const precSolve = _makeComplexPrecSolve(rt, M1arg, M2arg, n);
34491
+ const r = gmresCoreComplex(
34492
+ matvec,
34493
+ precSolve,
34494
+ bCV,
34495
+ n,
34496
+ restart,
34497
+ tol,
34498
+ maxit,
34499
+ x0CV
34500
+ );
34501
+ flag = r.flag;
34502
+ relres = r.relres;
34503
+ iter = r.iter;
34504
+ resvec = r.resvec;
34505
+ xTensor = RTV.tensor(
34506
+ new FloatXArray(r.x.re),
34507
+ [n, 1],
34508
+ new FloatXArray(r.x.im)
34509
+ );
34510
+ }
34511
+ } else {
34512
+ let bData;
34513
+ if (isRuntimeNumber(bArg)) {
34514
+ bData = new Float64Array([bArg]);
34515
+ } else {
34516
+ bData = toF64(bArg.data);
34517
+ }
34518
+ let x0 = null;
34519
+ if (x0Arg !== null && !_isEmpty(x0Arg)) {
34520
+ if (isRuntimeTensor(x0Arg)) x0 = toF64(x0Arg.data);
34521
+ else if (isRuntimeNumber(x0Arg)) x0 = new Float64Array([x0Arg]);
34522
+ }
34523
+ let xResult;
34524
+ const Ais_matrix = _isMatrixArg(Aarg);
34525
+ const M1is_matrix = _isMatrixArg(M1arg);
34526
+ const M2is_matrix = _isMatrixArg(M2arg);
34527
+ if (Ais_matrix && M1is_matrix && M2is_matrix) {
34528
+ const Adata = _extractRealMatrix(Aarg);
34529
+ const M1data = _extractMatrix(M1arg);
34530
+ const M2data = _extractMatrix(M2arg);
34531
+ const bridge = getEffectiveBridge("gmres", "gmres");
34532
+ if (bridge?.gmres) {
34533
+ const result = bridge.gmres(
34534
+ Adata,
34535
+ n,
34536
+ bData,
34537
+ restart,
34538
+ tol,
34539
+ maxit,
34540
+ M1data,
34541
+ M2data,
34542
+ x0
34543
+ );
34544
+ xResult = result.x;
34545
+ flag = result.flag;
34546
+ relres = result.relres;
34547
+ iter = [result.iter[0], result.iter[1]];
34548
+ resvec = result.resvec;
34549
+ } else {
34550
+ const r = _gmresWithCallbacks(
34551
+ Adata,
34552
+ n,
34553
+ bData,
34554
+ restart,
34555
+ tol,
34556
+ maxit,
34557
+ M1data,
34558
+ M2data,
34559
+ x0
34560
+ );
34561
+ xResult = r.x;
34562
+ flag = r.flag;
34563
+ relres = r.relres;
34564
+ iter = r.iter;
34565
+ resvec = r.resvec;
34566
+ }
34567
+ } else {
34568
+ const matvec = _makeMatvec(rt, Aarg, n);
34569
+ const precSolve = _makePrecSolve(rt, M1arg, M2arg, n);
34570
+ const r = gmresCore(matvec, precSolve, bData, n, restart, tol, maxit, x0);
34571
+ xResult = r.x;
34572
+ flag = r.flag;
34573
+ relres = r.relres;
34574
+ iter = r.iter;
34575
+ resvec = r.resvec;
34576
+ }
34577
+ xTensor = RTV.tensor(new FloatXArray(xResult), [n, 1]);
34578
+ }
34579
+ if (nargout <= 1) {
34580
+ if (flag === 0) {
34581
+ rt.output(
34582
+ `gmres converged at iteration ${iter[1]} to a solution with relative residual ${relres.toExponential(1)}.
34583
+ `
34584
+ );
34585
+ } else {
34586
+ rt.output(
34587
+ `gmres stopped at iteration ${iter[1]} without converging to the desired tolerance ${tol}
34588
+ because the maximum number of iterations was reached.
34589
+ The iterate returned (number ${iter[1]}) has relative residual ${relres.toExponential(1)}.
34590
+ `
34591
+ );
34592
+ }
34593
+ }
34594
+ if (nargout <= 1) return xTensor;
34595
+ if (nargout === 2) return [xTensor, flag];
34596
+ if (nargout === 3) return [xTensor, flag, relres];
34597
+ if (nargout === 4) {
34598
+ const iterTensor2 = RTV.tensor(new FloatXArray([iter[0], iter[1]]), [1, 2]);
34599
+ return [xTensor, flag, relres, iterTensor2];
34600
+ }
34601
+ const iterTensor = RTV.tensor(new FloatXArray([iter[0], iter[1]]), [1, 2]);
34602
+ const resvecTensor = RTV.tensor(new FloatXArray(resvec), [resvec.length, 1]);
34603
+ return [xTensor, flag, relres, iterTensor, resvecTensor];
34604
+ }
34605
+ function _isEmpty(v) {
34606
+ if (isRuntimeTensor(v) && v.data.length === 0) return true;
34607
+ return false;
34608
+ }
34609
+ function _toNum(v, ctx) {
34610
+ if (isRuntimeNumber(v)) return v;
34611
+ if (isRuntimeTensor(v) && v.data.length === 1) return v.data[0];
34612
+ throw new RuntimeError(`${ctx} must be a scalar`);
34613
+ }
34614
+ function _extractMatrix(arg) {
34615
+ if (arg === null || _isEmpty(arg)) return null;
34616
+ if (isRuntimeNumber(arg)) return new Float64Array([arg]);
34617
+ if (isRuntimeSparseMatrix(arg)) {
34618
+ const dense = sparseToDense(arg);
34619
+ return toF64(dense.data);
34620
+ }
34621
+ if (isRuntimeTensor(arg)) return toF64(arg.data);
34622
+ return null;
34623
+ }
34624
+ function _makeMatvec(rt, Aarg, n) {
34625
+ if (isRuntimeFunction(Aarg)) {
34626
+ return (x) => {
34627
+ const xTensor = RTV.tensor(new FloatXArray(x), [n, 1]);
34628
+ const resultRaw = rt.index(Aarg, [xTensor], 1);
34629
+ const rv = ensureRuntimeValue(resultRaw);
34630
+ if (isRuntimeNumber(rv)) return new Float64Array([rv]);
34631
+ if (isRuntimeTensor(rv)) return toF64(rv.data);
34632
+ throw new RuntimeError("gmres: A(x) must return a numeric vector");
34633
+ };
34634
+ }
34635
+ let Adata;
34636
+ if (isRuntimeNumber(Aarg)) {
34637
+ Adata = new Float64Array([Aarg]);
34638
+ } else if (isRuntimeSparseMatrix(Aarg)) {
34639
+ Adata = toF64(sparseToDense(Aarg).data);
34640
+ } else if (isRuntimeTensor(Aarg)) {
34641
+ Adata = toF64(Aarg.data);
34642
+ } else {
34643
+ throw new RuntimeError("gmres: A must be a matrix or function handle");
34644
+ }
34645
+ return (x) => {
34646
+ const y = new Float64Array(n);
34647
+ for (let i = 0; i < n; i++) {
34648
+ let s = 0;
34649
+ for (let j = 0; j < n; j++) s += Adata[i + j * n] * x[j];
34650
+ y[i] = s;
34651
+ }
34652
+ return y;
34653
+ };
34654
+ }
34655
+ function _makePrecSolve(rt, M1arg, M2arg, n) {
34656
+ const hasM1 = M1arg !== null && !_isEmpty(M1arg);
34657
+ const hasM2 = M2arg !== null && !_isEmpty(M2arg);
34658
+ if (!hasM1 && !hasM2) return null;
34659
+ const solve1 = hasM1 ? _makeSinglePrecSolve(rt, M1arg, n) : null;
34660
+ const solve2 = hasM2 ? _makeSinglePrecSolve(rt, M2arg, n) : null;
34661
+ return (r) => {
34662
+ let z = r;
34663
+ if (solve1) z = solve1(z);
34664
+ if (solve2) z = solve2(z);
34665
+ return z;
34666
+ };
34667
+ }
34668
+ function _makeSinglePrecSolve(rt, Marg, n) {
34669
+ if (isRuntimeFunction(Marg)) {
34670
+ return (r) => {
34671
+ const rTensor = RTV.tensor(new FloatXArray(r), [n, 1]);
34672
+ const resultRaw = rt.index(Marg, [rTensor], 1);
34673
+ const rv = ensureRuntimeValue(resultRaw);
34674
+ if (isRuntimeNumber(rv)) return new Float64Array([rv]);
34675
+ if (isRuntimeTensor(rv)) return toF64(rv.data);
34676
+ throw new RuntimeError("gmres: M(x) must return a numeric vector");
34677
+ };
34678
+ }
34679
+ let Mdata;
34680
+ if (isRuntimeNumber(Marg)) {
34681
+ Mdata = new Float64Array([Marg]);
34682
+ } else if (isRuntimeSparseMatrix(Marg)) {
34683
+ Mdata = toF64(sparseToDense(Marg).data);
34684
+ } else if (isRuntimeTensor(Marg)) {
34685
+ Mdata = toF64(Marg.data);
34686
+ } else {
34687
+ throw new RuntimeError(
34688
+ "gmres: preconditioner must be a matrix or function handle"
34689
+ );
34690
+ }
34691
+ const LU = new Float64Array(Mdata);
34692
+ const ipiv = new Int32Array(n);
34693
+ const info = dgetrf(n, n, LU, n, ipiv);
34694
+ if (info > 0)
34695
+ throw new RuntimeError("gmres: preconditioner matrix is singular");
34696
+ return (r) => {
34697
+ const z = new Float64Array(r);
34698
+ luSolveInPlace(n, LU, ipiv, z);
34699
+ return z;
34700
+ };
34701
+ }
34702
+ function _gmresWithCallbacks(A, n, b, restart, tol, maxit, M1, M2, x0) {
34703
+ const matvec = (x) => {
34704
+ const y = new Float64Array(n);
34705
+ for (let i = 0; i < n; i++) {
34706
+ let s = 0;
34707
+ for (let j = 0; j < n; j++) s += A[i + j * n] * x[j];
34708
+ y[i] = s;
34709
+ }
34710
+ return y;
34711
+ };
34712
+ let precSolve = null;
34713
+ if (M1 || M2) {
34714
+ const m1lu = M1 ? new Float64Array(M1) : null;
34715
+ const m1ipiv = M1 ? new Int32Array(n) : null;
34716
+ if (m1lu && m1ipiv) dgetrf(n, n, m1lu, n, m1ipiv);
34717
+ const m2lu = M2 ? new Float64Array(M2) : null;
34718
+ const m2ipiv = M2 ? new Int32Array(n) : null;
34719
+ if (m2lu && m2ipiv) dgetrf(n, n, m2lu, n, m2ipiv);
34720
+ precSolve = (r) => {
34721
+ const z = new Float64Array(r);
34722
+ if (m1lu && m1ipiv) luSolveInPlace(n, m1lu, m1ipiv, z);
34723
+ if (m2lu && m2ipiv) luSolveInPlace(n, m2lu, m2ipiv, z);
34724
+ return z;
34725
+ };
34726
+ }
34727
+ return gmresCore(matvec, precSolve, b, n, restart, tol, maxit, x0);
34728
+ }
34729
+ function _isComplexArg(v) {
34730
+ if (v === null) return false;
34731
+ if (isRuntimeComplexNumber(v)) return true;
34732
+ if (isRuntimeTensor(v) && v.imag) return true;
34733
+ if (isRuntimeSparseMatrix(v) && v.pi) return true;
34734
+ return false;
34735
+ }
34736
+ function _isMatrixArg(v) {
34737
+ if (v === null) return true;
34738
+ if (_isEmpty(v)) return true;
34739
+ return isRuntimeTensor(v) || isRuntimeNumber(v) || isRuntimeComplexNumber(v) || isRuntimeSparseMatrix(v);
34740
+ }
34741
+ function _extractRealMatrix(arg) {
34742
+ if (arg === null) return null;
34743
+ if (isRuntimeNumber(arg)) return new Float64Array([arg]);
34744
+ if (isRuntimeSparseMatrix(arg)) return toF64(sparseToDense(arg).data);
34745
+ if (isRuntimeTensor(arg)) return toF64(arg.data);
34746
+ return null;
34747
+ }
34748
+ function _toComplexVec(v, n) {
34749
+ if (isRuntimeComplexNumber(v)) {
34750
+ return { re: new Float64Array([v.re]), im: new Float64Array([v.im]) };
34751
+ }
34752
+ if (isRuntimeTensor(v)) {
34753
+ return {
34754
+ re: new Float64Array(toF64(v.data)),
34755
+ im: v.imag ? new Float64Array(toF64(v.imag)) : new Float64Array(n)
34756
+ };
34757
+ }
34758
+ if (isRuntimeNumber(v)) {
34759
+ const re2 = new Float64Array(1);
34760
+ re2[0] = v;
34761
+ return { re: re2, im: new Float64Array(1) };
34762
+ }
34763
+ throw new RuntimeError("gmres: cannot convert argument to complex vector");
34764
+ }
34765
+ function _toComplexMatrix(v, len) {
34766
+ if (isRuntimeTensor(v)) {
34767
+ return {
34768
+ re: new Float64Array(toF64(v.data)),
34769
+ im: v.imag ? new Float64Array(toF64(v.imag)) : new Float64Array(len)
34770
+ };
34771
+ }
34772
+ if (isRuntimeSparseMatrix(v)) {
34773
+ const dense = sparseToDense(v);
34774
+ return {
34775
+ re: new Float64Array(toF64(dense.data)),
34776
+ im: dense.imag ? new Float64Array(toF64(dense.imag)) : new Float64Array(len)
34777
+ };
34778
+ }
34779
+ if (isRuntimeNumber(v)) {
34780
+ const re2 = new Float64Array(1);
34781
+ re2[0] = v;
34782
+ return { re: re2, im: new Float64Array(1) };
34783
+ }
34784
+ throw new RuntimeError("gmres: cannot convert to complex matrix");
34785
+ }
34786
+ function _extractComplexMatrix(arg, len) {
34787
+ if (arg === null || _isEmpty(arg)) return null;
34788
+ return _toComplexMatrix(arg, len);
34789
+ }
34790
+ function _makeComplexMatvec(rt, Aarg, n) {
34791
+ if (isRuntimeFunction(Aarg)) {
34792
+ return (x) => {
34793
+ const xTensor = RTV.tensor(
34794
+ new FloatXArray(x.re),
34795
+ [n, 1],
34796
+ new FloatXArray(x.im)
34797
+ );
34798
+ const resultRaw = rt.index(Aarg, [xTensor], 1);
34799
+ const rv = ensureRuntimeValue(resultRaw);
34800
+ if (isRuntimeTensor(rv)) {
34801
+ return {
34802
+ re: new Float64Array(toF64(rv.data)),
34803
+ im: rv.imag ? new Float64Array(toF64(rv.imag)) : new Float64Array(n)
34804
+ };
34805
+ }
34806
+ if (isRuntimeComplexNumber(rv)) {
34807
+ return { re: new Float64Array([rv.re]), im: new Float64Array([rv.im]) };
34808
+ }
34809
+ if (isRuntimeNumber(rv)) {
34810
+ const re2 = new Float64Array(1);
34811
+ re2[0] = rv;
34812
+ return { re: re2, im: new Float64Array(1) };
34813
+ }
34814
+ throw new RuntimeError("gmres: A(x) must return a numeric vector");
34815
+ };
34816
+ }
34817
+ const { re: ARe, im: AIm } = _toComplexMatrix(Aarg, n * n);
34818
+ return (x) => {
34819
+ const yRe = new Float64Array(n);
34820
+ const yIm = new Float64Array(n);
34821
+ for (let i = 0; i < n; i++) {
34822
+ let sR = 0, sI = 0;
34823
+ for (let j = 0; j < n; j++) {
34824
+ const aR = ARe[i + j * n], aI = AIm[i + j * n];
34825
+ sR += aR * x.re[j] - aI * x.im[j];
34826
+ sI += aR * x.im[j] + aI * x.re[j];
34827
+ }
34828
+ yRe[i] = sR;
34829
+ yIm[i] = sI;
34830
+ }
34831
+ return { re: yRe, im: yIm };
34832
+ };
34833
+ }
34834
+ function _makeComplexPrecSolve(rt, M1arg, M2arg, n) {
34835
+ const hasM1 = M1arg !== null && !_isEmpty(M1arg);
34836
+ const hasM2 = M2arg !== null && !_isEmpty(M2arg);
34837
+ if (!hasM1 && !hasM2) return null;
34838
+ const solve1 = hasM1 ? _makeSingleComplexPrecSolve(rt, M1arg, n) : null;
34839
+ const solve2 = hasM2 ? _makeSingleComplexPrecSolve(rt, M2arg, n) : null;
34840
+ return (r) => {
34841
+ let z = r;
34842
+ if (solve1) z = solve1(z);
34843
+ if (solve2) z = solve2(z);
34844
+ return z;
34845
+ };
34846
+ }
34847
+ function _makeSingleComplexPrecSolve(rt, Marg, n) {
34848
+ if (isRuntimeFunction(Marg)) {
34849
+ return (r) => {
34850
+ const rTensor = RTV.tensor(
34851
+ new FloatXArray(r.re),
34852
+ [n, 1],
34853
+ new FloatXArray(r.im)
34854
+ );
34855
+ const resultRaw = rt.index(Marg, [rTensor], 1);
34856
+ const rv = ensureRuntimeValue(resultRaw);
34857
+ if (isRuntimeTensor(rv)) {
34858
+ return {
34859
+ re: new Float64Array(toF64(rv.data)),
34860
+ im: rv.imag ? new Float64Array(toF64(rv.imag)) : new Float64Array(n)
34861
+ };
34862
+ }
34863
+ throw new RuntimeError("gmres: M(x) must return a numeric vector");
34864
+ };
34865
+ }
34866
+ const { re: MRe, im: MIm } = _toComplexMatrix(Marg, n * n);
34867
+ const ipiv = new Int32Array(n);
34868
+ _complexLuFactor(n, MRe, MIm, ipiv);
34869
+ return (r) => {
34870
+ const zRe = new Float64Array(r.re);
34871
+ const zIm = new Float64Array(r.im);
34872
+ complexLuSolveInPlace(n, MRe, MIm, ipiv, zRe, zIm);
34873
+ return { re: zRe, im: zIm };
34874
+ };
34875
+ }
34876
+ function _complexLuFactor(n, re2, im2, ipiv) {
34877
+ for (let k = 0; k < n; k++) {
34878
+ let maxVal = -1, maxIdx = k;
34879
+ for (let i = k; i < n; i++) {
34880
+ const v = Math.sqrt(
34881
+ re2[i + k * n] * re2[i + k * n] + im2[i + k * n] * im2[i + k * n]
34882
+ );
34883
+ if (v > maxVal) {
34884
+ maxVal = v;
34885
+ maxIdx = i;
34886
+ }
34887
+ }
34888
+ ipiv[k] = maxIdx + 1;
34889
+ if (maxIdx !== k) {
34890
+ for (let j = 0; j < n; j++) {
34891
+ let tmp = re2[k + j * n];
34892
+ re2[k + j * n] = re2[maxIdx + j * n];
34893
+ re2[maxIdx + j * n] = tmp;
34894
+ tmp = im2[k + j * n];
34895
+ im2[k + j * n] = im2[maxIdx + j * n];
34896
+ im2[maxIdx + j * n] = tmp;
34897
+ }
34898
+ }
34899
+ const dR = re2[k + k * n], dI = im2[k + k * n];
34900
+ const dAbs2 = dR * dR + dI * dI;
34901
+ if (dAbs2 === 0) continue;
34902
+ for (let i = k + 1; i < n; i++) {
34903
+ const aR = re2[i + k * n], aI = im2[i + k * n];
34904
+ const lR = (aR * dR + aI * dI) / dAbs2;
34905
+ const lI = (aI * dR - aR * dI) / dAbs2;
34906
+ re2[i + k * n] = lR;
34907
+ im2[i + k * n] = lI;
34908
+ for (let j = k + 1; j < n; j++) {
34909
+ re2[i + j * n] -= lR * re2[k + j * n] - lI * im2[k + j * n];
34910
+ im2[i + j * n] -= lR * im2[k + j * n] + lI * re2[k + j * n];
34911
+ }
34912
+ }
34913
+ }
34914
+ }
33619
34915
 
33620
34916
  // src/numbl-core/runtime/runtimeMemberAccess.ts
33621
34917
  function getMember(rt, base, name) {
@@ -33858,6 +35154,11 @@ var Runtime = class _Runtime {
33858
35154
  // Custom builtins (execution-specific overrides).
33859
35155
  // These take priority over IBuiltins.
33860
35156
  customBuiltins = {};
35157
+ // Per-runtime JIT helpers ($h) — populated by executeCode after the runtime
35158
+ // is constructed. Cloned from the global jitHelpers and extended with
35159
+ // ib_<name> entries for any .numbl.js user functions of this execution.
35160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35161
+ jitHelpers = null;
33861
35162
  // JIT compilation callback: compiles and evaluates a specialized function at
33862
35163
  // runtime. Set by executeCode() to close over the LoweringContext and Codegen.
33863
35164
  // The callback handles the full resolution chain (local → class method →
@@ -33878,6 +35179,8 @@ var Runtime = class _Runtime {
33878
35179
  profileStack = [];
33879
35180
  profileAccum = /* @__PURE__ */ new Map();
33880
35181
  profileCounts = /* @__PURE__ */ new Map();
35182
+ // Profiling: interpreted loops with >1000 iterations (keyed by "file:line")
35183
+ hotLoops = /* @__PURE__ */ new Map();
33881
35184
  // ── Builtin initialization ──────────────────────────────────────────
33882
35185
  initBuiltins() {
33883
35186
  registerSpecialBuiltins(this);
@@ -33896,6 +35199,10 @@ var Runtime = class _Runtime {
33896
35199
  this.builtins["imagesc"] = (_nargout, args) => {
33897
35200
  this.imagesc_call(args.map((a) => ensureRuntimeValue(a)));
33898
35201
  };
35202
+ this.builtins["pcolor"] = (_nargout, args) => {
35203
+ this.pcolor_call(args.map((a) => ensureRuntimeValue(a)));
35204
+ if (_nargout >= 1) return RTV.dummyHandle();
35205
+ };
33899
35206
  this.builtins["contour"] = (_nargout, args) => {
33900
35207
  this.contour_call(
33901
35208
  args.map((a) => ensureRuntimeValue(a)),
@@ -33980,8 +35287,21 @@ var Runtime = class _Runtime {
33980
35287
  this.builtins["colormap"] = (_nargout, args) => {
33981
35288
  if (args.length > 0) {
33982
35289
  const rv = ensureRuntimeValue(args[0]);
33983
- const name = toString(rv).replace(/^"|"$/g, "");
33984
- plotInstr(this.plotInstructions, { type: "set_colormap", name });
35290
+ if (isRuntimeTensor(rv) && rv.shape.length === 2 && rv.shape[1] === 3) {
35291
+ const rows = rv.shape[0];
35292
+ const data = [];
35293
+ for (let i = 0; i < rows; i++) {
35294
+ data.push([rv.data[i], rv.data[rows + i], rv.data[2 * rows + i]]);
35295
+ }
35296
+ plotInstr(this.plotInstructions, {
35297
+ type: "set_colormap",
35298
+ name: "__custom__",
35299
+ data
35300
+ });
35301
+ } else {
35302
+ const name = toString(rv).replace(/^"|"$/g, "");
35303
+ plotInstr(this.plotInstructions, { type: "set_colormap", name });
35304
+ }
33985
35305
  }
33986
35306
  };
33987
35307
  this.builtins["view"] = (_nargout, args) => {
@@ -33995,10 +35315,6 @@ var Runtime = class _Runtime {
33995
35315
  });
33996
35316
  }
33997
35317
  };
33998
- this.builtins["colorbar"] = (_nargout, args) => {
33999
- const val = args.length > 0 ? toString(ensureRuntimeValue(args[0])) : "on";
34000
- plotInstr(this.plotInstructions, { type: "set_colorbar", value: val });
34001
- };
34002
35318
  this.builtins["axis"] = (_nargout, args) => {
34003
35319
  if (args.length > 0) {
34004
35320
  const val = toString(ensureRuntimeValue(args[0])).replace(
@@ -34008,6 +35324,17 @@ var Runtime = class _Runtime {
34008
35324
  plotInstr(this.plotInstructions, { type: "set_axis", value: val });
34009
35325
  }
34010
35326
  };
35327
+ this.builtins["caxis"] = (_nargout, args) => {
35328
+ if (args.length > 0) {
35329
+ const rv = ensureRuntimeValue(args[0]);
35330
+ if (isRuntimeTensor(rv) && rv.data.length >= 2) {
35331
+ plotInstr(this.plotInstructions, {
35332
+ type: "set_caxis",
35333
+ limits: [rv.data[0], rv.data[1]]
35334
+ });
35335
+ }
35336
+ }
35337
+ };
34011
35338
  }
34012
35339
  profileEnter(key) {
34013
35340
  if (!this.profilingEnabled) return;
@@ -34114,8 +35441,9 @@ var Runtime = class _Runtime {
34114
35441
  if (e instanceof RuntimeError) {
34115
35442
  return RTV.struct(
34116
35443
  /* @__PURE__ */ new Map([
34117
- ["message", RTV.char(e.message)],
34118
35444
  ["identifier", RTV.char(e.identifier)],
35445
+ ["message", RTV.char(e.message)],
35446
+ ["cause", RTV.cell([], [0, 0])],
34119
35447
  ["stack", buildStackField(e)]
34120
35448
  ])
34121
35449
  );
@@ -34123,16 +35451,18 @@ var Runtime = class _Runtime {
34123
35451
  if (e instanceof Error) {
34124
35452
  return RTV.struct(
34125
35453
  /* @__PURE__ */ new Map([
34126
- ["message", RTV.char(e.message)],
34127
35454
  ["identifier", RTV.char("")],
35455
+ ["message", RTV.char(e.message)],
35456
+ ["cause", RTV.cell([], [0, 0])],
34128
35457
  ["stack", emptyStackField()]
34129
35458
  ])
34130
35459
  );
34131
35460
  }
34132
35461
  return RTV.struct(
34133
35462
  /* @__PURE__ */ new Map([
34134
- ["message", RTV.char(String(e))],
34135
35463
  ["identifier", RTV.char("")],
35464
+ ["message", RTV.char(String(e))],
35465
+ ["cause", RTV.cell([], [0, 0])],
34136
35466
  ["stack", emptyStackField()]
34137
35467
  ])
34138
35468
  );
@@ -34871,6 +36201,9 @@ var Runtime = class _Runtime {
34871
36201
  imagesc_call(args) {
34872
36202
  imagescCall(this.plotInstructions, args);
34873
36203
  }
36204
+ pcolor_call(args) {
36205
+ pcolorCall(this.plotInstructions, args);
36206
+ }
34874
36207
  contour_call(args, filled) {
34875
36208
  contourCall(this.plotInstructions, args, filled);
34876
36209
  }
@@ -36256,7 +37589,7 @@ defineBuiltin({
36256
37589
  },
36257
37590
  apply: (args) => {
36258
37591
  const first = textValue(args[0]) ?? String(args[0]);
36259
- if (args.length >= 2 && first.includes(":")) {
37592
+ if (args.length >= 2 && /^[A-Za-z]\w*(:[A-Za-z]\w*)+$/.test(first)) {
36260
37593
  const msg2 = args.length === 2 ? textValue(args[1]) ?? String(args[1]) : sprintfFormat(
36261
37594
  textValue(args[1]) ?? String(args[1]),
36262
37595
  args.slice(2)
@@ -36294,6 +37627,134 @@ defineBuiltin({
36294
37627
  }
36295
37628
  ]
36296
37629
  });
37630
+ function makeMException(identifier, message) {
37631
+ return RTV.struct(
37632
+ /* @__PURE__ */ new Map([
37633
+ ["identifier", RTV.char(identifier)],
37634
+ ["message", RTV.char(message)],
37635
+ ["cause", RTV.cell([], [0, 0])],
37636
+ ["stack", emptyStackField()]
37637
+ ])
37638
+ );
37639
+ }
37640
+ defineBuiltin({
37641
+ name: "MException",
37642
+ help: {
37643
+ signatures: [
37644
+ "ME = MException(errID, msg)",
37645
+ "ME = MException(errID, msg, A1, ..., An)"
37646
+ ],
37647
+ description: "Construct an exception object with an error identifier and message. Extra arguments format the message with sprintf-style conversion specifiers. In numbl the result is a struct with fields identifier, message, cause, and stack."
37648
+ },
37649
+ cases: [
37650
+ {
37651
+ match: (argTypes) => {
37652
+ if (argTypes.length < 2) return null;
37653
+ const k0 = argTypes[0].kind;
37654
+ const k1 = argTypes[1].kind;
37655
+ if (k0 !== "string" && k0 !== "char") return null;
37656
+ if (k1 !== "string" && k1 !== "char") return null;
37657
+ return [{ kind: "struct", fields: {} }];
37658
+ },
37659
+ apply: (args) => {
37660
+ const errID = textValue(args[0]) ?? String(args[0]);
37661
+ const fmt = textValue(args[1]) ?? String(args[1]);
37662
+ const msg = args.length === 2 ? fmt : sprintfFormat(fmt, args.slice(2));
37663
+ return makeMException(errID, msg);
37664
+ }
37665
+ }
37666
+ ]
37667
+ });
37668
+ function throwException(me) {
37669
+ if (isRuntimeStruct(me)) {
37670
+ const msgVal = me.fields.get("message");
37671
+ const idVal = me.fields.get("identifier");
37672
+ const msg = msgVal ? textValue(msgVal) ?? String(msgVal) : "";
37673
+ const err = new RuntimeError(msg);
37674
+ if (idVal) err.identifier = textValue(idVal) ?? "";
37675
+ throw err;
37676
+ }
37677
+ throw new RuntimeError(String(me));
37678
+ }
37679
+ defineBuiltin({
37680
+ name: "throw",
37681
+ help: {
37682
+ signatures: ["throw(ME)"],
37683
+ description: "Throw an exception. ME is an MException-shaped struct with identifier and message fields."
37684
+ },
37685
+ cases: [
37686
+ {
37687
+ match: (argTypes) => {
37688
+ if (argTypes.length !== 1) return null;
37689
+ return [];
37690
+ },
37691
+ apply: (args) => throwException(args[0])
37692
+ }
37693
+ ]
37694
+ });
37695
+ defineBuiltin({
37696
+ name: "throwAsCaller",
37697
+ help: {
37698
+ signatures: ["throwAsCaller(ME)"],
37699
+ description: "Throw an exception as if from the calling function. In numbl this is equivalent to throw(ME) \u2014 the throw site is not hidden from the stack."
37700
+ },
37701
+ cases: [
37702
+ {
37703
+ match: (argTypes) => {
37704
+ if (argTypes.length !== 1) return null;
37705
+ return [];
37706
+ },
37707
+ apply: (args) => throwException(args[0])
37708
+ }
37709
+ ]
37710
+ });
37711
+ defineBuiltin({
37712
+ name: "getReport",
37713
+ help: {
37714
+ signatures: ["S = getReport(ME)", "S = getReport(ME, TYPE)"],
37715
+ description: "Return a formatted error report for an MException-shaped struct as a char string. TYPE is 'basic' or 'extended' (default 'extended'). The 'extended' form appends stack frames if present."
37716
+ },
37717
+ cases: [
37718
+ {
37719
+ match: (argTypes) => {
37720
+ if (argTypes.length < 1 || argTypes.length > 4) return null;
37721
+ return [{ kind: "char" }];
37722
+ },
37723
+ apply: (args) => {
37724
+ const me = args[0];
37725
+ if (!isRuntimeStruct(me)) {
37726
+ return RTV.char(String(me));
37727
+ }
37728
+ const msgVal = me.fields.get("message");
37729
+ const message = msgVal ? textValue(msgVal) ?? String(msgVal) : "";
37730
+ let type = "extended";
37731
+ if (args.length >= 2) {
37732
+ const t = textValue(args[1]);
37733
+ if (t === "basic" || t === "extended") type = t;
37734
+ }
37735
+ if (type === "basic") return RTV.char(message);
37736
+ const stackVal = me.fields.get("stack");
37737
+ const lines = [message];
37738
+ const appendFrame = (frame) => {
37739
+ if (!isRuntimeStruct(frame)) return;
37740
+ const nameVal = frame.fields.get("name");
37741
+ const lineVal = frame.fields.get("line");
37742
+ const name = nameVal ? textValue(nameVal) ?? "" : "";
37743
+ const lineNum = typeof lineVal === "number" ? lineVal : 0;
37744
+ if (name) lines.push(`Error in ${name} (line ${lineNum})`);
37745
+ };
37746
+ if (stackVal) {
37747
+ if (isRuntimeStructArray(stackVal)) {
37748
+ for (const el of stackVal.elements) appendFrame(el);
37749
+ } else if (isRuntimeStruct(stackVal)) {
37750
+ appendFrame(stackVal);
37751
+ }
37752
+ }
37753
+ return RTV.char(lines.join("\n"));
37754
+ }
37755
+ }
37756
+ ]
37757
+ });
36297
37758
 
36298
37759
  // src/numbl-core/interpreter/builtins/introspection.ts
36299
37760
  function anyToLogicalCase(applyFn) {
@@ -38178,6 +39639,150 @@ registerIBuiltin({
38178
39639
  };
38179
39640
  }
38180
39641
  });
39642
+ var RE_ALPHA = /\p{L}/u;
39643
+ var RE_ALPHANUM = /[\p{L}\p{N}]/u;
39644
+ var RE_CNTRL = /\p{Cc}/u;
39645
+ var RE_GRAPHIC = /[\p{L}\p{N}\p{P}\p{S}\p{M}]/u;
39646
+ var RE_LOWER = /\p{Ll}/u;
39647
+ var RE_PUNCT = /\p{P}/u;
39648
+ var RE_UPPER = /\p{Lu}/u;
39649
+ var RE_WSPACE = /\s/u;
39650
+ function isstrpropPredicate(category) {
39651
+ switch (category) {
39652
+ case "alpha":
39653
+ return (cp) => RE_ALPHA.test(String.fromCodePoint(cp));
39654
+ case "alphanum":
39655
+ return (cp) => RE_ALPHANUM.test(String.fromCodePoint(cp));
39656
+ case "cntrl":
39657
+ return (cp) => RE_CNTRL.test(String.fromCodePoint(cp));
39658
+ case "digit":
39659
+ return (cp) => cp >= 48 && cp <= 57;
39660
+ case "graphic":
39661
+ return (cp) => RE_GRAPHIC.test(String.fromCodePoint(cp));
39662
+ case "lower":
39663
+ return (cp) => RE_LOWER.test(String.fromCodePoint(cp));
39664
+ case "print":
39665
+ return (cp) => cp === 32 || RE_GRAPHIC.test(String.fromCodePoint(cp));
39666
+ case "punct":
39667
+ return (cp) => RE_PUNCT.test(String.fromCodePoint(cp));
39668
+ case "wspace":
39669
+ return (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
39670
+ case "upper":
39671
+ return (cp) => RE_UPPER.test(String.fromCodePoint(cp));
39672
+ case "xdigit":
39673
+ return (cp) => cp >= 48 && cp <= 57 || cp >= 65 && cp <= 70 || cp >= 97 && cp <= 102;
39674
+ default:
39675
+ return null;
39676
+ }
39677
+ }
39678
+ function stringCodePoints(s) {
39679
+ const out = [];
39680
+ for (const ch of s) out.push(ch.codePointAt(0));
39681
+ return out;
39682
+ }
39683
+ function logicalRowFromString(s, pred, shape) {
39684
+ const cps = stringCodePoints(s);
39685
+ const data = new FloatXArray(cps.length);
39686
+ for (let i = 0; i < cps.length; i++) data[i] = pred(cps[i]) ? 1 : 0;
39687
+ return {
39688
+ kind: "tensor",
39689
+ data,
39690
+ shape: shape ?? [1, cps.length],
39691
+ _isLogical: true,
39692
+ _rc: 1
39693
+ };
39694
+ }
39695
+ function logicalFromNumericTensor(data, shape, pred) {
39696
+ const out = new FloatXArray(data.length);
39697
+ for (let i = 0; i < data.length; i++) {
39698
+ out[i] = pred(Math.round(data[i])) ? 1 : 0;
39699
+ }
39700
+ return {
39701
+ kind: "tensor",
39702
+ data: out,
39703
+ shape: [...shape],
39704
+ _isLogical: true,
39705
+ _rc: 1
39706
+ };
39707
+ }
39708
+ function isstrpropApply(args) {
39709
+ const v = args[0];
39710
+ const category = toString(args[1]);
39711
+ const pred = isstrpropPredicate(category);
39712
+ if (!pred) {
39713
+ throw new RuntimeError(`isstrprop: unknown category '${category}'`);
39714
+ }
39715
+ let forceCell = false;
39716
+ if (args.length >= 3) {
39717
+ const flagName = toString(args[2]).toLowerCase();
39718
+ if (flagName !== "forcecelloutput") {
39719
+ throw new RuntimeError(`isstrprop: unknown name '${toString(args[2])}'`);
39720
+ }
39721
+ if (args.length < 4) {
39722
+ throw new RuntimeError("isstrprop: 'ForceCellOutput' requires a value");
39723
+ }
39724
+ forceCell = toNumber(args[3]) !== 0;
39725
+ }
39726
+ if (isRuntimeCell(v)) {
39727
+ const out = new Array(v.data.length);
39728
+ for (let i = 0; i < v.data.length; i++) {
39729
+ const elem = v.data[i];
39730
+ const s = isText(elem) ? toString(elem) : "";
39731
+ out[i] = logicalRowFromString(s, pred);
39732
+ }
39733
+ return RTV.cell(out, [...v.shape]);
39734
+ }
39735
+ let result;
39736
+ if (isRuntimeString(v)) {
39737
+ result = logicalRowFromString(toString(v), pred);
39738
+ } else if (isRuntimeChar(v)) {
39739
+ const c = v;
39740
+ result = logicalRowFromString(
39741
+ c.value,
39742
+ pred,
39743
+ c.shape ? [...c.shape] : void 0
39744
+ );
39745
+ } else if (isRuntimeTensor(v)) {
39746
+ result = logicalFromNumericTensor(v.data, v.shape, pred);
39747
+ } else if (isRuntimeNumber(v) || isRuntimeLogical(v)) {
39748
+ const cp = Math.round(toNumber(v));
39749
+ const data = new FloatXArray(1);
39750
+ data[0] = pred(cp) ? 1 : 0;
39751
+ result = { kind: "tensor", data, shape: [1, 1], _isLogical: true, _rc: 1 };
39752
+ } else {
39753
+ throw new RuntimeError("isstrprop: unsupported input type");
39754
+ }
39755
+ if (forceCell) return RTV.cell([result], [1, 1]);
39756
+ return result;
39757
+ }
39758
+ registerIBuiltin({
39759
+ name: "isstrprop",
39760
+ help: {
39761
+ signatures: [
39762
+ "TF = isstrprop(str, category)",
39763
+ "TF = isstrprop(str, category, 'ForceCellOutput', tf)"
39764
+ ],
39765
+ description: "Test which characters in str belong to a category (alpha, alphanum, cntrl, digit, graphic, lower, print, punct, upper, wspace, xdigit). Returns a logical array, or a cell of logical vectors when str is a cell array or ForceCellOutput is true. Numeric input is treated as Unicode code points."
39766
+ },
39767
+ resolve: (argTypes) => {
39768
+ if (argTypes.length < 2 || argTypes.length > 4) return null;
39769
+ if (!isTextType(argTypes[1])) return null;
39770
+ if (argTypes.length >= 3 && !isTextType(argTypes[2])) return null;
39771
+ if (argTypes.length === 4) {
39772
+ const k = argTypes[3].kind;
39773
+ if (k !== "number" && k !== "boolean") return null;
39774
+ }
39775
+ const t0 = argTypes[0];
39776
+ if (t0.kind !== "char" && t0.kind !== "string" && t0.kind !== "number" && t0.kind !== "boolean" && t0.kind !== "tensor" && t0.kind !== "cell") {
39777
+ return null;
39778
+ }
39779
+ const isCell = t0.kind === "cell";
39780
+ return {
39781
+ outputTypes: isCell ? [{ kind: "cell" }] : [{ kind: "tensor", isComplex: false, isLogical: true }],
39782
+ apply: (args) => isstrpropApply(args)
39783
+ };
39784
+ }
39785
+ });
38181
39786
  registerIBuiltin({
38182
39787
  name: "contains",
38183
39788
  resolve: (argTypes) => {
@@ -39150,32 +40755,31 @@ defineBuiltin({
39150
40755
  }
39151
40756
  ]
39152
40757
  });
39153
- defineBuiltin({
39154
- name: "ismember",
39155
- cases: [
39156
- {
39157
- match: (argTypes, nargout) => {
39158
- if (argTypes.length < 2) return null;
39159
- const out = [{ kind: "unknown" }];
39160
- if (nargout > 1) out.push({ kind: "unknown" });
39161
- return out;
39162
- },
39163
- apply: (args, nargout) => {
39164
- if (args.length < 2)
39165
- throw new RuntimeError("ismember requires 2 arguments");
39166
- const v = args[0];
39167
- const b = args[1];
39168
- const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
39169
- const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every(
39170
- (e) => isRuntimeString(e) || isRuntimeChar(e)
39171
- );
39172
- if (isStringLike(v) || isCellOfStrings(v) || isStringLike(b) || isCellOfStrings(b))
39173
- return ismemberStrings(v, b, nargout);
39174
- return ismemberNumeric(v, b, nargout);
39175
- }
40758
+ var ismemberCases = [
40759
+ {
40760
+ match: (argTypes, nargout) => {
40761
+ if (argTypes.length < 2) return null;
40762
+ const out = [{ kind: "unknown" }];
40763
+ if (nargout > 1) out.push({ kind: "unknown" });
40764
+ return out;
40765
+ },
40766
+ apply: (args, nargout) => {
40767
+ if (args.length < 2)
40768
+ throw new RuntimeError("ismember requires 2 arguments");
40769
+ const v = args[0];
40770
+ const b = args[1];
40771
+ const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
40772
+ const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every(
40773
+ (e) => isRuntimeString(e) || isRuntimeChar(e)
40774
+ );
40775
+ if (isStringLike(v) || isCellOfStrings(v) || isStringLike(b) || isCellOfStrings(b))
40776
+ return ismemberStrings(v, b, nargout);
40777
+ return ismemberNumeric(v, b, nargout);
39176
40778
  }
39177
- ]
39178
- });
40779
+ }
40780
+ ];
40781
+ defineBuiltin({ name: "ismember", cases: ismemberCases });
40782
+ defineBuiltin({ name: "ismembc", cases: ismemberCases });
39179
40783
  function ismemberStrings(v, b, nargout) {
39180
40784
  const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
39181
40785
  const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every((e) => isRuntimeString(e) || isRuntimeChar(e));
@@ -43717,11 +45321,11 @@ defineBuiltin({
43717
45321
  }
43718
45322
  } else {
43719
45323
  const p2 = n / 2;
43720
- const sub = new Array(p2 * p2).fill(0);
45324
+ const sub2 = new Array(p2 * p2).fill(0);
43721
45325
  const sset = (r, c, v) => {
43722
- sub[c * p2 + r] = v;
45326
+ sub2[c * p2 + r] = v;
43723
45327
  };
43724
- const sget = (r, c) => sub[c * p2 + r];
45328
+ const sget = (r, c) => sub2[c * p2 + r];
43725
45329
  let si = 0;
43726
45330
  let sj = Math.floor(p2 / 2);
43727
45331
  for (let k2 = 1; k2 <= p2 * p2; k2++) {
@@ -47024,6 +48628,88 @@ for (const [name, fn, scaleFn] of besselDefs) {
47024
48628
  })
47025
48629
  });
47026
48630
  }
48631
+ function besselhScalar(nu, k, z, scale) {
48632
+ const J = besselj(nu, z);
48633
+ const Y = bessely(nu, z);
48634
+ let re2 = J;
48635
+ let im2 = k === 1 ? Y : -Y;
48636
+ if (scale) {
48637
+ const c = Math.cos(z);
48638
+ const s = Math.sin(z);
48639
+ if (k === 1) {
48640
+ const newRe = re2 * c + im2 * s;
48641
+ const newIm = im2 * c - re2 * s;
48642
+ re2 = newRe;
48643
+ im2 = newIm;
48644
+ } else {
48645
+ const newRe = re2 * c - im2 * s;
48646
+ const newIm = re2 * s + im2 * c;
48647
+ re2 = newRe;
48648
+ im2 = newIm;
48649
+ }
48650
+ }
48651
+ return { re: re2, im: im2 };
48652
+ }
48653
+ function binaryApplyComplex(a, b, fn, name) {
48654
+ const aIsT = isRuntimeTensor(a);
48655
+ const bIsT = isRuntimeTensor(b);
48656
+ const buildTensor = (len, shape, iter) => {
48657
+ const re2 = new FloatXArray(len);
48658
+ const im2 = new FloatXArray(len);
48659
+ let hasImag = false;
48660
+ for (let i = 0; i < len; i++) {
48661
+ const r2 = iter(i);
48662
+ re2[i] = r2.re;
48663
+ im2[i] = r2.im;
48664
+ if (r2.im !== 0) hasImag = true;
48665
+ }
48666
+ return RTV.tensor(re2, shape, hasImag ? im2 : void 0);
48667
+ };
48668
+ if (aIsT && bIsT) {
48669
+ if (a.data.length !== b.data.length)
48670
+ throw new RuntimeError(`${name}: array dimensions must agree`);
48671
+ return buildTensor(a.data.length, a.shape, (i) => fn(a.data[i], b.data[i]));
48672
+ }
48673
+ if (aIsT) {
48674
+ const bv = toNumber(b);
48675
+ return buildTensor(a.data.length, a.shape, (i) => fn(a.data[i], bv));
48676
+ }
48677
+ if (bIsT) {
48678
+ const av = toNumber(a);
48679
+ return buildTensor(b.data.length, b.shape, (i) => fn(av, b.data[i]));
48680
+ }
48681
+ const r = fn(toNumber(a), toNumber(b));
48682
+ return r.im === 0 ? RTV.num(r.re) : RTV.complex(r.re, r.im);
48683
+ }
48684
+ registerIBuiltin({
48685
+ name: "besselh",
48686
+ resolve: () => ({
48687
+ outputTypes: [{ kind: "unknown" }],
48688
+ apply: (args) => {
48689
+ if (args.length < 2 || args.length > 4)
48690
+ throw new RuntimeError("besselh requires 2 to 4 arguments");
48691
+ const nuArg = args[0];
48692
+ let k = 1;
48693
+ let zArg;
48694
+ let scale = false;
48695
+ if (args.length === 2) {
48696
+ zArg = args[1];
48697
+ } else {
48698
+ k = Math.round(toNumber(args[1]));
48699
+ if (k !== 1 && k !== 2)
48700
+ throw new RuntimeError("besselh: K must be 1 or 2");
48701
+ zArg = args[2];
48702
+ if (args.length === 4) scale = toNumber(args[3]) === 1;
48703
+ }
48704
+ return binaryApplyComplex(
48705
+ nuArg,
48706
+ zArg,
48707
+ (nu, z) => besselhScalar(nu, k, z, scale),
48708
+ "besselh"
48709
+ );
48710
+ }
48711
+ })
48712
+ });
47027
48713
  var airyFns = [airyAi, airyAiPrime, airyBi, airyBiPrime];
47028
48714
  var airyComplexKeys = ["ai", "aip", "bi", "bip"];
47029
48715
  function scaleAiry(n, x, val) {
@@ -48389,6 +50075,14 @@ var H = {
48389
50075
  signatures: ["Y = besselk(NU, Z)", "Y = besselk(NU, Z, SCALE)"],
48390
50076
  description: "Modified Bessel function of the second kind. SCALE=1 applies exponential scaling."
48391
50077
  },
50078
+ besselh: {
50079
+ signatures: [
50080
+ "H = besselh(NU, Z)",
50081
+ "H = besselh(NU, K, Z)",
50082
+ "H = besselh(NU, K, Z, SCALE)"
50083
+ ],
50084
+ description: "Hankel function (Bessel function of the third kind). K=1 (default) gives H^(1) = J + i*Y; K=2 gives H^(2) = J - i*Y. SCALE=1 multiplies by exp(-i*Z) for K=1 or exp(+i*Z) for K=2."
50085
+ },
48392
50086
  airy: {
48393
50087
  signatures: ["Y = airy(X)", "Y = airy(K, X)", "Y = airy(K, X, SCALE)"],
48394
50088
  description: "Airy functions. K selects the function: 0=Ai (default), 1=Ai', 2=Bi, 3=Bi'. SCALE=1 applies exponential scaling."
@@ -48439,6 +50133,15 @@ var H = {
48439
50133
  signatures: ["TF = isequal(A, B, ...)"],
48440
50134
  description: "True if all inputs are equal (NaN ~= NaN)."
48441
50135
  },
50136
+ // ── Dynamic evaluation ────────────────────────────────────────────────
50137
+ evalin: {
50138
+ signatures: ["V = evalin(WS, EXPR)", "V = evalin(WS, EXPR, DEFAULT)"],
50139
+ description: "Evaluate EXPR in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables read by evalin must be declared in the\nfunction that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
50140
+ },
50141
+ assignin: {
50142
+ signatures: ["assignin(WS, NAME, VALUE)"],
50143
+ description: "Assign VALUE to variable NAME in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables written by assignin must be declared in\nthe function that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
50144
+ },
48442
50145
  // ── Misc ──────────────────────────────────────────────────────────────
48443
50146
  disp: {
48444
50147
  signatures: ["disp(X)"],
@@ -49253,6 +50956,46 @@ function formatDiagnostics(infos, getSource) {
49253
50956
  return infos.map((i) => formatDiagnostic(i, getSource)).join("\n\n");
49254
50957
  }
49255
50958
  function formatDiagnostic(info, getSource) {
50959
+ if (info.callStack != null && info.callStack.length > 0) {
50960
+ const stack = info.callStack;
50961
+ const N = stack.length;
50962
+ const parts = [];
50963
+ const innerName = stack[N - 1].name;
50964
+ const innerFile = info.file;
50965
+ const innerLine = info.line ?? 0;
50966
+ if (innerFile && innerLine > 0) {
50967
+ parts.push(`Error using ${innerName} (${innerFile}:${innerLine})`);
50968
+ } else if (innerLine > 0) {
50969
+ parts.push(`Error using ${innerName} (line ${innerLine})`);
50970
+ } else {
50971
+ parts.push(`Error using ${innerName}`);
50972
+ }
50973
+ parts.push(info.message);
50974
+ for (let i = N - 2; i >= 0; i--) {
50975
+ const name = stack[i].name;
50976
+ const callerFrame = stack[i + 1];
50977
+ const file = callerFrame.callerFile;
50978
+ const line = callerFrame.callerLine;
50979
+ parts.push("");
50980
+ if (file && line > 0) {
50981
+ parts.push(`Error in ${name} (${file}:${line})`);
50982
+ const srcLine = getSource ? getSourceLine(getSource, file, line) : callerFrame.callerSourceLine ?? null;
50983
+ if (srcLine) parts.push(` ${srcLine}`);
50984
+ } else if (line > 0) {
50985
+ parts.push(`Error in ${name} (line ${line})`);
50986
+ } else {
50987
+ parts.push(`Error in ${name}`);
50988
+ }
50989
+ }
50990
+ const outermost = stack[0];
50991
+ if (outermost.callerFile && outermost.callerLine > 0) {
50992
+ parts.push("");
50993
+ parts.push(`Error in ${outermost.callerFile}:${outermost.callerLine}`);
50994
+ const srcLine = getSource ? getSourceLine(getSource, outermost.callerFile, outermost.callerLine) : outermost.callerSourceLine ?? null;
50995
+ if (srcLine) parts.push(` ${srcLine}`);
50996
+ }
50997
+ return parts.join("\n");
50998
+ }
49256
50999
  const labels = {
49257
51000
  syntax: "SyntaxError",
49258
51001
  semantic: "SemanticError",
@@ -49270,61 +51013,6 @@ function formatDiagnostic(info, getSource) {
49270
51013
  result += `
49271
51014
  ${info.snippet}`;
49272
51015
  }
49273
- if (info.callStack != null && info.callStack.length > 0) {
49274
- result += `
49275
- Call stack (most recent call first):`;
49276
- const stack = info.callStack;
49277
- const N = stack.length;
49278
- for (let i = N - 1; i >= 0; i--) {
49279
- const name = stack[i].name;
49280
- let loc;
49281
- let file = null;
49282
- let line = 0;
49283
- if (i === N - 1) {
49284
- file = info.file;
49285
- line = info.line ?? 0;
49286
- if (file && line > 0) {
49287
- loc = `${file}:${line}`;
49288
- } else if (line > 0) {
49289
- loc = `line ${line}`;
49290
- } else {
49291
- loc = "unknown";
49292
- }
49293
- } else {
49294
- const callerFrame = stack[i + 1];
49295
- file = callerFrame.callerFile;
49296
- line = callerFrame.callerLine;
49297
- if (file && line > 0) {
49298
- loc = `${file}:${line}`;
49299
- } else if (line > 0) {
49300
- loc = `line ${line}`;
49301
- } else {
49302
- loc = "unknown";
49303
- }
49304
- }
49305
- result += `
49306
- at ${name} (${loc})`;
49307
- if (file && line > 0) {
49308
- let srcLine = null;
49309
- if (getSource) {
49310
- srcLine = getSourceLine(getSource, file, line);
49311
- } else if (i === N - 1) {
49312
- } else {
49313
- srcLine = stack[i + 1].callerSourceLine ?? null;
49314
- }
49315
- if (srcLine) result += `
49316
- ${srcLine}`;
49317
- }
49318
- }
49319
- const outermost = stack[0];
49320
- if (outermost.callerFile && outermost.callerLine > 0) {
49321
- result += `
49322
- at ${outermost.callerFile}:${outermost.callerLine}`;
49323
- const srcLine = getSource ? getSourceLine(getSource, outermost.callerFile, outermost.callerLine) : outermost.callerSourceLine ?? null;
49324
- if (srcLine) result += `
49325
- ${srcLine}`;
49326
- }
49327
- }
49328
51016
  return result;
49329
51017
  }
49330
51018
  function getSourceLine(getSource, file, line) {
@@ -49336,7 +51024,7 @@ function getSourceLine(getSource, file, line) {
49336
51024
  }
49337
51025
 
49338
51026
  // src/numbl-core/version.ts
49339
- var NUMBL_VERSION = "0.1.4";
51027
+ var NUMBL_VERSION = "0.1.6";
49340
51028
 
49341
51029
  // src/cli-repl.ts
49342
51030
  import { createInterface } from "readline";
@@ -49354,7 +51042,7 @@ import { homedir as homedir2 } from "os";
49354
51042
  // src/numbl-core/jsUserFunctions.ts
49355
51043
  function funcNameFromFile(fileName) {
49356
51044
  const base = fileName.split("/").pop();
49357
- return base.replace(/\.js$/, "");
51045
+ return base.replace(/\.numbl\.js$/, "");
49358
51046
  }
49359
51047
  function buildWasmMap(wasmFiles) {
49360
51048
  const map = /* @__PURE__ */ new Map();
@@ -49378,6 +51066,9 @@ function parseDirectives(source) {
49378
51066
  }
49379
51067
  return directives;
49380
51068
  }
51069
+ function isNumblJsFile(fileName) {
51070
+ return fileName.endsWith(".numbl.js");
51071
+ }
49381
51072
  function nativeLibFilename(baseName) {
49382
51073
  const platform = typeof process !== "undefined" ? process.platform : "linux";
49383
51074
  switch (platform) {
@@ -49451,7 +51142,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
49451
51142
  for (const file of jsFiles) {
49452
51143
  const base = file.name.split("/").pop();
49453
51144
  if (base.startsWith("_")) {
49454
- const libName = base.replace(/\.js$/, "");
51145
+ const libName = base.replace(/\.numbl\.js$/, "");
49455
51146
  libraryFiles.set(libName, file);
49456
51147
  } else {
49457
51148
  functionFiles.push(file);
@@ -49461,13 +51152,13 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
49461
51152
  function importJS(name) {
49462
51153
  const cached = libCache.get(name);
49463
51154
  if (cached === LOADING) {
49464
- throw new RuntimeError(`Circular dependency detected: ${name}.js`);
51155
+ throw new RuntimeError(`Circular dependency detected: ${name}.numbl.js`);
49465
51156
  }
49466
51157
  if (libCache.has(name)) return cached;
49467
51158
  const libFile = libraryFiles.get(name);
49468
51159
  if (!libFile) {
49469
51160
  throw new RuntimeError(
49470
- `importJS: library '${name}.js' not found in workspace`
51161
+ `importJS: library '${name}.numbl.js' not found in workspace`
49471
51162
  );
49472
51163
  }
49473
51164
  libCache.set(name, LOADING);
@@ -49480,7 +51171,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
49480
51171
  );
49481
51172
  const dummyRegister = () => {
49482
51173
  throw new RuntimeError(
49483
- `Library file '${name}.js' must not call register(). Use return {...} to export values.`
51174
+ `Library file '${name}.numbl.js' must not call register(). Use return {...} to export values.`
49484
51175
  );
49485
51176
  };
49486
51177
  const factory = new Function(
@@ -49515,7 +51206,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
49515
51206
  }
49516
51207
  if (builtin) {
49517
51208
  throw new Error(
49518
- "register() called more than once \u2014 only one registration per .js file"
51209
+ "register() called more than once \u2014 only one registration per .numbl.js file"
49519
51210
  );
49520
51211
  }
49521
51212
  builtin = {
@@ -49582,7 +51273,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge2) {
49582
51273
  };
49583
51274
  };
49584
51275
  }
49585
- result.push(builtin);
51276
+ result.push({ name: funcName, fileName: file.name, builtin });
49586
51277
  } catch (e) {
49587
51278
  const msg = e instanceof Error ? e.message : String(e);
49588
51279
  throw new Error(
@@ -49795,7 +51486,7 @@ function resolveFunction(name, argTypes, callSite, index2) {
49795
51486
  };
49796
51487
  }
49797
51488
  if (index2.jsUserFunctions.has(name)) {
49798
- return { kind: "builtin", name };
51489
+ return { kind: "jsUserFunction", name, argTypes };
49799
51490
  }
49800
51491
  if (index2.workspaceClasses.has(name)) {
49801
51492
  return {
@@ -50648,7 +52339,8 @@ function lowerMultiAssign(ctx, stmt) {
50648
52339
  if (args.some((a) => a === null)) return null;
50649
52340
  const loweredArgs = args;
50650
52341
  const argJitTypes = loweredArgs.map((a) => a.jitType);
50651
- const ib = getIBuiltin(rhs.name);
52342
+ const jsEntry = ctx.interp?.ctx.registry.jsUserFunctionsByName.get(rhs.name);
52343
+ const ib = jsEntry?.builtin ?? getIBuiltin(rhs.name);
50652
52344
  if (!ib) return null;
50653
52345
  const resolution = ib.resolve(argJitTypes, nargout);
50654
52346
  if (!resolution || resolution.outputTypes.length < nargout) return null;
@@ -51072,7 +52764,8 @@ function resolveUserFunction(interp, name, argJitTypes) {
51072
52764
  return null;
51073
52765
  }
51074
52766
  function lowerIBuiltinCall(ctx, expr) {
51075
- const ib = getIBuiltin(expr.name);
52767
+ const jsEntry = ctx.interp?.ctx.registry.jsUserFunctionsByName.get(expr.name);
52768
+ const ib = jsEntry?.builtin ?? getIBuiltin(expr.name);
51076
52769
  if (!ib) return null;
51077
52770
  const args = expr.args.map((a) => lowerExpr(ctx, a));
51078
52771
  if (args.some((a) => a === null)) return null;
@@ -51315,10 +53008,10 @@ function idxN(base, indices) {
51315
53008
  let stride = 1;
51316
53009
  for (let k = 0; k < indices.length; k++) {
51317
53010
  const dimSize = k < s.length ? s[k] : 1;
51318
- const sub = Math.round(indices[k]) - 1;
51319
- if (sub < 0 || sub >= dimSize)
53011
+ const sub2 = Math.round(indices[k]) - 1;
53012
+ if (sub2 < 0 || sub2 >= dimSize)
51320
53013
  throw new Error("Index exceeds array bounds");
51321
- lin += sub * stride;
53014
+ lin += sub2 * stride;
51322
53015
  stride *= dimSize;
51323
53016
  }
51324
53017
  if (base.imag !== void 0) {
@@ -51429,6 +53122,48 @@ setDynamicRegisterHook((b) => {
51429
53122
  return typeof result === "boolean" ? result ? 1 : 0 : result;
51430
53123
  };
51431
53124
  });
53125
+ function buildPerRuntimeJitHelpers(jsUserFunctions) {
53126
+ const h = Object.assign({}, jitHelpers);
53127
+ for (const [name, ib] of jsUserFunctions) {
53128
+ h[`ib_${name}`] = (...args) => {
53129
+ const pe = h._profileEnter;
53130
+ const pl = h._profileLeave;
53131
+ pe("jsUserFunction:jit:" + name);
53132
+ const rtArgs = args;
53133
+ const argTypes = rtArgs.map(inferJitType);
53134
+ const res = ib.resolve(argTypes, 1);
53135
+ if (!res) {
53136
+ pl();
53137
+ throw new Error(`JIT ib_${name}: resolve failed`);
53138
+ }
53139
+ const result = res.apply(rtArgs, 1);
53140
+ pl();
53141
+ return typeof result === "boolean" ? result ? 1 : 0 : result;
53142
+ };
53143
+ }
53144
+ const origIbcall = h.ibcall;
53145
+ h.ibcall = (name, nargout, ...args) => {
53146
+ const ib = jsUserFunctions.get(name);
53147
+ if (!ib) return origIbcall(name, nargout, ...args);
53148
+ const pe = h._profileEnter;
53149
+ const pl = h._profileLeave;
53150
+ pe("jsUserFunction:jit:" + name);
53151
+ const rtArgs = args;
53152
+ const argTypes = rtArgs.map(inferJitType);
53153
+ const res = ib.resolve(argTypes, nargout);
53154
+ if (!res) {
53155
+ pl();
53156
+ throw new Error(`JIT ibcall: resolve failed for ${name}`);
53157
+ }
53158
+ const result = res.apply(rtArgs, nargout);
53159
+ pl();
53160
+ if (Array.isArray(result)) {
53161
+ return result.map((v) => typeof v === "boolean" ? v ? 1 : 0 : v);
53162
+ }
53163
+ return [typeof result === "boolean" ? result ? 1 : 0 : result];
53164
+ };
53165
+ return h;
53166
+ }
51432
53167
 
51433
53168
  // src/numbl-core/interpreter/jit/jitLoopAnalysis.ts
51434
53169
  function analyzeForLoop(stmt) {
@@ -51843,18 +53578,26 @@ function execStmt(stmt) {
51843
53578
  }
51844
53579
  case "While": {
51845
53580
  if (this.optimization >= 1 && tryJitWhile(this, stmt)) return null;
53581
+ const _whileStart = this.rt.profilingEnabled ? performance.now() : 0;
53582
+ let _whileIters = 0;
51846
53583
  while (true) {
51847
53584
  const cond = this.evalExpr(stmt.cond);
51848
53585
  if (!this.rt.toBool(cond)) break;
53586
+ _whileIters++;
51849
53587
  const signal = this.execStmts(stmt.body);
51850
53588
  if (signal instanceof BreakSignal) break;
51851
53589
  if (signal instanceof ContinueSignal) continue;
51852
- if (signal instanceof ReturnSignal) return signal;
53590
+ if (signal instanceof ReturnSignal) {
53591
+ recordHotLoop(this, stmt, "while", _whileIters, _whileStart);
53592
+ return signal;
53593
+ }
51853
53594
  }
53595
+ recordHotLoop(this, stmt, "while", _whileIters, _whileStart);
51854
53596
  return null;
51855
53597
  }
51856
53598
  case "For": {
51857
53599
  if (this.optimization >= 1 && tryJitFor(this, stmt)) return null;
53600
+ const _forStart = this.rt.profilingEnabled ? performance.now() : 0;
51858
53601
  const iterVal = this.evalExpr(stmt.expr);
51859
53602
  const rv = ensureRuntimeValue(iterVal);
51860
53603
  const iterItems = forIter(rv);
@@ -51863,8 +53606,12 @@ function execStmt(stmt) {
51863
53606
  const signal = this.execStmts(stmt.body);
51864
53607
  if (signal instanceof BreakSignal) break;
51865
53608
  if (signal instanceof ContinueSignal) continue;
51866
- if (signal instanceof ReturnSignal) return signal;
53609
+ if (signal instanceof ReturnSignal) {
53610
+ recordHotLoop(this, stmt, "for", _i + 1, _forStart);
53611
+ return signal;
53612
+ }
51867
53613
  }
53614
+ recordHotLoop(this, stmt, "for", iterItems.length, _forStart);
51868
53615
  return null;
51869
53616
  }
51870
53617
  case "Switch": {
@@ -52626,6 +54373,33 @@ function isOutputExpr(expr) {
52626
54373
  if (expr.type === "Ident") return outputFunctions.includes(expr.name);
52627
54374
  return false;
52628
54375
  }
54376
+ function recordHotLoop(interp, stmt, kind, iterations, startTime) {
54377
+ if (!interp.rt.profilingEnabled || iterations <= 1e3 || !stmt.span) return;
54378
+ const durationMs = performance.now() - startTime;
54379
+ let table = interp.lineTableCache.get(stmt.span.file);
54380
+ if (!table) {
54381
+ const src = interp.fileSources.get(stmt.span.file) ?? "";
54382
+ table = buildLineTable(src);
54383
+ interp.lineTableCache.set(stmt.span.file, table);
54384
+ }
54385
+ const line = offsetToLineFast(table, stmt.span.start);
54386
+ const key = `${stmt.span.file}:${line}`;
54387
+ const prev = interp.rt.hotLoops.get(key);
54388
+ if (prev) {
54389
+ prev.callCount++;
54390
+ prev.totalTimeMs += durationMs;
54391
+ if (iterations > prev.iterations) prev.iterations = iterations;
54392
+ } else {
54393
+ interp.rt.hotLoops.set(key, {
54394
+ file: stmt.span.file,
54395
+ line,
54396
+ kind,
54397
+ iterations,
54398
+ callCount: 1,
54399
+ totalTimeMs: durationMs
54400
+ });
54401
+ }
54402
+ }
52629
54403
 
52630
54404
  // src/numbl-core/interpreter/interpreterFunctions.ts
52631
54405
  var interpreterFunctions_exports = {};
@@ -52644,6 +54418,7 @@ __export(interpreterFunctions_exports, {
52644
54418
  instantiateClass: () => instantiateClass,
52645
54419
  interpretClassMethod: () => interpretClassMethod,
52646
54420
  interpretConstructor: () => interpretConstructor,
54421
+ interpretJsUserFunction: () => interpretJsUserFunction,
52647
54422
  interpretLocalFunction: () => interpretLocalFunction,
52648
54423
  interpretPrivateFunction: () => interpretPrivateFunction,
52649
54424
  interpretTarget: () => interpretTarget,
@@ -52707,7 +54482,8 @@ function tryJitCall(interp, fn, args, nargout) {
52707
54482
  const rt = interp.rt;
52708
54483
  try {
52709
54484
  const factory = new Function("$h", "$rt", ...paramNames, jsBody);
52710
- compiledFn = (...callArgs) => factory(jitHelpers, rt, ...callArgs);
54485
+ const helpers = rt.jitHelpers ?? jitHelpers;
54486
+ compiledFn = (...callArgs) => factory(helpers, rt, ...callArgs);
52711
54487
  } catch {
52712
54488
  fnWithCache._jitCache.set(cacheKey, null);
52713
54489
  return JIT_SKIP;
@@ -52809,27 +54585,76 @@ register("exist", (ctx, args) => {
52809
54585
  if (args.length < 1) return FALL_THROUGH;
52810
54586
  const nameArg = toString(ensureRuntimeValue(args[0]));
52811
54587
  const typeArg = args.length >= 2 ? toString(ensureRuntimeValue(args[1])) : "";
54588
+ const fio = ctx.rt.fileIO;
54589
+ const isBuiltin2 = () => !!(ctx.rt.builtins[nameArg] || getIBuiltin(nameArg));
54590
+ const fileTypeFromExt = (path) => /\.numbl\.js$/i.test(path) ? 3 : 2;
54591
+ const nameHasKnownExt = /\.(numbl\.js|m|mlx|mlapp)$/i.test(nameArg);
54592
+ const isAbsolutePath = (p2) => p2.startsWith("/") || p2.startsWith("\\") || /^[a-zA-Z]:[/\\]/.test(p2);
54593
+ const joinPath = (dir, name) => {
54594
+ if (!dir) return name;
54595
+ if (dir.endsWith("/") || dir.endsWith("\\")) return dir + name;
54596
+ return dir + "/" + name;
54597
+ };
54598
+ const walkSearchPath = (acceptDir) => {
54599
+ if (!fio?.existsPath) return 0;
54600
+ const dirs = isAbsolutePath(nameArg) ? [""] : ctx.rt.searchPaths.length > 0 ? ctx.rt.searchPaths : [""];
54601
+ for (const dir of dirs) {
54602
+ if (acceptDir) {
54603
+ const t = fio.existsPath(joinPath(dir, nameArg));
54604
+ if (t === "dir") return 7;
54605
+ }
54606
+ if (!nameHasKnownExt) {
54607
+ for (const ext of [".m", ".mlx", ".mlapp"]) {
54608
+ const t = fio.existsPath(joinPath(dir, nameArg + ext));
54609
+ if (t === "file") {
54610
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54611
+ return ws?.kind === "class" ? 8 : 2;
54612
+ }
54613
+ }
54614
+ const numblJs = fio.existsPath(joinPath(dir, nameArg + ".numbl.js"));
54615
+ if (numblJs === "file") return 3;
54616
+ }
54617
+ const lit = fio.existsPath(joinPath(dir, nameArg));
54618
+ if (lit === "file") return fileTypeFromExt(nameArg);
54619
+ }
54620
+ return 0;
54621
+ };
54622
+ const workspaceTypeId = () => {
54623
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54624
+ if (!ws) return 0;
54625
+ switch (ws.kind) {
54626
+ case "function":
54627
+ return 2;
54628
+ case "jsfunction":
54629
+ return 3;
54630
+ case "class":
54631
+ return 8;
54632
+ }
54633
+ };
52812
54634
  if (typeArg === "var") {
52813
54635
  return ctx.env.has(nameArg) ? 1 : 0;
52814
54636
  }
52815
54637
  if (typeArg === "builtin") {
52816
- return ctx.rt.builtins[nameArg] || getIBuiltin(nameArg) ? 5 : 0;
54638
+ return isBuiltin2() ? 5 : 0;
54639
+ }
54640
+ if (typeArg === "class") {
54641
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54642
+ return ws?.kind === "class" ? 8 : 0;
52817
54643
  }
52818
54644
  if (typeArg === "dir") {
52819
- const fio = ctx.rt.fileIO;
52820
- if (!fio?.existsPath) return FALL_THROUGH;
52821
- return fio.existsPath(nameArg) === "dir" ? 7 : 0;
52822
- }
52823
- if (typeArg === "file" || typeArg === "") {
52824
- const fio = ctx.rt.fileIO;
52825
- if (!fio?.existsPath) return FALL_THROUGH;
52826
- const result = fio.existsPath(nameArg);
52827
- if (typeArg === "file") return result === "file" ? 2 : 0;
54645
+ return walkSearchPath(true) === 7 ? 7 : 0;
54646
+ }
54647
+ if (typeArg === "file") {
54648
+ const t = walkSearchPath(true);
54649
+ if (t) return t;
54650
+ return workspaceTypeId();
54651
+ }
54652
+ if (typeArg === "") {
52828
54653
  if (ctx.env.has(nameArg)) return 1;
52829
- if (result === "dir") return 7;
52830
- if (result === "file") return 2;
52831
- if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) return 5;
52832
- return 0;
54654
+ if (isBuiltin2()) return 5;
54655
+ const t = walkSearchPath(true);
54656
+ if (t) return t;
54657
+ return workspaceTypeId();
52833
54658
  }
52834
54659
  return FALL_THROUGH;
52835
54660
  });
@@ -52837,8 +54662,8 @@ register("which", (ctx, args) => {
52837
54662
  if (args.length < 1) return FALL_THROUGH;
52838
54663
  const nameArg = toString(ensureRuntimeValue(args[0]));
52839
54664
  if (ctx.env.has(nameArg)) return RTV.char("variable");
52840
- const filePath = ctx.lookupWorkspaceFile(nameArg);
52841
- if (filePath) return RTV.char(filePath);
54665
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54666
+ if (ws) return RTV.char(ws.path);
52842
54667
  if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) {
52843
54668
  return RTV.char("built-in");
52844
54669
  }
@@ -52985,9 +54810,11 @@ function callFunction(name, args, nargout) {
52985
54810
  rt: this.rt,
52986
54811
  lookupWorkspaceFile: (n) => {
52987
54812
  const entry = this.ctx.registry.filesByFuncName.get(n);
52988
- if (entry) return entry.fileName;
54813
+ if (entry) return { path: entry.fileName, kind: "function" };
52989
54814
  const classInfo = this.ctx.getClassInfo(n);
52990
- if (classInfo) return classInfo.fileName;
54815
+ if (classInfo) return { path: classInfo.fileName, kind: "class" };
54816
+ const jsEntry = this.ctx.registry.jsUserFunctionsByName.get(n);
54817
+ if (jsEntry) return { path: jsEntry.fileName, kind: "jsfunction" };
52991
54818
  return void 0;
52992
54819
  }
52993
54820
  };
@@ -53053,6 +54880,8 @@ function interpretTarget(target, args, nargout) {
53053
54880
  return this.interpretLocalFunction(target, args, nargout);
53054
54881
  case "workspaceFunction":
53055
54882
  return this.interpretWorkspaceFunction(target, args, nargout);
54883
+ case "jsUserFunction":
54884
+ return this.interpretJsUserFunction(target, args, nargout);
53056
54885
  case "classMethod":
53057
54886
  return this.interpretClassMethod(target, args, nargout);
53058
54887
  case "workspaceClassConstructor":
@@ -53061,6 +54890,36 @@ function interpretTarget(target, args, nargout) {
53061
54890
  return this.interpretPrivateFunction(target, args, nargout);
53062
54891
  }
53063
54892
  }
54893
+ function interpretJsUserFunction(target, args, nargout) {
54894
+ const entry = this.ctx.registry.jsUserFunctionsByName.get(target.name);
54895
+ if (!entry) {
54896
+ throw new RuntimeError(`JS user function '${target.name}' not found`);
54897
+ }
54898
+ const ib = entry.builtin;
54899
+ const margs = args.map((a) => ensureRuntimeValue(a));
54900
+ const argTypes = margs.map(inferJitType);
54901
+ const resolution = ib.resolve(argTypes, nargout);
54902
+ if (!resolution) {
54903
+ const typeNames = argTypes.map((t) => t.kind);
54904
+ throw new RuntimeError(
54905
+ `JS user function '${target.name}' does not support these argument types: (${typeNames.join(", ")})`
54906
+ );
54907
+ }
54908
+ const isVoid = resolution.outputTypes.length === 0;
54909
+ if (isVoid && nargout > 0) {
54910
+ throw new RuntimeError("Too many output arguments.");
54911
+ }
54912
+ return this.withFileContext(entry.fileName, void 0, void 0, () => {
54913
+ if (this.rt.profilingEnabled) {
54914
+ this.rt.profileEnter("jsUserFunction:interp:" + target.name);
54915
+ const result2 = resolution.apply(margs, nargout);
54916
+ this.rt.profileLeave();
54917
+ return isVoid ? void 0 : result2;
54918
+ }
54919
+ const result = resolution.apply(margs, nargout);
54920
+ return isVoid ? void 0 : result;
54921
+ });
54922
+ }
53064
54923
  function interpretLocalFunction(target, args, nargout) {
53065
54924
  const { source } = target;
53066
54925
  if (source.from === "main") {
@@ -53925,6 +55784,7 @@ function extractClassInfo(classDef, qualifiedName, fileName, source) {
53925
55784
  function createWorkspaceRegistry() {
53926
55785
  return {
53927
55786
  filesByFuncName: /* @__PURE__ */ new Map(),
55787
+ jsUserFunctionsByName: /* @__PURE__ */ new Map(),
53928
55788
  fileContexts: /* @__PURE__ */ new Map(),
53929
55789
  classesByName: /* @__PURE__ */ new Map(),
53930
55790
  localClassesByName: /* @__PURE__ */ new Map(),
@@ -54118,6 +55978,7 @@ var LoweringContext = class _LoweringContext {
54118
55978
  /** Clear workspace-level registrations so they can be rebuilt after addpath/rmpath. */
54119
55979
  clearWorkspaceRegistrations() {
54120
55980
  this.registry.filesByFuncName.clear();
55981
+ this.registry.jsUserFunctionsByName.clear();
54121
55982
  this.registry.classesByName.clear();
54122
55983
  this.registry.privateFilesByDir.clear();
54123
55984
  this.registry.fileContexts.clear();
@@ -54125,6 +55986,14 @@ var LoweringContext = class _LoweringContext {
54125
55986
  this.registry.externalAccessByFile.clear();
54126
55987
  this.registry.functionIndex = null;
54127
55988
  }
55989
+ /**
55990
+ * Register a JS user function (.numbl.js) in the workspace registry.
55991
+ * Uses first-wins semantics so search-path priority is honored.
55992
+ */
55993
+ registerJsUserFunction(funcName, fileName, builtin) {
55994
+ if (this.registry.jsUserFunctionsByName.has(funcName)) return;
55995
+ this.registry.jsUserFunctionsByName.set(funcName, { fileName, builtin });
55996
+ }
54128
55997
  // ── Private function management ──────────────────────────────────
54129
55998
  /**
54130
55999
  * Get the effective directory for this context's file, used for
@@ -54388,19 +56257,15 @@ var LoweringContext = class _LoweringContext {
54388
56257
  * Should be called once after registerWorkspaceFiles() and registerLocalFunctionAST().
54389
56258
  * Parses all workspace files eagerly to discover subfunctions.
54390
56259
  */
54391
- buildFunctionIndex(jsUserFunctionNames) {
56260
+ buildFunctionIndex() {
54392
56261
  const builtins = /* @__PURE__ */ new Set([
54393
56262
  ...getAllBuiltinNames(),
54394
56263
  ...getAllIBuiltinNames(),
54395
56264
  ...SPECIAL_BUILTIN_NAMES
54396
56265
  ]);
54397
- const jsUserFunctions = /* @__PURE__ */ new Set();
54398
- if (jsUserFunctionNames) {
54399
- for (const name of jsUserFunctionNames) {
54400
- builtins.delete(name);
54401
- jsUserFunctions.add(name);
54402
- }
54403
- }
56266
+ const jsUserFunctions = new Set(
56267
+ this.registry.jsUserFunctionsByName.keys()
56268
+ );
54404
56269
  const mainLocalFunctions = new Set(this.localFunctionASTs.keys());
54405
56270
  const workspaceFunctions = new Set(this.registry.filesByFuncName.keys());
54406
56271
  const workspaceClasses = /* @__PURE__ */ new Set([
@@ -54934,7 +56799,7 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54934
56799
  for (const f of workspaceFiles) {
54935
56800
  if (f.name.endsWith(".m")) {
54936
56801
  mWorkspaceFiles.push(f);
54937
- } else if (f.name.endsWith(".js")) {
56802
+ } else if (isNumblJsFile(f.name)) {
54938
56803
  jsWorkspaceFiles.push(f);
54939
56804
  } else if (f.name.endsWith(".wasm")) {
54940
56805
  wasmWorkspaceFiles.push(f);
@@ -54958,6 +56823,8 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54958
56823
  for (const f of cwdFiles) {
54959
56824
  if (f.name.endsWith(".m")) {
54960
56825
  mWorkspaceFiles.push(f);
56826
+ } else if (isNumblJsFile(f.name)) {
56827
+ jsWorkspaceFiles.push(f);
54961
56828
  }
54962
56829
  }
54963
56830
  }
@@ -54971,7 +56838,6 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54971
56838
  wasmWorkspaceFiles,
54972
56839
  nativeBridge2
54973
56840
  );
54974
- const jsUserFunctionNames = jsUserFunctions.map((ib) => ib.name);
54975
56841
  const stdlibShimNames = /* @__PURE__ */ new Set();
54976
56842
  for (const f of stdlibFiles) {
54977
56843
  mWorkspaceFiles.push(f);
@@ -55018,19 +56884,21 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
55018
56884
  if (mWorkspaceFiles.length > 0) {
55019
56885
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
55020
56886
  }
55021
- const functionIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
56887
+ for (const entry of jsUserFunctions) {
56888
+ ctx.registerJsUserFunction(entry.name, entry.fileName, entry.builtin);
56889
+ }
56890
+ const functionIndex = ctx.buildFunctionIndex();
55022
56891
  const savedSpecialBuiltins = /* @__PURE__ */ new Map();
55023
56892
  for (const name of SPECIAL_BUILTIN_NAMES) {
55024
56893
  const existing = getIBuiltin(name);
55025
56894
  if (existing) savedSpecialBuiltins.set(name, existing);
55026
56895
  }
55027
56896
  const rt = new Runtime(options, options.initialVariableValues);
55028
- const savedIBuiltins = /* @__PURE__ */ new Map();
55029
- for (const ib of jsUserFunctions) {
55030
- const orig = getIBuiltin(ib.name);
55031
- if (orig) savedIBuiltins.set(ib.name, orig);
55032
- registerDynamicIBuiltin(ib);
56897
+ const jsBuiltinMap = /* @__PURE__ */ new Map();
56898
+ for (const [n, e] of ctx.registry.jsUserFunctionsByName.entries()) {
56899
+ jsBuiltinMap.set(n, e.builtin);
55033
56900
  }
56901
+ rt.jitHelpers = buildPerRuntimeJitHelpers(jsBuiltinMap);
55034
56902
  if (options.customBuiltins) {
55035
56903
  Object.assign(rt.builtins, options.customBuiltins);
55036
56904
  Object.assign(rt.customBuiltins, options.customBuiltins);
@@ -55065,6 +56933,14 @@ ${jsCode}`
55065
56933
  interpreter.installRuntimeCallbacks();
55066
56934
  rt.searchPaths = ctx.registry.searchPaths;
55067
56935
  let pathsModified = false;
56936
+ const loadedJsUserFunctions = [...jsUserFunctions];
56937
+ const rebuildJitHelpers = () => {
56938
+ const map = /* @__PURE__ */ new Map();
56939
+ for (const [n, e] of ctx.registry.jsUserFunctionsByName.entries()) {
56940
+ map.set(n, e.builtin);
56941
+ }
56942
+ rt.jitHelpers = buildPerRuntimeJitHelpers(map);
56943
+ };
55068
56944
  const rebuildWorkspace = () => {
55069
56945
  const paths = ctx.registry.searchPaths;
55070
56946
  const priorityOf = (name) => {
@@ -55083,11 +56959,18 @@ ${jsCode}`
55083
56959
  return bestIdx;
55084
56960
  };
55085
56961
  mWorkspaceFiles.sort((a, b) => priorityOf(a.name) - priorityOf(b.name));
56962
+ loadedJsUserFunctions.sort(
56963
+ (a, b) => priorityOf(a.fileName) - priorityOf(b.fileName)
56964
+ );
55086
56965
  ctx.clearWorkspaceRegistrations();
55087
56966
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
55088
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
56967
+ for (const entry of loadedJsUserFunctions) {
56968
+ ctx.registerJsUserFunction(entry.name, entry.fileName, entry.builtin);
56969
+ }
56970
+ const newIndex = ctx.buildFunctionIndex();
55089
56971
  interpreter.functionIndex = newIndex;
55090
56972
  interpreter.clearAllCaches();
56973
+ rebuildJitHelpers();
55091
56974
  pathsModified = true;
55092
56975
  };
55093
56976
  rt.onPathChange = (action, dir, position) => {
@@ -55126,24 +57009,32 @@ ${jsCode}`
55126
57009
  }
55127
57010
  interpreter.fileSources.set(f.name, f.source);
55128
57011
  mWorkspaceFiles.push(f);
55129
- } else if (f.name.endsWith(".js")) {
57012
+ } else if (isNumblJsFile(f.name)) {
55130
57013
  newJsFiles.push(f);
57014
+ if (!jsWorkspaceFiles.some((e) => e.name === f.name)) {
57015
+ jsWorkspaceFiles.push(f);
57016
+ }
55131
57017
  } else if (f.name.endsWith(".wasm")) {
55132
57018
  newWasmFiles.push(f);
57019
+ if (!wasmWorkspaceFiles.some((e) => e.name === f.name)) {
57020
+ wasmWorkspaceFiles.push(f);
57021
+ }
55133
57022
  }
55134
57023
  }
55135
57024
  if (newJsFiles.length > 0) {
55136
- const newIBuiltins = loadJsUserFunctions(
57025
+ const loaded = loadJsUserFunctions(
55137
57026
  newJsFiles,
55138
57027
  newWasmFiles,
55139
57028
  nativeBridge2
55140
57029
  );
55141
- for (const ib of newIBuiltins) {
55142
- const orig = getIBuiltin(ib.name);
55143
- if (orig) savedIBuiltins.set(ib.name, orig);
55144
- registerDynamicIBuiltin(ib);
55145
- if (!jsUserFunctionNames.includes(ib.name)) {
55146
- jsUserFunctionNames.push(ib.name);
57030
+ for (const entry of loaded) {
57031
+ const existingIdx = loadedJsUserFunctions.findIndex(
57032
+ (e) => e.fileName === entry.fileName
57033
+ );
57034
+ if (existingIdx >= 0) {
57035
+ loadedJsUserFunctions[existingIdx] = entry;
57036
+ } else {
57037
+ loadedJsUserFunctions.push(entry);
55147
57038
  }
55148
57039
  }
55149
57040
  }
@@ -55161,6 +57052,21 @@ ${jsCode}`
55161
57052
  mWorkspaceFiles.splice(i, 1);
55162
57053
  }
55163
57054
  }
57055
+ for (let i = loadedJsUserFunctions.length - 1; i >= 0; i--) {
57056
+ if (loadedJsUserFunctions[i].fileName.startsWith(prefix)) {
57057
+ loadedJsUserFunctions.splice(i, 1);
57058
+ }
57059
+ }
57060
+ for (let i = jsWorkspaceFiles.length - 1; i >= 0; i--) {
57061
+ if (jsWorkspaceFiles[i].name.startsWith(prefix)) {
57062
+ jsWorkspaceFiles.splice(i, 1);
57063
+ }
57064
+ }
57065
+ for (let i = wasmWorkspaceFiles.length - 1; i >= 0; i--) {
57066
+ if (wasmWorkspaceFiles[i].name.startsWith(prefix)) {
57067
+ wasmWorkspaceFiles.splice(i, 1);
57068
+ }
57069
+ }
55164
57070
  }
55165
57071
  rebuildWorkspace();
55166
57072
  };
@@ -55188,6 +57094,24 @@ ${jsCode}`
55188
57094
  mWorkspaceFiles.splice(i, 1);
55189
57095
  }
55190
57096
  }
57097
+ for (let i = loadedJsUserFunctions.length - 1; i >= 0; i--) {
57098
+ const fname = loadedJsUserFunctions[i].fileName;
57099
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
57100
+ loadedJsUserFunctions.splice(i, 1);
57101
+ }
57102
+ }
57103
+ for (let i = jsWorkspaceFiles.length - 1; i >= 0; i--) {
57104
+ const fname = jsWorkspaceFiles[i].name;
57105
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
57106
+ jsWorkspaceFiles.splice(i, 1);
57107
+ }
57108
+ }
57109
+ for (let i = wasmWorkspaceFiles.length - 1; i >= 0; i--) {
57110
+ const fname = wasmWorkspaceFiles[i].name;
57111
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
57112
+ wasmWorkspaceFiles.splice(i, 1);
57113
+ }
57114
+ }
55191
57115
  const oldIdx = ctx.registry.searchPaths.indexOf(implicitCwdPath);
55192
57116
  if (oldIdx >= 0) ctx.registry.searchPaths.splice(oldIdx, 1);
55193
57117
  implicitCwdPath = null;
@@ -55204,6 +57128,8 @@ ${jsCode}`
55204
57128
  newFiles = fileIO.scanDirectory(absNewCwd);
55205
57129
  } catch {
55206
57130
  }
57131
+ const newJsFiles = [];
57132
+ const newWasmFiles = [];
55207
57133
  for (const f of newFiles) {
55208
57134
  if (f.name.endsWith(".m") && !ctx.fileASTCache.has(f.name)) {
55209
57135
  try {
@@ -55220,6 +57146,33 @@ ${jsCode}`
55220
57146
  }
55221
57147
  interpreter.fileSources.set(f.name, f.source);
55222
57148
  mWorkspaceFiles.push(f);
57149
+ } else if (isNumblJsFile(f.name)) {
57150
+ newJsFiles.push(f);
57151
+ if (!jsWorkspaceFiles.some((e) => e.name === f.name)) {
57152
+ jsWorkspaceFiles.push(f);
57153
+ }
57154
+ } else if (f.name.endsWith(".wasm")) {
57155
+ newWasmFiles.push(f);
57156
+ if (!wasmWorkspaceFiles.some((e) => e.name === f.name)) {
57157
+ wasmWorkspaceFiles.push(f);
57158
+ }
57159
+ }
57160
+ }
57161
+ if (newJsFiles.length > 0) {
57162
+ const loaded = loadJsUserFunctions(
57163
+ newJsFiles,
57164
+ newWasmFiles,
57165
+ nativeBridge2
57166
+ );
57167
+ for (const entry of loaded) {
57168
+ const existingIdx = loadedJsUserFunctions.findIndex(
57169
+ (e) => e.fileName === entry.fileName
57170
+ );
57171
+ if (existingIdx >= 0) {
57172
+ loadedJsUserFunctions[existingIdx] = entry;
57173
+ } else {
57174
+ loadedJsUserFunctions.push(entry);
57175
+ }
55223
57176
  }
55224
57177
  }
55225
57178
  }
@@ -55229,9 +57182,11 @@ ${jsCode}`
55229
57182
  const nestedSearchPaths = ctx.registry.searchPaths.filter(
55230
57183
  (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
55231
57184
  );
55232
- const nestedWorkspaceFiles = mWorkspaceFiles.filter(
55233
- (f) => !stdlibShimNames.has(f.name)
55234
- );
57185
+ const nestedWorkspaceFiles = [
57186
+ ...mWorkspaceFiles.filter((f) => !stdlibShimNames.has(f.name)),
57187
+ ...jsWorkspaceFiles,
57188
+ ...wasmWorkspaceFiles
57189
+ ];
55235
57190
  const evalResult = executeCode(
55236
57191
  code,
55237
57192
  {
@@ -55277,16 +57232,19 @@ ${jitSections.join("\n\n")}` : "// No JS generated",
55277
57232
  executionTimeMs,
55278
57233
  jitCompileTimeMs: rt.getJitCompileTimeMs(),
55279
57234
  builtins: rt.getBuiltinProfile(),
55280
- dispatches: rt.getDispatchProfile()
57235
+ dispatches: rt.getDispatchProfile(),
57236
+ hotLoops: [...rt.hotLoops.values()]
55281
57237
  };
55282
57238
  }
55283
57239
  if (pathsModified) {
55284
57240
  result.searchPaths = ctx.registry.searchPaths.filter(
55285
57241
  (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
55286
57242
  );
55287
- result.workspaceFiles = mWorkspaceFiles.filter(
55288
- (f) => !stdlibShimNames.has(f.name)
55289
- );
57243
+ result.workspaceFiles = [
57244
+ ...mWorkspaceFiles.filter((f) => !stdlibShimNames.has(f.name)),
57245
+ ...jsWorkspaceFiles,
57246
+ ...wasmWorkspaceFiles
57247
+ ];
55290
57248
  }
55291
57249
  result.implicitCwdPath = implicitCwdPath;
55292
57250
  return result;
@@ -55316,14 +57274,6 @@ ${jitSections.join("\n\n")}` : "// No JS generated";
55316
57274
  jitHelpers._profileEnter = Function.prototype;
55317
57275
  jitHelpers._profileLeave = Function.prototype;
55318
57276
  }
55319
- for (const ib of jsUserFunctions) {
55320
- const orig = savedIBuiltins.get(ib.name);
55321
- if (orig) {
55322
- registerDynamicIBuiltin(orig);
55323
- } else {
55324
- unregisterIBuiltin(ib.name);
55325
- }
55326
- }
55327
57277
  for (const [, ib] of savedSpecialBuiltins) {
55328
57278
  registerDynamicIBuiltin(ib);
55329
57279
  }
@@ -55375,7 +57325,7 @@ function scanMFiles(dirPath, excludeFile) {
55375
57325
  if (entry.startsWith("@") || entry.startsWith("+") || entry === "private") {
55376
57326
  files.push(...scanMFiles(fullPath, excludeFile));
55377
57327
  }
55378
- } else if (stat.isFile() && (entry.endsWith(".m") || entry.endsWith(".js"))) {
57328
+ } else if (stat.isFile() && (entry.endsWith(".m") || entry.endsWith(".numbl.js"))) {
55379
57329
  const source = readFileSync2(fullPath, "utf-8");
55380
57330
  files.push({
55381
57331
  name: fullPath,
@@ -55957,8 +57907,8 @@ var NodeFileIOAdapter = class {
55957
57907
  } catch {
55958
57908
  }
55959
57909
  }
55960
- for (const sub of subdirs) {
55961
- walkDir(join3(dir, sub));
57910
+ for (const sub2 of subdirs) {
57911
+ walkDir(join3(dir, sub2));
55962
57912
  }
55963
57913
  };
55964
57914
  walkDir(absBase);
@@ -56944,7 +58894,8 @@ async function executeWithOptions(code, mainFileName, workspaceFiles, opts, sear
56944
58894
  executionTimeMs: pd.executionTimeMs,
56945
58895
  jitCompileTimeMs: pd.jitCompileTimeMs,
56946
58896
  builtins,
56947
- dispatches
58897
+ dispatches,
58898
+ hotLoops: pd.hotLoops ?? []
56948
58899
  };
56949
58900
  writeFileSync3(opts.profileOutput, JSON.stringify(profile, null, 2) + "\n");
56950
58901
  console.error(`Profile written to ${opts.profileOutput}`);
@@ -57302,6 +59253,20 @@ function cmdShowProfile(args) {
57302
59253
  } else {
57303
59254
  console.log("\nNo builtin functions were called.");
57304
59255
  }
59256
+ const hotLoops = data.hotLoops ?? [];
59257
+ if (hotLoops.length > 0) {
59258
+ hotLoops.sort((a, b) => b.totalTimeMs - a.totalTimeMs);
59259
+ const locW = Math.max(20, ...hotLoops.map((l) => `${l.file}:${l.line}`.length)) + 2;
59260
+ const header = "Location".padEnd(locW) + pad("Kind", 8) + pad("Calls", 8) + pad("Max Iters", 12) + pad("Total (ms)", 13);
59261
+ console.log("\nInterpreted loops (>1000 iterations):");
59262
+ console.log(header);
59263
+ console.log("\u2500".repeat(header.length));
59264
+ for (const l of hotLoops) {
59265
+ const loc = `${l.file}:${l.line}`;
59266
+ const line = loc.padEnd(locW) + pad(l.kind, 8) + pad(String(l.callCount ?? 1), 8) + pad(String(l.iterations), 12) + pad(fmt(l.totalTimeMs ?? 0, 2), 13);
59267
+ console.log(line);
59268
+ }
59269
+ }
57305
59270
  }
57306
59271
  async function main() {
57307
59272
  const args = process.argv.slice(2);