@grain/stdlib 0.5.3 → 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.
package/number.gr CHANGED
@@ -16,32 +16,48 @@ import {
16
16
  isInteger,
17
17
  isRational,
18
18
  isBoxedNumber,
19
+ scalbn,
19
20
  } from "runtime/numbers"
20
21
  import { parseInt } from "runtime/stringUtils"
21
22
  import { newFloat64, newInt64 } from "runtime/dataStructures"
22
23
  import Tags from "runtime/unsafe/tags"
24
+ import Exception from "runtime/exception"
23
25
 
24
26
  /**
25
27
  * @section Constants: Number constant values.
26
28
  */
27
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
+
28
44
  /**
29
45
  * Pi represented as a Number value.
30
- *
46
+ *
31
47
  * @since v0.5.2
32
48
  */
33
49
  export let pi = 3.141592653589793
34
50
 
35
51
  /**
36
52
  * Tau represented as a Number value.
37
- *
53
+ *
38
54
  * @since v0.5.2
39
55
  */
40
56
  export let tau = 6.283185307179586
41
57
 
42
58
  /**
43
59
  * Euler's number represented as a Number value.
44
- *
60
+ *
45
61
  * @since v0.5.2
46
62
  */
47
63
  export let e = 2.718281828459045
@@ -94,6 +110,464 @@ export let mul = (*)
94
110
  */
95
111
  export let div = (/)
96
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
+ }
97
571
  /**
98
572
  * Computes the square root of its operand.
99
573
  *
@@ -140,8 +614,9 @@ export let sign = x => {
140
614
  * @returns The smaller of the two operands
141
615
  *
142
616
  * @since v0.4.0
617
+ * @history v0.5.4: Handle NaN properly
143
618
  */
144
- 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
145
620
 
146
621
  /**
147
622
  * Returns the larger of its operands.
@@ -151,8 +626,9 @@ export let min = (x: Number, y: Number) => if (y > x) x else y
151
626
  * @returns The larger of the two operands
152
627
  *
153
628
  * @since v0.4.0
629
+ * @history v0.5.4: Handle NaN properly
154
630
  */
155
- 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
156
632
 
157
633
  /**
158
634
  * Rounds its operand up to the next largest integer.
@@ -161,12 +637,19 @@ export let max = (x: Number, y: Number) => if (x > y) x else y
161
637
  * @returns The next largest integer of the operand
162
638
  *
163
639
  * @since v0.4.0
640
+ * @history v0.5.4: Handle NaN and Infinity properly
164
641
  */
165
642
  @unsafe
166
643
  export let ceil = (x: Number) => {
167
- let xval = coerceNumberToWasmF64(x)
168
- let ceiling = WasmI64.truncF64S(WasmF64.ceil(xval))
169
- 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
+ }
170
653
  }
171
654
 
172
655
  /**
@@ -176,12 +659,19 @@ export let ceil = (x: Number) => {
176
659
  * @returns The previous integer of the operand
177
660
  *
178
661
  * @since v0.4.0
662
+ * @history v0.5.4: Handle NaN and Infinity properly
179
663
  */
180
664
  @unsafe
181
665
  export let floor = (x: Number) => {
182
- let xval = coerceNumberToWasmF64(x)
183
- let floored = WasmI64.truncF64S(WasmF64.floor(xval))
184
- 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
+ }
185
675
  }
186
676
 
187
677
  /**
@@ -191,12 +681,19 @@ export let floor = (x: Number) => {
191
681
  * @returns The integer part of the operand
192
682
  *
193
683
  * @since v0.4.0
684
+ * @history v0.5.4: Handle NaN and Infinity properly
194
685
  */
195
686
  @unsafe
196
687
  export let trunc = (x: Number) => {
197
- let xval = coerceNumberToWasmF64(x)
198
- let trunced = WasmI64.truncF64S(xval)
199
- 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
+ }
200
697
  }
201
698
 
202
699
  /**
@@ -206,12 +703,19 @@ export let trunc = (x: Number) => {
206
703
  * @returns The nearest integer to the operand
207
704
  *
208
705
  * @since v0.4.0
706
+ * @history v0.5.4: Handle NaN and Infinity properly
209
707
  */
210
708
  @unsafe
211
709
  export let round = (x: Number) => {
212
- let xval = coerceNumberToWasmF64(x)
213
- let rounded = WasmI64.truncF64S(WasmF64.nearest(xval))
214
- 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
+ }
215
719
  }
216
720
 
217
721
  /**
@@ -417,14 +921,19 @@ let chebyshevSine = (radians: Number) => {
417
921
  * @returns The computed sine
418
922
  *
419
923
  * @since v0.5.2
924
+ * @history v0.5.4: Handle NaN and Infinity
420
925
  */
421
926
  export let sin = (radians: Number) => {
422
- let quot = reduceToPiBound(radians)
423
- let bounded = radians - pi * quot
424
- if (quot % 2 == 0) {
425
- chebyshevSine(bounded)
927
+ if (radians != radians || radians == infinity) {
928
+ nan
426
929
  } else {
427
- 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
+ }
428
937
  }
429
938
  }
430
939
 
@@ -435,7 +944,123 @@ export let sin = (radians: Number) => {
435
944
  * @returns The computed cosine
436
945
  *
437
946
  * @since v0.5.2
947
+ * @history v0.5.4: Handle NaN and Infinity
438
948
  */
439
949
  export let cos = (radians: Number) => {
440
- 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
+ }
441
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)