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