thyscript 1.0.0
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/bin/thy-node.js +332 -0
- package/bin/thy.js +828 -0
- package/bin/thys.js +25 -0
- package/examples/calculator.thy +274 -0
- package/examples/calculator.thy.js +628 -0
- package/examples/hello.thy +34 -0
- package/examples/login.thy +382 -0
- package/examples/login.thy.js +710 -0
- package/package.json +54 -0
- package/scripts/_class.js +18 -0
- package/scripts/_class2.js +11 -0
- package/scripts/_class3.js +21 -0
- package/scripts/_dstruct.js +20 -0
- package/scripts/_dstruct2.js +24 -0
- package/scripts/_scaffold.js +16 -0
- package/scripts/_scaffold2.js +16 -0
- package/scripts/_scaffold3.js +26 -0
- package/scripts/_tpl_compile.js +13 -0
- package/scripts/_tpl_test.js +9 -0
- package/scripts/_typeof.js +26 -0
- package/scripts/_typeof2.js +25 -0
- package/scripts/patch_genmethod.js +44 -0
- package/scripts/smoke_test.js +167 -0
- package/scripts/smoke_test2.js +151 -0
- package/scripts/test_control_panel.js +58 -0
- package/src/compiler/CodeGenerator.js +1449 -0
- package/src/compiler/JITCompiler.js +325 -0
- package/src/database/DatabaseEngine.js +931 -0
- package/src/errors/ErrorDetector.js +305 -0
- package/src/errors/ErrorMessages.js +497 -0
- package/src/index.js +169 -0
- package/src/lexer/Lexer.js +342 -0
- package/src/lexer/TokenTypes.js +1234 -0
- package/src/memory/MemoryManager.js +283 -0
- package/src/parser/AST.js +606 -0
- package/src/parser/Parser.js +1942 -0
- package/src/runtime/ThyRuntime.js +265 -0
- package/src/runtime/stdlib/ArrayLib.js +207 -0
- package/src/runtime/stdlib/FileLib.js +168 -0
- package/src/runtime/stdlib/MathLib.js +411 -0
- package/src/runtime/stdlib/StringLib.js +387 -0
- package/src/server/ControlPanel.js +1188 -0
- package/src/server/HTMLCSSEngine.js +866 -0
- package/src/server/SecurityEngine.js +714 -0
- package/src/server/ServerEngine.js +738 -0
- package/tests/test1_math.thy +82 -0
- package/tests/test2_oop.thy +133 -0
- package/tests/test3_server.thy +143 -0
- package/tests/test4_functions.thy +159 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ThyMathLib - ThyScript Standard Math Library
|
|
5
|
+
* Provides comprehensive math operations: linear algebra, calculus, statistics,
|
|
6
|
+
* FFT, ML activations, number theory, and more.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
let math;
|
|
10
|
+
try {
|
|
11
|
+
math = require('mathjs');
|
|
12
|
+
} catch {
|
|
13
|
+
math = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class ThyMathLib {
|
|
17
|
+
// ─── Constants ────────────────────────────────────────────────────────────
|
|
18
|
+
static PI = Math.PI;
|
|
19
|
+
static E = Math.E;
|
|
20
|
+
static PHI = (1 + Math.sqrt(5)) / 2;
|
|
21
|
+
static SQRT2 = Math.SQRT2;
|
|
22
|
+
static LN2 = Math.LN2;
|
|
23
|
+
static LN10 = Math.LN10;
|
|
24
|
+
static TAU = Math.PI * 2;
|
|
25
|
+
static INF = Infinity;
|
|
26
|
+
static NAN = NaN;
|
|
27
|
+
|
|
28
|
+
// ─── Expression Evaluation ────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Evaluate a math expression string using mathjs (or fallback to Function).
|
|
32
|
+
* @param {string} expr
|
|
33
|
+
* @returns {*}
|
|
34
|
+
*/
|
|
35
|
+
calculate(expr) {
|
|
36
|
+
if (math) {
|
|
37
|
+
return math.evaluate(expr);
|
|
38
|
+
}
|
|
39
|
+
// Safe-ish fallback: only allow math-like expressions
|
|
40
|
+
const allowed = /^[0-9+\-*/().^%\s,e a-zA-Z]+$/;
|
|
41
|
+
if (!allowed.test(expr)) throw new Error(`ThyMathLib: Unsafe expression: ${expr}`);
|
|
42
|
+
return Function(`"use strict"; with(Math){ return (${expr}); }`)(); // eslint-disable-line no-new-func
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Matrix / Vector / Tensor ─────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a mathjs matrix from a 2D array.
|
|
49
|
+
* @param {number[][]} data
|
|
50
|
+
* @returns {object}
|
|
51
|
+
*/
|
|
52
|
+
matrix(data) {
|
|
53
|
+
if (math) return math.matrix(data);
|
|
54
|
+
return { type: 'matrix', data, rows: data.length, cols: data[0]?.length ?? 0 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a vector (1D array wrapper).
|
|
59
|
+
* @param {number[]} data
|
|
60
|
+
* @returns {object}
|
|
61
|
+
*/
|
|
62
|
+
vector(data) {
|
|
63
|
+
if (!Array.isArray(data)) throw new TypeError('ThyMathLib.vector: expected array');
|
|
64
|
+
return { type: 'vector', data, length: data.length };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a tensor-like structure.
|
|
69
|
+
* @param {number[]} shape e.g. [2,3,4]
|
|
70
|
+
* @param {number[]} data flat data array
|
|
71
|
+
* @returns {object}
|
|
72
|
+
*/
|
|
73
|
+
tensor(shape, data) {
|
|
74
|
+
const size = shape.reduce((a, b) => a * b, 1);
|
|
75
|
+
if (data && data.length !== size) throw new RangeError(`ThyMathLib.tensor: data length ${data.length} doesn't match shape ${shape}`);
|
|
76
|
+
return { type: 'tensor', shape, data: data ?? new Float64Array(size), size };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Interpolation & Clamping ─────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/** Linear interpolation between a and b by factor t. */
|
|
82
|
+
lerp(a, b, t) { return a + (b - a) * t; }
|
|
83
|
+
|
|
84
|
+
/** Clamp v to [min, max]. */
|
|
85
|
+
clamp(v, min, max) { return Math.min(Math.max(v, min), max); }
|
|
86
|
+
|
|
87
|
+
// ─── Vector Operations ────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/** Normalize a vector (returns plain array). */
|
|
90
|
+
normalize(v) {
|
|
91
|
+
const arr = Array.isArray(v) ? v : v.data;
|
|
92
|
+
const mag = this.magnitude(arr);
|
|
93
|
+
if (mag === 0) throw new Error('ThyMathLib.normalize: zero-length vector');
|
|
94
|
+
return arr.map(x => x / mag);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Dot product of two vectors. */
|
|
98
|
+
dot(a, b) {
|
|
99
|
+
const av = Array.isArray(a) ? a : a.data;
|
|
100
|
+
const bv = Array.isArray(b) ? b : b.data;
|
|
101
|
+
if (av.length !== bv.length) throw new RangeError('ThyMathLib.dot: dimension mismatch');
|
|
102
|
+
return av.reduce((sum, x, i) => sum + x * bv[i], 0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Cross product of two 3D vectors. */
|
|
106
|
+
cross(a, b) {
|
|
107
|
+
const av = Array.isArray(a) ? a : a.data;
|
|
108
|
+
const bv = Array.isArray(b) ? b : b.data;
|
|
109
|
+
if (av.length !== 3 || bv.length !== 3) throw new RangeError('ThyMathLib.cross: only 3D supported');
|
|
110
|
+
return [
|
|
111
|
+
av[1] * bv[2] - av[2] * bv[1],
|
|
112
|
+
av[2] * bv[0] - av[0] * bv[2],
|
|
113
|
+
av[0] * bv[1] - av[1] * bv[0],
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Euclidean magnitude (L2 norm) of a vector. */
|
|
118
|
+
magnitude(v) {
|
|
119
|
+
const arr = Array.isArray(v) ? v : v.data;
|
|
120
|
+
return Math.sqrt(arr.reduce((s, x) => s + x * x, 0));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Euclidean distance between two points (arrays). */
|
|
124
|
+
distance(a, b) {
|
|
125
|
+
const av = Array.isArray(a) ? a : a.data;
|
|
126
|
+
const bv = Array.isArray(b) ? b : b.data;
|
|
127
|
+
if (av.length !== bv.length) throw new RangeError('ThyMathLib.distance: dimension mismatch');
|
|
128
|
+
return Math.sqrt(av.reduce((s, x, i) => s + (x - bv[i]) ** 2, 0));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── Matrix Operations ────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
/** Matrix determinant (plain 2D array). Uses LU decomposition. */
|
|
134
|
+
determinant(m) {
|
|
135
|
+
const data = Array.isArray(m) ? m : m.data;
|
|
136
|
+
if (math) return math.det(data);
|
|
137
|
+
return this._det(data.map(r => [...r]));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_det(m) {
|
|
141
|
+
const n = m.length;
|
|
142
|
+
if (n === 1) return m[0][0];
|
|
143
|
+
if (n === 2) return m[0][0] * m[1][1] - m[0][1] * m[1][0];
|
|
144
|
+
let sign = 1, det = 0;
|
|
145
|
+
for (let col = 0; col < n; col++) {
|
|
146
|
+
const sub = m.slice(1).map(r => r.filter((_, c) => c !== col));
|
|
147
|
+
det += sign * m[0][col] * this._det(sub);
|
|
148
|
+
sign *= -1;
|
|
149
|
+
}
|
|
150
|
+
return det;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Matrix inverse (uses mathjs or Gauss-Jordan). */
|
|
154
|
+
inverse(m) {
|
|
155
|
+
const data = Array.isArray(m) ? m : m.data;
|
|
156
|
+
if (math) return math.inv(data);
|
|
157
|
+
return this._gaussJordanInverse(data.map(r => [...r]));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
_gaussJordanInverse(m) {
|
|
161
|
+
const n = m.length;
|
|
162
|
+
const aug = m.map((r, i) => [...r, ...Array.from({ length: n }, (_, j) => (i === j ? 1 : 0))]);
|
|
163
|
+
for (let col = 0; col < n; col++) {
|
|
164
|
+
let pivot = col;
|
|
165
|
+
for (let row = col + 1; row < n; row++) {
|
|
166
|
+
if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col])) pivot = row;
|
|
167
|
+
}
|
|
168
|
+
[aug[col], aug[pivot]] = [aug[pivot], aug[col]];
|
|
169
|
+
const d = aug[col][col];
|
|
170
|
+
if (Math.abs(d) < 1e-12) throw new Error('ThyMathLib.inverse: matrix is singular');
|
|
171
|
+
for (let j = 0; j < 2 * n; j++) aug[col][j] /= d;
|
|
172
|
+
for (let row = 0; row < n; row++) {
|
|
173
|
+
if (row === col) continue;
|
|
174
|
+
const f = aug[row][col];
|
|
175
|
+
for (let j = 0; j < 2 * n; j++) aug[row][j] -= f * aug[col][j];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return aug.map(r => r.slice(n));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Transpose a 2D matrix. */
|
|
182
|
+
transpose(m) {
|
|
183
|
+
const data = Array.isArray(m) ? m : m.data;
|
|
184
|
+
if (math) return math.transpose(data);
|
|
185
|
+
const rows = data.length, cols = data[0].length;
|
|
186
|
+
return Array.from({ length: cols }, (_, c) => Array.from({ length: rows }, (_, r) => data[r][c]));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── Calculus ─────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
/** Numerical gradient (central difference). */
|
|
192
|
+
gradient(fn, x, h = 1e-7) { return (fn(x + h) - fn(x - h)) / (2 * h); }
|
|
193
|
+
|
|
194
|
+
/** Numerical integration using Simpson's 1/3 rule. */
|
|
195
|
+
integrate(fn, a, b, steps = 1000) {
|
|
196
|
+
if (steps % 2 !== 0) steps++;
|
|
197
|
+
const h = (b - a) / steps;
|
|
198
|
+
let sum = fn(a) + fn(b);
|
|
199
|
+
for (let i = 1; i < steps; i++) {
|
|
200
|
+
sum += fn(a + i * h) * (i % 2 === 0 ? 2 : 4);
|
|
201
|
+
}
|
|
202
|
+
return (h / 3) * sum;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Numerical differentiation (central difference). */
|
|
206
|
+
differentiate(fn, x, h = 1e-7) { return this.gradient(fn, x, h); }
|
|
207
|
+
|
|
208
|
+
// ─── Linear System Solver ─────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/** Solve Ax = b using Gaussian elimination with partial pivoting. */
|
|
211
|
+
solve(A, b) {
|
|
212
|
+
const n = A.length;
|
|
213
|
+
const M = A.map((r, i) => [...r, b[i]]);
|
|
214
|
+
for (let col = 0; col < n; col++) {
|
|
215
|
+
let pivot = col;
|
|
216
|
+
for (let row = col + 1; row < n; row++) {
|
|
217
|
+
if (Math.abs(M[row][col]) > Math.abs(M[pivot][col])) pivot = row;
|
|
218
|
+
}
|
|
219
|
+
[M[col], M[pivot]] = [M[pivot], M[col]];
|
|
220
|
+
if (Math.abs(M[col][col]) < 1e-12) throw new Error('ThyMathLib.solve: singular matrix');
|
|
221
|
+
for (let row = col + 1; row < n; row++) {
|
|
222
|
+
const f = M[row][col] / M[col][col];
|
|
223
|
+
for (let j = col; j <= n; j++) M[row][j] -= f * M[col][j];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const x = new Array(n).fill(0);
|
|
227
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
228
|
+
x[i] = M[i][n];
|
|
229
|
+
for (let j = i + 1; j < n; j++) x[i] -= M[i][j] * x[j];
|
|
230
|
+
x[i] /= M[i][i];
|
|
231
|
+
}
|
|
232
|
+
return x;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ─── FFT ─────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/** Cooley-Tukey radix-2 FFT. Input must be power-of-2 length. Returns [{re,im}]. */
|
|
238
|
+
fft(signal) {
|
|
239
|
+
const n = signal.length;
|
|
240
|
+
if (n === 1) return [{ re: signal[0].re ?? signal[0], im: signal[0].im ?? 0 }];
|
|
241
|
+
if ((n & (n - 1)) !== 0) throw new RangeError('ThyMathLib.fft: length must be power of 2');
|
|
242
|
+
const even = this.fft(signal.filter((_, i) => i % 2 === 0));
|
|
243
|
+
const odd = this.fft(signal.filter((_, i) => i % 2 !== 0));
|
|
244
|
+
const result = new Array(n);
|
|
245
|
+
for (let k = 0; k < n / 2; k++) {
|
|
246
|
+
const angle = -2 * Math.PI * k / n;
|
|
247
|
+
const tRe = Math.cos(angle) * odd[k].re - Math.sin(angle) * odd[k].im;
|
|
248
|
+
const tIm = Math.cos(angle) * odd[k].im + Math.sin(angle) * odd[k].re;
|
|
249
|
+
result[k] = { re: even[k].re + tRe, im: even[k].im + tIm };
|
|
250
|
+
result[k + n / 2] = { re: even[k].re - tRe, im: even[k].im - tIm };
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Inverse FFT. */
|
|
256
|
+
ifft(spectrum) {
|
|
257
|
+
const conj = spectrum.map(s => ({ re: s.re, im: -(s.im ?? 0) }));
|
|
258
|
+
const fwdResult = this.fft(conj);
|
|
259
|
+
const n = fwdResult.length;
|
|
260
|
+
return fwdResult.map(s => ({ re: s.re / n, im: -s.im / n }));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ─── Trigonometric & Transcendental ───────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
sin(x) { return Math.sin(x); }
|
|
266
|
+
cos(x) { return Math.cos(x); }
|
|
267
|
+
tan(x) { return Math.tan(x); }
|
|
268
|
+
asin(x) { return Math.asin(x); }
|
|
269
|
+
acos(x) { return Math.acos(x); }
|
|
270
|
+
atan(x) { return Math.atan(x); }
|
|
271
|
+
atan2(y, x) { return Math.atan2(y, x); }
|
|
272
|
+
sinh(x) { return Math.sinh(x); }
|
|
273
|
+
cosh(x) { return Math.cosh(x); }
|
|
274
|
+
tanh(x) { return Math.tanh(x); }
|
|
275
|
+
log(x) { return Math.log(x); }
|
|
276
|
+
log2(x) { return Math.log2(x); }
|
|
277
|
+
log10(x) { return Math.log10(x); }
|
|
278
|
+
exp(x) { return Math.exp(x); }
|
|
279
|
+
pow(x, y){ return Math.pow(x, y); }
|
|
280
|
+
sqrt(x) { return Math.sqrt(x); }
|
|
281
|
+
cbrt(x) { return Math.cbrt(x); }
|
|
282
|
+
abs(x) { return Math.abs(x); }
|
|
283
|
+
ceil(x) { return Math.ceil(x); }
|
|
284
|
+
floor(x) { return Math.floor(x); }
|
|
285
|
+
round(x) { return Math.round(x); }
|
|
286
|
+
trunc(x) { return Math.trunc(x); }
|
|
287
|
+
sign(x) { return Math.sign(x); }
|
|
288
|
+
hypot(...args) { return Math.hypot(...args); }
|
|
289
|
+
|
|
290
|
+
// ─── Seeded Random ────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Seeded pseudo-random generator (mulberry32).
|
|
294
|
+
* @param {number} seed
|
|
295
|
+
* @returns {function} - call to get next random in [0,1)
|
|
296
|
+
*/
|
|
297
|
+
random(seed = Date.now()) {
|
|
298
|
+
let s = seed >>> 0;
|
|
299
|
+
return function () {
|
|
300
|
+
s |= 0; s = s + 0x6D2B79F5 | 0;
|
|
301
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
302
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
303
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ─── Statistics ───────────────────────────────────────────────────────────
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Compute descriptive statistics for an array.
|
|
311
|
+
* @param {number[]} data
|
|
312
|
+
* @returns {{ mean, median, std, variance, min, max, count }}
|
|
313
|
+
*/
|
|
314
|
+
stats(data) {
|
|
315
|
+
if (!data || data.length === 0) throw new RangeError('ThyMathLib.stats: empty dataset');
|
|
316
|
+
const n = data.length;
|
|
317
|
+
const sorted = [...data].sort((a, b) => a - b);
|
|
318
|
+
const mean = data.reduce((s, x) => s + x, 0) / n;
|
|
319
|
+
const variance = data.reduce((s, x) => s + (x - mean) ** 2, 0) / n;
|
|
320
|
+
const std = Math.sqrt(variance);
|
|
321
|
+
const mid = Math.floor(n / 2);
|
|
322
|
+
const median = n % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
323
|
+
return { mean, median, std, variance, min: sorted[0], max: sorted[n - 1], count: n };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ─── Combinatorics ────────────────────────────────────────────────────────
|
|
327
|
+
|
|
328
|
+
/** Number of permutations P(n, k). */
|
|
329
|
+
permutations(n, k) { return this.factorial(n) / this.factorial(n - k); }
|
|
330
|
+
|
|
331
|
+
/** Number of combinations C(n, k). */
|
|
332
|
+
combinations(n, k) {
|
|
333
|
+
if (k > n) return 0;
|
|
334
|
+
if (k === 0 || k === n) return 1;
|
|
335
|
+
let result = 1;
|
|
336
|
+
for (let i = 0; i < k; i++) result = result * (n - i) / (i + 1);
|
|
337
|
+
return Math.round(result);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ─── Number Theory ────────────────────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
/** Check if n is prime. */
|
|
343
|
+
isPrime(n) {
|
|
344
|
+
if (n < 2) return false;
|
|
345
|
+
if (n < 4) return true;
|
|
346
|
+
if (n % 2 === 0 || n % 3 === 0) return false;
|
|
347
|
+
for (let i = 5; i * i <= n; i += 6) {
|
|
348
|
+
if (n % i === 0 || n % (i + 2) === 0) return false;
|
|
349
|
+
}
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Return the nth prime (1-indexed). */
|
|
354
|
+
prime(n) {
|
|
355
|
+
if (n < 1) throw new RangeError('ThyMathLib.prime: n must be >= 1');
|
|
356
|
+
let count = 0, num = 1;
|
|
357
|
+
while (count < n) { num++; if (this.isPrime(num)) count++; }
|
|
358
|
+
return num;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** Fibonacci number (iterative). */
|
|
362
|
+
fibonacci(n) {
|
|
363
|
+
if (n < 0) throw new RangeError('ThyMathLib.fibonacci: n must be >= 0');
|
|
364
|
+
if (n <= 1) return n;
|
|
365
|
+
let a = 0, b = 1;
|
|
366
|
+
for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
|
|
367
|
+
return b;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Greatest common divisor (Euclidean). */
|
|
371
|
+
gcd(a, b) {
|
|
372
|
+
a = Math.abs(Math.round(a)); b = Math.abs(Math.round(b));
|
|
373
|
+
while (b) { [a, b] = [b, a % b]; }
|
|
374
|
+
return a;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Least common multiple. */
|
|
378
|
+
lcm(a, b) { return Math.abs(a * b) / this.gcd(a, b); }
|
|
379
|
+
|
|
380
|
+
/** Factorial (iterative, returns BigInt for n > 18). */
|
|
381
|
+
factorial(n) {
|
|
382
|
+
if (n < 0) throw new RangeError('ThyMathLib.factorial: n must be >= 0');
|
|
383
|
+
if (n <= 1) return 1;
|
|
384
|
+
if (n > 18) {
|
|
385
|
+
let r = 1n;
|
|
386
|
+
for (let i = 2n; i <= BigInt(n); i++) r *= i;
|
|
387
|
+
return r;
|
|
388
|
+
}
|
|
389
|
+
let r = 1;
|
|
390
|
+
for (let i = 2; i <= n; i++) r *= i;
|
|
391
|
+
return r;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ─── ML Activations ───────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
/** Sigmoid activation: 1 / (1 + e^-x). */
|
|
397
|
+
sigmoid(x) { return 1 / (1 + Math.exp(-x)); }
|
|
398
|
+
|
|
399
|
+
/** ReLU activation: max(0, x). */
|
|
400
|
+
relu(x) { return Math.max(0, x); }
|
|
401
|
+
|
|
402
|
+
/** Softmax of an array (numerically stable). */
|
|
403
|
+
softmax(arr) {
|
|
404
|
+
const max = Math.max(...arr);
|
|
405
|
+
const exps = arr.map(x => Math.exp(x - max));
|
|
406
|
+
const sum = exps.reduce((a, b) => a + b, 0);
|
|
407
|
+
return exps.map(e => e / sum);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
module.exports = ThyMathLib;
|