porffor 0.14.0-c6edfd328 → 0.14.0-d6c141d91
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/CONTRIBUTING.md +4 -2
- package/bf_bun +0 -0
- package/bf_deno +0 -0
- package/bf_porf +0 -0
- package/bf_porf_fast +0 -0
- package/bf_porf_lto +0 -0
- package/compiler/2c.js +65 -2
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +71 -1
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +2 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/set.ts +2 -0
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +2 -0
- package/compiler/builtins.js +26 -4
- package/compiler/codegen.js +126 -141
- package/compiler/generated_builtins.js +284 -7
- package/compiler/index.js +1 -1
- package/compiler/parse.js +1 -1
- package/compiler/precompile.js +2 -2
- package/package.json +1 -1
- package/runner/index.js +4 -3
- package/w.js +1 -0
package/CONTRIBUTING.md
CHANGED
@@ -198,11 +198,13 @@ Store the character code into the `out` pointer variable, and increment it.
|
|
198
198
|
|
199
199
|
## Porffor-specific TS notes
|
200
200
|
|
201
|
-
- For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`)
|
201
|
+
- For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`).
|
202
202
|
- You might spot `Porffor.fastOr`/`Porffor.fastAnd`, these are non-short circuiting versions of `||`/`&&`, taking any number of conditions as arguments. You shouldn't don't need to use or worry about these.
|
203
|
-
- **There are ~no objects, you cannot use them
|
203
|
+
- **There are ~no objects, you cannot use them.**
|
204
204
|
- Attempt to avoid string/array-heavy code and use more variables instead if possible, easier on memory and CPU/perf.
|
205
205
|
- Do not set a return type for prototype methods, it can cause errors/unexpected results.
|
206
|
+
- You cannot use other functions in the file not exported, or variables not inside the current function.
|
207
|
+
- `if (...)` uses a fast truthy implementation which is not spec-compliant as most conditions should be strictly checked. To use spec-compliant behavior, use `if (Boolean(...))`.
|
206
208
|
|
207
209
|
<br>
|
208
210
|
|
package/bf_bun
ADDED
Binary file
|
package/bf_deno
ADDED
Binary file
|
package/bf_porf
ADDED
Binary file
|
package/bf_porf_fast
ADDED
Binary file
|
package/bf_porf_lto
ADDED
Binary file
|
package/compiler/2c.js
CHANGED
@@ -119,6 +119,9 @@ const removeBrackets = str => {
|
|
119
119
|
};
|
120
120
|
|
121
121
|
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
122
|
+
// fix declaring order for c
|
123
|
+
funcs.reverse();
|
124
|
+
|
122
125
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
123
126
|
for (const k in x) {
|
124
127
|
acc[x[k]] = k;
|
@@ -156,6 +159,11 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
156
159
|
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
|
157
160
|
}
|
158
161
|
|
162
|
+
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
163
|
+
prepend.set('argv', `int _argc; char** _argv;`);
|
164
|
+
prependMain.set('argv', `_argc = argc; _argv = argv;`);
|
165
|
+
}
|
166
|
+
|
159
167
|
if (out) out += '\n';
|
160
168
|
|
161
169
|
let depth = 1;
|
@@ -207,8 +215,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
207
215
|
|
208
216
|
const returns = f.returns.length > 0;
|
209
217
|
|
210
|
-
const shouldInline = f.internal;
|
211
|
-
|
218
|
+
const shouldInline = false; // f.internal;
|
219
|
+
if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
|
220
|
+
else out += `${f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
212
221
|
|
213
222
|
if (f.name === 'main') {
|
214
223
|
out += [...prependMain.values()].join('\n');
|
@@ -459,6 +468,60 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
459
468
|
winIncludes.set('windows.h', true);
|
460
469
|
break;
|
461
470
|
|
471
|
+
case '__Porffor_readArgv':
|
472
|
+
includes.set('stdlib.h', true);
|
473
|
+
|
474
|
+
prepend.set('__Porffor_readArgv',
|
475
|
+
`void __Porffor_readArgv(u32 index, u32 outPtr) {
|
476
|
+
if (index >= _argc) {
|
477
|
+
printf("expected %d arguments\\n", index);
|
478
|
+
exit(1);
|
479
|
+
}
|
480
|
+
|
481
|
+
char* arg = _argv[index];
|
482
|
+
|
483
|
+
u32 read = 0;
|
484
|
+
char* out = _memory + outPtr + 4;
|
485
|
+
char ch;
|
486
|
+
while ((ch = *(arg++)) != 0) {
|
487
|
+
out[read++] = ch;
|
488
|
+
}
|
489
|
+
|
490
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
491
|
+
}`);
|
492
|
+
|
493
|
+
line(`__Porffor_readArgv((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
|
494
|
+
vals.pop();
|
495
|
+
break;
|
496
|
+
|
497
|
+
case '__Porffor_readFile':
|
498
|
+
includes.set('stdio.h', true);
|
499
|
+
includes.set('stdlib.h', true);
|
500
|
+
|
501
|
+
prepend.set('__Porffor_readFile',
|
502
|
+
`void __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
503
|
+
char* path = _memory + pathPtr + 4;
|
504
|
+
FILE* fp = fopen(path, "r");
|
505
|
+
if (fp == NULL) {
|
506
|
+
printf("failed to open file: %s\\n", path);
|
507
|
+
exit(1);
|
508
|
+
}
|
509
|
+
|
510
|
+
u32 read = 0;
|
511
|
+
char* out = _memory + outPtr + 4;
|
512
|
+
char ch;
|
513
|
+
while ((ch = fgetc(fp)) != EOF) {
|
514
|
+
out[read++] = ch;
|
515
|
+
}
|
516
|
+
|
517
|
+
fclose(fp);
|
518
|
+
|
519
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
520
|
+
}`);
|
521
|
+
line(`__Porffor_readFile((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
|
522
|
+
vals.pop();
|
523
|
+
break;
|
524
|
+
|
462
525
|
default:
|
463
526
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
464
527
|
break;
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import type {} from './porffor.d.ts';
|
2
|
+
|
1
3
|
export const __Array_isArray = (x: unknown): boolean =>
|
2
4
|
// Porffor.wasm`local.get ${x+1}` == Porffor.TYPES.array;
|
3
5
|
Porffor.rawType(x) == Porffor.TYPES.array;
|
@@ -146,10 +148,78 @@ export const __Array_prototype_valueOf = (_this: any[]) => {
|
|
146
148
|
return _this;
|
147
149
|
};
|
148
150
|
|
149
|
-
|
151
|
+
|
152
|
+
export const __Array_prototype_forEach = (_this: any[], callbackFn: any) => {
|
150
153
|
const len: i32 = _this.length;
|
151
154
|
let i: i32 = 0;
|
152
155
|
while (i < len) {
|
153
156
|
callbackFn(_this[i], i++, _this);
|
154
157
|
}
|
158
|
+
};
|
159
|
+
|
160
|
+
export const __Array_prototype_filter = (_this: any[], callbackFn: any) => {
|
161
|
+
const out: any[] = [];
|
162
|
+
|
163
|
+
const len: i32 = _this.length;
|
164
|
+
let i: i32 = 0;
|
165
|
+
while (i < len) {
|
166
|
+
const el: any = _this[i];
|
167
|
+
if (Boolean(callbackFn(el, i++, _this))) out.push(el);
|
168
|
+
}
|
169
|
+
|
170
|
+
return out;
|
171
|
+
};
|
172
|
+
|
173
|
+
export const __Array_prototype_map = (_this: any[], callbackFn: any) => {
|
174
|
+
const out: any[] = [];
|
175
|
+
|
176
|
+
const len: i32 = _this.length;
|
177
|
+
let i: i32 = 0;
|
178
|
+
while (i < len) {
|
179
|
+
out.push(callbackFn(_this[i], i++, _this));
|
180
|
+
}
|
181
|
+
|
182
|
+
return out;
|
183
|
+
};
|
184
|
+
|
185
|
+
export const __Array_prototype_find = (_this: any[], callbackFn: any) => {
|
186
|
+
const len: i32 = _this.length;
|
187
|
+
let i: i32 = 0;
|
188
|
+
while (i < len) {
|
189
|
+
const el: any = _this[i];
|
190
|
+
if (Boolean(callbackFn(el, i++, _this))) return el;
|
191
|
+
}
|
192
|
+
};
|
193
|
+
|
194
|
+
export const __Array_prototype_findLast = (_this: any[], callbackFn: any) => {
|
195
|
+
let i: i32 = _this.length;
|
196
|
+
while (i > 0) {
|
197
|
+
const el: any = _this[--i];
|
198
|
+
if (Boolean(callbackFn(el, i, _this))) return el;
|
199
|
+
}
|
200
|
+
};
|
201
|
+
|
202
|
+
export const __Array_prototype_findIndex = (_this: any[], callbackFn: any) => {
|
203
|
+
const len: i32 = _this.length;
|
204
|
+
let i: i32 = 0;
|
205
|
+
while (i < len) {
|
206
|
+
if (Boolean(callbackFn(_this[i], i++, _this))) return i;
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
export const __Array_prototype_findLastIndex = (_this: any[], callbackFn: any) => {
|
211
|
+
let i: i32 = _this.length;
|
212
|
+
while (i > 0) {
|
213
|
+
if (Boolean(callbackFn(_this[--i], i, _this))) return i;
|
214
|
+
}
|
215
|
+
};
|
216
|
+
|
217
|
+
export const __Array_prototype_every = (_this: any[], callbackFn: any) => {
|
218
|
+
const len: i32 = _this.length;
|
219
|
+
let i: i32 = 0;
|
220
|
+
while (i < len) {
|
221
|
+
if (!Boolean(callbackFn(_this[i], i++, _this))) return false;
|
222
|
+
}
|
223
|
+
|
224
|
+
return true;
|
155
225
|
};
|
package/compiler/builtins/int.ts
CHANGED
@@ -0,0 +1,410 @@
|
|
1
|
+
import type {} from './porffor.d.ts';
|
2
|
+
|
3
|
+
// todo: use any and Number(x) in all these later
|
4
|
+
// todo: specify the rest of this file later
|
5
|
+
// todo/perf: make i32 variants later
|
6
|
+
// todo/perf: add a compiler pref for accuracy vs perf (epsilion?)
|
7
|
+
|
8
|
+
export const __Math_exp = (x: number): number => {
|
9
|
+
if (!Number.isFinite(x)) {
|
10
|
+
if (x == -Infinity) return 0;
|
11
|
+
return x;
|
12
|
+
}
|
13
|
+
|
14
|
+
if (x < 0) {
|
15
|
+
// exp(-x) = 1 / exp(+x)
|
16
|
+
return 1 / Math.exp(-x);
|
17
|
+
}
|
18
|
+
|
19
|
+
const k: number = Math.floor(x / Math.LN2);
|
20
|
+
const r: number = x - k * Math.LN2;
|
21
|
+
|
22
|
+
// Taylor series via Horner's method
|
23
|
+
let term: number = r;
|
24
|
+
let sum: number = 1 + r;
|
25
|
+
let i: number = 2;
|
26
|
+
|
27
|
+
while (Math.abs(term) > 1e-15) {
|
28
|
+
term *= r / i;
|
29
|
+
sum += term;
|
30
|
+
i++;
|
31
|
+
}
|
32
|
+
|
33
|
+
return sum * (1 << k);
|
34
|
+
};
|
35
|
+
|
36
|
+
export const __Math_log2 = (y: number): number => {
|
37
|
+
if (y <= 0) return NaN;
|
38
|
+
if (!Number.isFinite(y)) return y;
|
39
|
+
|
40
|
+
// approx using log knowledge
|
41
|
+
let x: number = y;
|
42
|
+
let exponent: number = 0;
|
43
|
+
|
44
|
+
while (x >= 2) {
|
45
|
+
x /= 2;
|
46
|
+
exponent++;
|
47
|
+
}
|
48
|
+
|
49
|
+
while (x < 1) {
|
50
|
+
x *= 2;
|
51
|
+
exponent--;
|
52
|
+
}
|
53
|
+
|
54
|
+
// 1 <= x < 2 -> 0 <= x < 1
|
55
|
+
x -= 1;
|
56
|
+
|
57
|
+
// refine with Newton-Raphson method
|
58
|
+
let delta: number;
|
59
|
+
do {
|
60
|
+
const e_x: number = Math.exp(x * Math.LN2);
|
61
|
+
delta = (e_x - y) / (e_x * Math.LN2);
|
62
|
+
x -= delta;
|
63
|
+
} while (Math.abs(delta) > 1e-15);
|
64
|
+
|
65
|
+
return x + exponent;
|
66
|
+
};
|
67
|
+
|
68
|
+
export const __Math_log = (y: number): number => {
|
69
|
+
if (y <= 0) {
|
70
|
+
if (y == 0) return -Infinity;
|
71
|
+
return NaN;
|
72
|
+
}
|
73
|
+
if (!Number.isFinite(y)) return y;
|
74
|
+
|
75
|
+
// guess using log knowledge
|
76
|
+
let x: number = y > 1 ? Math.log2(y) : 0;
|
77
|
+
|
78
|
+
// refine with Newton-Raphson method
|
79
|
+
let delta: number;
|
80
|
+
do {
|
81
|
+
const e_x: number = Math.exp(x);
|
82
|
+
delta = (e_x - y) / e_x;
|
83
|
+
x -= delta;
|
84
|
+
} while (Math.abs(delta) > 1e-15);
|
85
|
+
|
86
|
+
return x;
|
87
|
+
};
|
88
|
+
|
89
|
+
export const __Math_log10 = (x: number): number => {
|
90
|
+
if (x <= 0) {
|
91
|
+
if (x == 0) return -Infinity;
|
92
|
+
return NaN;
|
93
|
+
}
|
94
|
+
if (!Number.isFinite(x)) return x;
|
95
|
+
|
96
|
+
return Math.log(x) / Math.LN10;
|
97
|
+
};
|
98
|
+
|
99
|
+
// 21.3.2.26 Math.pow (base, exponent)
|
100
|
+
// https://tc39.es/ecma262/#sec-math.pow
|
101
|
+
export const __Math_pow = (base: number, exponent: number): number => {
|
102
|
+
// 1. Set base to ? ToNumber(base).
|
103
|
+
// 2. Set exponent to ? ToNumber(exponent).
|
104
|
+
// todo
|
105
|
+
|
106
|
+
// 3. Return Number::exponentiate(base, exponent).
|
107
|
+
|
108
|
+
// Number::exponentiate (base, exponent)
|
109
|
+
// https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate
|
110
|
+
// 1. If exponent is NaN, return NaN.
|
111
|
+
if (Number.isNaN(exponent)) return NaN;
|
112
|
+
|
113
|
+
// 2. If exponent is either +0𝔽 or -0𝔽, return 1𝔽.
|
114
|
+
if (exponent == 0) return 1;
|
115
|
+
|
116
|
+
if (!Number.isFinite(base)) {
|
117
|
+
// 3. If base is NaN, return NaN.
|
118
|
+
if (Number.isNaN(base)) return base;
|
119
|
+
|
120
|
+
// 4. If base is +∞𝔽, then
|
121
|
+
if (base == Infinity) {
|
122
|
+
// a. If exponent > +0𝔽, return +∞𝔽. Otherwise, return +0𝔽.
|
123
|
+
if (exponent > 0) return base;
|
124
|
+
return 0;
|
125
|
+
}
|
126
|
+
|
127
|
+
// 5. If base is -∞𝔽, then
|
128
|
+
const isOdd = exponent % 2 == 1;
|
129
|
+
|
130
|
+
// a. If exponent > +0𝔽, then
|
131
|
+
if (exponent > 0) {
|
132
|
+
// i. If exponent is an odd integral Number, return -∞𝔽. Otherwise, return +∞𝔽.
|
133
|
+
if (isOdd) return -Infinity;
|
134
|
+
return Infinity;
|
135
|
+
}
|
136
|
+
|
137
|
+
// b. Else,
|
138
|
+
// i. If exponent is an odd integral Number, return -0𝔽. Otherwise, return +0𝔽.
|
139
|
+
if (isOdd) return -0;
|
140
|
+
return 0;
|
141
|
+
}
|
142
|
+
|
143
|
+
if (base == 0) {
|
144
|
+
// 6. If base is +0𝔽, then
|
145
|
+
if (1 / base == Infinity) {
|
146
|
+
// a. If exponent > +0𝔽, return +0𝔽. Otherwise, return +∞𝔽.
|
147
|
+
if (exponent > 0) return 0;
|
148
|
+
return Infinity;
|
149
|
+
}
|
150
|
+
|
151
|
+
// 7. If base is -0𝔽, then
|
152
|
+
const isOdd = exponent % 2 == 1;
|
153
|
+
|
154
|
+
// a. If exponent > +0𝔽, then
|
155
|
+
if (exponent > 0) {
|
156
|
+
// i. If exponent is an odd integral Number, return -0𝔽. Otherwise, return +0𝔽.
|
157
|
+
if (isOdd) return -0;
|
158
|
+
return 0;
|
159
|
+
}
|
160
|
+
|
161
|
+
// b. Else,
|
162
|
+
// i. If exponent is an odd integral Number, return -∞𝔽. Otherwise, return +∞𝔽.
|
163
|
+
if (isOdd) return -Infinity;
|
164
|
+
return Infinity;
|
165
|
+
}
|
166
|
+
|
167
|
+
// 8. Assert: base is finite and is neither +0𝔽 nor -0𝔽.
|
168
|
+
// todo
|
169
|
+
|
170
|
+
// 9. If exponent is +∞𝔽, then
|
171
|
+
if (exponent == Infinity) {
|
172
|
+
const abs = Math.abs(base);
|
173
|
+
|
174
|
+
// a. If abs(ℝ(base)) > 1, return +∞𝔽.
|
175
|
+
if (abs > 1) return Infinity;
|
176
|
+
|
177
|
+
// b. If abs(ℝ(base)) = 1, return NaN.
|
178
|
+
if (abs == 1) return NaN;
|
179
|
+
|
180
|
+
// c. If abs(ℝ(base)) < 1, return +0𝔽.
|
181
|
+
return 0;
|
182
|
+
}
|
183
|
+
|
184
|
+
// 10. If exponent is -∞𝔽, then
|
185
|
+
if (exponent == -Infinity) {
|
186
|
+
const abs = Math.abs(base);
|
187
|
+
|
188
|
+
// a. If abs(ℝ(base)) > 1, return +0𝔽.
|
189
|
+
if (abs > 1) return 0;
|
190
|
+
|
191
|
+
// b. If abs(ℝ(base)) = 1, return NaN.
|
192
|
+
if (abs == 1) return NaN;
|
193
|
+
|
194
|
+
// c. If abs(ℝ(base)) < 1, return +∞𝔽.
|
195
|
+
return Infinity;
|
196
|
+
}
|
197
|
+
|
198
|
+
// 11. Assert: exponent is finite and is neither +0𝔽 nor -0𝔽.
|
199
|
+
// todo
|
200
|
+
|
201
|
+
// 12. If base < -0𝔽 and exponent is not an integral Number, return NaN.
|
202
|
+
if (base < 0) if (!Number.isInteger(exponent)) return NaN;
|
203
|
+
|
204
|
+
// 13. Return an implementation-approximated Number value representing the result of raising ℝ(base) to the ℝ(exponent) power.
|
205
|
+
return Math.exp(exponent * Math.log(base));
|
206
|
+
};
|
207
|
+
|
208
|
+
|
209
|
+
export const __Math_expm1 = (x: number): number => {
|
210
|
+
if (!Number.isFinite(x)) {
|
211
|
+
if (x == -Infinity) return -1;
|
212
|
+
return x;
|
213
|
+
}
|
214
|
+
|
215
|
+
// use exp(x) - 1 for large x (perf)
|
216
|
+
if (Math.abs(x) > 1e-5) return Math.exp(x) - 1;
|
217
|
+
|
218
|
+
// Taylor series
|
219
|
+
let sum: number = x;
|
220
|
+
let term: number = x;
|
221
|
+
let i: number = 2;
|
222
|
+
|
223
|
+
while (Math.abs(term) > 1e-15) {
|
224
|
+
term *= x / i;
|
225
|
+
sum += term;
|
226
|
+
i++;
|
227
|
+
}
|
228
|
+
|
229
|
+
return sum;
|
230
|
+
};
|
231
|
+
|
232
|
+
export const __Math_log1p = (x: number): number => {
|
233
|
+
if (x == -1) return -Infinity; // log(0) = -inf
|
234
|
+
if (!Number.isFinite(x)) return x;
|
235
|
+
|
236
|
+
// use exp(x) - 1 for large x (perf)
|
237
|
+
if (Math.abs(x) > 1e-5) return Math.log(1 + x);
|
238
|
+
|
239
|
+
// Taylor series
|
240
|
+
let sum: number = 0;
|
241
|
+
let term: number = x;
|
242
|
+
let i: number = 2;
|
243
|
+
|
244
|
+
while (Math.abs(term) > 1e-15) {
|
245
|
+
term *= -x / i;
|
246
|
+
sum += term;
|
247
|
+
i++;
|
248
|
+
}
|
249
|
+
|
250
|
+
return sum;
|
251
|
+
};
|
252
|
+
|
253
|
+
|
254
|
+
export const __Math_sqrt = (y: number): number => {
|
255
|
+
if (y <= 0) {
|
256
|
+
if (y == 0) return 0;
|
257
|
+
return NaN;
|
258
|
+
}
|
259
|
+
if (!Number.isFinite(y)) return y;
|
260
|
+
|
261
|
+
// Babylonian method
|
262
|
+
let x: number = y;
|
263
|
+
let prev: number;
|
264
|
+
|
265
|
+
do {
|
266
|
+
prev = x;
|
267
|
+
x = 0.5 * (x + y / x);
|
268
|
+
} while (Math.abs(prev - x) > 1e-15);
|
269
|
+
|
270
|
+
return x;
|
271
|
+
};
|
272
|
+
|
273
|
+
export const __Math_cbrt = (y: number): number => {
|
274
|
+
if (y == 0) return 0; // cbrt(0) = 0
|
275
|
+
if (!Number.isFinite(y)) return y;
|
276
|
+
|
277
|
+
// Babylonian method
|
278
|
+
let x = Math.abs(y);
|
279
|
+
|
280
|
+
let prev: number;
|
281
|
+
|
282
|
+
do {
|
283
|
+
prev = x;
|
284
|
+
x = (2 * x + y / (x * x)) / 3;
|
285
|
+
} while (Math.abs(prev - x) > 1e-15);
|
286
|
+
|
287
|
+
return y < 0 ? -x : x;
|
288
|
+
};
|
289
|
+
|
290
|
+
|
291
|
+
// todo: varargs
|
292
|
+
export const __Math_hypot = (x: number, y: number): number => Math.sqrt(x * x + y * y);
|
293
|
+
|
294
|
+
export const __Math_sin = (x: number): number => {
|
295
|
+
// -inf <= x <= inf -> 0 <= x <= 2pi
|
296
|
+
const piX2: number = Math.PI * 2;
|
297
|
+
x %= piX2;
|
298
|
+
if (x < 0) x += piX2;
|
299
|
+
|
300
|
+
const x2: number = x * x;
|
301
|
+
|
302
|
+
return x * (
|
303
|
+
1 + x2 * (
|
304
|
+
-1.66666666666666307295e-1 + x2 * (
|
305
|
+
8.33333333332211858878e-3 + x2 * (
|
306
|
+
-1.98412698295895385996e-4 + x2 * (
|
307
|
+
2.75573136213857245213e-6 + x2 * (
|
308
|
+
-2.50507477628578072866e-8 + x2 * (
|
309
|
+
1.58962301576546568060e-10
|
310
|
+
)
|
311
|
+
)
|
312
|
+
)
|
313
|
+
)
|
314
|
+
)
|
315
|
+
)
|
316
|
+
);
|
317
|
+
|
318
|
+
// todo: investigate which is better (consider perf and accuracy)
|
319
|
+
// const x2 = x * x;
|
320
|
+
// const x4 = x2 * x2;
|
321
|
+
// const x6 = x4 * x2;
|
322
|
+
// const x8 = x4 * x4;
|
323
|
+
// const x10 = x6 * x4;
|
324
|
+
// const x12 = x6 * x6;
|
325
|
+
// const x14 = x12 * x2;
|
326
|
+
|
327
|
+
// return x * (
|
328
|
+
// 1 - x2 / 6 + x4 / 120 - x6 / 5040 + x8 / 362880 - x10 / 39916800 + x12 / 6227020800 - x14 / 1307674368000
|
329
|
+
// );
|
330
|
+
};
|
331
|
+
|
332
|
+
export const __Math_cos = (x: number): number => Math.sin(x - Math.PI / 2);
|
333
|
+
export const __Math_tan = (x: number): number => Math.sin(x) / Math.cos(x);
|
334
|
+
|
335
|
+
export const __Math_sinh = (x: number): number => (Math.exp(x) - Math.exp(-x)) / 2;
|
336
|
+
export const __Math_cosh = (x: number): number => (Math.exp(x) + Math.exp(-x)) / 2;
|
337
|
+
export const __Math_tanh = (x: number): number => Math.sinh(x) / Math.cosh(x);
|
338
|
+
|
339
|
+
|
340
|
+
export const __Math_asinh = (x: number): number => Math.log(x + Math.sqrt(x * x + 1));
|
341
|
+
export const __Math_acosh = (x: number): number => {
|
342
|
+
if (x < 1) return NaN;
|
343
|
+
return Math.log(x + Math.sqrt(x * x - 1));
|
344
|
+
};
|
345
|
+
export const __Math_atanh = (x: number): number => {
|
346
|
+
if (Math.abs(x) >= 1) return NaN;
|
347
|
+
return 0.5 * Math.log((1 + x) / (1 - x));
|
348
|
+
};
|
349
|
+
|
350
|
+
|
351
|
+
export const __Math_asin = (x: number): number => {
|
352
|
+
if (x <= -1) {
|
353
|
+
if (x == -1) return -Math.PI / 2;
|
354
|
+
return NaN;
|
355
|
+
}
|
356
|
+
if (x >= 1) {
|
357
|
+
if (x == 1) return Math.PI / 2;
|
358
|
+
return NaN;
|
359
|
+
}
|
360
|
+
|
361
|
+
// Taylor series
|
362
|
+
let sum: number = x;
|
363
|
+
let term: number = x;
|
364
|
+
let n: number = 1;
|
365
|
+
|
366
|
+
while (Math.abs(term) > 1e-15) {
|
367
|
+
term *= x * x * (2 * n - 1) * (2 * n - 1) / ((2 * n) * (2 * n + 1));
|
368
|
+
sum += term / (2 * n + 1);
|
369
|
+
n++;
|
370
|
+
}
|
371
|
+
|
372
|
+
return sum;
|
373
|
+
};
|
374
|
+
|
375
|
+
export const __Math_acos = (x: number): number => Math.asin(x) - Math.PI / 2;
|
376
|
+
|
377
|
+
export const __Math_atan = (x: number): number => {
|
378
|
+
if (x == Infinity) return Math.PI / 2
|
379
|
+
if (x == -Infinity) return -Math.PI / 2;
|
380
|
+
|
381
|
+
// Taylor series
|
382
|
+
let sum: number = x;
|
383
|
+
let term: number = x;
|
384
|
+
let n: number = 1;
|
385
|
+
|
386
|
+
while (Math.abs(term) > 1e-15) {
|
387
|
+
term *= -x * x * (2 * n - 1) / ((2 * n) * (2 * n + 1));
|
388
|
+
sum += term;
|
389
|
+
n++;
|
390
|
+
}
|
391
|
+
|
392
|
+
return sum;
|
393
|
+
};
|
394
|
+
|
395
|
+
export const __Math_atan2 = (y: number, x: number): number => {
|
396
|
+
if (x == 0) {
|
397
|
+
if (y > 0) return Math.PI / 2;
|
398
|
+
if (y < 0) return -Math.PI / 2;
|
399
|
+
|
400
|
+
return NaN;
|
401
|
+
}
|
402
|
+
|
403
|
+
const ratio = y / x;
|
404
|
+
if (x > 0) {
|
405
|
+
return Math.atan(ratio);
|
406
|
+
}
|
407
|
+
|
408
|
+
if (y >= 0) return Math.atan(ratio) + Math.PI;
|
409
|
+
return Math.atan(ratio) - Math.PI;
|
410
|
+
};
|