@grain/stdlib 0.5.2 → 0.5.4

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/array.gr +61 -1
  3. package/array.md +113 -0
  4. package/bigint.md +30 -30
  5. package/buffer.gr +24 -22
  6. package/char.gr +2 -2
  7. package/float32.md +3 -3
  8. package/float64.md +3 -3
  9. package/immutablemap.gr +493 -0
  10. package/immutablemap.md +479 -0
  11. package/immutablepriorityqueue.gr +360 -0
  12. package/immutablepriorityqueue.md +291 -0
  13. package/immutableset.gr +498 -0
  14. package/immutableset.md +449 -0
  15. package/list.gr +75 -2
  16. package/list.md +110 -0
  17. package/map.gr +1 -2
  18. package/marshal.gr +1058 -0
  19. package/marshal.md +76 -0
  20. package/number.gr +689 -23
  21. package/number.md +362 -27
  22. package/package.json +1 -1
  23. package/pervasives.gr +16 -5
  24. package/pervasives.md +28 -0
  25. package/priorityqueue.gr +261 -0
  26. package/priorityqueue.md +309 -0
  27. package/queue.gr +14 -1
  28. package/queue.md +16 -1
  29. package/regex.gr +90 -67
  30. package/runtime/bigint.gr +4 -4
  31. package/runtime/compare.gr +179 -0
  32. package/runtime/compare.md +6 -0
  33. package/runtime/equal.gr +3 -3
  34. package/runtime/exception.gr +9 -5
  35. package/runtime/exception.md +8 -2
  36. package/runtime/gc.gr +2 -1
  37. package/runtime/malloc.gr +1 -3
  38. package/runtime/numberUtils.gr +11 -11
  39. package/runtime/numbers.gr +423 -100
  40. package/runtime/numbers.md +50 -0
  41. package/runtime/string.gr +4 -2
  42. package/set.gr +26 -27
  43. package/stack.gr +12 -0
  44. package/stack.md +15 -0
  45. package/string.gr +409 -53
  46. package/string.md +164 -1
  47. package/sys/file.gr +4 -4
  48. package/sys/file.md +3 -3
  49. package/sys/process.gr +3 -3
  50. package/sys/process.md +3 -3
  51. package/sys/random.gr +2 -2
  52. package/sys/random.md +2 -2
  53. package/sys/time.gr +2 -2
  54. package/sys/time.md +2 -2
package/number.gr CHANGED
@@ -13,33 +13,51 @@ import {
13
13
  coerceNumberToWasmF64,
14
14
  reducedInteger,
15
15
  isFloat,
16
+ isInteger,
17
+ isRational,
16
18
  isBoxedNumber,
19
+ scalbn,
17
20
  } from "runtime/numbers"
18
21
  import { parseInt } from "runtime/stringUtils"
19
22
  import { newFloat64, newInt64 } from "runtime/dataStructures"
20
23
  import Tags from "runtime/unsafe/tags"
24
+ import Exception from "runtime/exception"
21
25
 
22
26
  /**
23
27
  * @section Constants: Number constant values.
24
28
  */
25
29
 
30
+ /**
31
+ * NaN represented as a Number value.
32
+ *
33
+ * @since v0.5.4
34
+ */
35
+ export let nan = 0.0 / 0.0
36
+
37
+ /**
38
+ * Infinity represented as a Number value.
39
+ *
40
+ * @since v0.5.4
41
+ */
42
+ export let infinity = 1.0 / 0.0
43
+
26
44
  /**
27
45
  * Pi represented as a Number value.
28
- *
46
+ *
29
47
  * @since v0.5.2
30
48
  */
31
49
  export let pi = 3.141592653589793
32
50
 
33
51
  /**
34
52
  * Tau represented as a Number value.
35
- *
53
+ *
36
54
  * @since v0.5.2
37
55
  */
38
56
  export let tau = 6.283185307179586
39
57
 
40
58
  /**
41
59
  * Euler's number represented as a Number value.
42
- *
60
+ *
43
61
  * @since v0.5.2
44
62
  */
45
63
  export let e = 2.718281828459045
@@ -92,6 +110,464 @@ export let mul = (*)
92
110
  */
93
111
  export let div = (/)
94
112
 
113
+ // Exponentiation by squaring https://en.wikipedia.org/wiki/Exponentiation_by_squaring special path for int^int
114
+ let rec expBySquaring = (y, x, n) => {
115
+ if (n == 0) {
116
+ 1
117
+ } else if (n == 1) {
118
+ x * y
119
+ } else if (n % 2 == 0) {
120
+ expBySquaring(y, x * x, n / 2)
121
+ } else {
122
+ expBySquaring(x * y, x * x, (n - 1) / 2)
123
+ }
124
+ }
125
+
126
+ // Math.pow is largely based on https://git.musl-libc.org/cgit/musl/tree/src/math/pow.c
127
+ /*
128
+ * ====================================================
129
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
130
+ *
131
+ * Permission to use, copy, modify, and distribute this
132
+ * software is freely granted, provided that this notice
133
+ * is preserved.
134
+ * ====================================================
135
+ */
136
+ /**
137
+ * Computes the exponentiation of the given base and power.
138
+ *
139
+ * @param base: The base number
140
+ * @param power: The exponent number
141
+ * @returns The base raised to the given power
142
+ *
143
+ * @since v0.5.4
144
+ */
145
+ @unsafe
146
+ export let pow = (base, power) => {
147
+ // TODO(#1476): Move this into runtime/numbers.gr
148
+ if (base == 1 && power != 0) {
149
+ 1
150
+ } else if (
151
+ isInteger(WasmI32.fromGrain(base)) && isInteger(WasmI32.fromGrain(power))
152
+ ) {
153
+ if (power < 0) expBySquaring(1, 1 / base, power * -1)
154
+ else expBySquaring(1, base, power)
155
+ } else {
156
+ // TODO(#553): Refactor once we have early return
157
+ // Based on https://git.musl-libc.org/cgit/musl/tree/src/math/pow.c
158
+ let (==) = WasmF64.eq
159
+ let (!=) = WasmF64.ne
160
+ let (<=) = WasmF64.le
161
+ let (/) = WasmF64.div
162
+ let (*) = WasmF64.mul
163
+ let (+) = WasmF64.add
164
+ // Constants
165
+ let infinity = 1.0W / 0.0W
166
+ let nan = 0.0W / 0.0W
167
+ let x = coerceNumberToWasmF64(base)
168
+ let y = coerceNumberToWasmF64(power)
169
+ let mut foundOutput = false, output = 0.0W
170
+ // Fast paths
171
+ if (WasmF64.abs(y) <= 2.0W) {
172
+ if (y == 2.0W) {
173
+ foundOutput = true
174
+ output = x * x
175
+ } else if (y == 0.5W) {
176
+ foundOutput = true
177
+ if (x != infinity) output = WasmF64.abs(WasmF64.sqrt(x))
178
+ else output = infinity
179
+ } else if (y == -1.0W) {
180
+ foundOutput = true
181
+ output = 1.0W / x
182
+ } else if (y == 1.0W) {
183
+ foundOutput = true
184
+ output = x
185
+ } else if (y == 0.0W) {
186
+ foundOutput = true
187
+ output = nan
188
+ }
189
+ }
190
+ // Full calculation
191
+ if (foundOutput) {
192
+ WasmI32.toGrain(newFloat64(output)): Number
193
+ } else {
194
+ let dp_h1 = WasmF64.reinterpretI64(0x3FE2B80340000000N)
195
+ let dp_l1 = WasmF64.reinterpretI64(0x3E4CFDEB43CFD006N)
196
+ let two53 = WasmF64.reinterpretI64(0x4340000000000000N)
197
+ let huge = WasmF64.reinterpretI64(0x7E37E43C8800759CN)
198
+ let tiny = WasmF64.reinterpretI64(0x01A56E1FC2F8F359N)
199
+ let l1 = WasmF64.reinterpretI64(0x3FE3333333333303N)
200
+ let l2 = WasmF64.reinterpretI64(0x3FDB6DB6DB6FABFFN)
201
+ let l3 = WasmF64.reinterpretI64(0x3FD55555518F264DN)
202
+ let l4 = WasmF64.reinterpretI64(0x3FD17460A91D4101N)
203
+ let l5 = WasmF64.reinterpretI64(0x3FCD864A93C9DB65N)
204
+ let l6 = WasmF64.reinterpretI64(0x3FCA7E284A454EEFN)
205
+ let p1 = WasmF64.reinterpretI64(0x3FC555555555553EN)
206
+ let p2 = WasmF64.reinterpretI64(0xBF66C16C16BEBD93N)
207
+ let p3 = WasmF64.reinterpretI64(0x3F11566AAF25DE2CN)
208
+ let p4 = WasmF64.reinterpretI64(0xBEBBBD41C5D26BF1N)
209
+ let p5 = WasmF64.reinterpretI64(0x3E66376972BEA4D0N)
210
+ let lg2 = WasmF64.reinterpretI64(0x3FE62E42FEFA39EFN)
211
+ let lg2_h = WasmF64.reinterpretI64(0x3FE62E4300000000N)
212
+ let lg2_l = WasmF64.reinterpretI64(0xBE205C610CA86C39N)
213
+ let ovt = WasmF64.reinterpretI64(0x3C971547652B82FEN)
214
+ let cp = WasmF64.reinterpretI64(0x3FEEC709DC3A03FDN)
215
+ let cp_h = WasmF64.reinterpretI64(0x3FEEC709E0000000N)
216
+ let cp_l = WasmF64.reinterpretI64(0xBE3E2FE0145B01F5N)
217
+ let ivln2 = WasmF64.reinterpretI64(0x3FF71547652B82FEN)
218
+ let ivln2_h = WasmF64.reinterpretI64(0x3FF7154760000000N)
219
+ let ivln2_l = WasmF64.reinterpretI64(0x3E54AE0BF85DDF44N)
220
+ let inv3 = WasmF64.reinterpretI64(0x3FD5555555555555N)
221
+ let (==) = WasmI32.eq
222
+ let (!=) = WasmI32.ne
223
+ let (>=) = WasmI32.geS
224
+ let (<=) = WasmI32.leS
225
+ let (&) = WasmI32.and
226
+ let (|) = WasmI32.or
227
+ let (>) = WasmI32.gtS
228
+ let (<) = WasmI32.ltS
229
+ let (<<) = WasmI32.shl
230
+ let (>>) = WasmI32.shrS
231
+ let (-) = WasmI32.sub
232
+ let (+) = WasmI32.add
233
+ let u_ = WasmI64.reinterpretF64(x)
234
+ let hx = WasmI32.wrapI64(WasmI64.shrS(u_, 32N))
235
+ let lx = WasmI32.wrapI64(u_)
236
+ let u_ = WasmI64.reinterpretF64(y)
237
+ let hy = WasmI32.wrapI64(WasmI64.shrS(u_, 32N))
238
+ let ly = WasmI32.wrapI64(u_)
239
+ let mut ix = hx & 0x7FFFFFFFn
240
+ let iy = hy & 0x7FFFFFFFn
241
+ if ((iy | ly) == 0n) { // x**0 = 1, even if x is NaN
242
+ 1 // return 1
243
+ } else if (
244
+ // Either Argument is Nan
245
+ ix > 0x7FF00000n ||
246
+ ix == 0x7FF00000n && lx != 0n ||
247
+ iy > 0x7FF00000n ||
248
+ iy == 0x7FF00000n && ly != 0n
249
+ ) {
250
+ WasmI32.toGrain(newFloat64(WasmF64.add(x, y))): Number
251
+ } else {
252
+ let mut yisint = 0n
253
+ let mut k = 0n
254
+ if (hx < 0n) {
255
+ if (iy >= 0x43400000n) {
256
+ yisint = 2n
257
+ } else if (iy >= 0x3FF00000n) {
258
+ k = (iy >> 20n) - 0x3FFn
259
+ let mut offset = 0n
260
+ let mut _ly = 0n
261
+ if (k > 20n) {
262
+ offset = 52n - k
263
+ _ly = ly
264
+ } else {
265
+ offset = 20n - k
266
+ _ly = iy
267
+ }
268
+ let jj = _ly >> offset
269
+ if (jj << offset == _ly) yisint = 2n - (jj & 1n)
270
+ }
271
+ }
272
+ if (ly == 0n) {
273
+ if (iy == 0x7FF00000n) { // y is +- inf
274
+ foundOutput = true
275
+ if ((ix - 0x3FF00000n | lx) == 0n) { // C: (-1)**+-inf is 1, JS: NaN
276
+ output = nan
277
+ } else if (ix >= 0x3FF00000n) { // (|x|>1)**+-inf = inf,0
278
+ if (hy >= 0n) output = y else output = 0.0W
279
+ } else { // (|x|<1)**+-inf = 0,inf
280
+ if (hy >= 0n) output = 0.0W else output = y * -1.0W
281
+ }
282
+ } else if (iy == 0x3FF00000n) {
283
+ foundOutput = true
284
+ if (hy >= 0n) output = x else output = 1.0W / x
285
+ } else if (hy == 0x3FE00000n) {
286
+ foundOutput = true
287
+ output = x * x
288
+ } else if (hy == 0x3FE00000n) {
289
+ if (hx >= 0n) {
290
+ foundOutput = true
291
+ output = WasmF64.sqrt(x)
292
+ }
293
+ }
294
+ }
295
+ if (foundOutput) {
296
+ WasmI32.toGrain(newFloat64(output)): Number
297
+ } else {
298
+ let mut ax = WasmF64.abs(x)
299
+ let mut z = 0.0W
300
+ if (
301
+ lx == 0n && (ix == 0n || ix == 0x7FF00000n || ix == 0x3FF00000n)
302
+ ) {
303
+ z = ax
304
+ if (hy < 0n) z = 1.0W / z
305
+ if (hx < 0n) {
306
+ if ((ix - 0x3FF00000n | yisint) == 0n) {
307
+ let d = WasmF64.sub(z, z)
308
+ z = d / d
309
+ } else if (yisint == 1n) z *= -1.0W
310
+ }
311
+ WasmI32.toGrain(newFloat64(z)): Number
312
+ } else {
313
+ let mut s = 1.0W
314
+ if (hx < 0n) {
315
+ if (yisint == 0n) {
316
+ let d = WasmF64.sub(x, x)
317
+ foundOutput = true
318
+ output = d / d
319
+ } else if (yisint == 1n) s = -1.0W
320
+ }
321
+ if (foundOutput) {
322
+ WasmI32.toGrain(newFloat64(output)): Number
323
+ } else {
324
+ let mut t1 = 0.0W,
325
+ t2 = 0.0W,
326
+ p_h = 0.0W,
327
+ p_l = 0.0W,
328
+ r = 0.0W,
329
+ t = 0.0W,
330
+ u = 0.0W,
331
+ v = 0.0W,
332
+ w = 0.0W
333
+ let mut j = 0n, n = 0n
334
+ if (iy > 0x41E00000n) {
335
+ if (iy > 0x43F00000n) {
336
+ if (ix <= 0x3FEFFFFFn) {
337
+ foundOutput = true
338
+ if (hy < 0n) output = huge * huge else output = tiny * tiny
339
+ } else if (ix >= 0x3FF00000n) {
340
+ foundOutput = true
341
+ if (hy > 0n) output = huge * huge else output = tiny * tiny
342
+ }
343
+ }
344
+ if (!foundOutput) {
345
+ if (ix < 0x3FEFFFFFn) {
346
+ foundOutput = true
347
+ if (hy < 0n) {
348
+ output = s * huge * huge
349
+ } else {
350
+ output = s * tiny * tiny
351
+ }
352
+ } else if (ix > 0x3FF00000n) {
353
+ foundOutput = true
354
+ if (hy > 0n) {
355
+ output = s * huge * huge
356
+ } else {
357
+ output = s * tiny * tiny
358
+ }
359
+ } else {
360
+ let (-) = WasmF64.sub
361
+ let (&) = WasmI64.and
362
+ t = ax - 1.0W
363
+ w = t * t * (0.5W - t * (inv3 - t * 0.25W))
364
+ u = ivln2_h * t
365
+ v = t * ivln2_l - w * ivln2
366
+ t1 = WasmF64.add(u, v)
367
+ t1 = WasmF64.reinterpretI64(
368
+ WasmI64.reinterpretF64(t1) & 0xFFFFFFFF00000000N
369
+ )
370
+ t2 = v - (t1 - u)
371
+ }
372
+ }
373
+ } else {
374
+ let mut ss = 0.0W,
375
+ s2 = 0.0W,
376
+ s_h = 0.0W,
377
+ s_l = 0.0W,
378
+ t_h = 0.0W,
379
+ t_l = 0.0W
380
+ n = 0n
381
+ if (ix < 0x00100000n) {
382
+ let (>>) = WasmI64.shrU
383
+ ax *= two53
384
+ n -= 53n
385
+ ix = WasmI32.wrapI64(WasmI64.reinterpretF64(ax) >> 32N)
386
+ }
387
+ n += (ix >> 20n) - 0x3FFn
388
+ j = ix & 0x000FFFFFn
389
+ ix = j | 0x3FF00000n
390
+ if (j <= 0x3988En) {
391
+ k = 0n
392
+ } else if (j < 0xBB67An) {
393
+ k = 1n
394
+ } else {
395
+ k = 0n
396
+ n += 1n
397
+ ix -= 0x00100000n
398
+ }
399
+ let (&) = WasmI64.and
400
+ let (|) = WasmI64.or
401
+ let (<<) = WasmI64.shl
402
+ ax = WasmF64.reinterpretI64(
403
+ WasmI64.reinterpretF64(ax) & 0xFFFFFFFFN |
404
+ WasmI64.extendI32S(ix) << 32N
405
+ )
406
+ let bp = if (k != 0n) 1.5W else 1.0W
407
+ u = WasmF64.sub(ax, bp)
408
+ v = 1.0W / WasmF64.add(ax, bp)
409
+ ss = u * v
410
+ s_h = ss
411
+ s_h = WasmF64.reinterpretI64(
412
+ WasmI64.reinterpretF64(s_h) & 0xFFFFFFFF00000000N
413
+ )
414
+ t_h = WasmF64.reinterpretI64(
415
+ WasmI64.shl(
416
+ WasmI64.extendI32S(
417
+ WasmI32.or(WasmI32.shrS(ix, 1n), 0x20000000n) +
418
+ 0x00080000n +
419
+ WasmI32.shl(k, 18n)
420
+ ),
421
+ 32N
422
+ )
423
+ )
424
+ let (-) = WasmF64.sub
425
+ let (+) = WasmF64.add
426
+ t_l = ax - (t_h - bp)
427
+ s_l = v * (u - s_h * t_h - s_h * t_l)
428
+ s2 = ss * ss
429
+ //formatter-ignore
430
+ r = s2 * s2 * (l1 + s2 * (l2 + s2 * (l3 + s2 * (l4 + s2 * (l5 + s2 * l6)))))
431
+ r += s_l * (s_h + ss)
432
+ s2 = s_h * s_h
433
+ t_h = 3.0W + s2 + r
434
+ t_h = WasmF64.reinterpretI64(
435
+ WasmI64.reinterpretF64(t_h) & 0xFFFFFFFF00000000N
436
+ )
437
+ t_l = r - (t_h - 3.0W - s2)
438
+ u = s_h * t_h
439
+ v = s_l * t_h + t_l * ss
440
+ p_h = u + v
441
+ p_h = WasmF64.reinterpretI64(
442
+ WasmI64.reinterpretF64(p_h) & 0xFFFFFFFF00000000N
443
+ )
444
+ p_l = v - (p_h - u)
445
+ let z_h = cp_h * p_h
446
+ let dp_l = if (k != 0n) dp_l1 else 0.0W
447
+ let z_l = cp_l * p_h + p_l * cp + dp_l
448
+ t = WasmF64.convertI32S(n)
449
+ let dp_h = if (k != 0n) dp_h1 else 0.0W
450
+ t1 = z_h + z_l + dp_h + t
451
+ t1 = WasmF64.reinterpretI64(
452
+ WasmI64.reinterpretF64(t1) & 0xFFFFFFFF00000000N
453
+ )
454
+ t2 = z_l - (t1 - t - dp_h - z_h)
455
+ }
456
+ if (foundOutput) {
457
+ WasmI32.toGrain(newFloat64(output)): Number
458
+ } else {
459
+ let (>) = WasmF64.gt
460
+ let (&) = WasmI64.and
461
+ let (-) = WasmF64.sub
462
+ let (+) = WasmF64.add
463
+ let (>>) = WasmI64.shrS
464
+ let y1 = WasmF64.reinterpretI64(
465
+ WasmI64.reinterpretF64(y) & 0xFFFFFFFF00000000N
466
+ )
467
+ p_l = (y - y1) * t1 + y * t2
468
+ p_h = y1 * t1
469
+ z = p_l + p_h
470
+ let u_ = WasmI64.reinterpretF64(z)
471
+ let j = WasmI32.wrapI64(u_ >> 32N)
472
+ let i = WasmI32.wrapI64(u_)
473
+ if (j >= 0x40900000n) {
474
+ if ((WasmI32.sub(j, 0x40900000n) | i) != 0n) {
475
+ foundOutput = true
476
+ output = s * huge * huge
477
+ } else if (p_l + ovt > z - p_h) {
478
+ foundOutput = true
479
+ output = s * huge * huge
480
+ }
481
+ } else if (WasmI32.and(j, 0x7FFFFFFFn) >= 0x4090CC00n) {
482
+ if (WasmI32.sub(j, 0xC090CC00n | i) != 0n) {
483
+ foundOutput = true
484
+ output = s * tiny * tiny
485
+ } else if (WasmF64.le(p_l, z - p_h)) {
486
+ foundOutput = true
487
+ output = s * tiny * tiny
488
+ }
489
+ }
490
+ if (foundOutput) {
491
+ WasmI32.toGrain(newFloat64(output)): Number
492
+ } else {
493
+ let (&) = WasmI32.and
494
+ let (>>) = WasmI32.shrS
495
+ let (-) = WasmI32.sub
496
+ let (+) = WasmI32.add
497
+ let (>) = WasmI32.gtS
498
+ let (*) = WasmI32.mul
499
+ let i = j & 0x7FFFFFFFn
500
+ k = (i >> 20n) - 0x3FFn
501
+ n = 0n
502
+ if (i > 0x3FE00000n) {
503
+ n = j + (0x00100000n >> k + 1n)
504
+ k = ((n & 0x7FFFFFFFn) >> 20n) - 0x3FFn
505
+ t = 0.0W
506
+ t = WasmF64.reinterpretI64(
507
+ WasmI64.shl(
508
+ WasmI64.extendI32S(
509
+ n & WasmI32.xor(0x000FFFFFn >> k, -1n)
510
+ ),
511
+ 32N
512
+ )
513
+ )
514
+ n = (n & 0x000FFFFFn | 0x00100000n) >> 20n - k
515
+ if (j < 0n) n *= -1n
516
+ p_h = WasmF64.sub(p_h, t)
517
+ }
518
+ let (&) = WasmI64.and
519
+ let (|) = WasmI64.or
520
+ let (*) = WasmF64.mul
521
+ let (-) = WasmF64.sub
522
+ let (+) = WasmF64.add
523
+ t = WasmF64.add(p_l, p_h)
524
+ t = WasmF64.reinterpretI64(
525
+ WasmI64.reinterpretF64(t) & 0xFFFFFFFF00000000N
526
+ )
527
+ u = t * lg2_h
528
+ v = (p_l - (t - p_h)) * lg2 + t * lg2_l
529
+ z = u + v
530
+ w = v - (z - u)
531
+ t = z * z
532
+ t1 = z - t * (p1 + t * (p2 + t * (p3 + t * (p4 + t * p5))))
533
+ r = z * t1 / (t1 - 2.0W) - (w + z * w)
534
+ z = 1.0W - (r - z)
535
+ let j = WasmI32.add(
536
+ WasmI32.wrapI64(
537
+ WasmI64.shrS(WasmI64.reinterpretF64(z), 32N)
538
+ ),
539
+ WasmI32.shl(n, 20n)
540
+ )
541
+ if (WasmI32.shrS(j, 20n) <= 0n) {
542
+ z = scalbn(z, n)
543
+ } else {
544
+ z = WasmF64.reinterpretI64(
545
+ WasmI64.reinterpretF64(z) & 0xFFFFFFFFN |
546
+ WasmI64.shl(WasmI64.extendI32S(j), 32N)
547
+ )
548
+ }
549
+ WasmI32.toGrain(newFloat64(s * z)): Number
550
+ }
551
+ }
552
+ }
553
+ }
554
+ }
555
+ }
556
+ }
557
+ }
558
+ }
559
+
560
+ /**
561
+ * Computes the exponentiation of Euler's number to the given power.
562
+ *
563
+ * @param power: The exponent number
564
+ * @returns The `Number.e` value raised to the given power
565
+ *
566
+ * @since v0.5.4
567
+ */
568
+ export let exp = power => {
569
+ if (power == 0) 1 else pow(e, power)
570
+ }
95
571
  /**
96
572
  * Computes the square root of its operand.
97
573
  *
@@ -138,8 +614,9 @@ export let sign = x => {
138
614
  * @returns The smaller of the two operands
139
615
  *
140
616
  * @since v0.4.0
617
+ * @history v0.5.4: Handle NaN properly
141
618
  */
142
- export let min = (x: Number, y: Number) => if (y > x) x else y
619
+ export let min = (x: Number, y: Number) => if (compare(x, y) < 0) x else y
143
620
 
144
621
  /**
145
622
  * Returns the larger of its operands.
@@ -149,8 +626,9 @@ export let min = (x: Number, y: Number) => if (y > x) x else y
149
626
  * @returns The larger of the two operands
150
627
  *
151
628
  * @since v0.4.0
629
+ * @history v0.5.4: Handle NaN properly
152
630
  */
153
- export let max = (x: Number, y: Number) => if (x > y) x else y
631
+ export let max = (x: Number, y: Number) => if (compare(x, y) > 0) x else y
154
632
 
155
633
  /**
156
634
  * Rounds its operand up to the next largest integer.
@@ -159,12 +637,19 @@ export let max = (x: Number, y: Number) => if (x > y) x else y
159
637
  * @returns The next largest integer of the operand
160
638
  *
161
639
  * @since v0.4.0
640
+ * @history v0.5.4: Handle NaN and Infinity properly
162
641
  */
163
642
  @unsafe
164
643
  export let ceil = (x: Number) => {
165
- let xval = coerceNumberToWasmF64(x)
166
- let ceiling = WasmI64.truncF64S(WasmF64.ceil(xval))
167
- WasmI32.toGrain(reducedInteger(ceiling)): Number
644
+ if (x != x) {
645
+ nan
646
+ } else if (x == infinity) {
647
+ infinity
648
+ } else {
649
+ let xval = coerceNumberToWasmF64(x)
650
+ let ceiling = WasmI64.truncF64S(WasmF64.ceil(xval))
651
+ WasmI32.toGrain(reducedInteger(ceiling)): Number
652
+ }
168
653
  }
169
654
 
170
655
  /**
@@ -174,12 +659,19 @@ export let ceil = (x: Number) => {
174
659
  * @returns The previous integer of the operand
175
660
  *
176
661
  * @since v0.4.0
662
+ * @history v0.5.4: Handle NaN and Infinity properly
177
663
  */
178
664
  @unsafe
179
665
  export let floor = (x: Number) => {
180
- let xval = coerceNumberToWasmF64(x)
181
- let floored = WasmI64.truncF64S(WasmF64.floor(xval))
182
- WasmI32.toGrain(reducedInteger(floored)): Number
666
+ if (x != x) {
667
+ nan
668
+ } else if (x == infinity) {
669
+ infinity
670
+ } else {
671
+ let xval = coerceNumberToWasmF64(x)
672
+ let floored = WasmI64.truncF64S(WasmF64.floor(xval))
673
+ WasmI32.toGrain(reducedInteger(floored)): Number
674
+ }
183
675
  }
184
676
 
185
677
  /**
@@ -189,12 +681,19 @@ export let floor = (x: Number) => {
189
681
  * @returns The integer part of the operand
190
682
  *
191
683
  * @since v0.4.0
684
+ * @history v0.5.4: Handle NaN and Infinity properly
192
685
  */
193
686
  @unsafe
194
687
  export let trunc = (x: Number) => {
195
- let xval = coerceNumberToWasmF64(x)
196
- let trunced = WasmI64.truncF64S(xval)
197
- WasmI32.toGrain(reducedInteger(trunced)): Number
688
+ if (x != x) {
689
+ nan
690
+ } else if (x == infinity) {
691
+ infinity
692
+ } else {
693
+ let xval = coerceNumberToWasmF64(x)
694
+ let trunced = WasmI64.truncF64S(xval)
695
+ WasmI32.toGrain(reducedInteger(trunced)): Number
696
+ }
198
697
  }
199
698
 
200
699
  /**
@@ -204,12 +703,19 @@ export let trunc = (x: Number) => {
204
703
  * @returns The nearest integer to the operand
205
704
  *
206
705
  * @since v0.4.0
706
+ * @history v0.5.4: Handle NaN and Infinity properly
207
707
  */
208
708
  @unsafe
209
709
  export let round = (x: Number) => {
210
- let xval = coerceNumberToWasmF64(x)
211
- let rounded = WasmI64.truncF64S(WasmF64.nearest(xval))
212
- WasmI32.toGrain(reducedInteger(rounded)): Number
710
+ if (x != x) {
711
+ nan
712
+ } else if (x == infinity) {
713
+ infinity
714
+ } else {
715
+ let xval = coerceNumberToWasmF64(x)
716
+ let rounded = WasmI64.truncF64S(WasmF64.nearest(xval))
717
+ WasmI32.toGrain(reducedInteger(rounded)): Number
718
+ }
213
719
  }
214
720
 
215
721
  /**
@@ -232,6 +738,45 @@ export let abs = (x: Number) => if (0 > x) x * -1 else x
232
738
  */
233
739
  export let neg = (x: Number) => x * -1
234
740
 
741
+ /**
742
+ * Checks if a number is a floating point value.
743
+ *
744
+ * @param x: The number to check
745
+ * @returns `true` if the value is a floating point number or `false` otherwise
746
+ *
747
+ * @since v0.5.3
748
+ */
749
+ @unsafe
750
+ export let isFloat = (x: Number) => {
751
+ isFloat(WasmI32.fromGrain(x))
752
+ }
753
+
754
+ /**
755
+ * Checks if a number is an integer.
756
+ *
757
+ * @param x: The number to check
758
+ * @returns `true` if the value is an integer or `false` otherwise
759
+ *
760
+ * @since v0.5.3
761
+ */
762
+ @unsafe
763
+ export let isInteger = (x: Number) => {
764
+ isInteger(WasmI32.fromGrain(x))
765
+ }
766
+
767
+ /**
768
+ * Checks if a number is a non-integer rational value.
769
+ *
770
+ * @param x: The number to check
771
+ * @returns `true` if the value is a non-integer rational number or `false` otherwise
772
+ *
773
+ * @since v0.5.3
774
+ */
775
+ @unsafe
776
+ export let isRational = (x: Number) => {
777
+ isRational(WasmI32.fromGrain(x))
778
+ }
779
+
235
780
  /**
236
781
  * Checks if a number is finite.
237
782
  * All values are finite exept for floating point NaN, infinity or negative infinity.
@@ -376,14 +921,19 @@ let chebyshevSine = (radians: Number) => {
376
921
  * @returns The computed sine
377
922
  *
378
923
  * @since v0.5.2
924
+ * @history v0.5.4: Handle NaN and Infinity
379
925
  */
380
926
  export let sin = (radians: Number) => {
381
- let quot = reduceToPiBound(radians)
382
- let bounded = radians - pi * quot
383
- if (quot % 2 == 0) {
384
- chebyshevSine(bounded)
927
+ if (radians != radians || radians == infinity) {
928
+ nan
385
929
  } else {
386
- neg(chebyshevSine(bounded))
930
+ let quot = reduceToPiBound(radians)
931
+ let bounded = radians - pi * quot
932
+ if (quot % 2 == 0) {
933
+ chebyshevSine(bounded)
934
+ } else {
935
+ neg(chebyshevSine(bounded))
936
+ }
387
937
  }
388
938
  }
389
939
 
@@ -394,7 +944,123 @@ export let sin = (radians: Number) => {
394
944
  * @returns The computed cosine
395
945
  *
396
946
  * @since v0.5.2
947
+ * @history v0.5.4: Handle NaN and Infinity
397
948
  */
398
949
  export let cos = (radians: Number) => {
399
- sin(pi / 2 + radians)
950
+ if (radians != radians || radians == infinity) {
951
+ nan
952
+ } else {
953
+ sin(pi / 2 + radians)
954
+ }
955
+ }
956
+
957
+ /**
958
+ * Computes the tangent of a number (in radians) using Chebyshev polynomials.
959
+ *
960
+ * @param radians: The input in radians
961
+ * @returns The computed tangent
962
+ *
963
+ * @since v0.5.4
964
+ */
965
+ export let tan = (radians: Number) => {
966
+ if (isNaN(radians) || isInfinite(radians)) {
967
+ nan
968
+ } else {
969
+ sin(radians) / cos(radians)
970
+ }
971
+ }
972
+
973
+ // Math.gamma implemented using the Lanczos approximation
974
+ // https://en.wikipedia.org/wiki/Lanczos_approximation
975
+ /**
976
+ * Computes the gamma function of a value using Lanczos approximation.
977
+ * Fails when the given value is zero.
978
+ *
979
+ * @param z: The value to interpolate
980
+ * @returns The gamma of the given value
981
+ *
982
+ * @since v0.5.4
983
+ */
984
+ export let rec gamma = z => {
985
+ if (z == 0) {
986
+ throw Exception.InvalidArgument("Gamma of 0 is undefined")
987
+ } else if (isInteger(z) && z > 0) {
988
+ let mut output = 1
989
+ for (let mut i = 1; i < z; i += 1) {
990
+ output *= i
991
+ }
992
+ output
993
+ } else {
994
+ let mut z = z
995
+ let g = 7
996
+ let c = [>
997
+ 0.99999999999980993,
998
+ 676.5203681218851,
999
+ -1259.1392167224028,
1000
+ 771.32342877765313,
1001
+ -176.61502916214059,
1002
+ 12.507343278686905,
1003
+ -0.13857109526572012,
1004
+ 9.9843695780195716e-6,
1005
+ 1.5056327351493116e-7,
1006
+ ]
1007
+ let mut output = 0
1008
+ if (z < 0.5) {
1009
+ output = pi / (sin(pi * z) * gamma(1 - z))
1010
+ } else if (z == 0.5) {
1011
+ // Handle this case separately because it is out of the domain of Number.pow when calculating
1012
+ output = 1.7724538509055159
1013
+ } else {
1014
+ z -= 1
1015
+ let mut x = c[0]
1016
+ for (let mut i = 1; i < g + 2; i += 1) {
1017
+ x += c[i] / (z + i)
1018
+ }
1019
+
1020
+ let t = z + g + 0.5
1021
+ output = sqrt(2 * pi) * pow(t, z + 0.5) * exp(t * -1) * x
1022
+ }
1023
+ if (abs(output) == infinity) infinity else output
1024
+ }
1025
+ }
1026
+
1027
+ /**
1028
+ * Computes the product of consecutive integers for an integer input and computes the gamma function for non-integer inputs.
1029
+ * Fails if the input is a negative number.
1030
+ *
1031
+ * @param n: The value to factorialize
1032
+ * @returns The factorial of the given value
1033
+ *
1034
+ * @since v0.5.4
1035
+ */
1036
+ export let rec factorial = n => {
1037
+ if (isInteger(n) && n < 0) gamma(abs(n) + 1) * -1 else if (
1038
+ !isInteger(n) && n < 0
1039
+ ) {
1040
+ throw Exception.InvalidArgument(
1041
+ "Cannot compute the factorial of a negative non-integer"
1042
+ )
1043
+ } else {
1044
+ gamma(n + 1)
1045
+ }
400
1046
  }
1047
+
1048
+ /**
1049
+ * Converts degrees to radians.
1050
+ *
1051
+ * @param degrees: The value to convert
1052
+ * @returns The value in radians
1053
+ *
1054
+ * @since v0.5.4
1055
+ */
1056
+ export let toRadians = degrees => degrees * (pi / 180)
1057
+
1058
+ /**
1059
+ * Converts radians to degrees.
1060
+ *
1061
+ * @param radians: The value to convert
1062
+ * @returns The value in degrees
1063
+ *
1064
+ * @since v0.5.4
1065
+ */
1066
+ export let toDegrees = radians => radians * (180 / pi)