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-lib/lib.js CHANGED
@@ -64,6 +64,9 @@ var RuntimeError = class extends Error {
64
64
  * Format error with location and snippet context.
65
65
  */
66
66
  toString() {
67
+ if (this.callStack != null && this.callStack.length > 0) {
68
+ return this._formatWithCallStack();
69
+ }
67
70
  let result = this.name;
68
71
  if (this.file && this.line !== null) {
69
72
  result += ` at ${this.file}:${this.line}`;
@@ -78,58 +81,51 @@ var RuntimeError = class extends Error {
78
81
  result += `
79
82
  ${this.snippet}`;
80
83
  }
81
- if (this.callStack != null && this.callStack.length > 0) {
82
- result += `
83
- Call stack (most recent call first):`;
84
- const N = this.callStack.length;
85
- for (let i = N - 1; i >= 0; i--) {
86
- const name = this.callStack[i].name;
87
- let loc;
88
- let frameFile = null;
89
- let frameLine = 0;
90
- if (i === N - 1) {
91
- frameFile = this.file;
92
- frameLine = this.line ?? 0;
93
- if (frameFile && frameLine > 0) {
94
- loc = `${frameFile}:${frameLine}`;
95
- } else if (frameLine > 0) {
96
- loc = `line ${frameLine}`;
97
- } else {
98
- loc = "unknown";
99
- }
100
- } else {
101
- const callerFrame = this.callStack[i + 1];
102
- frameFile = callerFrame.callerFile;
103
- frameLine = callerFrame.callerLine;
104
- if (frameFile && frameLine > 0) {
105
- loc = `${frameFile}:${frameLine}`;
106
- } else if (frameLine > 0) {
107
- loc = `line ${frameLine}`;
108
- } else {
109
- loc = "unknown";
110
- }
111
- }
112
- result += `
113
- at ${name} (${loc})`;
114
- if (frameFile && frameLine > 0) {
115
- const srcLine = this._getSourceLine(frameFile, frameLine);
116
- if (srcLine) result += `
117
- ${srcLine}`;
118
- }
119
- }
120
- const outermost = this.callStack[0];
121
- if (outermost.callerFile && outermost.callerLine > 0) {
122
- result += `
123
- at ${outermost.callerFile}:${outermost.callerLine}`;
124
- const srcLine = this._getSourceLine(
125
- outermost.callerFile,
126
- outermost.callerLine
127
- );
128
- if (srcLine) result += `
129
- ${srcLine}`;
84
+ return result;
85
+ }
86
+ /** Format error with MATLAB-style call stack. */
87
+ _formatWithCallStack() {
88
+ const stack = this.callStack;
89
+ const N = stack.length;
90
+ const parts = [];
91
+ const innerName = stack[N - 1].name;
92
+ const innerFile = this.file;
93
+ const innerLine = this.line ?? 0;
94
+ if (innerFile && innerLine > 0) {
95
+ parts.push(`Error using ${innerName} (${innerFile}:${innerLine})`);
96
+ } else if (innerLine > 0) {
97
+ parts.push(`Error using ${innerName} (line ${innerLine})`);
98
+ } else {
99
+ parts.push(`Error using ${innerName}`);
100
+ }
101
+ parts.push(this.message);
102
+ for (let i = N - 2; i >= 0; i--) {
103
+ const name = stack[i].name;
104
+ const callerFrame = stack[i + 1];
105
+ const file = callerFrame.callerFile;
106
+ const line = callerFrame.callerLine;
107
+ parts.push("");
108
+ if (file && line > 0) {
109
+ parts.push(`Error in ${name} (${file}:${line})`);
110
+ const srcLine = this._getSourceLine(file, line);
111
+ if (srcLine) parts.push(` ${srcLine}`);
112
+ } else if (line > 0) {
113
+ parts.push(`Error in ${name} (line ${line})`);
114
+ } else {
115
+ parts.push(`Error in ${name}`);
130
116
  }
131
117
  }
132
- return result;
118
+ const outermost = stack[0];
119
+ if (outermost.callerFile && outermost.callerLine > 0) {
120
+ parts.push("");
121
+ parts.push(`Error in ${outermost.callerFile}:${outermost.callerLine}`);
122
+ const srcLine = this._getSourceLine(
123
+ outermost.callerFile,
124
+ outermost.callerLine
125
+ );
126
+ if (srcLine) parts.push(` ${srcLine}`);
127
+ }
128
+ return parts.join("\n");
133
129
  }
134
130
  /** Look up a single trimmed source line from fileSources. */
135
131
  _getSourceLine(file, line) {
@@ -1577,6 +1573,435 @@ function dgetrf(m, n, a, lda, ipiv) {
1577
1573
  return info;
1578
1574
  }
1579
1575
 
1576
+ // src/numbl-core/helpers/gmres.ts
1577
+ function gmresCore(matvec, precSolve, b, n, restart, tol, maxit, x0) {
1578
+ const x = x0 ? new Float64Array(x0) : new Float64Array(n);
1579
+ if (restart <= 0 || restart > n) restart = n;
1580
+ let r = sub(b, matvec(x), n);
1581
+ if (precSolve) r = precSolve(r);
1582
+ let beta = nrm2(r, n);
1583
+ let normMb;
1584
+ if (precSolve) {
1585
+ normMb = nrm2(precSolve(new Float64Array(b)), n);
1586
+ } else {
1587
+ normMb = nrm2(b, n);
1588
+ }
1589
+ if (normMb === 0) normMb = 1;
1590
+ const resvecList = [beta];
1591
+ if (beta / normMb <= tol) {
1592
+ return {
1593
+ x,
1594
+ flag: 0,
1595
+ relres: beta / normMb,
1596
+ iter: [0, 0],
1597
+ resvec: new Float64Array(resvecList)
1598
+ };
1599
+ }
1600
+ let flag = 1;
1601
+ let outerIter = 0;
1602
+ let innerIter = 0;
1603
+ for (let outer = 1; outer <= maxit; outer++) {
1604
+ outerIter = outer;
1605
+ const V = new Float64Array(n * (restart + 1));
1606
+ for (let i = 0; i < n; i++) V[i] = r[i] / beta;
1607
+ const H2 = new Float64Array((restart + 1) * restart);
1608
+ const cs = new Float64Array(restart);
1609
+ const sn = new Float64Array(restart);
1610
+ const g = new Float64Array(restart + 1);
1611
+ g[0] = beta;
1612
+ let converged = false;
1613
+ for (let j = 0; j < restart; j++) {
1614
+ innerIter = j + 1;
1615
+ let w = matvec(V.subarray(j * n, (j + 1) * n));
1616
+ if (precSolve) w = precSolve(w);
1617
+ const ldh = restart + 1;
1618
+ for (let i = 0; i <= j; i++) {
1619
+ const viOff = i * n;
1620
+ let hij = 0;
1621
+ for (let k = 0; k < n; k++) hij += w[k] * V[viOff + k];
1622
+ H2[i + j * ldh] = hij;
1623
+ for (let k = 0; k < n; k++) w[k] -= hij * V[viOff + k];
1624
+ }
1625
+ const wnorm = nrm2(w, n);
1626
+ H2[j + 1 + j * ldh] = wnorm;
1627
+ if (wnorm > 1e-300) {
1628
+ const vOff = (j + 1) * n;
1629
+ for (let k = 0; k < n; k++) V[vOff + k] = w[k] / wnorm;
1630
+ }
1631
+ for (let i = 0; i < j; i++) {
1632
+ const hi = H2[i + j * ldh];
1633
+ const hi1 = H2[i + 1 + j * ldh];
1634
+ H2[i + j * ldh] = cs[i] * hi + sn[i] * hi1;
1635
+ H2[i + 1 + j * ldh] = -sn[i] * hi + cs[i] * hi1;
1636
+ }
1637
+ const [c, s] = givensRotation(H2[j + j * ldh], H2[j + 1 + j * ldh]);
1638
+ cs[j] = c;
1639
+ sn[j] = s;
1640
+ H2[j + j * ldh] = c * H2[j + j * ldh] + s * H2[j + 1 + j * ldh];
1641
+ H2[j + 1 + j * ldh] = 0;
1642
+ const gj = g[j];
1643
+ g[j] = c * gj;
1644
+ g[j + 1] = -s * gj;
1645
+ const residNorm = Math.abs(g[j + 1]);
1646
+ resvecList.push(residNorm);
1647
+ if (residNorm / normMb <= tol) {
1648
+ const y2 = backSolve(H2, g, j + 1, ldh);
1649
+ for (let k = 0; k < n; k++) {
1650
+ for (let l = 0; l <= j; l++) x[k] += V[k + l * n] * y2[l];
1651
+ }
1652
+ flag = 0;
1653
+ converged = true;
1654
+ break;
1655
+ }
1656
+ }
1657
+ if (converged) break;
1658
+ const y = backSolve(H2, g, restart, restart + 1);
1659
+ for (let k = 0; k < n; k++) {
1660
+ for (let l = 0; l < restart; l++) x[k] += V[k + l * n] * y[l];
1661
+ }
1662
+ r = sub(b, matvec(x), n);
1663
+ if (precSolve) r = precSolve(r);
1664
+ beta = nrm2(r, n);
1665
+ if (beta / normMb <= tol) {
1666
+ flag = 0;
1667
+ innerIter = 0;
1668
+ break;
1669
+ }
1670
+ }
1671
+ let relres;
1672
+ if (flag === 0) {
1673
+ let finalR = sub(b, matvec(x), n);
1674
+ if (precSolve) finalR = precSolve(finalR);
1675
+ relres = nrm2(finalR, n) / normMb;
1676
+ } else {
1677
+ relres = beta / normMb;
1678
+ }
1679
+ return {
1680
+ x,
1681
+ flag,
1682
+ relres,
1683
+ iter: [outerIter, innerIter],
1684
+ resvec: new Float64Array(resvecList)
1685
+ };
1686
+ }
1687
+ function nrm2(a, n) {
1688
+ let s = 0;
1689
+ for (let i = 0; i < n; i++) s += a[i] * a[i];
1690
+ return Math.sqrt(s);
1691
+ }
1692
+ function sub(a, b, n) {
1693
+ const r = new Float64Array(n);
1694
+ for (let i = 0; i < n; i++) r[i] = a[i] - b[i];
1695
+ return r;
1696
+ }
1697
+ function givensRotation(a, b) {
1698
+ if (b === 0) return [1, 0];
1699
+ if (Math.abs(b) > Math.abs(a)) {
1700
+ const t2 = a / b;
1701
+ const s = 1 / Math.sqrt(1 + t2 * t2);
1702
+ return [s * t2, s];
1703
+ }
1704
+ const t = b / a;
1705
+ const c = 1 / Math.sqrt(1 + t * t);
1706
+ return [c, c * t];
1707
+ }
1708
+ function backSolve(H2, g, m, ldh) {
1709
+ const y = new Float64Array(m);
1710
+ for (let i = 0; i < m; i++) y[i] = g[i];
1711
+ for (let i = m - 1; i >= 0; i--) {
1712
+ for (let j = i + 1; j < m; j++) y[i] -= H2[i + j * ldh] * y[j];
1713
+ y[i] /= H2[i + i * ldh];
1714
+ }
1715
+ return y;
1716
+ }
1717
+ function gmresCoreComplex(matvec, precSolve, b, n, restart, tol, maxit, x0) {
1718
+ const xRe = x0 ? new Float64Array(x0.re) : new Float64Array(n);
1719
+ const xIm = x0 ? new Float64Array(x0.im) : new Float64Array(n);
1720
+ if (restart <= 0 || restart > n) restart = n;
1721
+ const ax = matvec({ re: xRe, im: xIm });
1722
+ let rRe = new Float64Array(n);
1723
+ let rIm = new Float64Array(n);
1724
+ for (let i = 0; i < n; i++) {
1725
+ rRe[i] = b.re[i] - ax.re[i];
1726
+ rIm[i] = b.im[i] - ax.im[i];
1727
+ }
1728
+ if (precSolve) {
1729
+ const pr = precSolve({ re: rRe, im: rIm });
1730
+ rRe = new Float64Array(pr.re);
1731
+ rIm = new Float64Array(pr.im);
1732
+ }
1733
+ let beta = cnrm2(rRe, rIm, n);
1734
+ let normMb;
1735
+ if (precSolve) {
1736
+ const mb = precSolve({
1737
+ re: new Float64Array(b.re),
1738
+ im: new Float64Array(b.im)
1739
+ });
1740
+ normMb = cnrm2(mb.re, mb.im, n);
1741
+ } else {
1742
+ normMb = cnrm2(b.re, b.im, n);
1743
+ }
1744
+ if (normMb === 0) normMb = 1;
1745
+ const resvecList = [beta];
1746
+ if (beta / normMb <= tol) {
1747
+ return {
1748
+ x: { re: xRe, im: xIm },
1749
+ flag: 0,
1750
+ relres: beta / normMb,
1751
+ iter: [0, 0],
1752
+ resvec: new Float64Array(resvecList)
1753
+ };
1754
+ }
1755
+ let flag = 1;
1756
+ let outerIter = 0;
1757
+ let innerIter = 0;
1758
+ for (let outer = 1; outer <= maxit; outer++) {
1759
+ outerIter = outer;
1760
+ const VRe = new Float64Array(n * (restart + 1));
1761
+ const VIm = new Float64Array(n * (restart + 1));
1762
+ for (let i = 0; i < n; i++) {
1763
+ VRe[i] = rRe[i] / beta;
1764
+ VIm[i] = rIm[i] / beta;
1765
+ }
1766
+ const ldh = restart + 1;
1767
+ const HRe = new Float64Array(ldh * restart);
1768
+ const HIm = new Float64Array(ldh * restart);
1769
+ const cs = new Float64Array(restart);
1770
+ const snRe = new Float64Array(restart);
1771
+ const snIm = new Float64Array(restart);
1772
+ const gRe = new Float64Array(restart + 1);
1773
+ const gIm = new Float64Array(restart + 1);
1774
+ gRe[0] = beta;
1775
+ let converged = false;
1776
+ for (let j = 0; j < restart; j++) {
1777
+ innerIter = j + 1;
1778
+ const vjOff = j * n;
1779
+ let wRe, wIm;
1780
+ const mv = matvec({
1781
+ re: VRe.subarray(vjOff, vjOff + n),
1782
+ im: VIm.subarray(vjOff, vjOff + n)
1783
+ });
1784
+ wRe = mv.re;
1785
+ wIm = mv.im;
1786
+ if (precSolve) {
1787
+ const pw = precSolve({ re: wRe, im: wIm });
1788
+ wRe = pw.re;
1789
+ wIm = pw.im;
1790
+ }
1791
+ if (wRe.length !== n) wRe = new Float64Array(wRe);
1792
+ if (wIm.length !== n) wIm = new Float64Array(wIm);
1793
+ for (let i = 0; i <= j; i++) {
1794
+ const viOff = i * n;
1795
+ let dRe = 0, dIm = 0;
1796
+ for (let k = 0; k < n; k++) {
1797
+ dRe += VRe[viOff + k] * wRe[k] + VIm[viOff + k] * wIm[k];
1798
+ dIm += VRe[viOff + k] * wIm[k] - VIm[viOff + k] * wRe[k];
1799
+ }
1800
+ HRe[i + j * ldh] = dRe;
1801
+ HIm[i + j * ldh] = dIm;
1802
+ for (let k = 0; k < n; k++) {
1803
+ wRe[k] -= dRe * VRe[viOff + k] - dIm * VIm[viOff + k];
1804
+ wIm[k] -= dRe * VIm[viOff + k] + dIm * VRe[viOff + k];
1805
+ }
1806
+ }
1807
+ const wnorm = cnrm2(wRe, wIm, n);
1808
+ HRe[j + 1 + j * ldh] = wnorm;
1809
+ if (wnorm > 1e-300) {
1810
+ const vOff = (j + 1) * n;
1811
+ for (let k = 0; k < n; k++) {
1812
+ VRe[vOff + k] = wRe[k] / wnorm;
1813
+ VIm[vOff + k] = wIm[k] / wnorm;
1814
+ }
1815
+ }
1816
+ for (let i = 0; i < j; i++) {
1817
+ const c2 = cs[i], sR2 = snRe[i], sI2 = snIm[i];
1818
+ const hiR = HRe[i + j * ldh], hiI = HIm[i + j * ldh];
1819
+ const hi1R = HRe[i + 1 + j * ldh], hi1I = HIm[i + 1 + j * ldh];
1820
+ HRe[i + j * ldh] = c2 * hiR + (sR2 * hi1R - sI2 * hi1I);
1821
+ HIm[i + j * ldh] = c2 * hiI + (sR2 * hi1I + sI2 * hi1R);
1822
+ HRe[i + 1 + j * ldh] = -(sR2 * hiR + sI2 * hiI) + c2 * hi1R;
1823
+ HIm[i + 1 + j * ldh] = -(-sI2 * hiR + sR2 * hiI) + c2 * hi1I;
1824
+ }
1825
+ const aR = HRe[j + j * ldh], aI = HIm[j + j * ldh];
1826
+ const bR2 = HRe[j + 1 + j * ldh], bI2 = HIm[j + 1 + j * ldh];
1827
+ const {
1828
+ c,
1829
+ sRe: sR,
1830
+ sIm: sI,
1831
+ rRe: rrR,
1832
+ rIm: rrI
1833
+ } = complexGivens(aR, aI, bR2, bI2);
1834
+ cs[j] = c;
1835
+ snRe[j] = sR;
1836
+ snIm[j] = sI;
1837
+ HRe[j + j * ldh] = rrR;
1838
+ HIm[j + j * ldh] = rrI;
1839
+ HRe[j + 1 + j * ldh] = 0;
1840
+ HIm[j + 1 + j * ldh] = 0;
1841
+ const gjR = gRe[j], gjI = gIm[j];
1842
+ const gj1R = gRe[j + 1], gj1I = gIm[j + 1];
1843
+ gRe[j] = c * gjR + (sR * gj1R - sI * gj1I);
1844
+ gIm[j] = c * gjI + (sR * gj1I + sI * gj1R);
1845
+ gRe[j + 1] = -(sR * gjR + sI * gjI) + c * gj1R;
1846
+ gIm[j + 1] = -(-sI * gjR + sR * gjI) + c * gj1I;
1847
+ const residNorm = Math.sqrt(
1848
+ gRe[j + 1] * gRe[j + 1] + gIm[j + 1] * gIm[j + 1]
1849
+ );
1850
+ resvecList.push(residNorm);
1851
+ if (residNorm / normMb <= tol) {
1852
+ const { yRe: yRe2, yIm: yIm2 } = complexBackSolve(HRe, HIm, gRe, gIm, j + 1, ldh);
1853
+ for (let k = 0; k < n; k++) {
1854
+ for (let l = 0; l <= j; l++) {
1855
+ xRe[k] += VRe[k + l * n] * yRe2[l] - VIm[k + l * n] * yIm2[l];
1856
+ xIm[k] += VRe[k + l * n] * yIm2[l] + VIm[k + l * n] * yRe2[l];
1857
+ }
1858
+ }
1859
+ flag = 0;
1860
+ converged = true;
1861
+ break;
1862
+ }
1863
+ }
1864
+ if (converged) break;
1865
+ const { yRe, yIm } = complexBackSolve(HRe, HIm, gRe, gIm, restart, ldh);
1866
+ for (let k = 0; k < n; k++) {
1867
+ for (let l = 0; l < restart; l++) {
1868
+ xRe[k] += VRe[k + l * n] * yRe[l] - VIm[k + l * n] * yIm[l];
1869
+ xIm[k] += VRe[k + l * n] * yIm[l] + VIm[k + l * n] * yRe[l];
1870
+ }
1871
+ }
1872
+ const ax2 = matvec({ re: xRe, im: xIm });
1873
+ rRe = new Float64Array(n);
1874
+ rIm = new Float64Array(n);
1875
+ for (let i = 0; i < n; i++) {
1876
+ rRe[i] = b.re[i] - ax2.re[i];
1877
+ rIm[i] = b.im[i] - ax2.im[i];
1878
+ }
1879
+ if (precSolve) {
1880
+ const pr = precSolve({ re: rRe, im: rIm });
1881
+ rRe = new Float64Array(pr.re);
1882
+ rIm = new Float64Array(pr.im);
1883
+ }
1884
+ beta = cnrm2(rRe, rIm, n);
1885
+ if (beta / normMb <= tol) {
1886
+ flag = 0;
1887
+ innerIter = 0;
1888
+ break;
1889
+ }
1890
+ }
1891
+ let relres;
1892
+ if (flag === 0) {
1893
+ const ax3 = matvec({ re: xRe, im: xIm });
1894
+ let fRe = new Float64Array(n), fIm = new Float64Array(n);
1895
+ for (let i = 0; i < n; i++) {
1896
+ fRe[i] = b.re[i] - ax3.re[i];
1897
+ fIm[i] = b.im[i] - ax3.im[i];
1898
+ }
1899
+ if (precSolve) {
1900
+ const pr = precSolve({ re: fRe, im: fIm });
1901
+ fRe = new Float64Array(pr.re);
1902
+ fIm = new Float64Array(pr.im);
1903
+ }
1904
+ relres = cnrm2(fRe, fIm, n) / normMb;
1905
+ } else {
1906
+ relres = beta / normMb;
1907
+ }
1908
+ return {
1909
+ x: { re: xRe, im: xIm },
1910
+ flag,
1911
+ relres,
1912
+ iter: [outerIter, innerIter],
1913
+ resvec: new Float64Array(resvecList)
1914
+ };
1915
+ }
1916
+ function cnrm2(re2, im2, n) {
1917
+ let s = 0;
1918
+ for (let i = 0; i < n; i++) s += re2[i] * re2[i] + im2[i] * im2[i];
1919
+ return Math.sqrt(s);
1920
+ }
1921
+ function complexGivens(aRe, aIm, bRe, bIm) {
1922
+ const absB = Math.sqrt(bRe * bRe + bIm * bIm);
1923
+ if (absB === 0) return { c: 1, sRe: 0, sIm: 0, rRe: aRe, rIm: aIm };
1924
+ const absA = Math.sqrt(aRe * aRe + aIm * aIm);
1925
+ if (absA === 0)
1926
+ return { c: 0, sRe: bRe / absB, sIm: -bIm / absB, rRe: absB, rIm: 0 };
1927
+ const norm = Math.sqrt(absA * absA + absB * absB);
1928
+ const c = absA / norm;
1929
+ const alpRe = aRe / absA, alpIm = aIm / absA;
1930
+ const sRe2 = (alpRe * bRe + alpIm * bIm) / norm;
1931
+ const sIm2 = (alpIm * bRe - alpRe * bIm) / norm;
1932
+ return { c, sRe: sRe2, sIm: sIm2, rRe: alpRe * norm, rIm: alpIm * norm };
1933
+ }
1934
+ function complexBackSolve(HRe, HIm, gRe, gIm, m, ldh) {
1935
+ const yRe = new Float64Array(m);
1936
+ const yIm = new Float64Array(m);
1937
+ for (let i = 0; i < m; i++) {
1938
+ yRe[i] = gRe[i];
1939
+ yIm[i] = gIm[i];
1940
+ }
1941
+ for (let i = m - 1; i >= 0; i--) {
1942
+ for (let j = i + 1; j < m; j++) {
1943
+ const hR = HRe[i + j * ldh], hI = HIm[i + j * ldh];
1944
+ yRe[i] -= hR * yRe[j] - hI * yIm[j];
1945
+ yIm[i] -= hR * yIm[j] + hI * yRe[j];
1946
+ }
1947
+ const dR = HRe[i + i * ldh], dI = HIm[i + i * ldh];
1948
+ const dAbs2 = dR * dR + dI * dI;
1949
+ const tmpR = yRe[i], tmpI = yIm[i];
1950
+ yRe[i] = (tmpR * dR + tmpI * dI) / dAbs2;
1951
+ yIm[i] = (tmpI * dR - tmpR * dI) / dAbs2;
1952
+ }
1953
+ return { yRe, yIm };
1954
+ }
1955
+ function complexLuSolveInPlace(n, LURe, LUIm, ipiv, rhsRe, rhsIm) {
1956
+ for (let i = 0; i < n; i++) {
1957
+ const pi = ipiv[i] - 1;
1958
+ if (pi !== i) {
1959
+ let tmp = rhsRe[i];
1960
+ rhsRe[i] = rhsRe[pi];
1961
+ rhsRe[pi] = tmp;
1962
+ tmp = rhsIm[i];
1963
+ rhsIm[i] = rhsIm[pi];
1964
+ rhsIm[pi] = tmp;
1965
+ }
1966
+ }
1967
+ for (let i = 1; i < n; i++) {
1968
+ for (let j = 0; j < i; j++) {
1969
+ const lR = LURe[i + j * n], lI = LUIm[i + j * n];
1970
+ rhsRe[i] -= lR * rhsRe[j] - lI * rhsIm[j];
1971
+ rhsIm[i] -= lR * rhsIm[j] + lI * rhsRe[j];
1972
+ }
1973
+ }
1974
+ for (let i = n - 1; i >= 0; i--) {
1975
+ for (let j = i + 1; j < n; j++) {
1976
+ const uR = LURe[i + j * n], uI = LUIm[i + j * n];
1977
+ rhsRe[i] -= uR * rhsRe[j] - uI * rhsIm[j];
1978
+ rhsIm[i] -= uR * rhsIm[j] + uI * rhsRe[j];
1979
+ }
1980
+ const dR = LURe[i + i * n], dI = LUIm[i + i * n];
1981
+ const dAbs2 = dR * dR + dI * dI;
1982
+ const tmpR = rhsRe[i], tmpI = rhsIm[i];
1983
+ rhsRe[i] = (tmpR * dR + tmpI * dI) / dAbs2;
1984
+ rhsIm[i] = (tmpI * dR - tmpR * dI) / dAbs2;
1985
+ }
1986
+ }
1987
+ function luSolveInPlace(n, LU, ipiv, rhs) {
1988
+ for (let i = 0; i < n; i++) {
1989
+ const pi = ipiv[i] - 1;
1990
+ if (pi !== i) {
1991
+ const tmp = rhs[i];
1992
+ rhs[i] = rhs[pi];
1993
+ rhs[pi] = tmp;
1994
+ }
1995
+ }
1996
+ for (let i = 1; i < n; i++) {
1997
+ for (let j = 0; j < i; j++) rhs[i] -= LU[i + j * n] * rhs[j];
1998
+ }
1999
+ for (let i = n - 1; i >= 0; i--) {
2000
+ for (let j = i + 1; j < n; j++) rhs[i] -= LU[i + j * n] * rhs[j];
2001
+ rhs[i] /= LU[i + i * n];
2002
+ }
2003
+ }
2004
+
1580
2005
  // src/ts-lapack/src/BLAS/dgemv.ts
1581
2006
  function dgemv(trans, m, n, alpha, a, aOff, lda, x, xOff, incx, beta, y, yOff, incy) {
1582
2007
  const notrans = trans === NOTRANS;
@@ -17138,6 +17563,160 @@ function cholComplex(dataRe, dataIm, n, upper) {
17138
17563
  }
17139
17564
  return { RRe: re2, RIm: im2, info };
17140
17565
  }
17566
+ function gmres(A, n, b, restart, tol, maxit, M1, M2, x0) {
17567
+ const matvecFn = (x) => {
17568
+ const y = new Float64Array(n);
17569
+ for (let i = 0; i < n; i++) {
17570
+ let s = 0;
17571
+ for (let j = 0; j < n; j++) s += A[i + j * n] * x[j];
17572
+ y[i] = s;
17573
+ }
17574
+ return y;
17575
+ };
17576
+ let precSolveFn = null;
17577
+ if (M1 || M2) {
17578
+ const m1lu = M1 ? new Float64Array(M1) : null;
17579
+ const m1ipiv = M1 ? new Int32Array(n) : null;
17580
+ if (m1lu && m1ipiv) {
17581
+ const info = dgetrf(n, n, m1lu, n, m1ipiv);
17582
+ if (info > 0) throw new Error("gmres: preconditioner M1 is singular");
17583
+ }
17584
+ const m2lu = M2 ? new Float64Array(M2) : null;
17585
+ const m2ipiv = M2 ? new Int32Array(n) : null;
17586
+ if (m2lu && m2ipiv) {
17587
+ const info = dgetrf(n, n, m2lu, n, m2ipiv);
17588
+ if (info > 0) throw new Error("gmres: preconditioner M2 is singular");
17589
+ }
17590
+ precSolveFn = (r) => {
17591
+ const z = new Float64Array(r);
17592
+ if (m1lu && m1ipiv) luSolveInPlace(n, m1lu, m1ipiv, z);
17593
+ if (m2lu && m2ipiv) luSolveInPlace(n, m2lu, m2ipiv, z);
17594
+ return z;
17595
+ };
17596
+ }
17597
+ const result = gmresCore(
17598
+ matvecFn,
17599
+ precSolveFn,
17600
+ b,
17601
+ n,
17602
+ restart,
17603
+ tol,
17604
+ maxit,
17605
+ x0
17606
+ );
17607
+ const iterArr = new Int32Array(2);
17608
+ iterArr[0] = result.iter[0];
17609
+ iterArr[1] = result.iter[1];
17610
+ return {
17611
+ x: result.x,
17612
+ flag: result.flag,
17613
+ relres: result.relres,
17614
+ iter: iterArr,
17615
+ resvec: result.resvec
17616
+ };
17617
+ }
17618
+ function gmresComplex(ARe, AIm, n, bRe, bIm, restart, tol, maxit, M1Re, M1Im, M2Re, M2Im, x0Re, x0Im) {
17619
+ const matvecFn = (x) => {
17620
+ const yRe = new Float64Array(n);
17621
+ const yIm = new Float64Array(n);
17622
+ for (let i = 0; i < n; i++) {
17623
+ let sRe2 = 0, sIm2 = 0;
17624
+ for (let j = 0; j < n; j++) {
17625
+ const aR = ARe[i + j * n], aI = AIm[i + j * n];
17626
+ sRe2 += aR * x.re[j] - aI * x.im[j];
17627
+ sIm2 += aR * x.im[j] + aI * x.re[j];
17628
+ }
17629
+ yRe[i] = sRe2;
17630
+ yIm[i] = sIm2;
17631
+ }
17632
+ return { re: yRe, im: yIm };
17633
+ };
17634
+ let precSolveFn = null;
17635
+ if (M1Re || M2Re) {
17636
+ const m1luRe = M1Re ? new Float64Array(M1Re) : null;
17637
+ const m1luIm = M1Im ? new Float64Array(M1Im) : null;
17638
+ const m1ipiv = M1Re ? new Int32Array(n) : null;
17639
+ if (m1luRe && m1luIm && m1ipiv) {
17640
+ complexLuFactor(n, m1luRe, m1luIm, m1ipiv);
17641
+ }
17642
+ const m2luRe = M2Re ? new Float64Array(M2Re) : null;
17643
+ const m2luIm = M2Im ? new Float64Array(M2Im) : null;
17644
+ const m2ipiv = M2Re ? new Int32Array(n) : null;
17645
+ if (m2luRe && m2luIm && m2ipiv) {
17646
+ complexLuFactor(n, m2luRe, m2luIm, m2ipiv);
17647
+ }
17648
+ precSolveFn = (r) => {
17649
+ const zRe = new Float64Array(r.re);
17650
+ const zIm = new Float64Array(r.im);
17651
+ if (m1luRe && m1luIm && m1ipiv)
17652
+ complexLuSolveInPlace(n, m1luRe, m1luIm, m1ipiv, zRe, zIm);
17653
+ if (m2luRe && m2luIm && m2ipiv)
17654
+ complexLuSolveInPlace(n, m2luRe, m2luIm, m2ipiv, zRe, zIm);
17655
+ return { re: zRe, im: zIm };
17656
+ };
17657
+ }
17658
+ const result = gmresCoreComplex(
17659
+ matvecFn,
17660
+ precSolveFn,
17661
+ { re: bRe, im: bIm },
17662
+ n,
17663
+ restart,
17664
+ tol,
17665
+ maxit,
17666
+ x0Re && x0Im ? { re: x0Re, im: x0Im } : null
17667
+ );
17668
+ const iterArr = new Int32Array(2);
17669
+ iterArr[0] = result.iter[0];
17670
+ iterArr[1] = result.iter[1];
17671
+ return {
17672
+ xRe: result.x.re,
17673
+ xIm: result.x.im,
17674
+ flag: result.flag,
17675
+ relres: result.relres,
17676
+ iter: iterArr,
17677
+ resvec: result.resvec
17678
+ };
17679
+ }
17680
+ function complexLuFactor(n, re2, im2, ipiv) {
17681
+ for (let k = 0; k < n; k++) {
17682
+ let maxVal = -1;
17683
+ let maxIdx = k;
17684
+ for (let i = k; i < n; i++) {
17685
+ const v = Math.sqrt(
17686
+ re2[i + k * n] * re2[i + k * n] + im2[i + k * n] * im2[i + k * n]
17687
+ );
17688
+ if (v > maxVal) {
17689
+ maxVal = v;
17690
+ maxIdx = i;
17691
+ }
17692
+ }
17693
+ ipiv[k] = maxIdx + 1;
17694
+ if (maxIdx !== k) {
17695
+ for (let j = 0; j < n; j++) {
17696
+ let tmp = re2[k + j * n];
17697
+ re2[k + j * n] = re2[maxIdx + j * n];
17698
+ re2[maxIdx + j * n] = tmp;
17699
+ tmp = im2[k + j * n];
17700
+ im2[k + j * n] = im2[maxIdx + j * n];
17701
+ im2[maxIdx + j * n] = tmp;
17702
+ }
17703
+ }
17704
+ const dR = re2[k + k * n], dI = im2[k + k * n];
17705
+ const dAbs2 = dR * dR + dI * dI;
17706
+ if (dAbs2 === 0) continue;
17707
+ for (let i = k + 1; i < n; i++) {
17708
+ const aR = re2[i + k * n], aI = im2[i + k * n];
17709
+ const lR = (aR * dR + aI * dI) / dAbs2;
17710
+ const lI = (aI * dR - aR * dI) / dAbs2;
17711
+ re2[i + k * n] = lR;
17712
+ im2[i + k * n] = lI;
17713
+ for (let j = k + 1; j < n; j++) {
17714
+ re2[i + j * n] -= lR * re2[k + j * n] - lI * im2[k + j * n];
17715
+ im2[i + j * n] -= lR * im2[k + j * n] + lI * re2[k + j * n];
17716
+ }
17717
+ }
17718
+ }
17719
+ }
17141
17720
  var _bridge2 = {
17142
17721
  inv,
17143
17722
  matmul,
@@ -17148,7 +17727,9 @@ var _bridge2 = {
17148
17727
  lu,
17149
17728
  svd,
17150
17729
  chol,
17151
- cholComplex
17730
+ cholComplex,
17731
+ gmres,
17732
+ gmresComplex
17152
17733
  };
17153
17734
  function getTsLapackBridge() {
17154
17735
  return _bridge2;
@@ -18115,9 +18696,6 @@ function registerDynamicIBuiltin(b) {
18115
18696
  registry.set(b.name, b);
18116
18697
  _onDynamicRegister?.(b);
18117
18698
  }
18118
- function unregisterIBuiltin(name) {
18119
- registry.delete(name);
18120
- }
18121
18699
  function getAllIBuiltinNames() {
18122
18700
  return Array.from(registry.keys());
18123
18701
  }
@@ -22025,11 +22603,27 @@ function catAlongDim(values, dimIdx) {
22025
22603
  if (tensors.length === 0) return RTV.tensor(new FloatXArray(0), [0, 0]);
22026
22604
  if (tensors.length === 1) return tensors[0];
22027
22605
  const ndim = Math.max(2, ...tensors.map((t) => t.shape.length));
22028
- const shapes = tensors.map((t) => {
22606
+ let shapes = tensors.map((t) => {
22029
22607
  const s = [...t.shape];
22030
22608
  while (s.length < ndim) s.push(1);
22031
22609
  return s;
22032
22610
  });
22611
+ const refIdx = tensors.findIndex((t) => t.data.length > 0);
22612
+ if (refIdx >= 0) {
22613
+ const ref = shapes[refIdx];
22614
+ const keep = tensors.map((t, i) => {
22615
+ if (t.data.length > 0) return true;
22616
+ for (let d = 0; d < ndim; d++) {
22617
+ if (d === dimIdx) continue;
22618
+ if (shapes[i][d] !== ref[d]) return false;
22619
+ }
22620
+ return true;
22621
+ });
22622
+ tensors = tensors.filter((_, i) => keep[i]);
22623
+ shapes = shapes.filter((_, i) => keep[i]);
22624
+ if (tensors.length === 0) return RTV.tensor(new FloatXArray(0), [0, 0]);
22625
+ if (tensors.length === 1) return tensors[0];
22626
+ }
22033
22627
  const refShape = shapes[0];
22034
22628
  for (let i = 1; i < shapes.length; i++) {
22035
22629
  for (let d = 0; d < ndim; d++) {
@@ -28135,7 +28729,7 @@ defineBuiltin({
28135
28729
  },
28136
28730
  apply: (args) => {
28137
28731
  const first = textValue(args[0]) ?? String(args[0]);
28138
- if (args.length >= 2 && first.includes(":")) {
28732
+ if (args.length >= 2 && /^[A-Za-z]\w*(:[A-Za-z]\w*)+$/.test(first)) {
28139
28733
  const msg2 = args.length === 2 ? textValue(args[1]) ?? String(args[1]) : sprintfFormat(
28140
28734
  textValue(args[1]) ?? String(args[1]),
28141
28735
  args.slice(2)
@@ -28173,6 +28767,134 @@ defineBuiltin({
28173
28767
  }
28174
28768
  ]
28175
28769
  });
28770
+ function makeMException(identifier, message) {
28771
+ return RTV.struct(
28772
+ /* @__PURE__ */ new Map([
28773
+ ["identifier", RTV.char(identifier)],
28774
+ ["message", RTV.char(message)],
28775
+ ["cause", RTV.cell([], [0, 0])],
28776
+ ["stack", emptyStackField()]
28777
+ ])
28778
+ );
28779
+ }
28780
+ defineBuiltin({
28781
+ name: "MException",
28782
+ help: {
28783
+ signatures: [
28784
+ "ME = MException(errID, msg)",
28785
+ "ME = MException(errID, msg, A1, ..., An)"
28786
+ ],
28787
+ 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."
28788
+ },
28789
+ cases: [
28790
+ {
28791
+ match: (argTypes) => {
28792
+ if (argTypes.length < 2) return null;
28793
+ const k0 = argTypes[0].kind;
28794
+ const k1 = argTypes[1].kind;
28795
+ if (k0 !== "string" && k0 !== "char") return null;
28796
+ if (k1 !== "string" && k1 !== "char") return null;
28797
+ return [{ kind: "struct", fields: {} }];
28798
+ },
28799
+ apply: (args) => {
28800
+ const errID = textValue(args[0]) ?? String(args[0]);
28801
+ const fmt = textValue(args[1]) ?? String(args[1]);
28802
+ const msg = args.length === 2 ? fmt : sprintfFormat(fmt, args.slice(2));
28803
+ return makeMException(errID, msg);
28804
+ }
28805
+ }
28806
+ ]
28807
+ });
28808
+ function throwException(me) {
28809
+ if (isRuntimeStruct(me)) {
28810
+ const msgVal = me.fields.get("message");
28811
+ const idVal = me.fields.get("identifier");
28812
+ const msg = msgVal ? textValue(msgVal) ?? String(msgVal) : "";
28813
+ const err = new RuntimeError(msg);
28814
+ if (idVal) err.identifier = textValue(idVal) ?? "";
28815
+ throw err;
28816
+ }
28817
+ throw new RuntimeError(String(me));
28818
+ }
28819
+ defineBuiltin({
28820
+ name: "throw",
28821
+ help: {
28822
+ signatures: ["throw(ME)"],
28823
+ description: "Throw an exception. ME is an MException-shaped struct with identifier and message fields."
28824
+ },
28825
+ cases: [
28826
+ {
28827
+ match: (argTypes) => {
28828
+ if (argTypes.length !== 1) return null;
28829
+ return [];
28830
+ },
28831
+ apply: (args) => throwException(args[0])
28832
+ }
28833
+ ]
28834
+ });
28835
+ defineBuiltin({
28836
+ name: "throwAsCaller",
28837
+ help: {
28838
+ signatures: ["throwAsCaller(ME)"],
28839
+ 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."
28840
+ },
28841
+ cases: [
28842
+ {
28843
+ match: (argTypes) => {
28844
+ if (argTypes.length !== 1) return null;
28845
+ return [];
28846
+ },
28847
+ apply: (args) => throwException(args[0])
28848
+ }
28849
+ ]
28850
+ });
28851
+ defineBuiltin({
28852
+ name: "getReport",
28853
+ help: {
28854
+ signatures: ["S = getReport(ME)", "S = getReport(ME, TYPE)"],
28855
+ 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."
28856
+ },
28857
+ cases: [
28858
+ {
28859
+ match: (argTypes) => {
28860
+ if (argTypes.length < 1 || argTypes.length > 4) return null;
28861
+ return [{ kind: "char" }];
28862
+ },
28863
+ apply: (args) => {
28864
+ const me = args[0];
28865
+ if (!isRuntimeStruct(me)) {
28866
+ return RTV.char(String(me));
28867
+ }
28868
+ const msgVal = me.fields.get("message");
28869
+ const message = msgVal ? textValue(msgVal) ?? String(msgVal) : "";
28870
+ let type = "extended";
28871
+ if (args.length >= 2) {
28872
+ const t = textValue(args[1]);
28873
+ if (t === "basic" || t === "extended") type = t;
28874
+ }
28875
+ if (type === "basic") return RTV.char(message);
28876
+ const stackVal = me.fields.get("stack");
28877
+ const lines = [message];
28878
+ const appendFrame = (frame) => {
28879
+ if (!isRuntimeStruct(frame)) return;
28880
+ const nameVal = frame.fields.get("name");
28881
+ const lineVal = frame.fields.get("line");
28882
+ const name = nameVal ? textValue(nameVal) ?? "" : "";
28883
+ const lineNum = typeof lineVal === "number" ? lineVal : 0;
28884
+ if (name) lines.push(`Error in ${name} (line ${lineNum})`);
28885
+ };
28886
+ if (stackVal) {
28887
+ if (isRuntimeStructArray(stackVal)) {
28888
+ for (const el of stackVal.elements) appendFrame(el);
28889
+ } else if (isRuntimeStruct(stackVal)) {
28890
+ appendFrame(stackVal);
28891
+ }
28892
+ }
28893
+ return RTV.char(lines.join("\n"));
28894
+ }
28895
+ }
28896
+ ]
28897
+ });
28176
28898
 
28177
28899
  // src/numbl-core/interpreter/builtins/introspection.ts
28178
28900
  function anyToLogicalCase(applyFn) {
@@ -30057,6 +30779,150 @@ registerIBuiltin({
30057
30779
  };
30058
30780
  }
30059
30781
  });
30782
+ var RE_ALPHA = /\p{L}/u;
30783
+ var RE_ALPHANUM = /[\p{L}\p{N}]/u;
30784
+ var RE_CNTRL = /\p{Cc}/u;
30785
+ var RE_GRAPHIC = /[\p{L}\p{N}\p{P}\p{S}\p{M}]/u;
30786
+ var RE_LOWER = /\p{Ll}/u;
30787
+ var RE_PUNCT = /\p{P}/u;
30788
+ var RE_UPPER = /\p{Lu}/u;
30789
+ var RE_WSPACE = /\s/u;
30790
+ function isstrpropPredicate(category) {
30791
+ switch (category) {
30792
+ case "alpha":
30793
+ return (cp) => RE_ALPHA.test(String.fromCodePoint(cp));
30794
+ case "alphanum":
30795
+ return (cp) => RE_ALPHANUM.test(String.fromCodePoint(cp));
30796
+ case "cntrl":
30797
+ return (cp) => RE_CNTRL.test(String.fromCodePoint(cp));
30798
+ case "digit":
30799
+ return (cp) => cp >= 48 && cp <= 57;
30800
+ case "graphic":
30801
+ return (cp) => RE_GRAPHIC.test(String.fromCodePoint(cp));
30802
+ case "lower":
30803
+ return (cp) => RE_LOWER.test(String.fromCodePoint(cp));
30804
+ case "print":
30805
+ return (cp) => cp === 32 || RE_GRAPHIC.test(String.fromCodePoint(cp));
30806
+ case "punct":
30807
+ return (cp) => RE_PUNCT.test(String.fromCodePoint(cp));
30808
+ case "wspace":
30809
+ return (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
30810
+ case "upper":
30811
+ return (cp) => RE_UPPER.test(String.fromCodePoint(cp));
30812
+ case "xdigit":
30813
+ return (cp) => cp >= 48 && cp <= 57 || cp >= 65 && cp <= 70 || cp >= 97 && cp <= 102;
30814
+ default:
30815
+ return null;
30816
+ }
30817
+ }
30818
+ function stringCodePoints(s) {
30819
+ const out = [];
30820
+ for (const ch of s) out.push(ch.codePointAt(0));
30821
+ return out;
30822
+ }
30823
+ function logicalRowFromString(s, pred, shape) {
30824
+ const cps = stringCodePoints(s);
30825
+ const data = new FloatXArray(cps.length);
30826
+ for (let i = 0; i < cps.length; i++) data[i] = pred(cps[i]) ? 1 : 0;
30827
+ return {
30828
+ kind: "tensor",
30829
+ data,
30830
+ shape: shape ?? [1, cps.length],
30831
+ _isLogical: true,
30832
+ _rc: 1
30833
+ };
30834
+ }
30835
+ function logicalFromNumericTensor(data, shape, pred) {
30836
+ const out = new FloatXArray(data.length);
30837
+ for (let i = 0; i < data.length; i++) {
30838
+ out[i] = pred(Math.round(data[i])) ? 1 : 0;
30839
+ }
30840
+ return {
30841
+ kind: "tensor",
30842
+ data: out,
30843
+ shape: [...shape],
30844
+ _isLogical: true,
30845
+ _rc: 1
30846
+ };
30847
+ }
30848
+ function isstrpropApply(args) {
30849
+ const v = args[0];
30850
+ const category = toString(args[1]);
30851
+ const pred = isstrpropPredicate(category);
30852
+ if (!pred) {
30853
+ throw new RuntimeError(`isstrprop: unknown category '${category}'`);
30854
+ }
30855
+ let forceCell = false;
30856
+ if (args.length >= 3) {
30857
+ const flagName = toString(args[2]).toLowerCase();
30858
+ if (flagName !== "forcecelloutput") {
30859
+ throw new RuntimeError(`isstrprop: unknown name '${toString(args[2])}'`);
30860
+ }
30861
+ if (args.length < 4) {
30862
+ throw new RuntimeError("isstrprop: 'ForceCellOutput' requires a value");
30863
+ }
30864
+ forceCell = toNumber(args[3]) !== 0;
30865
+ }
30866
+ if (isRuntimeCell(v)) {
30867
+ const out = new Array(v.data.length);
30868
+ for (let i = 0; i < v.data.length; i++) {
30869
+ const elem = v.data[i];
30870
+ const s = isText(elem) ? toString(elem) : "";
30871
+ out[i] = logicalRowFromString(s, pred);
30872
+ }
30873
+ return RTV.cell(out, [...v.shape]);
30874
+ }
30875
+ let result;
30876
+ if (isRuntimeString(v)) {
30877
+ result = logicalRowFromString(toString(v), pred);
30878
+ } else if (isRuntimeChar(v)) {
30879
+ const c = v;
30880
+ result = logicalRowFromString(
30881
+ c.value,
30882
+ pred,
30883
+ c.shape ? [...c.shape] : void 0
30884
+ );
30885
+ } else if (isRuntimeTensor(v)) {
30886
+ result = logicalFromNumericTensor(v.data, v.shape, pred);
30887
+ } else if (isRuntimeNumber(v) || isRuntimeLogical(v)) {
30888
+ const cp = Math.round(toNumber(v));
30889
+ const data = new FloatXArray(1);
30890
+ data[0] = pred(cp) ? 1 : 0;
30891
+ result = { kind: "tensor", data, shape: [1, 1], _isLogical: true, _rc: 1 };
30892
+ } else {
30893
+ throw new RuntimeError("isstrprop: unsupported input type");
30894
+ }
30895
+ if (forceCell) return RTV.cell([result], [1, 1]);
30896
+ return result;
30897
+ }
30898
+ registerIBuiltin({
30899
+ name: "isstrprop",
30900
+ help: {
30901
+ signatures: [
30902
+ "TF = isstrprop(str, category)",
30903
+ "TF = isstrprop(str, category, 'ForceCellOutput', tf)"
30904
+ ],
30905
+ 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."
30906
+ },
30907
+ resolve: (argTypes) => {
30908
+ if (argTypes.length < 2 || argTypes.length > 4) return null;
30909
+ if (!isTextType(argTypes[1])) return null;
30910
+ if (argTypes.length >= 3 && !isTextType(argTypes[2])) return null;
30911
+ if (argTypes.length === 4) {
30912
+ const k = argTypes[3].kind;
30913
+ if (k !== "number" && k !== "boolean") return null;
30914
+ }
30915
+ const t0 = argTypes[0];
30916
+ if (t0.kind !== "char" && t0.kind !== "string" && t0.kind !== "number" && t0.kind !== "boolean" && t0.kind !== "tensor" && t0.kind !== "cell") {
30917
+ return null;
30918
+ }
30919
+ const isCell = t0.kind === "cell";
30920
+ return {
30921
+ outputTypes: isCell ? [{ kind: "cell" }] : [{ kind: "tensor", isComplex: false, isLogical: true }],
30922
+ apply: (args) => isstrpropApply(args)
30923
+ };
30924
+ }
30925
+ });
30060
30926
  registerIBuiltin({
30061
30927
  name: "contains",
30062
30928
  resolve: (argTypes) => {
@@ -31029,32 +31895,31 @@ defineBuiltin({
31029
31895
  }
31030
31896
  ]
31031
31897
  });
31032
- defineBuiltin({
31033
- name: "ismember",
31034
- cases: [
31035
- {
31036
- match: (argTypes, nargout) => {
31037
- if (argTypes.length < 2) return null;
31038
- const out = [{ kind: "unknown" }];
31039
- if (nargout > 1) out.push({ kind: "unknown" });
31040
- return out;
31041
- },
31042
- apply: (args, nargout) => {
31043
- if (args.length < 2)
31044
- throw new RuntimeError("ismember requires 2 arguments");
31045
- const v = args[0];
31046
- const b = args[1];
31047
- const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
31048
- const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every(
31049
- (e) => isRuntimeString(e) || isRuntimeChar(e)
31050
- );
31051
- if (isStringLike(v) || isCellOfStrings(v) || isStringLike(b) || isCellOfStrings(b))
31052
- return ismemberStrings(v, b, nargout);
31053
- return ismemberNumeric(v, b, nargout);
31054
- }
31898
+ var ismemberCases = [
31899
+ {
31900
+ match: (argTypes, nargout) => {
31901
+ if (argTypes.length < 2) return null;
31902
+ const out = [{ kind: "unknown" }];
31903
+ if (nargout > 1) out.push({ kind: "unknown" });
31904
+ return out;
31905
+ },
31906
+ apply: (args, nargout) => {
31907
+ if (args.length < 2)
31908
+ throw new RuntimeError("ismember requires 2 arguments");
31909
+ const v = args[0];
31910
+ const b = args[1];
31911
+ const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
31912
+ const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every(
31913
+ (e) => isRuntimeString(e) || isRuntimeChar(e)
31914
+ );
31915
+ if (isStringLike(v) || isCellOfStrings(v) || isStringLike(b) || isCellOfStrings(b))
31916
+ return ismemberStrings(v, b, nargout);
31917
+ return ismemberNumeric(v, b, nargout);
31055
31918
  }
31056
- ]
31057
- });
31919
+ }
31920
+ ];
31921
+ defineBuiltin({ name: "ismember", cases: ismemberCases });
31922
+ defineBuiltin({ name: "ismembc", cases: ismemberCases });
31058
31923
  function ismemberStrings(v, b, nargout) {
31059
31924
  const isStringLike = (x) => isRuntimeString(x) || isRuntimeChar(x);
31060
31925
  const isCellOfStrings = (x) => isRuntimeCell(x) && x.data.every((e) => isRuntimeString(e) || isRuntimeChar(e));
@@ -35596,11 +36461,11 @@ defineBuiltin({
35596
36461
  }
35597
36462
  } else {
35598
36463
  const p2 = n / 2;
35599
- const sub = new Array(p2 * p2).fill(0);
36464
+ const sub2 = new Array(p2 * p2).fill(0);
35600
36465
  const sset = (r, c, v) => {
35601
- sub[c * p2 + r] = v;
36466
+ sub2[c * p2 + r] = v;
35602
36467
  };
35603
- const sget = (r, c) => sub[c * p2 + r];
36468
+ const sget = (r, c) => sub2[c * p2 + r];
35604
36469
  let si = 0;
35605
36470
  let sj = Math.floor(p2 / 2);
35606
36471
  for (let k2 = 1; k2 <= p2 * p2; k2++) {
@@ -38464,13 +39329,7 @@ registerIBuiltin({
38464
39329
  })
38465
39330
  });
38466
39331
  function getMexExt() {
38467
- if (typeof process === "undefined") return "";
38468
- const platform = process.platform;
38469
- const cpuArch = process.arch;
38470
- if (platform === "win32") return "mexw64";
38471
- if (platform === "darwin")
38472
- return cpuArch === "arm64" ? "mexmaca64" : "mexmaci64";
38473
- return "mexa64";
39332
+ return "numbl.js";
38474
39333
  }
38475
39334
  defineBuiltin({
38476
39335
  name: "mexext",
@@ -39045,6 +39904,88 @@ for (const [name, fn, scaleFn] of besselDefs) {
39045
39904
  })
39046
39905
  });
39047
39906
  }
39907
+ function besselhScalar(nu, k, z, scale) {
39908
+ const J = besselj(nu, z);
39909
+ const Y = bessely(nu, z);
39910
+ let re2 = J;
39911
+ let im2 = k === 1 ? Y : -Y;
39912
+ if (scale) {
39913
+ const c = Math.cos(z);
39914
+ const s = Math.sin(z);
39915
+ if (k === 1) {
39916
+ const newRe = re2 * c + im2 * s;
39917
+ const newIm = im2 * c - re2 * s;
39918
+ re2 = newRe;
39919
+ im2 = newIm;
39920
+ } else {
39921
+ const newRe = re2 * c - im2 * s;
39922
+ const newIm = re2 * s + im2 * c;
39923
+ re2 = newRe;
39924
+ im2 = newIm;
39925
+ }
39926
+ }
39927
+ return { re: re2, im: im2 };
39928
+ }
39929
+ function binaryApplyComplex(a, b, fn, name) {
39930
+ const aIsT = isRuntimeTensor(a);
39931
+ const bIsT = isRuntimeTensor(b);
39932
+ const buildTensor = (len, shape, iter) => {
39933
+ const re2 = new FloatXArray(len);
39934
+ const im2 = new FloatXArray(len);
39935
+ let hasImag = false;
39936
+ for (let i = 0; i < len; i++) {
39937
+ const r2 = iter(i);
39938
+ re2[i] = r2.re;
39939
+ im2[i] = r2.im;
39940
+ if (r2.im !== 0) hasImag = true;
39941
+ }
39942
+ return RTV.tensor(re2, shape, hasImag ? im2 : void 0);
39943
+ };
39944
+ if (aIsT && bIsT) {
39945
+ if (a.data.length !== b.data.length)
39946
+ throw new RuntimeError(`${name}: array dimensions must agree`);
39947
+ return buildTensor(a.data.length, a.shape, (i) => fn(a.data[i], b.data[i]));
39948
+ }
39949
+ if (aIsT) {
39950
+ const bv = toNumber(b);
39951
+ return buildTensor(a.data.length, a.shape, (i) => fn(a.data[i], bv));
39952
+ }
39953
+ if (bIsT) {
39954
+ const av = toNumber(a);
39955
+ return buildTensor(b.data.length, b.shape, (i) => fn(av, b.data[i]));
39956
+ }
39957
+ const r = fn(toNumber(a), toNumber(b));
39958
+ return r.im === 0 ? RTV.num(r.re) : RTV.complex(r.re, r.im);
39959
+ }
39960
+ registerIBuiltin({
39961
+ name: "besselh",
39962
+ resolve: () => ({
39963
+ outputTypes: [{ kind: "unknown" }],
39964
+ apply: (args) => {
39965
+ if (args.length < 2 || args.length > 4)
39966
+ throw new RuntimeError("besselh requires 2 to 4 arguments");
39967
+ const nuArg = args[0];
39968
+ let k = 1;
39969
+ let zArg;
39970
+ let scale = false;
39971
+ if (args.length === 2) {
39972
+ zArg = args[1];
39973
+ } else {
39974
+ k = Math.round(toNumber(args[1]));
39975
+ if (k !== 1 && k !== 2)
39976
+ throw new RuntimeError("besselh: K must be 1 or 2");
39977
+ zArg = args[2];
39978
+ if (args.length === 4) scale = toNumber(args[3]) === 1;
39979
+ }
39980
+ return binaryApplyComplex(
39981
+ nuArg,
39982
+ zArg,
39983
+ (nu, z) => besselhScalar(nu, k, z, scale),
39984
+ "besselh"
39985
+ );
39986
+ }
39987
+ })
39988
+ });
39048
39989
  var airyFns = [airyAi, airyAiPrime, airyBi, airyBiPrime];
39049
39990
  var airyComplexKeys = ["ai", "aip", "bi", "bip"];
39050
39991
  function scaleAiry(n, x, val) {
@@ -39640,7 +40581,7 @@ registerIBuiltin({
39640
40581
  };
39641
40582
  }
39642
40583
  });
39643
- for (const name of ["groot", "gcf", "gca", "shg", "newplot", "caxis"]) {
40584
+ for (const name of ["groot", "gcf", "gca", "shg", "newplot"]) {
39644
40585
  registerIBuiltin({
39645
40586
  name,
39646
40587
  resolve: () => ({
@@ -39653,7 +40594,32 @@ registerIBuiltin({
39653
40594
  name: "get",
39654
40595
  resolve: () => ({
39655
40596
  outputTypes: [{ kind: "unknown" }],
39656
- apply: () => RTV.dummyHandle()
40597
+ apply: (args) => {
40598
+ if (args.length >= 2) {
40599
+ const prop = args[1];
40600
+ const propStr = isRuntimeChar(prop) ? prop.value : isRuntimeString(prop) ? prop : "";
40601
+ if (propStr.toLowerCase() === "colormap") {
40602
+ const n = 64;
40603
+ const data = new Float64Array(n * 3);
40604
+ for (let i = 0; i < n; i++) {
40605
+ const t = i / (n - 1);
40606
+ if (t < 0.5) {
40607
+ const s = t * 2;
40608
+ data[i] = 0.2 * (1 - s);
40609
+ data[n + i] = 0.1 + 0.6 * s;
40610
+ data[2 * n + i] = 0.9 - 0.3 * s;
40611
+ } else {
40612
+ const s = (t - 0.5) * 2;
40613
+ data[i] = 0.2 + 0.8 * s;
40614
+ data[n + i] = 0.7 + 0.3 * s;
40615
+ data[2 * n + i] = 0.6 - 0.5 * s;
40616
+ }
40617
+ }
40618
+ return RTV.tensor(data, [n, 3]);
40619
+ }
40620
+ }
40621
+ return RTV.dummyHandle();
40622
+ }
39657
40623
  })
39658
40624
  });
39659
40625
  registerIBuiltin({
@@ -41440,6 +42406,14 @@ var H = {
41440
42406
  signatures: ["Y = besselk(NU, Z)", "Y = besselk(NU, Z, SCALE)"],
41441
42407
  description: "Modified Bessel function of the second kind. SCALE=1 applies exponential scaling."
41442
42408
  },
42409
+ besselh: {
42410
+ signatures: [
42411
+ "H = besselh(NU, Z)",
42412
+ "H = besselh(NU, K, Z)",
42413
+ "H = besselh(NU, K, Z, SCALE)"
42414
+ ],
42415
+ 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."
42416
+ },
41443
42417
  airy: {
41444
42418
  signatures: ["Y = airy(X)", "Y = airy(K, X)", "Y = airy(K, X, SCALE)"],
41445
42419
  description: "Airy functions. K selects the function: 0=Ai (default), 1=Ai', 2=Bi, 3=Bi'. SCALE=1 applies exponential scaling."
@@ -41490,6 +42464,15 @@ var H = {
41490
42464
  signatures: ["TF = isequal(A, B, ...)"],
41491
42465
  description: "True if all inputs are equal (NaN ~= NaN)."
41492
42466
  },
42467
+ // ── Dynamic evaluation ────────────────────────────────────────────────
42468
+ evalin: {
42469
+ signatures: ["V = evalin(WS, EXPR)", "V = evalin(WS, EXPR, DEFAULT)"],
42470
+ 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."
42471
+ },
42472
+ assignin: {
42473
+ signatures: ["assignin(WS, NAME, VALUE)"],
42474
+ 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."
42475
+ },
41493
42476
  // ── Misc ──────────────────────────────────────────────────────────────
41494
42477
  disp: {
41495
42478
  signatures: ["disp(X)"],
@@ -42689,6 +43672,84 @@ function expandXY(xVal, yVal, rows, cols) {
42689
43672
  }
42690
43673
  return { x, y };
42691
43674
  }
43675
+ var PCOLOR_NAME_VALUE_KEYS = /* @__PURE__ */ new Set(["edgecolor", "facealpha"]);
43676
+ function isPcolorNameValueKey(v) {
43677
+ if (!isRuntimeString(v) && !isRuntimeChar(v)) return null;
43678
+ const lower = isRuntimeString(v) ? v.toLocaleLowerCase() : v.value.toLowerCase();
43679
+ if (PCOLOR_NAME_VALUE_KEYS.has(lower)) return lower;
43680
+ return null;
43681
+ }
43682
+ function applyPcolorNameValue(trace, key, value) {
43683
+ switch (key) {
43684
+ case "edgecolor": {
43685
+ const s = getStringValueIfString(value);
43686
+ if (s !== void 0 && s.toLowerCase() === "none") {
43687
+ trace.edgeColor = "none";
43688
+ break;
43689
+ }
43690
+ const c = resolveColor(value);
43691
+ if (c) trace.edgeColor = c;
43692
+ break;
43693
+ }
43694
+ case "facealpha": {
43695
+ const n = typeof value === "number" ? value : toNumber(value);
43696
+ trace.faceAlpha = n;
43697
+ break;
43698
+ }
43699
+ }
43700
+ }
43701
+ function parsePcolorArgs(args) {
43702
+ let pos = 0;
43703
+ let xData;
43704
+ let yData;
43705
+ let cData;
43706
+ let rows;
43707
+ let cols;
43708
+ let numericCount = 0;
43709
+ for (let i = pos; i < args.length; i++) {
43710
+ if (isNumericArg(args[i])) numericCount++;
43711
+ else break;
43712
+ }
43713
+ if (numericCount === 1) {
43714
+ const c = args[pos++];
43715
+ const info = getMatrixInfo(c);
43716
+ rows = info.rows;
43717
+ cols = info.cols;
43718
+ cData = info.data;
43719
+ const gen = generateMeshgrid(rows, cols);
43720
+ xData = gen.x;
43721
+ yData = gen.y;
43722
+ } else if (numericCount >= 3) {
43723
+ const x = args[pos++];
43724
+ const y = args[pos++];
43725
+ const c = args[pos++];
43726
+ const cInfo = getMatrixInfo(c);
43727
+ rows = cInfo.rows;
43728
+ cols = cInfo.cols;
43729
+ cData = cInfo.data;
43730
+ const expanded = expandXY(x, y, rows, cols);
43731
+ xData = expanded.x;
43732
+ yData = expanded.y;
43733
+ } else {
43734
+ throw new Error("pcolor requires 1 or 3 numeric input arguments");
43735
+ }
43736
+ const trace = {
43737
+ x: xData,
43738
+ y: yData,
43739
+ c: cData,
43740
+ rows,
43741
+ cols
43742
+ };
43743
+ while (pos < args.length) {
43744
+ const key = isPcolorNameValueKey(args[pos]);
43745
+ if (!key) break;
43746
+ pos++;
43747
+ if (pos >= args.length) break;
43748
+ const value = args[pos++];
43749
+ applyPcolorNameValue(trace, key, value);
43750
+ }
43751
+ return trace;
43752
+ }
42692
43753
  function applySurfNameValue(trace, key, value) {
42693
43754
  switch (key) {
42694
43755
  case "edgecolor": {
@@ -43724,24 +44785,38 @@ function plotInstr(plotInstructions, instr) {
43724
44785
  value: resolveOnOff(instr.value)
43725
44786
  });
43726
44787
  break;
43727
- case "set_colorbar":
43728
- plotInstructions.push({
44788
+ case "set_colorbar": {
44789
+ const cbInstr = {
43729
44790
  type: "set_colorbar",
43730
44791
  value: resolveStr(instr.value)
43731
- });
44792
+ };
44793
+ if (instr.location !== void 0) {
44794
+ cbInstr.location = resolveStr(instr.location);
44795
+ }
44796
+ plotInstructions.push(cbInstr);
43732
44797
  break;
43733
- case "set_colormap":
43734
- plotInstructions.push({
44798
+ }
44799
+ case "set_colormap": {
44800
+ const cmInstr = {
43735
44801
  type: "set_colormap",
43736
44802
  name: resolveStr(instr.name).replace(/^"|"$/g, "")
43737
- });
44803
+ };
44804
+ if (instr.data) cmInstr.data = instr.data;
44805
+ plotInstructions.push(cmInstr);
43738
44806
  break;
44807
+ }
43739
44808
  case "set_axis":
43740
44809
  plotInstructions.push({
43741
44810
  type: "set_axis",
43742
44811
  value: resolveStr(instr.value).replace(/^"|"$/g, "")
43743
44812
  });
43744
44813
  break;
44814
+ case "set_caxis":
44815
+ plotInstructions.push({
44816
+ type: "set_caxis",
44817
+ limits: instr.limits
44818
+ });
44819
+ break;
43745
44820
  }
43746
44821
  }
43747
44822
  function viewCall(plotInstructions, args) {
@@ -43784,6 +44859,10 @@ function imagescCall(plotInstructions, args) {
43784
44859
  const trace = parseImagescArgs(args);
43785
44860
  plotInstructions.push({ type: "imagesc", trace });
43786
44861
  }
44862
+ function pcolorCall(plotInstructions, args) {
44863
+ const trace = parsePcolorArgs(args);
44864
+ plotInstructions.push({ type: "pcolor", trace });
44865
+ }
43787
44866
  function contourCall(plotInstructions, args, filled) {
43788
44867
  const trace = parseContourArgs(args, filled);
43789
44868
  plotInstructions.push({ type: "contour", trace });
@@ -44546,6 +45625,7 @@ var SPECIAL_BUILTIN_NAMES = [
44546
45625
  "surf",
44547
45626
  "scatter",
44548
45627
  "imagesc",
45628
+ "pcolor",
44549
45629
  "contour",
44550
45630
  "contourf",
44551
45631
  "mesh",
@@ -44588,6 +45668,7 @@ var SPECIAL_BUILTIN_NAMES = [
44588
45668
  "zlabel",
44589
45669
  "colorbar",
44590
45670
  "axis",
45671
+ "caxis",
44591
45672
  "mfilename",
44592
45673
  "addpath",
44593
45674
  "rmpath",
@@ -44613,7 +45694,8 @@ var SPECIAL_BUILTIN_NAMES = [
44613
45694
  "ode23",
44614
45695
  "deval",
44615
45696
  "toc",
44616
- "quadgk"
45697
+ "quadgk",
45698
+ "gmres"
44617
45699
  ];
44618
45700
 
44619
45701
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -45953,6 +47035,53 @@ function registerSpecialBuiltins(rt) {
45953
47035
  plotInstr(rt.plotInstructions, { type: "clf" });
45954
47036
  return nargout >= 1 ? RTV.num(1) : void 0;
45955
47037
  });
47038
+ registerSpecial("colorbar", (nargout, args) => {
47039
+ const LOCATIONS = /* @__PURE__ */ new Set([
47040
+ "east",
47041
+ "west",
47042
+ "north",
47043
+ "south",
47044
+ "eastoutside",
47045
+ "westoutside",
47046
+ "northoutside",
47047
+ "southoutside"
47048
+ ]);
47049
+ let value = "on";
47050
+ let location;
47051
+ let i = 0;
47052
+ while (i < args.length) {
47053
+ let s;
47054
+ try {
47055
+ s = toString(args[i]);
47056
+ } catch {
47057
+ break;
47058
+ }
47059
+ const lower = s.toLowerCase();
47060
+ if (lower === "off") {
47061
+ value = "off";
47062
+ i++;
47063
+ continue;
47064
+ }
47065
+ if (LOCATIONS.has(lower)) {
47066
+ location = lower;
47067
+ i++;
47068
+ continue;
47069
+ }
47070
+ i += 2;
47071
+ }
47072
+ plotInstr(rt.plotInstructions, {
47073
+ type: "set_colorbar",
47074
+ value,
47075
+ location
47076
+ });
47077
+ if (nargout >= 1) {
47078
+ return RTV.struct({
47079
+ Location: RTV.char(location ?? "eastoutside"),
47080
+ Visible: RTV.char(value === "off" ? "off" : "on")
47081
+ });
47082
+ }
47083
+ return void 0;
47084
+ });
45956
47085
  registerSpecial("ode45", (nargout, args) => {
45957
47086
  return _ode45Impl(rt, nargout, args, dormandPrince45);
45958
47087
  });
@@ -45962,6 +47091,9 @@ function registerSpecialBuiltins(rt) {
45962
47091
  registerSpecial("deval", (_nargout, args) => {
45963
47092
  return _devalImpl(args);
45964
47093
  });
47094
+ registerSpecial("gmres", (nargout, args) => {
47095
+ return _gmresImpl(rt, nargout, args);
47096
+ });
45965
47097
  }
45966
47098
  function _ode45Impl(rt, nargout, args, tableau = dormandPrince45) {
45967
47099
  const solverName = tableau.name;
@@ -46190,6 +47322,530 @@ function _devalImpl(args) {
46190
47322
  }
46191
47323
  return RTV.tensor(yData, [neq, nPts]);
46192
47324
  }
47325
+ function _gmresImpl(rt, nargout, args) {
47326
+ if (args.length < 2)
47327
+ throw new RuntimeError("gmres requires at least 2 arguments (A, b)");
47328
+ const Aarg = ensureRuntimeValue(args[0]);
47329
+ const bArg = ensureRuntimeValue(args[1]);
47330
+ if (!isRuntimeTensor(bArg) && !isRuntimeNumber(bArg) && !isRuntimeComplexNumber(bArg))
47331
+ throw new RuntimeError("gmres: b must be a numeric vector");
47332
+ let n;
47333
+ if (isRuntimeTensor(bArg)) {
47334
+ n = bArg.data.length;
47335
+ } else {
47336
+ n = 1;
47337
+ }
47338
+ const restartArg = args.length >= 3 ? ensureRuntimeValue(args[2]) : null;
47339
+ const tolArg = args.length >= 4 ? ensureRuntimeValue(args[3]) : null;
47340
+ const maxitArg = args.length >= 5 ? ensureRuntimeValue(args[4]) : null;
47341
+ const M1arg = args.length >= 6 ? ensureRuntimeValue(args[5]) : null;
47342
+ const M2arg = args.length >= 7 ? ensureRuntimeValue(args[6]) : null;
47343
+ const x0Arg = args.length >= 8 ? ensureRuntimeValue(args[7]) : null;
47344
+ let restart = n;
47345
+ if (restartArg !== null && !_isEmpty(restartArg)) {
47346
+ restart = _toNum(restartArg, "gmres: restart");
47347
+ }
47348
+ if (restart <= 0 || restart > n) restart = n;
47349
+ const noRestart = restart === n;
47350
+ const tol = tolArg !== null && !_isEmpty(tolArg) ? _toNum(tolArg, "gmres: tol") : 1e-6;
47351
+ let maxit;
47352
+ if (maxitArg !== null && !_isEmpty(maxitArg)) {
47353
+ maxit = _toNum(maxitArg, "gmres: maxit");
47354
+ } else {
47355
+ maxit = noRestart ? 1 : Math.max(1, Math.min(Math.ceil(n / restart), 10));
47356
+ }
47357
+ if (noRestart && maxitArg === null) restart = Math.min(n, 10);
47358
+ const isComplex2 = _isComplexArg(Aarg) || _isComplexArg(bArg) || M1arg !== null && _isComplexArg(M1arg) || M2arg !== null && _isComplexArg(M2arg) || x0Arg !== null && _isComplexArg(x0Arg);
47359
+ let flag;
47360
+ let relres;
47361
+ let iter;
47362
+ let resvec;
47363
+ let xTensor;
47364
+ if (isComplex2) {
47365
+ const bCV = _toComplexVec(bArg, n);
47366
+ const x0CV = x0Arg !== null && !_isEmpty(x0Arg) ? _toComplexVec(x0Arg, n) : null;
47367
+ const allMatrices = _isMatrixArg(Aarg) && _isMatrixArg(M1arg) && _isMatrixArg(M2arg);
47368
+ if (allMatrices) {
47369
+ const { re: ARe, im: AIm } = _toComplexMatrix(Aarg, n * n);
47370
+ const M1cv = _extractComplexMatrix(M1arg, n * n);
47371
+ const M2cv = _extractComplexMatrix(M2arg, n * n);
47372
+ const bridge = getEffectiveBridge("gmresComplex", "gmresComplex");
47373
+ if (bridge?.gmresComplex) {
47374
+ const r = bridge.gmresComplex(
47375
+ ARe,
47376
+ AIm,
47377
+ n,
47378
+ bCV.re,
47379
+ bCV.im,
47380
+ restart,
47381
+ tol,
47382
+ maxit,
47383
+ M1cv?.re ?? null,
47384
+ M1cv?.im ?? null,
47385
+ M2cv?.re ?? null,
47386
+ M2cv?.im ?? null,
47387
+ x0CV?.re ?? null,
47388
+ x0CV?.im ?? null
47389
+ );
47390
+ flag = r.flag;
47391
+ relres = r.relres;
47392
+ iter = [r.iter[0], r.iter[1]];
47393
+ resvec = r.resvec;
47394
+ xTensor = RTV.tensor(
47395
+ new FloatXArray(r.xRe),
47396
+ [n, 1],
47397
+ new FloatXArray(r.xIm)
47398
+ );
47399
+ } else {
47400
+ const matvec = _makeComplexMatvec(rt, Aarg, n);
47401
+ const precSolve = _makeComplexPrecSolve(rt, M1arg, M2arg, n);
47402
+ const r = gmresCoreComplex(
47403
+ matvec,
47404
+ precSolve,
47405
+ bCV,
47406
+ n,
47407
+ restart,
47408
+ tol,
47409
+ maxit,
47410
+ x0CV
47411
+ );
47412
+ flag = r.flag;
47413
+ relres = r.relres;
47414
+ iter = r.iter;
47415
+ resvec = r.resvec;
47416
+ xTensor = RTV.tensor(
47417
+ new FloatXArray(r.x.re),
47418
+ [n, 1],
47419
+ new FloatXArray(r.x.im)
47420
+ );
47421
+ }
47422
+ } else {
47423
+ const matvec = _makeComplexMatvec(rt, Aarg, n);
47424
+ const precSolve = _makeComplexPrecSolve(rt, M1arg, M2arg, n);
47425
+ const r = gmresCoreComplex(
47426
+ matvec,
47427
+ precSolve,
47428
+ bCV,
47429
+ n,
47430
+ restart,
47431
+ tol,
47432
+ maxit,
47433
+ x0CV
47434
+ );
47435
+ flag = r.flag;
47436
+ relres = r.relres;
47437
+ iter = r.iter;
47438
+ resvec = r.resvec;
47439
+ xTensor = RTV.tensor(
47440
+ new FloatXArray(r.x.re),
47441
+ [n, 1],
47442
+ new FloatXArray(r.x.im)
47443
+ );
47444
+ }
47445
+ } else {
47446
+ let bData;
47447
+ if (isRuntimeNumber(bArg)) {
47448
+ bData = new Float64Array([bArg]);
47449
+ } else {
47450
+ bData = toF64(bArg.data);
47451
+ }
47452
+ let x0 = null;
47453
+ if (x0Arg !== null && !_isEmpty(x0Arg)) {
47454
+ if (isRuntimeTensor(x0Arg)) x0 = toF64(x0Arg.data);
47455
+ else if (isRuntimeNumber(x0Arg)) x0 = new Float64Array([x0Arg]);
47456
+ }
47457
+ let xResult;
47458
+ const Ais_matrix = _isMatrixArg(Aarg);
47459
+ const M1is_matrix = _isMatrixArg(M1arg);
47460
+ const M2is_matrix = _isMatrixArg(M2arg);
47461
+ if (Ais_matrix && M1is_matrix && M2is_matrix) {
47462
+ const Adata = _extractRealMatrix(Aarg);
47463
+ const M1data = _extractMatrix(M1arg);
47464
+ const M2data = _extractMatrix(M2arg);
47465
+ const bridge = getEffectiveBridge("gmres", "gmres");
47466
+ if (bridge?.gmres) {
47467
+ const result = bridge.gmres(
47468
+ Adata,
47469
+ n,
47470
+ bData,
47471
+ restart,
47472
+ tol,
47473
+ maxit,
47474
+ M1data,
47475
+ M2data,
47476
+ x0
47477
+ );
47478
+ xResult = result.x;
47479
+ flag = result.flag;
47480
+ relres = result.relres;
47481
+ iter = [result.iter[0], result.iter[1]];
47482
+ resvec = result.resvec;
47483
+ } else {
47484
+ const r = _gmresWithCallbacks(
47485
+ Adata,
47486
+ n,
47487
+ bData,
47488
+ restart,
47489
+ tol,
47490
+ maxit,
47491
+ M1data,
47492
+ M2data,
47493
+ x0
47494
+ );
47495
+ xResult = r.x;
47496
+ flag = r.flag;
47497
+ relres = r.relres;
47498
+ iter = r.iter;
47499
+ resvec = r.resvec;
47500
+ }
47501
+ } else {
47502
+ const matvec = _makeMatvec(rt, Aarg, n);
47503
+ const precSolve = _makePrecSolve(rt, M1arg, M2arg, n);
47504
+ const r = gmresCore(matvec, precSolve, bData, n, restart, tol, maxit, x0);
47505
+ xResult = r.x;
47506
+ flag = r.flag;
47507
+ relres = r.relres;
47508
+ iter = r.iter;
47509
+ resvec = r.resvec;
47510
+ }
47511
+ xTensor = RTV.tensor(new FloatXArray(xResult), [n, 1]);
47512
+ }
47513
+ if (nargout <= 1) {
47514
+ if (flag === 0) {
47515
+ rt.output(
47516
+ `gmres converged at iteration ${iter[1]} to a solution with relative residual ${relres.toExponential(1)}.
47517
+ `
47518
+ );
47519
+ } else {
47520
+ rt.output(
47521
+ `gmres stopped at iteration ${iter[1]} without converging to the desired tolerance ${tol}
47522
+ because the maximum number of iterations was reached.
47523
+ The iterate returned (number ${iter[1]}) has relative residual ${relres.toExponential(1)}.
47524
+ `
47525
+ );
47526
+ }
47527
+ }
47528
+ if (nargout <= 1) return xTensor;
47529
+ if (nargout === 2) return [xTensor, flag];
47530
+ if (nargout === 3) return [xTensor, flag, relres];
47531
+ if (nargout === 4) {
47532
+ const iterTensor2 = RTV.tensor(new FloatXArray([iter[0], iter[1]]), [1, 2]);
47533
+ return [xTensor, flag, relres, iterTensor2];
47534
+ }
47535
+ const iterTensor = RTV.tensor(new FloatXArray([iter[0], iter[1]]), [1, 2]);
47536
+ const resvecTensor = RTV.tensor(new FloatXArray(resvec), [resvec.length, 1]);
47537
+ return [xTensor, flag, relres, iterTensor, resvecTensor];
47538
+ }
47539
+ function _isEmpty(v) {
47540
+ if (isRuntimeTensor(v) && v.data.length === 0) return true;
47541
+ return false;
47542
+ }
47543
+ function _toNum(v, ctx) {
47544
+ if (isRuntimeNumber(v)) return v;
47545
+ if (isRuntimeTensor(v) && v.data.length === 1) return v.data[0];
47546
+ throw new RuntimeError(`${ctx} must be a scalar`);
47547
+ }
47548
+ function _extractMatrix(arg) {
47549
+ if (arg === null || _isEmpty(arg)) return null;
47550
+ if (isRuntimeNumber(arg)) return new Float64Array([arg]);
47551
+ if (isRuntimeSparseMatrix(arg)) {
47552
+ const dense = sparseToDense(arg);
47553
+ return toF64(dense.data);
47554
+ }
47555
+ if (isRuntimeTensor(arg)) return toF64(arg.data);
47556
+ return null;
47557
+ }
47558
+ function _makeMatvec(rt, Aarg, n) {
47559
+ if (isRuntimeFunction(Aarg)) {
47560
+ return (x) => {
47561
+ const xTensor = RTV.tensor(new FloatXArray(x), [n, 1]);
47562
+ const resultRaw = rt.index(Aarg, [xTensor], 1);
47563
+ const rv = ensureRuntimeValue(resultRaw);
47564
+ if (isRuntimeNumber(rv)) return new Float64Array([rv]);
47565
+ if (isRuntimeTensor(rv)) return toF64(rv.data);
47566
+ throw new RuntimeError("gmres: A(x) must return a numeric vector");
47567
+ };
47568
+ }
47569
+ let Adata;
47570
+ if (isRuntimeNumber(Aarg)) {
47571
+ Adata = new Float64Array([Aarg]);
47572
+ } else if (isRuntimeSparseMatrix(Aarg)) {
47573
+ Adata = toF64(sparseToDense(Aarg).data);
47574
+ } else if (isRuntimeTensor(Aarg)) {
47575
+ Adata = toF64(Aarg.data);
47576
+ } else {
47577
+ throw new RuntimeError("gmres: A must be a matrix or function handle");
47578
+ }
47579
+ return (x) => {
47580
+ const y = new Float64Array(n);
47581
+ for (let i = 0; i < n; i++) {
47582
+ let s = 0;
47583
+ for (let j = 0; j < n; j++) s += Adata[i + j * n] * x[j];
47584
+ y[i] = s;
47585
+ }
47586
+ return y;
47587
+ };
47588
+ }
47589
+ function _makePrecSolve(rt, M1arg, M2arg, n) {
47590
+ const hasM1 = M1arg !== null && !_isEmpty(M1arg);
47591
+ const hasM2 = M2arg !== null && !_isEmpty(M2arg);
47592
+ if (!hasM1 && !hasM2) return null;
47593
+ const solve1 = hasM1 ? _makeSinglePrecSolve(rt, M1arg, n) : null;
47594
+ const solve2 = hasM2 ? _makeSinglePrecSolve(rt, M2arg, n) : null;
47595
+ return (r) => {
47596
+ let z = r;
47597
+ if (solve1) z = solve1(z);
47598
+ if (solve2) z = solve2(z);
47599
+ return z;
47600
+ };
47601
+ }
47602
+ function _makeSinglePrecSolve(rt, Marg, n) {
47603
+ if (isRuntimeFunction(Marg)) {
47604
+ return (r) => {
47605
+ const rTensor = RTV.tensor(new FloatXArray(r), [n, 1]);
47606
+ const resultRaw = rt.index(Marg, [rTensor], 1);
47607
+ const rv = ensureRuntimeValue(resultRaw);
47608
+ if (isRuntimeNumber(rv)) return new Float64Array([rv]);
47609
+ if (isRuntimeTensor(rv)) return toF64(rv.data);
47610
+ throw new RuntimeError("gmres: M(x) must return a numeric vector");
47611
+ };
47612
+ }
47613
+ let Mdata;
47614
+ if (isRuntimeNumber(Marg)) {
47615
+ Mdata = new Float64Array([Marg]);
47616
+ } else if (isRuntimeSparseMatrix(Marg)) {
47617
+ Mdata = toF64(sparseToDense(Marg).data);
47618
+ } else if (isRuntimeTensor(Marg)) {
47619
+ Mdata = toF64(Marg.data);
47620
+ } else {
47621
+ throw new RuntimeError(
47622
+ "gmres: preconditioner must be a matrix or function handle"
47623
+ );
47624
+ }
47625
+ const LU = new Float64Array(Mdata);
47626
+ const ipiv = new Int32Array(n);
47627
+ const info = dgetrf(n, n, LU, n, ipiv);
47628
+ if (info > 0)
47629
+ throw new RuntimeError("gmres: preconditioner matrix is singular");
47630
+ return (r) => {
47631
+ const z = new Float64Array(r);
47632
+ luSolveInPlace(n, LU, ipiv, z);
47633
+ return z;
47634
+ };
47635
+ }
47636
+ function _gmresWithCallbacks(A, n, b, restart, tol, maxit, M1, M2, x0) {
47637
+ const matvec = (x) => {
47638
+ const y = new Float64Array(n);
47639
+ for (let i = 0; i < n; i++) {
47640
+ let s = 0;
47641
+ for (let j = 0; j < n; j++) s += A[i + j * n] * x[j];
47642
+ y[i] = s;
47643
+ }
47644
+ return y;
47645
+ };
47646
+ let precSolve = null;
47647
+ if (M1 || M2) {
47648
+ const m1lu = M1 ? new Float64Array(M1) : null;
47649
+ const m1ipiv = M1 ? new Int32Array(n) : null;
47650
+ if (m1lu && m1ipiv) dgetrf(n, n, m1lu, n, m1ipiv);
47651
+ const m2lu = M2 ? new Float64Array(M2) : null;
47652
+ const m2ipiv = M2 ? new Int32Array(n) : null;
47653
+ if (m2lu && m2ipiv) dgetrf(n, n, m2lu, n, m2ipiv);
47654
+ precSolve = (r) => {
47655
+ const z = new Float64Array(r);
47656
+ if (m1lu && m1ipiv) luSolveInPlace(n, m1lu, m1ipiv, z);
47657
+ if (m2lu && m2ipiv) luSolveInPlace(n, m2lu, m2ipiv, z);
47658
+ return z;
47659
+ };
47660
+ }
47661
+ return gmresCore(matvec, precSolve, b, n, restart, tol, maxit, x0);
47662
+ }
47663
+ function _isComplexArg(v) {
47664
+ if (v === null) return false;
47665
+ if (isRuntimeComplexNumber(v)) return true;
47666
+ if (isRuntimeTensor(v) && v.imag) return true;
47667
+ if (isRuntimeSparseMatrix(v) && v.pi) return true;
47668
+ return false;
47669
+ }
47670
+ function _isMatrixArg(v) {
47671
+ if (v === null) return true;
47672
+ if (_isEmpty(v)) return true;
47673
+ return isRuntimeTensor(v) || isRuntimeNumber(v) || isRuntimeComplexNumber(v) || isRuntimeSparseMatrix(v);
47674
+ }
47675
+ function _extractRealMatrix(arg) {
47676
+ if (arg === null) return null;
47677
+ if (isRuntimeNumber(arg)) return new Float64Array([arg]);
47678
+ if (isRuntimeSparseMatrix(arg)) return toF64(sparseToDense(arg).data);
47679
+ if (isRuntimeTensor(arg)) return toF64(arg.data);
47680
+ return null;
47681
+ }
47682
+ function _toComplexVec(v, n) {
47683
+ if (isRuntimeComplexNumber(v)) {
47684
+ return { re: new Float64Array([v.re]), im: new Float64Array([v.im]) };
47685
+ }
47686
+ if (isRuntimeTensor(v)) {
47687
+ return {
47688
+ re: new Float64Array(toF64(v.data)),
47689
+ im: v.imag ? new Float64Array(toF64(v.imag)) : new Float64Array(n)
47690
+ };
47691
+ }
47692
+ if (isRuntimeNumber(v)) {
47693
+ const re2 = new Float64Array(1);
47694
+ re2[0] = v;
47695
+ return { re: re2, im: new Float64Array(1) };
47696
+ }
47697
+ throw new RuntimeError("gmres: cannot convert argument to complex vector");
47698
+ }
47699
+ function _toComplexMatrix(v, len) {
47700
+ if (isRuntimeTensor(v)) {
47701
+ return {
47702
+ re: new Float64Array(toF64(v.data)),
47703
+ im: v.imag ? new Float64Array(toF64(v.imag)) : new Float64Array(len)
47704
+ };
47705
+ }
47706
+ if (isRuntimeSparseMatrix(v)) {
47707
+ const dense = sparseToDense(v);
47708
+ return {
47709
+ re: new Float64Array(toF64(dense.data)),
47710
+ im: dense.imag ? new Float64Array(toF64(dense.imag)) : new Float64Array(len)
47711
+ };
47712
+ }
47713
+ if (isRuntimeNumber(v)) {
47714
+ const re2 = new Float64Array(1);
47715
+ re2[0] = v;
47716
+ return { re: re2, im: new Float64Array(1) };
47717
+ }
47718
+ throw new RuntimeError("gmres: cannot convert to complex matrix");
47719
+ }
47720
+ function _extractComplexMatrix(arg, len) {
47721
+ if (arg === null || _isEmpty(arg)) return null;
47722
+ return _toComplexMatrix(arg, len);
47723
+ }
47724
+ function _makeComplexMatvec(rt, Aarg, n) {
47725
+ if (isRuntimeFunction(Aarg)) {
47726
+ return (x) => {
47727
+ const xTensor = RTV.tensor(
47728
+ new FloatXArray(x.re),
47729
+ [n, 1],
47730
+ new FloatXArray(x.im)
47731
+ );
47732
+ const resultRaw = rt.index(Aarg, [xTensor], 1);
47733
+ const rv = ensureRuntimeValue(resultRaw);
47734
+ if (isRuntimeTensor(rv)) {
47735
+ return {
47736
+ re: new Float64Array(toF64(rv.data)),
47737
+ im: rv.imag ? new Float64Array(toF64(rv.imag)) : new Float64Array(n)
47738
+ };
47739
+ }
47740
+ if (isRuntimeComplexNumber(rv)) {
47741
+ return { re: new Float64Array([rv.re]), im: new Float64Array([rv.im]) };
47742
+ }
47743
+ if (isRuntimeNumber(rv)) {
47744
+ const re2 = new Float64Array(1);
47745
+ re2[0] = rv;
47746
+ return { re: re2, im: new Float64Array(1) };
47747
+ }
47748
+ throw new RuntimeError("gmres: A(x) must return a numeric vector");
47749
+ };
47750
+ }
47751
+ const { re: ARe, im: AIm } = _toComplexMatrix(Aarg, n * n);
47752
+ return (x) => {
47753
+ const yRe = new Float64Array(n);
47754
+ const yIm = new Float64Array(n);
47755
+ for (let i = 0; i < n; i++) {
47756
+ let sR = 0, sI = 0;
47757
+ for (let j = 0; j < n; j++) {
47758
+ const aR = ARe[i + j * n], aI = AIm[i + j * n];
47759
+ sR += aR * x.re[j] - aI * x.im[j];
47760
+ sI += aR * x.im[j] + aI * x.re[j];
47761
+ }
47762
+ yRe[i] = sR;
47763
+ yIm[i] = sI;
47764
+ }
47765
+ return { re: yRe, im: yIm };
47766
+ };
47767
+ }
47768
+ function _makeComplexPrecSolve(rt, M1arg, M2arg, n) {
47769
+ const hasM1 = M1arg !== null && !_isEmpty(M1arg);
47770
+ const hasM2 = M2arg !== null && !_isEmpty(M2arg);
47771
+ if (!hasM1 && !hasM2) return null;
47772
+ const solve1 = hasM1 ? _makeSingleComplexPrecSolve(rt, M1arg, n) : null;
47773
+ const solve2 = hasM2 ? _makeSingleComplexPrecSolve(rt, M2arg, n) : null;
47774
+ return (r) => {
47775
+ let z = r;
47776
+ if (solve1) z = solve1(z);
47777
+ if (solve2) z = solve2(z);
47778
+ return z;
47779
+ };
47780
+ }
47781
+ function _makeSingleComplexPrecSolve(rt, Marg, n) {
47782
+ if (isRuntimeFunction(Marg)) {
47783
+ return (r) => {
47784
+ const rTensor = RTV.tensor(
47785
+ new FloatXArray(r.re),
47786
+ [n, 1],
47787
+ new FloatXArray(r.im)
47788
+ );
47789
+ const resultRaw = rt.index(Marg, [rTensor], 1);
47790
+ const rv = ensureRuntimeValue(resultRaw);
47791
+ if (isRuntimeTensor(rv)) {
47792
+ return {
47793
+ re: new Float64Array(toF64(rv.data)),
47794
+ im: rv.imag ? new Float64Array(toF64(rv.imag)) : new Float64Array(n)
47795
+ };
47796
+ }
47797
+ throw new RuntimeError("gmres: M(x) must return a numeric vector");
47798
+ };
47799
+ }
47800
+ const { re: MRe, im: MIm } = _toComplexMatrix(Marg, n * n);
47801
+ const ipiv = new Int32Array(n);
47802
+ _complexLuFactor(n, MRe, MIm, ipiv);
47803
+ return (r) => {
47804
+ const zRe = new Float64Array(r.re);
47805
+ const zIm = new Float64Array(r.im);
47806
+ complexLuSolveInPlace(n, MRe, MIm, ipiv, zRe, zIm);
47807
+ return { re: zRe, im: zIm };
47808
+ };
47809
+ }
47810
+ function _complexLuFactor(n, re2, im2, ipiv) {
47811
+ for (let k = 0; k < n; k++) {
47812
+ let maxVal = -1, maxIdx = k;
47813
+ for (let i = k; i < n; i++) {
47814
+ const v = Math.sqrt(
47815
+ re2[i + k * n] * re2[i + k * n] + im2[i + k * n] * im2[i + k * n]
47816
+ );
47817
+ if (v > maxVal) {
47818
+ maxVal = v;
47819
+ maxIdx = i;
47820
+ }
47821
+ }
47822
+ ipiv[k] = maxIdx + 1;
47823
+ if (maxIdx !== k) {
47824
+ for (let j = 0; j < n; j++) {
47825
+ let tmp = re2[k + j * n];
47826
+ re2[k + j * n] = re2[maxIdx + j * n];
47827
+ re2[maxIdx + j * n] = tmp;
47828
+ tmp = im2[k + j * n];
47829
+ im2[k + j * n] = im2[maxIdx + j * n];
47830
+ im2[maxIdx + j * n] = tmp;
47831
+ }
47832
+ }
47833
+ const dR = re2[k + k * n], dI = im2[k + k * n];
47834
+ const dAbs2 = dR * dR + dI * dI;
47835
+ if (dAbs2 === 0) continue;
47836
+ for (let i = k + 1; i < n; i++) {
47837
+ const aR = re2[i + k * n], aI = im2[i + k * n];
47838
+ const lR = (aR * dR + aI * dI) / dAbs2;
47839
+ const lI = (aI * dR - aR * dI) / dAbs2;
47840
+ re2[i + k * n] = lR;
47841
+ im2[i + k * n] = lI;
47842
+ for (let j = k + 1; j < n; j++) {
47843
+ re2[i + j * n] -= lR * re2[k + j * n] - lI * im2[k + j * n];
47844
+ im2[i + j * n] -= lR * im2[k + j * n] + lI * re2[k + j * n];
47845
+ }
47846
+ }
47847
+ }
47848
+ }
46193
47849
 
46194
47850
  // src/numbl-core/interpreter/builtins/index.ts
46195
47851
  registerExtraBuiltinNames(getAllIBuiltinNames());
@@ -46208,6 +47864,8 @@ function dispatchPlotCall(rt, name, args) {
46208
47864
  return rt.scatter_call(args.map((a) => ensureRuntimeValue(a)));
46209
47865
  case "imagesc":
46210
47866
  return rt.imagesc_call(args.map((a) => ensureRuntimeValue(a)));
47867
+ case "pcolor":
47868
+ return rt.pcolor_call(args.map((a) => ensureRuntimeValue(a)));
46211
47869
  case "contour":
46212
47870
  return rt.contour_call(
46213
47871
  args.map((a) => ensureRuntimeValue(a)),
@@ -47130,10 +48788,10 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
47130
48788
  let stride = 1;
47131
48789
  for (let k = 0; k < nIdx; k++) {
47132
48790
  const dimSize = k < s.length ? s[k] : 1;
47133
- const sub = Math.round(indices[k]) - 1;
47134
- if (sub < 0 || sub >= dimSize)
48791
+ const sub2 = Math.round(indices[k]) - 1;
48792
+ if (sub2 < 0 || sub2 >= dimSize)
47135
48793
  throw new RuntimeError("Index exceeds array bounds");
47136
- lin += sub * stride;
48794
+ lin += sub2 * stride;
47137
48795
  stride *= dimSize;
47138
48796
  }
47139
48797
  if (t.imag !== void 0) {
@@ -47330,16 +48988,24 @@ function indexStore(rt, base, indices, rhs, skipSubsasgn = false) {
47330
48988
  }
47331
48989
  return dictInsertSingle(mv, key, rhsMv2);
47332
48990
  }
47333
- if (indices.length === 1 && (isRuntimeStructArray(mv) || isRuntimeTensor(mv) && mv.data.length === 0)) {
48991
+ if (indices.length === 1 && (isRuntimeStructArray(mv) || isRuntimeStruct(mv) || isRuntimeTensor(mv) && mv.data.length === 0)) {
47334
48992
  const rhsMv2 = ensureRuntimeValue(rhs);
47335
48993
  if (isRuntimeStruct(rhsMv2)) {
47336
48994
  const idxVal = ensureRuntimeValue(indices[0]);
47337
48995
  const k = Math.round(toNumber(idxVal)) - 1;
47338
- const existingElements = isRuntimeStructArray(mv) ? [...mv.elements] : [];
47339
- const existingFieldNames = isRuntimeStructArray(mv) ? mv.fieldNames : [];
48996
+ const existingElements = isRuntimeStructArray(mv) ? [...mv.elements] : isRuntimeStruct(mv) ? [mv] : [];
48997
+ const existingFieldNames = isRuntimeStructArray(mv) ? mv.fieldNames : isRuntimeStruct(mv) ? [...mv.fields.keys()] : [];
47340
48998
  const allFieldNames = [
47341
48999
  .../* @__PURE__ */ new Set([...existingFieldNames, ...[...rhsMv2.fields.keys()]])
47342
49000
  ];
49001
+ for (let ei = 0; ei < existingElements.length; ei++) {
49002
+ const el = existingElements[ei];
49003
+ for (const f of allFieldNames) {
49004
+ if (!el.fields.has(f)) {
49005
+ el.fields.set(f, RTV.tensor(new FloatXArray(0), [0, 0]));
49006
+ }
49007
+ }
49008
+ }
47343
49009
  while (existingElements.length <= k) {
47344
49010
  existingElements.push(
47345
49011
  RTV.struct(
@@ -47769,6 +49435,11 @@ var Runtime = class _Runtime {
47769
49435
  // Custom builtins (execution-specific overrides).
47770
49436
  // These take priority over IBuiltins.
47771
49437
  customBuiltins = {};
49438
+ // Per-runtime JIT helpers ($h) — populated by executeCode after the runtime
49439
+ // is constructed. Cloned from the global jitHelpers and extended with
49440
+ // ib_<name> entries for any .numbl.js user functions of this execution.
49441
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49442
+ jitHelpers = null;
47772
49443
  // JIT compilation callback: compiles and evaluates a specialized function at
47773
49444
  // runtime. Set by executeCode() to close over the LoweringContext and Codegen.
47774
49445
  // The callback handles the full resolution chain (local → class method →
@@ -47789,6 +49460,8 @@ var Runtime = class _Runtime {
47789
49460
  profileStack = [];
47790
49461
  profileAccum = /* @__PURE__ */ new Map();
47791
49462
  profileCounts = /* @__PURE__ */ new Map();
49463
+ // Profiling: interpreted loops with >1000 iterations (keyed by "file:line")
49464
+ hotLoops = /* @__PURE__ */ new Map();
47792
49465
  // ── Builtin initialization ──────────────────────────────────────────
47793
49466
  initBuiltins() {
47794
49467
  registerSpecialBuiltins(this);
@@ -47807,6 +49480,10 @@ var Runtime = class _Runtime {
47807
49480
  this.builtins["imagesc"] = (_nargout, args) => {
47808
49481
  this.imagesc_call(args.map((a) => ensureRuntimeValue(a)));
47809
49482
  };
49483
+ this.builtins["pcolor"] = (_nargout, args) => {
49484
+ this.pcolor_call(args.map((a) => ensureRuntimeValue(a)));
49485
+ if (_nargout >= 1) return RTV.dummyHandle();
49486
+ };
47810
49487
  this.builtins["contour"] = (_nargout, args) => {
47811
49488
  this.contour_call(
47812
49489
  args.map((a) => ensureRuntimeValue(a)),
@@ -47891,8 +49568,21 @@ var Runtime = class _Runtime {
47891
49568
  this.builtins["colormap"] = (_nargout, args) => {
47892
49569
  if (args.length > 0) {
47893
49570
  const rv = ensureRuntimeValue(args[0]);
47894
- const name = toString(rv).replace(/^"|"$/g, "");
47895
- plotInstr(this.plotInstructions, { type: "set_colormap", name });
49571
+ if (isRuntimeTensor(rv) && rv.shape.length === 2 && rv.shape[1] === 3) {
49572
+ const rows = rv.shape[0];
49573
+ const data = [];
49574
+ for (let i = 0; i < rows; i++) {
49575
+ data.push([rv.data[i], rv.data[rows + i], rv.data[2 * rows + i]]);
49576
+ }
49577
+ plotInstr(this.plotInstructions, {
49578
+ type: "set_colormap",
49579
+ name: "__custom__",
49580
+ data
49581
+ });
49582
+ } else {
49583
+ const name = toString(rv).replace(/^"|"$/g, "");
49584
+ plotInstr(this.plotInstructions, { type: "set_colormap", name });
49585
+ }
47896
49586
  }
47897
49587
  };
47898
49588
  this.builtins["view"] = (_nargout, args) => {
@@ -47906,10 +49596,6 @@ var Runtime = class _Runtime {
47906
49596
  });
47907
49597
  }
47908
49598
  };
47909
- this.builtins["colorbar"] = (_nargout, args) => {
47910
- const val = args.length > 0 ? toString(ensureRuntimeValue(args[0])) : "on";
47911
- plotInstr(this.plotInstructions, { type: "set_colorbar", value: val });
47912
- };
47913
49599
  this.builtins["axis"] = (_nargout, args) => {
47914
49600
  if (args.length > 0) {
47915
49601
  const val = toString(ensureRuntimeValue(args[0])).replace(
@@ -47919,6 +49605,17 @@ var Runtime = class _Runtime {
47919
49605
  plotInstr(this.plotInstructions, { type: "set_axis", value: val });
47920
49606
  }
47921
49607
  };
49608
+ this.builtins["caxis"] = (_nargout, args) => {
49609
+ if (args.length > 0) {
49610
+ const rv = ensureRuntimeValue(args[0]);
49611
+ if (isRuntimeTensor(rv) && rv.data.length >= 2) {
49612
+ plotInstr(this.plotInstructions, {
49613
+ type: "set_caxis",
49614
+ limits: [rv.data[0], rv.data[1]]
49615
+ });
49616
+ }
49617
+ }
49618
+ };
47922
49619
  }
47923
49620
  profileEnter(key) {
47924
49621
  if (!this.profilingEnabled) return;
@@ -48025,8 +49722,9 @@ var Runtime = class _Runtime {
48025
49722
  if (e instanceof RuntimeError) {
48026
49723
  return RTV.struct(
48027
49724
  /* @__PURE__ */ new Map([
48028
- ["message", RTV.char(e.message)],
48029
49725
  ["identifier", RTV.char(e.identifier)],
49726
+ ["message", RTV.char(e.message)],
49727
+ ["cause", RTV.cell([], [0, 0])],
48030
49728
  ["stack", buildStackField(e)]
48031
49729
  ])
48032
49730
  );
@@ -48034,16 +49732,18 @@ var Runtime = class _Runtime {
48034
49732
  if (e instanceof Error) {
48035
49733
  return RTV.struct(
48036
49734
  /* @__PURE__ */ new Map([
48037
- ["message", RTV.char(e.message)],
48038
49735
  ["identifier", RTV.char("")],
49736
+ ["message", RTV.char(e.message)],
49737
+ ["cause", RTV.cell([], [0, 0])],
48039
49738
  ["stack", emptyStackField()]
48040
49739
  ])
48041
49740
  );
48042
49741
  }
48043
49742
  return RTV.struct(
48044
49743
  /* @__PURE__ */ new Map([
48045
- ["message", RTV.char(String(e))],
48046
49744
  ["identifier", RTV.char("")],
49745
+ ["message", RTV.char(String(e))],
49746
+ ["cause", RTV.cell([], [0, 0])],
48047
49747
  ["stack", emptyStackField()]
48048
49748
  ])
48049
49749
  );
@@ -48782,6 +50482,9 @@ var Runtime = class _Runtime {
48782
50482
  imagesc_call(args) {
48783
50483
  imagescCall(this.plotInstructions, args);
48784
50484
  }
50485
+ pcolor_call(args) {
50486
+ pcolorCall(this.plotInstructions, args);
50487
+ }
48785
50488
  contour_call(args, filled) {
48786
50489
  contourCall(this.plotInstructions, args, filled);
48787
50490
  }
@@ -48937,7 +50640,7 @@ function buildStackField(e) {
48937
50640
  // src/numbl-core/jsUserFunctions.ts
48938
50641
  function funcNameFromFile(fileName) {
48939
50642
  const base = fileName.split("/").pop();
48940
- return base.replace(/\.js$/, "");
50643
+ return base.replace(/\.numbl\.js$/, "");
48941
50644
  }
48942
50645
  function buildWasmMap(wasmFiles) {
48943
50646
  const map = /* @__PURE__ */ new Map();
@@ -48961,6 +50664,9 @@ function parseDirectives(source) {
48961
50664
  }
48962
50665
  return directives;
48963
50666
  }
50667
+ function isNumblJsFile(fileName) {
50668
+ return fileName.endsWith(".numbl.js");
50669
+ }
48964
50670
  function nativeLibFilename(baseName) {
48965
50671
  const platform = typeof process !== "undefined" ? process.platform : "linux";
48966
50672
  switch (platform) {
@@ -49034,7 +50740,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge) {
49034
50740
  for (const file of jsFiles) {
49035
50741
  const base = file.name.split("/").pop();
49036
50742
  if (base.startsWith("_")) {
49037
- const libName = base.replace(/\.js$/, "");
50743
+ const libName = base.replace(/\.numbl\.js$/, "");
49038
50744
  libraryFiles.set(libName, file);
49039
50745
  } else {
49040
50746
  functionFiles.push(file);
@@ -49044,13 +50750,13 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge) {
49044
50750
  function importJS(name) {
49045
50751
  const cached = libCache.get(name);
49046
50752
  if (cached === LOADING) {
49047
- throw new RuntimeError(`Circular dependency detected: ${name}.js`);
50753
+ throw new RuntimeError(`Circular dependency detected: ${name}.numbl.js`);
49048
50754
  }
49049
50755
  if (libCache.has(name)) return cached;
49050
50756
  const libFile = libraryFiles.get(name);
49051
50757
  if (!libFile) {
49052
50758
  throw new RuntimeError(
49053
- `importJS: library '${name}.js' not found in workspace`
50759
+ `importJS: library '${name}.numbl.js' not found in workspace`
49054
50760
  );
49055
50761
  }
49056
50762
  libCache.set(name, LOADING);
@@ -49063,7 +50769,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge) {
49063
50769
  );
49064
50770
  const dummyRegister = () => {
49065
50771
  throw new RuntimeError(
49066
- `Library file '${name}.js' must not call register(). Use return {...} to export values.`
50772
+ `Library file '${name}.numbl.js' must not call register(). Use return {...} to export values.`
49067
50773
  );
49068
50774
  };
49069
50775
  const factory = new Function(
@@ -49098,7 +50804,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge) {
49098
50804
  }
49099
50805
  if (builtin) {
49100
50806
  throw new Error(
49101
- "register() called more than once \u2014 only one registration per .js file"
50807
+ "register() called more than once \u2014 only one registration per .numbl.js file"
49102
50808
  );
49103
50809
  }
49104
50810
  builtin = {
@@ -49165,7 +50871,7 @@ function loadJsUserFunctions(jsFiles, wasmFiles, nativeBridge) {
49165
50871
  };
49166
50872
  };
49167
50873
  }
49168
- result.push(builtin);
50874
+ result.push({ name: funcName, fileName: file.name, builtin });
49169
50875
  } catch (e) {
49170
50876
  const msg = e instanceof Error ? e.message : String(e);
49171
50877
  throw new Error(
@@ -49378,7 +51084,7 @@ function resolveFunction(name, argTypes, callSite, index2) {
49378
51084
  };
49379
51085
  }
49380
51086
  if (index2.jsUserFunctions.has(name)) {
49381
- return { kind: "builtin", name };
51087
+ return { kind: "jsUserFunction", name, argTypes };
49382
51088
  }
49383
51089
  if (index2.workspaceClasses.has(name)) {
49384
51090
  return {
@@ -50231,7 +51937,8 @@ function lowerMultiAssign(ctx, stmt) {
50231
51937
  if (args.some((a) => a === null)) return null;
50232
51938
  const loweredArgs = args;
50233
51939
  const argJitTypes = loweredArgs.map((a) => a.jitType);
50234
- const ib = getIBuiltin(rhs.name);
51940
+ const jsEntry = ctx.interp?.ctx.registry.jsUserFunctionsByName.get(rhs.name);
51941
+ const ib = jsEntry?.builtin ?? getIBuiltin(rhs.name);
50235
51942
  if (!ib) return null;
50236
51943
  const resolution = ib.resolve(argJitTypes, nargout);
50237
51944
  if (!resolution || resolution.outputTypes.length < nargout) return null;
@@ -50655,7 +52362,8 @@ function resolveUserFunction(interp, name, argJitTypes) {
50655
52362
  return null;
50656
52363
  }
50657
52364
  function lowerIBuiltinCall(ctx, expr) {
50658
- const ib = getIBuiltin(expr.name);
52365
+ const jsEntry = ctx.interp?.ctx.registry.jsUserFunctionsByName.get(expr.name);
52366
+ const ib = jsEntry?.builtin ?? getIBuiltin(expr.name);
50659
52367
  if (!ib) return null;
50660
52368
  const args = expr.args.map((a) => lowerExpr(ctx, a));
50661
52369
  if (args.some((a) => a === null)) return null;
@@ -50898,10 +52606,10 @@ function idxN(base, indices) {
50898
52606
  let stride = 1;
50899
52607
  for (let k = 0; k < indices.length; k++) {
50900
52608
  const dimSize = k < s.length ? s[k] : 1;
50901
- const sub = Math.round(indices[k]) - 1;
50902
- if (sub < 0 || sub >= dimSize)
52609
+ const sub2 = Math.round(indices[k]) - 1;
52610
+ if (sub2 < 0 || sub2 >= dimSize)
50903
52611
  throw new Error("Index exceeds array bounds");
50904
- lin += sub * stride;
52612
+ lin += sub2 * stride;
50905
52613
  stride *= dimSize;
50906
52614
  }
50907
52615
  if (base.imag !== void 0) {
@@ -51012,6 +52720,48 @@ setDynamicRegisterHook((b) => {
51012
52720
  return typeof result === "boolean" ? result ? 1 : 0 : result;
51013
52721
  };
51014
52722
  });
52723
+ function buildPerRuntimeJitHelpers(jsUserFunctions) {
52724
+ const h = Object.assign({}, jitHelpers);
52725
+ for (const [name, ib] of jsUserFunctions) {
52726
+ h[`ib_${name}`] = (...args) => {
52727
+ const pe = h._profileEnter;
52728
+ const pl = h._profileLeave;
52729
+ pe("jsUserFunction:jit:" + name);
52730
+ const rtArgs = args;
52731
+ const argTypes = rtArgs.map(inferJitType);
52732
+ const res = ib.resolve(argTypes, 1);
52733
+ if (!res) {
52734
+ pl();
52735
+ throw new Error(`JIT ib_${name}: resolve failed`);
52736
+ }
52737
+ const result = res.apply(rtArgs, 1);
52738
+ pl();
52739
+ return typeof result === "boolean" ? result ? 1 : 0 : result;
52740
+ };
52741
+ }
52742
+ const origIbcall = h.ibcall;
52743
+ h.ibcall = (name, nargout, ...args) => {
52744
+ const ib = jsUserFunctions.get(name);
52745
+ if (!ib) return origIbcall(name, nargout, ...args);
52746
+ const pe = h._profileEnter;
52747
+ const pl = h._profileLeave;
52748
+ pe("jsUserFunction:jit:" + name);
52749
+ const rtArgs = args;
52750
+ const argTypes = rtArgs.map(inferJitType);
52751
+ const res = ib.resolve(argTypes, nargout);
52752
+ if (!res) {
52753
+ pl();
52754
+ throw new Error(`JIT ibcall: resolve failed for ${name}`);
52755
+ }
52756
+ const result = res.apply(rtArgs, nargout);
52757
+ pl();
52758
+ if (Array.isArray(result)) {
52759
+ return result.map((v) => typeof v === "boolean" ? v ? 1 : 0 : v);
52760
+ }
52761
+ return [typeof result === "boolean" ? result ? 1 : 0 : result];
52762
+ };
52763
+ return h;
52764
+ }
51015
52765
 
51016
52766
  // src/numbl-core/interpreter/jit/jitLoopAnalysis.ts
51017
52767
  function analyzeForLoop(stmt) {
@@ -51426,18 +53176,26 @@ function execStmt(stmt) {
51426
53176
  }
51427
53177
  case "While": {
51428
53178
  if (this.optimization >= 1 && tryJitWhile(this, stmt)) return null;
53179
+ const _whileStart = this.rt.profilingEnabled ? performance.now() : 0;
53180
+ let _whileIters = 0;
51429
53181
  while (true) {
51430
53182
  const cond = this.evalExpr(stmt.cond);
51431
53183
  if (!this.rt.toBool(cond)) break;
53184
+ _whileIters++;
51432
53185
  const signal = this.execStmts(stmt.body);
51433
53186
  if (signal instanceof BreakSignal) break;
51434
53187
  if (signal instanceof ContinueSignal) continue;
51435
- if (signal instanceof ReturnSignal) return signal;
53188
+ if (signal instanceof ReturnSignal) {
53189
+ recordHotLoop(this, stmt, "while", _whileIters, _whileStart);
53190
+ return signal;
53191
+ }
51436
53192
  }
53193
+ recordHotLoop(this, stmt, "while", _whileIters, _whileStart);
51437
53194
  return null;
51438
53195
  }
51439
53196
  case "For": {
51440
53197
  if (this.optimization >= 1 && tryJitFor(this, stmt)) return null;
53198
+ const _forStart = this.rt.profilingEnabled ? performance.now() : 0;
51441
53199
  const iterVal = this.evalExpr(stmt.expr);
51442
53200
  const rv = ensureRuntimeValue(iterVal);
51443
53201
  const iterItems = forIter(rv);
@@ -51446,8 +53204,12 @@ function execStmt(stmt) {
51446
53204
  const signal = this.execStmts(stmt.body);
51447
53205
  if (signal instanceof BreakSignal) break;
51448
53206
  if (signal instanceof ContinueSignal) continue;
51449
- if (signal instanceof ReturnSignal) return signal;
53207
+ if (signal instanceof ReturnSignal) {
53208
+ recordHotLoop(this, stmt, "for", _i + 1, _forStart);
53209
+ return signal;
53210
+ }
51450
53211
  }
53212
+ recordHotLoop(this, stmt, "for", iterItems.length, _forStart);
51451
53213
  return null;
51452
53214
  }
51453
53215
  case "Switch": {
@@ -52209,6 +53971,33 @@ function isOutputExpr(expr) {
52209
53971
  if (expr.type === "Ident") return outputFunctions.includes(expr.name);
52210
53972
  return false;
52211
53973
  }
53974
+ function recordHotLoop(interp, stmt, kind, iterations, startTime) {
53975
+ if (!interp.rt.profilingEnabled || iterations <= 1e3 || !stmt.span) return;
53976
+ const durationMs = performance.now() - startTime;
53977
+ let table = interp.lineTableCache.get(stmt.span.file);
53978
+ if (!table) {
53979
+ const src = interp.fileSources.get(stmt.span.file) ?? "";
53980
+ table = buildLineTable(src);
53981
+ interp.lineTableCache.set(stmt.span.file, table);
53982
+ }
53983
+ const line = offsetToLineFast(table, stmt.span.start);
53984
+ const key = `${stmt.span.file}:${line}`;
53985
+ const prev = interp.rt.hotLoops.get(key);
53986
+ if (prev) {
53987
+ prev.callCount++;
53988
+ prev.totalTimeMs += durationMs;
53989
+ if (iterations > prev.iterations) prev.iterations = iterations;
53990
+ } else {
53991
+ interp.rt.hotLoops.set(key, {
53992
+ file: stmt.span.file,
53993
+ line,
53994
+ kind,
53995
+ iterations,
53996
+ callCount: 1,
53997
+ totalTimeMs: durationMs
53998
+ });
53999
+ }
54000
+ }
52212
54001
 
52213
54002
  // src/numbl-core/interpreter/interpreterFunctions.ts
52214
54003
  var interpreterFunctions_exports = {};
@@ -52227,6 +54016,7 @@ __export(interpreterFunctions_exports, {
52227
54016
  instantiateClass: () => instantiateClass,
52228
54017
  interpretClassMethod: () => interpretClassMethod,
52229
54018
  interpretConstructor: () => interpretConstructor,
54019
+ interpretJsUserFunction: () => interpretJsUserFunction,
52230
54020
  interpretLocalFunction: () => interpretLocalFunction,
52231
54021
  interpretPrivateFunction: () => interpretPrivateFunction,
52232
54022
  interpretTarget: () => interpretTarget,
@@ -52290,7 +54080,8 @@ function tryJitCall(interp, fn, args, nargout) {
52290
54080
  const rt = interp.rt;
52291
54081
  try {
52292
54082
  const factory = new Function("$h", "$rt", ...paramNames, jsBody);
52293
- compiledFn = (...callArgs) => factory(jitHelpers, rt, ...callArgs);
54083
+ const helpers = rt.jitHelpers ?? jitHelpers;
54084
+ compiledFn = (...callArgs) => factory(helpers, rt, ...callArgs);
52294
54085
  } catch {
52295
54086
  fnWithCache._jitCache.set(cacheKey, null);
52296
54087
  return JIT_SKIP;
@@ -52392,27 +54183,76 @@ register("exist", (ctx, args) => {
52392
54183
  if (args.length < 1) return FALL_THROUGH;
52393
54184
  const nameArg = toString(ensureRuntimeValue(args[0]));
52394
54185
  const typeArg = args.length >= 2 ? toString(ensureRuntimeValue(args[1])) : "";
54186
+ const fio = ctx.rt.fileIO;
54187
+ const isBuiltin2 = () => !!(ctx.rt.builtins[nameArg] || getIBuiltin(nameArg));
54188
+ const fileTypeFromExt = (path) => /\.numbl\.js$/i.test(path) ? 3 : 2;
54189
+ const nameHasKnownExt = /\.(numbl\.js|m|mlx|mlapp)$/i.test(nameArg);
54190
+ const isAbsolutePath = (p2) => p2.startsWith("/") || p2.startsWith("\\") || /^[a-zA-Z]:[/\\]/.test(p2);
54191
+ const joinPath = (dir, name) => {
54192
+ if (!dir) return name;
54193
+ if (dir.endsWith("/") || dir.endsWith("\\")) return dir + name;
54194
+ return dir + "/" + name;
54195
+ };
54196
+ const walkSearchPath = (acceptDir) => {
54197
+ if (!fio?.existsPath) return 0;
54198
+ const dirs = isAbsolutePath(nameArg) ? [""] : ctx.rt.searchPaths.length > 0 ? ctx.rt.searchPaths : [""];
54199
+ for (const dir of dirs) {
54200
+ if (acceptDir) {
54201
+ const t = fio.existsPath(joinPath(dir, nameArg));
54202
+ if (t === "dir") return 7;
54203
+ }
54204
+ if (!nameHasKnownExt) {
54205
+ for (const ext of [".m", ".mlx", ".mlapp"]) {
54206
+ const t = fio.existsPath(joinPath(dir, nameArg + ext));
54207
+ if (t === "file") {
54208
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54209
+ return ws?.kind === "class" ? 8 : 2;
54210
+ }
54211
+ }
54212
+ const numblJs = fio.existsPath(joinPath(dir, nameArg + ".numbl.js"));
54213
+ if (numblJs === "file") return 3;
54214
+ }
54215
+ const lit = fio.existsPath(joinPath(dir, nameArg));
54216
+ if (lit === "file") return fileTypeFromExt(nameArg);
54217
+ }
54218
+ return 0;
54219
+ };
54220
+ const workspaceTypeId = () => {
54221
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54222
+ if (!ws) return 0;
54223
+ switch (ws.kind) {
54224
+ case "function":
54225
+ return 2;
54226
+ case "jsfunction":
54227
+ return 3;
54228
+ case "class":
54229
+ return 8;
54230
+ }
54231
+ };
52395
54232
  if (typeArg === "var") {
52396
54233
  return ctx.env.has(nameArg) ? 1 : 0;
52397
54234
  }
52398
54235
  if (typeArg === "builtin") {
52399
- return ctx.rt.builtins[nameArg] || getIBuiltin(nameArg) ? 5 : 0;
54236
+ return isBuiltin2() ? 5 : 0;
54237
+ }
54238
+ if (typeArg === "class") {
54239
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54240
+ return ws?.kind === "class" ? 8 : 0;
52400
54241
  }
52401
54242
  if (typeArg === "dir") {
52402
- const fio = ctx.rt.fileIO;
52403
- if (!fio?.existsPath) return FALL_THROUGH;
52404
- return fio.existsPath(nameArg) === "dir" ? 7 : 0;
52405
- }
52406
- if (typeArg === "file" || typeArg === "") {
52407
- const fio = ctx.rt.fileIO;
52408
- if (!fio?.existsPath) return FALL_THROUGH;
52409
- const result = fio.existsPath(nameArg);
52410
- if (typeArg === "file") return result === "file" ? 2 : 0;
54243
+ return walkSearchPath(true) === 7 ? 7 : 0;
54244
+ }
54245
+ if (typeArg === "file") {
54246
+ const t = walkSearchPath(true);
54247
+ if (t) return t;
54248
+ return workspaceTypeId();
54249
+ }
54250
+ if (typeArg === "") {
52411
54251
  if (ctx.env.has(nameArg)) return 1;
52412
- if (result === "dir") return 7;
52413
- if (result === "file") return 2;
52414
- if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) return 5;
52415
- return 0;
54252
+ if (isBuiltin2()) return 5;
54253
+ const t = walkSearchPath(true);
54254
+ if (t) return t;
54255
+ return workspaceTypeId();
52416
54256
  }
52417
54257
  return FALL_THROUGH;
52418
54258
  });
@@ -52420,8 +54260,8 @@ register("which", (ctx, args) => {
52420
54260
  if (args.length < 1) return FALL_THROUGH;
52421
54261
  const nameArg = toString(ensureRuntimeValue(args[0]));
52422
54262
  if (ctx.env.has(nameArg)) return RTV.char("variable");
52423
- const filePath = ctx.lookupWorkspaceFile(nameArg);
52424
- if (filePath) return RTV.char(filePath);
54263
+ const ws = ctx.lookupWorkspaceFile(nameArg);
54264
+ if (ws) return RTV.char(ws.path);
52425
54265
  if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) {
52426
54266
  return RTV.char("built-in");
52427
54267
  }
@@ -52568,9 +54408,11 @@ function callFunction(name, args, nargout) {
52568
54408
  rt: this.rt,
52569
54409
  lookupWorkspaceFile: (n) => {
52570
54410
  const entry = this.ctx.registry.filesByFuncName.get(n);
52571
- if (entry) return entry.fileName;
54411
+ if (entry) return { path: entry.fileName, kind: "function" };
52572
54412
  const classInfo = this.ctx.getClassInfo(n);
52573
- if (classInfo) return classInfo.fileName;
54413
+ if (classInfo) return { path: classInfo.fileName, kind: "class" };
54414
+ const jsEntry = this.ctx.registry.jsUserFunctionsByName.get(n);
54415
+ if (jsEntry) return { path: jsEntry.fileName, kind: "jsfunction" };
52574
54416
  return void 0;
52575
54417
  }
52576
54418
  };
@@ -52636,6 +54478,8 @@ function interpretTarget(target, args, nargout) {
52636
54478
  return this.interpretLocalFunction(target, args, nargout);
52637
54479
  case "workspaceFunction":
52638
54480
  return this.interpretWorkspaceFunction(target, args, nargout);
54481
+ case "jsUserFunction":
54482
+ return this.interpretJsUserFunction(target, args, nargout);
52639
54483
  case "classMethod":
52640
54484
  return this.interpretClassMethod(target, args, nargout);
52641
54485
  case "workspaceClassConstructor":
@@ -52644,6 +54488,36 @@ function interpretTarget(target, args, nargout) {
52644
54488
  return this.interpretPrivateFunction(target, args, nargout);
52645
54489
  }
52646
54490
  }
54491
+ function interpretJsUserFunction(target, args, nargout) {
54492
+ const entry = this.ctx.registry.jsUserFunctionsByName.get(target.name);
54493
+ if (!entry) {
54494
+ throw new RuntimeError(`JS user function '${target.name}' not found`);
54495
+ }
54496
+ const ib = entry.builtin;
54497
+ const margs = args.map((a) => ensureRuntimeValue(a));
54498
+ const argTypes = margs.map(inferJitType);
54499
+ const resolution = ib.resolve(argTypes, nargout);
54500
+ if (!resolution) {
54501
+ const typeNames = argTypes.map((t) => t.kind);
54502
+ throw new RuntimeError(
54503
+ `JS user function '${target.name}' does not support these argument types: (${typeNames.join(", ")})`
54504
+ );
54505
+ }
54506
+ const isVoid = resolution.outputTypes.length === 0;
54507
+ if (isVoid && nargout > 0) {
54508
+ throw new RuntimeError("Too many output arguments.");
54509
+ }
54510
+ return this.withFileContext(entry.fileName, void 0, void 0, () => {
54511
+ if (this.rt.profilingEnabled) {
54512
+ this.rt.profileEnter("jsUserFunction:interp:" + target.name);
54513
+ const result2 = resolution.apply(margs, nargout);
54514
+ this.rt.profileLeave();
54515
+ return isVoid ? void 0 : result2;
54516
+ }
54517
+ const result = resolution.apply(margs, nargout);
54518
+ return isVoid ? void 0 : result;
54519
+ });
54520
+ }
52647
54521
  function interpretLocalFunction(target, args, nargout) {
52648
54522
  const { source } = target;
52649
54523
  if (source.from === "main") {
@@ -53508,6 +55382,7 @@ function extractClassInfo(classDef, qualifiedName, fileName, source) {
53508
55382
  function createWorkspaceRegistry() {
53509
55383
  return {
53510
55384
  filesByFuncName: /* @__PURE__ */ new Map(),
55385
+ jsUserFunctionsByName: /* @__PURE__ */ new Map(),
53511
55386
  fileContexts: /* @__PURE__ */ new Map(),
53512
55387
  classesByName: /* @__PURE__ */ new Map(),
53513
55388
  localClassesByName: /* @__PURE__ */ new Map(),
@@ -53701,6 +55576,7 @@ var LoweringContext = class _LoweringContext {
53701
55576
  /** Clear workspace-level registrations so they can be rebuilt after addpath/rmpath. */
53702
55577
  clearWorkspaceRegistrations() {
53703
55578
  this.registry.filesByFuncName.clear();
55579
+ this.registry.jsUserFunctionsByName.clear();
53704
55580
  this.registry.classesByName.clear();
53705
55581
  this.registry.privateFilesByDir.clear();
53706
55582
  this.registry.fileContexts.clear();
@@ -53708,6 +55584,14 @@ var LoweringContext = class _LoweringContext {
53708
55584
  this.registry.externalAccessByFile.clear();
53709
55585
  this.registry.functionIndex = null;
53710
55586
  }
55587
+ /**
55588
+ * Register a JS user function (.numbl.js) in the workspace registry.
55589
+ * Uses first-wins semantics so search-path priority is honored.
55590
+ */
55591
+ registerJsUserFunction(funcName, fileName, builtin) {
55592
+ if (this.registry.jsUserFunctionsByName.has(funcName)) return;
55593
+ this.registry.jsUserFunctionsByName.set(funcName, { fileName, builtin });
55594
+ }
53711
55595
  // ── Private function management ──────────────────────────────────
53712
55596
  /**
53713
55597
  * Get the effective directory for this context's file, used for
@@ -53971,19 +55855,15 @@ var LoweringContext = class _LoweringContext {
53971
55855
  * Should be called once after registerWorkspaceFiles() and registerLocalFunctionAST().
53972
55856
  * Parses all workspace files eagerly to discover subfunctions.
53973
55857
  */
53974
- buildFunctionIndex(jsUserFunctionNames) {
55858
+ buildFunctionIndex() {
53975
55859
  const builtins = /* @__PURE__ */ new Set([
53976
55860
  ...getAllBuiltinNames(),
53977
55861
  ...getAllIBuiltinNames(),
53978
55862
  ...SPECIAL_BUILTIN_NAMES
53979
55863
  ]);
53980
- const jsUserFunctions = /* @__PURE__ */ new Set();
53981
- if (jsUserFunctionNames) {
53982
- for (const name of jsUserFunctionNames) {
53983
- builtins.delete(name);
53984
- jsUserFunctions.add(name);
53985
- }
53986
- }
55864
+ const jsUserFunctions = new Set(
55865
+ this.registry.jsUserFunctionsByName.keys()
55866
+ );
53987
55867
  const mainLocalFunctions = new Set(this.localFunctionASTs.keys());
53988
55868
  const workspaceFunctions = new Set(this.registry.filesByFuncName.keys());
53989
55869
  const workspaceClasses = /* @__PURE__ */ new Set([
@@ -54517,7 +56397,7 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54517
56397
  for (const f of workspaceFiles) {
54518
56398
  if (f.name.endsWith(".m")) {
54519
56399
  mWorkspaceFiles.push(f);
54520
- } else if (f.name.endsWith(".js")) {
56400
+ } else if (isNumblJsFile(f.name)) {
54521
56401
  jsWorkspaceFiles.push(f);
54522
56402
  } else if (f.name.endsWith(".wasm")) {
54523
56403
  wasmWorkspaceFiles.push(f);
@@ -54541,6 +56421,8 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54541
56421
  for (const f of cwdFiles) {
54542
56422
  if (f.name.endsWith(".m")) {
54543
56423
  mWorkspaceFiles.push(f);
56424
+ } else if (isNumblJsFile(f.name)) {
56425
+ jsWorkspaceFiles.push(f);
54544
56426
  }
54545
56427
  }
54546
56428
  }
@@ -54554,7 +56436,6 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54554
56436
  wasmWorkspaceFiles,
54555
56437
  nativeBridge
54556
56438
  );
54557
- const jsUserFunctionNames = jsUserFunctions.map((ib) => ib.name);
54558
56439
  const stdlibShimNames = /* @__PURE__ */ new Set();
54559
56440
  for (const f of stdlibFiles) {
54560
56441
  mWorkspaceFiles.push(f);
@@ -54601,19 +56482,21 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
54601
56482
  if (mWorkspaceFiles.length > 0) {
54602
56483
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
54603
56484
  }
54604
- const functionIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
56485
+ for (const entry of jsUserFunctions) {
56486
+ ctx.registerJsUserFunction(entry.name, entry.fileName, entry.builtin);
56487
+ }
56488
+ const functionIndex = ctx.buildFunctionIndex();
54605
56489
  const savedSpecialBuiltins = /* @__PURE__ */ new Map();
54606
56490
  for (const name of SPECIAL_BUILTIN_NAMES) {
54607
56491
  const existing = getIBuiltin(name);
54608
56492
  if (existing) savedSpecialBuiltins.set(name, existing);
54609
56493
  }
54610
56494
  const rt = new Runtime(options, options.initialVariableValues);
54611
- const savedIBuiltins = /* @__PURE__ */ new Map();
54612
- for (const ib of jsUserFunctions) {
54613
- const orig = getIBuiltin(ib.name);
54614
- if (orig) savedIBuiltins.set(ib.name, orig);
54615
- registerDynamicIBuiltin(ib);
56495
+ const jsBuiltinMap = /* @__PURE__ */ new Map();
56496
+ for (const [n, e] of ctx.registry.jsUserFunctionsByName.entries()) {
56497
+ jsBuiltinMap.set(n, e.builtin);
54616
56498
  }
56499
+ rt.jitHelpers = buildPerRuntimeJitHelpers(jsBuiltinMap);
54617
56500
  if (options.customBuiltins) {
54618
56501
  Object.assign(rt.builtins, options.customBuiltins);
54619
56502
  Object.assign(rt.customBuiltins, options.customBuiltins);
@@ -54648,6 +56531,14 @@ ${jsCode}`
54648
56531
  interpreter.installRuntimeCallbacks();
54649
56532
  rt.searchPaths = ctx.registry.searchPaths;
54650
56533
  let pathsModified = false;
56534
+ const loadedJsUserFunctions = [...jsUserFunctions];
56535
+ const rebuildJitHelpers = () => {
56536
+ const map = /* @__PURE__ */ new Map();
56537
+ for (const [n, e] of ctx.registry.jsUserFunctionsByName.entries()) {
56538
+ map.set(n, e.builtin);
56539
+ }
56540
+ rt.jitHelpers = buildPerRuntimeJitHelpers(map);
56541
+ };
54651
56542
  const rebuildWorkspace = () => {
54652
56543
  const paths = ctx.registry.searchPaths;
54653
56544
  const priorityOf = (name) => {
@@ -54666,11 +56557,18 @@ ${jsCode}`
54666
56557
  return bestIdx;
54667
56558
  };
54668
56559
  mWorkspaceFiles.sort((a, b) => priorityOf(a.name) - priorityOf(b.name));
56560
+ loadedJsUserFunctions.sort(
56561
+ (a, b) => priorityOf(a.fileName) - priorityOf(b.fileName)
56562
+ );
54669
56563
  ctx.clearWorkspaceRegistrations();
54670
56564
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
54671
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
56565
+ for (const entry of loadedJsUserFunctions) {
56566
+ ctx.registerJsUserFunction(entry.name, entry.fileName, entry.builtin);
56567
+ }
56568
+ const newIndex = ctx.buildFunctionIndex();
54672
56569
  interpreter.functionIndex = newIndex;
54673
56570
  interpreter.clearAllCaches();
56571
+ rebuildJitHelpers();
54674
56572
  pathsModified = true;
54675
56573
  };
54676
56574
  rt.onPathChange = (action, dir, position) => {
@@ -54709,24 +56607,32 @@ ${jsCode}`
54709
56607
  }
54710
56608
  interpreter.fileSources.set(f.name, f.source);
54711
56609
  mWorkspaceFiles.push(f);
54712
- } else if (f.name.endsWith(".js")) {
56610
+ } else if (isNumblJsFile(f.name)) {
54713
56611
  newJsFiles.push(f);
56612
+ if (!jsWorkspaceFiles.some((e) => e.name === f.name)) {
56613
+ jsWorkspaceFiles.push(f);
56614
+ }
54714
56615
  } else if (f.name.endsWith(".wasm")) {
54715
56616
  newWasmFiles.push(f);
56617
+ if (!wasmWorkspaceFiles.some((e) => e.name === f.name)) {
56618
+ wasmWorkspaceFiles.push(f);
56619
+ }
54716
56620
  }
54717
56621
  }
54718
56622
  if (newJsFiles.length > 0) {
54719
- const newIBuiltins = loadJsUserFunctions(
56623
+ const loaded = loadJsUserFunctions(
54720
56624
  newJsFiles,
54721
56625
  newWasmFiles,
54722
56626
  nativeBridge
54723
56627
  );
54724
- for (const ib of newIBuiltins) {
54725
- const orig = getIBuiltin(ib.name);
54726
- if (orig) savedIBuiltins.set(ib.name, orig);
54727
- registerDynamicIBuiltin(ib);
54728
- if (!jsUserFunctionNames.includes(ib.name)) {
54729
- jsUserFunctionNames.push(ib.name);
56628
+ for (const entry of loaded) {
56629
+ const existingIdx = loadedJsUserFunctions.findIndex(
56630
+ (e) => e.fileName === entry.fileName
56631
+ );
56632
+ if (existingIdx >= 0) {
56633
+ loadedJsUserFunctions[existingIdx] = entry;
56634
+ } else {
56635
+ loadedJsUserFunctions.push(entry);
54730
56636
  }
54731
56637
  }
54732
56638
  }
@@ -54744,6 +56650,21 @@ ${jsCode}`
54744
56650
  mWorkspaceFiles.splice(i, 1);
54745
56651
  }
54746
56652
  }
56653
+ for (let i = loadedJsUserFunctions.length - 1; i >= 0; i--) {
56654
+ if (loadedJsUserFunctions[i].fileName.startsWith(prefix)) {
56655
+ loadedJsUserFunctions.splice(i, 1);
56656
+ }
56657
+ }
56658
+ for (let i = jsWorkspaceFiles.length - 1; i >= 0; i--) {
56659
+ if (jsWorkspaceFiles[i].name.startsWith(prefix)) {
56660
+ jsWorkspaceFiles.splice(i, 1);
56661
+ }
56662
+ }
56663
+ for (let i = wasmWorkspaceFiles.length - 1; i >= 0; i--) {
56664
+ if (wasmWorkspaceFiles[i].name.startsWith(prefix)) {
56665
+ wasmWorkspaceFiles.splice(i, 1);
56666
+ }
56667
+ }
54747
56668
  }
54748
56669
  rebuildWorkspace();
54749
56670
  };
@@ -54771,6 +56692,24 @@ ${jsCode}`
54771
56692
  mWorkspaceFiles.splice(i, 1);
54772
56693
  }
54773
56694
  }
56695
+ for (let i = loadedJsUserFunctions.length - 1; i >= 0; i--) {
56696
+ const fname = loadedJsUserFunctions[i].fileName;
56697
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
56698
+ loadedJsUserFunctions.splice(i, 1);
56699
+ }
56700
+ }
56701
+ for (let i = jsWorkspaceFiles.length - 1; i >= 0; i--) {
56702
+ const fname = jsWorkspaceFiles[i].name;
56703
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
56704
+ jsWorkspaceFiles.splice(i, 1);
56705
+ }
56706
+ }
56707
+ for (let i = wasmWorkspaceFiles.length - 1; i >= 0; i--) {
56708
+ const fname = wasmWorkspaceFiles[i].name;
56709
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
56710
+ wasmWorkspaceFiles.splice(i, 1);
56711
+ }
56712
+ }
54774
56713
  const oldIdx = ctx.registry.searchPaths.indexOf(implicitCwdPath);
54775
56714
  if (oldIdx >= 0) ctx.registry.searchPaths.splice(oldIdx, 1);
54776
56715
  implicitCwdPath = null;
@@ -54787,6 +56726,8 @@ ${jsCode}`
54787
56726
  newFiles = fileIO.scanDirectory(absNewCwd);
54788
56727
  } catch {
54789
56728
  }
56729
+ const newJsFiles = [];
56730
+ const newWasmFiles = [];
54790
56731
  for (const f of newFiles) {
54791
56732
  if (f.name.endsWith(".m") && !ctx.fileASTCache.has(f.name)) {
54792
56733
  try {
@@ -54803,6 +56744,33 @@ ${jsCode}`
54803
56744
  }
54804
56745
  interpreter.fileSources.set(f.name, f.source);
54805
56746
  mWorkspaceFiles.push(f);
56747
+ } else if (isNumblJsFile(f.name)) {
56748
+ newJsFiles.push(f);
56749
+ if (!jsWorkspaceFiles.some((e) => e.name === f.name)) {
56750
+ jsWorkspaceFiles.push(f);
56751
+ }
56752
+ } else if (f.name.endsWith(".wasm")) {
56753
+ newWasmFiles.push(f);
56754
+ if (!wasmWorkspaceFiles.some((e) => e.name === f.name)) {
56755
+ wasmWorkspaceFiles.push(f);
56756
+ }
56757
+ }
56758
+ }
56759
+ if (newJsFiles.length > 0) {
56760
+ const loaded = loadJsUserFunctions(
56761
+ newJsFiles,
56762
+ newWasmFiles,
56763
+ nativeBridge
56764
+ );
56765
+ for (const entry of loaded) {
56766
+ const existingIdx = loadedJsUserFunctions.findIndex(
56767
+ (e) => e.fileName === entry.fileName
56768
+ );
56769
+ if (existingIdx >= 0) {
56770
+ loadedJsUserFunctions[existingIdx] = entry;
56771
+ } else {
56772
+ loadedJsUserFunctions.push(entry);
56773
+ }
54806
56774
  }
54807
56775
  }
54808
56776
  }
@@ -54812,9 +56780,11 @@ ${jsCode}`
54812
56780
  const nestedSearchPaths = ctx.registry.searchPaths.filter(
54813
56781
  (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
54814
56782
  );
54815
- const nestedWorkspaceFiles = mWorkspaceFiles.filter(
54816
- (f) => !stdlibShimNames.has(f.name)
54817
- );
56783
+ const nestedWorkspaceFiles = [
56784
+ ...mWorkspaceFiles.filter((f) => !stdlibShimNames.has(f.name)),
56785
+ ...jsWorkspaceFiles,
56786
+ ...wasmWorkspaceFiles
56787
+ ];
54818
56788
  const evalResult = executeCode(
54819
56789
  code,
54820
56790
  {
@@ -54860,16 +56830,19 @@ ${jitSections.join("\n\n")}` : "// No JS generated",
54860
56830
  executionTimeMs,
54861
56831
  jitCompileTimeMs: rt.getJitCompileTimeMs(),
54862
56832
  builtins: rt.getBuiltinProfile(),
54863
- dispatches: rt.getDispatchProfile()
56833
+ dispatches: rt.getDispatchProfile(),
56834
+ hotLoops: [...rt.hotLoops.values()]
54864
56835
  };
54865
56836
  }
54866
56837
  if (pathsModified) {
54867
56838
  result.searchPaths = ctx.registry.searchPaths.filter(
54868
56839
  (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
54869
56840
  );
54870
- result.workspaceFiles = mWorkspaceFiles.filter(
54871
- (f) => !stdlibShimNames.has(f.name)
54872
- );
56841
+ result.workspaceFiles = [
56842
+ ...mWorkspaceFiles.filter((f) => !stdlibShimNames.has(f.name)),
56843
+ ...jsWorkspaceFiles,
56844
+ ...wasmWorkspaceFiles
56845
+ ];
54873
56846
  }
54874
56847
  result.implicitCwdPath = implicitCwdPath;
54875
56848
  return result;
@@ -54899,14 +56872,6 @@ ${jitSections.join("\n\n")}` : "// No JS generated";
54899
56872
  jitHelpers._profileEnter = Function.prototype;
54900
56873
  jitHelpers._profileLeave = Function.prototype;
54901
56874
  }
54902
- for (const ib of jsUserFunctions) {
54903
- const orig = savedIBuiltins.get(ib.name);
54904
- if (orig) {
54905
- registerDynamicIBuiltin(orig);
54906
- } else {
54907
- unregisterIBuiltin(ib.name);
54908
- }
54909
- }
54910
56875
  for (const [, ib] of savedSpecialBuiltins) {
54911
56876
  registerDynamicIBuiltin(ib);
54912
56877
  }