jz 0.0.0 → 0.1.1

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/module/math.js ADDED
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Math module - Math.sin, Math.cos, Math.sqrt, Math.PI, etc.
3
+ *
4
+ * Module API:
5
+ * - ctx.core.emit['math.X'] = (args) => WasmNode - custom emitters
6
+ * - ctx.core.stdlib['math.X'] = '(func ...)' - WAT function definitions
7
+ * - ctx.deps['math.X'] = ['dep1', 'dep2'] - stdlib dependencies
8
+ * - include('math.X') - marks stdlib for inclusion (called by emitters)
9
+ *
10
+ * Prepare resolves Math.sin(x) → ['()', 'math.sin', x]
11
+ * Compile looks up ctx.core.emit['math.sin'] and calls it.
12
+ *
13
+ * @module math
14
+ */
15
+
16
+ import { typed, asF64, asI32, temp, arrayLoop } from '../src/ir.js'
17
+ import { emit } from '../src/emit.js'
18
+ import { inc } from '../src/ctx.js'
19
+ import { repOf } from '../src/analyze.js'
20
+
21
+ export default (ctx) => {
22
+ // Helpers: all math ops take f64 and return f64
23
+ const f = (op, a) => typed([op, asF64(emit(a))], 'f64')
24
+ const f2 = (op, a, b) => typed([op, asF64(emit(a)), asF64(emit(b))], 'f64')
25
+ // floor/ceil/trunc/round are no-ops on integer-valued operands. When the
26
+ // arg is a local whose every def is integer-valued (intCertain lattice),
27
+ // skip the wasm op and just hand back the operand cast to f64.
28
+ const fInt = (op, a) => typeof a === 'string' && repOf(a)?.intCertain === true
29
+ ? asF64(emit(a))
30
+ : f(op, a)
31
+ const call = (name, ...args) => (inc(name), typed(['call', `$${name}`, ...args.map(a => asF64(emit(a)))], 'f64'))
32
+ const callDeps = (deps, name, ...args) => (inc(...deps), call(name, ...args))
33
+
34
+ // Constants
35
+ ctx.core.emit['math.PI'] = () => typed(['f64.const', Math.PI], 'f64')
36
+ ctx.core.emit['math.E'] = () => typed(['f64.const', Math.E], 'f64')
37
+ ctx.core.emit['math.LN2'] = () => typed(['f64.const', Math.LN2], 'f64')
38
+ ctx.core.emit['math.LN10'] = () => typed(['f64.const', Math.LN10], 'f64')
39
+ ctx.core.emit['math.LOG2E'] = () => typed(['f64.const', Math.LOG2E], 'f64')
40
+ ctx.core.emit['math.LOG10E'] = () => typed(['f64.const', Math.LOG10E], 'f64')
41
+ ctx.core.emit['math.SQRT2'] = () => typed(['f64.const', Math.SQRT2], 'f64')
42
+ ctx.core.emit['math.SQRT1_2'] = () => typed(['f64.const', Math.SQRT1_2], 'f64')
43
+
44
+ /** Emit array reduce with a WASM binary op (for Math.max(...arr), Math.min(...arr)) */
45
+ function emitArrayReduce(wasmOp, arrExpr, initVal) {
46
+ const acc = temp('mr')
47
+ const loop = arrayLoop(emit(arrExpr), (_ptr, _len, _i, item) => [
48
+ ['local.set', `$${acc}`, [wasmOp, ['local.get', `$${acc}`], asF64(item)]]
49
+ ])
50
+ return typed(['block', ['result', 'f64'],
51
+ ['local.set', `$${acc}`, ['f64.const', initVal]],
52
+ ...loop,
53
+ ['local.get', `$${acc}`]], 'f64')
54
+ }
55
+
56
+ // Built-in WASM ops
57
+ ctx.core.emit['math.sqrt'] = a => f('f64.sqrt', a)
58
+ ctx.core.emit['math.abs'] = a => f('f64.abs', a)
59
+ ctx.core.emit['math.floor'] = a => fInt('f64.floor', a)
60
+ ctx.core.emit['math.ceil'] = a => fInt('f64.ceil', a)
61
+ ctx.core.emit['math.trunc'] = a => fInt('f64.trunc', a)
62
+ ctx.core.emit['math.min'] = (a, b, ...rest) => {
63
+ // Spread: Math.min(...arr) — iterate array to find min
64
+ if (!b && Array.isArray(a) && a[0] === '...') return emitArrayReduce('f64.min', a[1], Infinity)
65
+ return f2('f64.min', a, b)
66
+ }
67
+ ctx.core.emit['math.max'] = (a, b, ...rest) => {
68
+ if (!b && Array.isArray(a) && a[0] === '...') return emitArrayReduce('f64.max', a[1], -Infinity)
69
+ return f2('f64.max', a, b)
70
+ }
71
+ ctx.core.emit['math.round'] = a => fInt('f64.nearest', a)
72
+ ctx.core.emit['math.fround'] = a => typed(['f64.promote_f32', ['f32.demote_f64', asF64(emit(a))]], 'f64')
73
+
74
+ // Sign
75
+ ctx.core.emit['math.sign'] = a => call('math.sign', a)
76
+
77
+ // Trig
78
+ ctx.core.emit['math.sin'] = a => call('math.sin', a)
79
+ ctx.core.emit['math.cos'] = a => call('math.cos', a)
80
+ ctx.core.emit['math.tan'] = a => callDeps(['math.sin', 'math.cos', 'math.tan'], 'math.tan', a)
81
+
82
+ // Inverse trig
83
+ ctx.core.emit['math.asin'] = a => callDeps(['math.atan', 'math.asin'], 'math.asin', a)
84
+ ctx.core.emit['math.acos'] = a => callDeps(['math.atan', 'math.asin', 'math.acos'], 'math.acos', a)
85
+ ctx.core.emit['math.atan'] = a => call('math.atan', a)
86
+ ctx.core.emit['math.atan2'] = (a, b) => callDeps(['math.atan', 'math.atan2'], 'math.atan2', a, b)
87
+
88
+ // Hyperbolic
89
+ ctx.core.emit['math.sinh'] = a => callDeps(['math.exp', 'math.sinh'], 'math.sinh', a)
90
+ ctx.core.emit['math.cosh'] = a => callDeps(['math.exp', 'math.cosh'], 'math.cosh', a)
91
+ ctx.core.emit['math.tanh'] = a => callDeps(['math.exp', 'math.tanh'], 'math.tanh', a)
92
+
93
+ // Inverse hyperbolic
94
+ ctx.core.emit['math.asinh'] = a => callDeps(['math.log', 'math.asinh'], 'math.asinh', a)
95
+ ctx.core.emit['math.acosh'] = a => callDeps(['math.log', 'math.acosh'], 'math.acosh', a)
96
+ ctx.core.emit['math.atanh'] = a => callDeps(['math.log', 'math.atanh'], 'math.atanh', a)
97
+
98
+ // Exponential and logarithmic
99
+ ctx.core.emit['math.exp'] = a => call('math.exp', a)
100
+ ctx.core.emit['math.expm1'] = a => callDeps(['math.exp', 'math.expm1'], 'math.expm1', a)
101
+ ctx.core.emit['math.log'] = a => call('math.log', a)
102
+ ctx.core.emit['math.log2'] = a => callDeps(['math.log', 'math.log2'], 'math.log2', a)
103
+ ctx.core.emit['math.log10'] = a => callDeps(['math.log', 'math.log10'], 'math.log10', a)
104
+ ctx.core.emit['math.log1p'] = a => callDeps(['math.log', 'math.log1p'], 'math.log1p', a)
105
+
106
+ // Power
107
+ ctx.core.emit['math.pow'] = (a, b) => callDeps(['math.exp', 'math.log', 'math.pow'], 'math.pow', a, b)
108
+ ctx.core.emit['**'] = ctx.core.emit['math.pow']
109
+ ctx.core.emit['math.cbrt'] = a => callDeps(['math.exp', 'math.log', 'math.pow', 'math.cbrt'], 'math.cbrt', a)
110
+ ctx.core.emit['math.hypot'] = (a, b) => call('math.hypot', a, b)
111
+
112
+ // Integer/bit operations: return i32 directly. Consumers `asF64`-rebox at
113
+ // store/return boundaries; consumers staying in i32 (bit chains, i32 locals)
114
+ // skip the convert/trunc round-trip entirely.
115
+ ctx.core.emit['math.clz32'] = a => typed(['i32.clz', asI32(emit(a))], 'i32')
116
+ ctx.core.emit['math.imul'] = (a, b) => typed(['i32.mul', asI32(emit(a)), asI32(emit(b))], 'i32')
117
+
118
+ // Random
119
+ ctx.core.emit['math.random'] = () => (inc('math.random'), typed(['call', '$math.random'], 'f64'))
120
+
121
+ // ============================================
122
+ // WAT stdlib implementations
123
+ // ============================================
124
+
125
+ ctx.core.stdlib['math.sign'] = `(func $math.sign (param $x f64) (result f64)
126
+ (if (result f64) (f64.eq (local.get $x) (f64.const 0.0))
127
+ (then (f64.const 0.0))
128
+ (else (if (result f64) (f64.gt (local.get $x) (f64.const 0.0))
129
+ (then (f64.const 1.0))
130
+ (else (f64.const -1.0))))))`
131
+
132
+ ctx.core.stdlib['math.sin'] = `(func $math.sin (param $x f64) (result f64)
133
+ (local $n i32) (local $r f64) (local $x2 f64) (local $sign f64)
134
+ (local.set $sign (f64.const 1.0))
135
+ (local.set $n (i32.trunc_f64_s (f64.floor (f64.div (local.get $x) (f64.const ${Math.PI})))))
136
+ (local.set $r (f64.sub (local.get $x) (f64.mul (f64.convert_i32_s (local.get $n)) (f64.const ${Math.PI}))))
137
+ (if (i32.and (local.get $n) (i32.const 1)) (then (local.set $sign (f64.const -1.0))))
138
+ (if (f64.gt (local.get $r) (f64.const ${Math.PI / 2})) (then (local.set $r (f64.sub (f64.const ${Math.PI}) (local.get $r)))))
139
+ (if (f64.lt (local.get $r) (f64.const 0.0)) (then
140
+ (local.set $r (f64.neg (local.get $r)))
141
+ (local.set $sign (f64.neg (local.get $sign)))))
142
+ (local.set $x2 (f64.mul (local.get $r) (local.get $r)))
143
+ (f64.mul (local.get $sign) (f64.mul (local.get $r) (f64.sub (f64.const 1.0) (f64.mul (local.get $x2)
144
+ (f64.sub (f64.const 0.16666666666666666) (f64.mul (local.get $x2)
145
+ (f64.sub (f64.const 0.008333333333333333) (f64.mul (local.get $x2)
146
+ (f64.sub (f64.const 0.0001984126984126984) (f64.mul (local.get $x2)
147
+ (f64.sub (f64.const 0.0000027557319223985893) (f64.mul (local.get $x2)
148
+ (f64.const 2.505210838544172e-8))))))))))))))`
149
+
150
+ ctx.core.stdlib['math.cos'] = `(func $math.cos (param $x f64) (result f64)
151
+ (local $n i32) (local $r f64) (local $x2 f64) (local $sign f64)
152
+ (local.set $sign (f64.const 1.0))
153
+ (local.set $n (i32.trunc_f64_s (f64.floor (f64.div (local.get $x) (f64.const ${Math.PI})))))
154
+ (local.set $r (f64.sub (local.get $x) (f64.mul (f64.convert_i32_s (local.get $n)) (f64.const ${Math.PI}))))
155
+ (if (i32.and (local.get $n) (i32.const 1)) (then (local.set $sign (f64.const -1.0))))
156
+ (if (f64.gt (local.get $r) (f64.const ${Math.PI / 2})) (then
157
+ (local.set $r (f64.sub (f64.const ${Math.PI}) (local.get $r)))
158
+ (local.set $sign (f64.neg (local.get $sign)))))
159
+ (if (f64.lt (local.get $r) (f64.const 0.0)) (then (local.set $r (f64.neg (local.get $r)))))
160
+ (local.set $x2 (f64.mul (local.get $r) (local.get $r)))
161
+ (f64.mul (local.get $sign) (f64.sub (f64.const 1.0) (f64.mul (local.get $x2)
162
+ (f64.sub (f64.const 0.5) (f64.mul (local.get $x2)
163
+ (f64.sub (f64.const 0.041666666666666664) (f64.mul (local.get $x2)
164
+ (f64.sub (f64.const 0.001388888888888889) (f64.mul (local.get $x2)
165
+ (f64.sub (f64.const 0.0000248015873015873) (f64.mul (local.get $x2)
166
+ (f64.const 2.7557319223985893e-7)))))))))))))`
167
+
168
+ ctx.core.stdlib['math.tan'] = `(func $math.tan (param $x f64) (result f64)
169
+ (f64.div (call $math.sin (local.get $x)) (call $math.cos (local.get $x))))`
170
+
171
+ ctx.core.stdlib['math.exp'] = `(func $math.exp (param $x f64) (result f64)
172
+ (local $k i32) (local $t f64) (local $t2 f64) (local $result f64) (local $pow2 f64)
173
+ (if (result f64) (f64.gt (local.get $x) (f64.const 709.0)) (then (f64.const 1.7976931348623157e+308)) (else
174
+ (if (result f64) (f64.lt (local.get $x) (f64.const -745.0)) (then (f64.const 0.0)) (else
175
+ (local.set $k (i32.trunc_f64_s (f64.div (local.get $x) (f64.const ${Math.LN2}))))
176
+ (local.set $t (f64.sub (local.get $x) (f64.mul (f64.convert_i32_s (local.get $k)) (f64.const ${Math.LN2}))))
177
+ (local.set $t2 (f64.mul (local.get $t) (local.get $t)))
178
+ (local.set $result (f64.add (f64.const 1.0) (f64.add (local.get $t)
179
+ (f64.mul (local.get $t2) (f64.add (f64.const 0.5)
180
+ (f64.mul (local.get $t) (f64.add (f64.const 0.16666666666666666)
181
+ (f64.mul (local.get $t) (f64.add (f64.const 0.041666666666666664)
182
+ (f64.mul (local.get $t) (f64.add (f64.const 0.008333333333333333)
183
+ (f64.mul (local.get $t) (f64.const 0.001388888888888889)))))))))))))
184
+ (local.set $pow2 (f64.const 1.0))
185
+ (if (i32.gt_s (local.get $k) (i32.const 0))
186
+ (then (block $done (loop $loop
187
+ (br_if $done (i32.le_s (local.get $k) (i32.const 0)))
188
+ (local.set $pow2 (f64.mul (local.get $pow2) (f64.const 2.0)))
189
+ (local.set $k (i32.sub (local.get $k) (i32.const 1)))
190
+ (br $loop)))
191
+ (local.set $result (f64.mul (local.get $result) (local.get $pow2))))
192
+ (else (if (i32.lt_s (local.get $k) (i32.const 0))
193
+ (then (block $done2 (loop $loop2
194
+ (br_if $done2 (i32.ge_s (local.get $k) (i32.const 0)))
195
+ (local.set $pow2 (f64.mul (local.get $pow2) (f64.const 2.0)))
196
+ (local.set $k (i32.add (local.get $k) (i32.const 1)))
197
+ (br $loop2)))
198
+ (local.set $result (f64.div (local.get $result) (local.get $pow2)))))))
199
+ (local.get $result))))))`
200
+
201
+ ctx.core.stdlib['math.expm1'] = `(func $math.expm1 (param $x f64) (result f64)
202
+ (f64.sub (call $math.exp (local.get $x)) (f64.const 1.0)))`
203
+
204
+ ctx.core.stdlib['math.log'] = `(func $math.log (param $x f64) (result f64)
205
+ (local $k i32) (local $y f64) (local $s f64) (local $z f64)
206
+ (if (f64.ne (local.get $x) (local.get $x))
207
+ (then (return (local.get $x))))
208
+ (if (f64.le (local.get $x) (f64.const 0.0))
209
+ (then (return (f64.const 0.0))))
210
+ (if (f64.eq (local.get $x) (f64.const 1.0))
211
+ (then (return (f64.const 0.0))))
212
+ (if (f64.eq (local.get $x) (f64.const inf))
213
+ (then (return (local.get $x))))
214
+ (local.set $k (i32.const 0))
215
+ (local.set $y (local.get $x))
216
+ (block $done_up
217
+ (loop $scale_up
218
+ (br_if $done_up (f64.lt (local.get $y) (f64.const 2.0)))
219
+ (local.set $y (f64.mul (local.get $y) (f64.const 0.5)))
220
+ (local.set $k (i32.add (local.get $k) (i32.const 1)))
221
+ (br $scale_up)))
222
+ (block $done_down
223
+ (loop $scale_down
224
+ (br_if $done_down (f64.ge (local.get $y) (f64.const 1.0)))
225
+ (local.set $y (f64.mul (local.get $y) (f64.const 2.0)))
226
+ (local.set $k (i32.sub (local.get $k) (i32.const 1)))
227
+ (br $scale_down)))
228
+ (local.set $s (f64.div (f64.sub (local.get $y) (f64.const 1.0)) (f64.add (local.get $y) (f64.const 1.0))))
229
+ (local.set $z (f64.mul (local.get $s) (local.get $s)))
230
+ (f64.add
231
+ (f64.mul (f64.convert_i32_s (local.get $k)) (f64.const ${Math.LN2}))
232
+ (f64.mul (f64.const 2.0) (f64.mul (local.get $s) (f64.add (f64.const 1.0)
233
+ (f64.mul (local.get $z) (f64.add (f64.const 0.3333333333333333)
234
+ (f64.mul (local.get $z) (f64.add (f64.const 0.2)
235
+ (f64.mul (local.get $z) (f64.add (f64.const 0.14285714285714285)
236
+ (f64.mul (local.get $z) (f64.add (f64.const 0.1111111111111111)
237
+ (f64.mul (local.get $z) (f64.const 0.09090909090909091)))))))))))))))`
238
+
239
+ ctx.core.stdlib['math.log2'] = `(func $math.log2 (param $x f64) (result f64)
240
+ (f64.div (call $math.log (local.get $x)) (f64.const ${Math.LN2})))`
241
+
242
+ ctx.core.stdlib['math.log10'] = `(func $math.log10 (param $x f64) (result f64)
243
+ (f64.div (call $math.log (local.get $x)) (f64.const ${Math.LN10})))`
244
+
245
+ ctx.core.stdlib['math.log1p'] = `(func $math.log1p (param $x f64) (result f64)
246
+ (call $math.log (f64.add (f64.const 1.0) (local.get $x))))`
247
+
248
+ ctx.core.stdlib['math.pow'] = `(func $math.pow (param $x f64) (param $y f64) (result f64)
249
+ (local $result f64) (local $n i32) (local $neg_base i32) (local $abs_x f64)
250
+ (if (result f64) (f64.eq (local.get $y) (f64.const 0.0))
251
+ (then (f64.const 1.0))
252
+ (else (if (result f64) (f64.eq (local.get $x) (f64.const 0.0))
253
+ (then (f64.const 0.0))
254
+ (else (if (result f64) (f64.eq (local.get $x) (f64.const 1.0))
255
+ (then (f64.const 1.0))
256
+ (else (if (result f64) (f64.eq (local.get $y) (f64.const 1.0))
257
+ (then (local.get $x))
258
+ (else
259
+ (if (result f64)
260
+ (i32.and
261
+ (f64.eq (f64.nearest (local.get $y)) (local.get $y))
262
+ (f64.le (f64.abs (local.get $y)) (f64.const 100.0)))
263
+ (then
264
+ (local.set $abs_x (f64.abs (local.get $x)))
265
+ (local.set $neg_base (i32.and (f64.lt (local.get $x) (f64.const 0.0))
266
+ (i32.and (i32.trunc_f64_s (local.get $y)) (i32.const 1))))
267
+ (local.set $n (i32.trunc_f64_s (f64.abs (local.get $y))))
268
+ (local.set $result (f64.const 1.0))
269
+ (block $done
270
+ (loop $loop
271
+ (br_if $done (i32.le_s (local.get $n) (i32.const 0)))
272
+ (if (i32.and (local.get $n) (i32.const 1))
273
+ (then (local.set $result (f64.mul (local.get $result) (local.get $abs_x)))))
274
+ (local.set $abs_x (f64.mul (local.get $abs_x) (local.get $abs_x)))
275
+ (local.set $n (i32.shr_s (local.get $n) (i32.const 1)))
276
+ (br $loop)))
277
+ (if (f64.lt (local.get $y) (f64.const 0.0))
278
+ (then (local.set $result (f64.div (f64.const 1.0) (local.get $result)))))
279
+ (if (local.get $neg_base)
280
+ (then (local.set $result (f64.neg (local.get $result)))))
281
+ (local.get $result))
282
+ (else
283
+ (if (result f64) (f64.lt (local.get $x) (f64.const 0.0))
284
+ (then (f64.const 0.0))
285
+ (else (call $math.exp (f64.mul (local.get $y) (call $math.log (local.get $x)))))))))))))))))`
286
+
287
+ ctx.core.stdlib['math.atan'] = `(func $math.atan (param $x f64) (result f64)
288
+ (local $x2 f64) (local $abs_x f64) (local $reduced f64)
289
+ (local.set $abs_x (f64.abs (local.get $x)))
290
+ (if (result f64) (f64.gt (local.get $abs_x) (f64.const 1.0))
291
+ (then
292
+ (if (result f64) (f64.gt (local.get $x) (f64.const 0.0))
293
+ (then (f64.sub (f64.const ${Math.PI / 2}) (call $math.atan (f64.div (f64.const 1.0) (local.get $x)))))
294
+ (else (f64.add (f64.neg (f64.const ${Math.PI / 2})) (call $math.atan (f64.div (f64.const 1.0) (local.get $x)))))))
295
+ (else
296
+ (if (result f64) (f64.gt (local.get $abs_x) (f64.const 0.5))
297
+ (then
298
+ (local.set $reduced (f64.div (local.get $x) (f64.add (f64.const 1.0) (f64.sqrt (f64.add (f64.const 1.0) (f64.mul (local.get $x) (local.get $x)))))))
299
+ (f64.mul (f64.const 2.0) (call $math.atan (local.get $reduced))))
300
+ (else
301
+ (local.set $x2 (f64.mul (local.get $x) (local.get $x)))
302
+ (f64.mul (local.get $x)
303
+ (f64.sub (f64.const 1.0)
304
+ (f64.mul (local.get $x2)
305
+ (f64.sub (f64.const 0.3333333333333333)
306
+ (f64.mul (local.get $x2)
307
+ (f64.sub (f64.const 0.2)
308
+ (f64.mul (local.get $x2)
309
+ (f64.sub (f64.const 0.14285714285714285)
310
+ (f64.mul (local.get $x2)
311
+ (f64.sub (f64.const 0.1111111111111111)
312
+ (f64.mul (local.get $x2)
313
+ (f64.sub (f64.const 0.09090909090909091)
314
+ (f64.mul (local.get $x2) (f64.const 0.07692307692307693)))))))))))))))))))`
315
+
316
+ ctx.core.stdlib['math.asin'] = `(func $math.asin (param $x f64) (result f64)
317
+ (if (result f64) (f64.gt (f64.abs (local.get $x)) (f64.const 1.0))
318
+ (then (f64.const 0.0))
319
+ (else (call $math.atan (f64.div (local.get $x)
320
+ (f64.sqrt (f64.sub (f64.const 1.0) (f64.mul (local.get $x) (local.get $x)))))))))`
321
+
322
+ ctx.core.stdlib['math.acos'] = `(func $math.acos (param $x f64) (result f64)
323
+ (f64.sub (f64.const ${Math.PI / 2}) (call $math.asin (local.get $x))))`
324
+
325
+ ctx.core.stdlib['math.atan2'] = `(func $math.atan2 (param $y f64) (param $x f64) (result f64)
326
+ (if (result f64) (f64.eq (local.get $x) (f64.const 0.0)) (then
327
+ (if (result f64) (f64.eq (local.get $y) (f64.const 0.0)) (then (f64.const 0.0)) (else
328
+ (if (result f64) (f64.gt (local.get $y) (f64.const 0.0)) (then (f64.const ${Math.PI / 2})) (else (f64.neg (f64.const ${Math.PI / 2})))))))
329
+ (else (if (result f64) (f64.ge (local.get $x) (f64.const 0.0))
330
+ (then (call $math.atan (f64.div (local.get $y) (local.get $x))))
331
+ (else (if (result f64) (f64.ge (local.get $y) (f64.const 0.0))
332
+ (then (f64.add (call $math.atan (f64.div (local.get $y) (local.get $x))) (f64.const ${Math.PI})))
333
+ (else (f64.sub (call $math.atan (f64.div (local.get $y) (local.get $x))) (f64.const ${Math.PI})))))))))`
334
+
335
+ ctx.core.stdlib['math.sinh'] = `(func $math.sinh (param $x f64) (result f64)
336
+ (local $ex f64)
337
+ (local.set $ex (call $math.exp (f64.abs (local.get $x))))
338
+ (local.set $ex (f64.mul (f64.const 0.5) (f64.sub (local.get $ex) (f64.div (f64.const 1.0) (local.get $ex)))))
339
+ (if (result f64) (f64.lt (local.get $x) (f64.const 0.0)) (then (f64.neg (local.get $ex))) (else (local.get $ex))))`
340
+
341
+ ctx.core.stdlib['math.cosh'] = `(func $math.cosh (param $x f64) (result f64)
342
+ (local $ex f64) (local.set $ex (call $math.exp (f64.abs (local.get $x))))
343
+ (f64.mul (f64.const 0.5) (f64.add (local.get $ex) (f64.div (f64.const 1.0) (local.get $ex)))))`
344
+
345
+ ctx.core.stdlib['math.tanh'] = `(func $math.tanh (param $x f64) (result f64)
346
+ (local $e2x f64)
347
+ (if (result f64) (f64.gt (f64.abs (local.get $x)) (f64.const 22.0))
348
+ (then (if (result f64) (f64.lt (local.get $x) (f64.const 0.0)) (then (f64.const -1.0)) (else (f64.const 1.0))))
349
+ (else (local.set $e2x (call $math.exp (f64.mul (f64.const 2.0) (f64.abs (local.get $x)))))
350
+ (local.set $e2x (f64.div (f64.sub (local.get $e2x) (f64.const 1.0)) (f64.add (local.get $e2x) (f64.const 1.0))))
351
+ (if (result f64) (f64.lt (local.get $x) (f64.const 0.0)) (then (f64.neg (local.get $e2x))) (else (local.get $e2x))))))`
352
+
353
+ ctx.core.stdlib['math.asinh'] = `(func $math.asinh (param $x f64) (result f64)
354
+ (call $math.log (f64.add (local.get $x) (f64.sqrt (f64.add (f64.mul (local.get $x) (local.get $x)) (f64.const 1.0))))))`
355
+
356
+ ctx.core.stdlib['math.acosh'] = `(func $math.acosh (param $x f64) (result f64)
357
+ (if (result f64) (f64.lt (local.get $x) (f64.const 1.0)) (then (f64.const 0.0)) (else
358
+ (call $math.log (f64.add (local.get $x) (f64.sqrt (f64.sub (f64.mul (local.get $x) (local.get $x)) (f64.const 1.0))))))))`
359
+
360
+ ctx.core.stdlib['math.atanh'] = `(func $math.atanh (param $x f64) (result f64)
361
+ (f64.mul (f64.const 0.5) (call $math.log (f64.div (f64.add (f64.const 1.0) (local.get $x)) (f64.sub (f64.const 1.0) (local.get $x))))))`
362
+
363
+ ctx.core.stdlib['math.cbrt'] = `(func $math.cbrt (param $x f64) (result f64)
364
+ (local $y f64)
365
+ (if (result f64) (f64.lt (local.get $x) (f64.const 0.0))
366
+ (then (f64.neg (call $math.cbrt (f64.neg (local.get $x)))))
367
+ (else (if (result f64) (f64.eq (local.get $x) (f64.const 0.0))
368
+ (then (f64.const 0.0))
369
+ (else
370
+ ;; Initial guess via pow, then Newton-Raphson: y = (2y + x/y²)/3
371
+ (local.set $y (call $math.pow (local.get $x) (f64.const 0.3333333333333333)))
372
+ (local.set $y (f64.div (f64.add (f64.mul (f64.const 2.0) (local.get $y)) (f64.div (local.get $x) (f64.mul (local.get $y) (local.get $y)))) (f64.const 3.0)))
373
+ (local.set $y (f64.div (f64.add (f64.mul (f64.const 2.0) (local.get $y)) (f64.div (local.get $x) (f64.mul (local.get $y) (local.get $y)))) (f64.const 3.0)))
374
+ (local.get $y))))))`
375
+
376
+ ctx.core.stdlib['math.hypot'] = `(func $math.hypot (param $x f64) (param $y f64) (result f64)
377
+ (f64.sqrt (f64.add (f64.mul (local.get $x) (local.get $x)) (f64.mul (local.get $y) (local.get $y)))))`
378
+
379
+ ctx.core.stdlib['math.random'] = `(func $math.random (result f64)
380
+ (local $s i32)
381
+ (local.set $s (global.get $math.rng_state))
382
+ (local.set $s (i32.xor (local.get $s) (i32.shl (local.get $s) (i32.const 13))))
383
+ (local.set $s (i32.xor (local.get $s) (i32.shr_u (local.get $s) (i32.const 17))))
384
+ (local.set $s (i32.xor (local.get $s) (i32.shl (local.get $s) (i32.const 5))))
385
+ (global.set $math.rng_state (local.get $s))
386
+ (f64.div (f64.convert_i32_u (i32.and (local.get $s) (i32.const 0x7FFFFFFF))) (f64.const 2147483647.0)))`
387
+
388
+ // Global for random state
389
+ ctx.scope.globals.set('math.rng_state', '(global $math.rng_state (mut i32) (i32.const 12345))')
390
+ }