@grain/stdlib 0.5.13 → 0.6.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.
Files changed (155) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/LICENSE +1 -1
  3. package/README.md +25 -2
  4. package/array.gr +1512 -199
  5. package/array.md +2032 -94
  6. package/bigint.gr +239 -140
  7. package/bigint.md +450 -106
  8. package/buffer.gr +595 -102
  9. package/buffer.md +903 -145
  10. package/bytes.gr +401 -110
  11. package/bytes.md +551 -63
  12. package/char.gr +228 -49
  13. package/char.md +373 -7
  14. package/exception.gr +26 -12
  15. package/exception.md +29 -5
  16. package/float32.gr +130 -109
  17. package/float32.md +185 -57
  18. package/float64.gr +112 -99
  19. package/float64.md +185 -57
  20. package/hash.gr +62 -40
  21. package/hash.md +27 -3
  22. package/int16.gr +430 -0
  23. package/int16.md +618 -0
  24. package/int32.gr +200 -269
  25. package/int32.md +254 -289
  26. package/int64.gr +142 -225
  27. package/int64.md +254 -289
  28. package/int8.gr +511 -0
  29. package/int8.md +786 -0
  30. package/json.gr +2071 -0
  31. package/json.md +646 -0
  32. package/list.gr +120 -68
  33. package/list.md +125 -80
  34. package/map.gr +560 -57
  35. package/map.md +672 -56
  36. package/marshal.gr +239 -227
  37. package/marshal.md +36 -4
  38. package/number.gr +626 -676
  39. package/number.md +738 -153
  40. package/option.gr +33 -35
  41. package/option.md +58 -42
  42. package/package.json +2 -2
  43. package/path.gr +148 -187
  44. package/path.md +47 -96
  45. package/pervasives.gr +75 -416
  46. package/pervasives.md +85 -180
  47. package/priorityqueue.gr +433 -74
  48. package/priorityqueue.md +422 -54
  49. package/queue.gr +362 -80
  50. package/queue.md +433 -38
  51. package/random.gr +67 -75
  52. package/random.md +68 -40
  53. package/range.gr +135 -63
  54. package/range.md +198 -43
  55. package/rational.gr +284 -0
  56. package/rational.md +545 -0
  57. package/regex.gr +933 -1066
  58. package/regex.md +59 -60
  59. package/result.gr +23 -25
  60. package/result.md +54 -39
  61. package/runtime/atof/common.gr +78 -82
  62. package/runtime/atof/common.md +22 -10
  63. package/runtime/atof/decimal.gr +102 -127
  64. package/runtime/atof/decimal.md +28 -7
  65. package/runtime/atof/lemire.gr +56 -71
  66. package/runtime/atof/lemire.md +9 -1
  67. package/runtime/atof/parse.gr +83 -110
  68. package/runtime/atof/parse.md +12 -2
  69. package/runtime/atof/slow.gr +28 -35
  70. package/runtime/atof/slow.md +9 -1
  71. package/runtime/atof/table.gr +19 -18
  72. package/runtime/atof/table.md +10 -2
  73. package/runtime/atoi/parse.gr +153 -136
  74. package/runtime/atoi/parse.md +50 -1
  75. package/runtime/bigint.gr +410 -517
  76. package/runtime/bigint.md +71 -57
  77. package/runtime/compare.gr +176 -85
  78. package/runtime/compare.md +31 -1
  79. package/runtime/dataStructures.gr +144 -32
  80. package/runtime/dataStructures.md +267 -31
  81. package/runtime/debugPrint.gr +34 -15
  82. package/runtime/debugPrint.md +37 -5
  83. package/runtime/equal.gr +53 -52
  84. package/runtime/equal.md +30 -1
  85. package/runtime/exception.gr +38 -47
  86. package/runtime/exception.md +10 -8
  87. package/runtime/gc.gr +23 -152
  88. package/runtime/gc.md +13 -17
  89. package/runtime/malloc.gr +31 -31
  90. package/runtime/malloc.md +11 -3
  91. package/runtime/numberUtils.gr +193 -174
  92. package/runtime/numberUtils.md +29 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +543 -245
  96. package/runtime/string.md +76 -6
  97. package/runtime/unsafe/constants.gr +30 -13
  98. package/runtime/unsafe/constants.md +80 -0
  99. package/runtime/unsafe/conv.gr +55 -28
  100. package/runtime/unsafe/conv.md +41 -9
  101. package/runtime/unsafe/memory.gr +10 -30
  102. package/runtime/unsafe/memory.md +15 -19
  103. package/runtime/unsafe/tags.gr +37 -21
  104. package/runtime/unsafe/tags.md +88 -8
  105. package/runtime/unsafe/wasmf32.gr +30 -36
  106. package/runtime/unsafe/wasmf32.md +64 -56
  107. package/runtime/unsafe/wasmf64.gr +30 -36
  108. package/runtime/unsafe/wasmf64.md +64 -56
  109. package/runtime/unsafe/wasmi32.gr +49 -66
  110. package/runtime/unsafe/wasmi32.md +102 -94
  111. package/runtime/unsafe/wasmi64.gr +52 -79
  112. package/runtime/unsafe/wasmi64.md +108 -100
  113. package/runtime/utils/printing.gr +13 -15
  114. package/runtime/utils/printing.md +11 -3
  115. package/runtime/wasi.gr +294 -295
  116. package/runtime/wasi.md +62 -42
  117. package/set.gr +574 -64
  118. package/set.md +634 -54
  119. package/stack.gr +181 -64
  120. package/stack.md +271 -42
  121. package/string.gr +453 -533
  122. package/string.md +241 -151
  123. package/uint16.gr +369 -0
  124. package/uint16.md +585 -0
  125. package/uint32.gr +470 -0
  126. package/uint32.md +737 -0
  127. package/uint64.gr +471 -0
  128. package/uint64.md +737 -0
  129. package/uint8.gr +369 -0
  130. package/uint8.md +585 -0
  131. package/uri.gr +1093 -0
  132. package/uri.md +477 -0
  133. package/{sys → wasi}/file.gr +914 -500
  134. package/{sys → wasi}/file.md +454 -50
  135. package/wasi/process.gr +292 -0
  136. package/{sys → wasi}/process.md +164 -6
  137. package/wasi/random.gr +77 -0
  138. package/wasi/random.md +80 -0
  139. package/{sys → wasi}/time.gr +15 -22
  140. package/{sys → wasi}/time.md +5 -5
  141. package/immutablearray.gr +0 -929
  142. package/immutablearray.md +0 -1038
  143. package/immutablemap.gr +0 -493
  144. package/immutablemap.md +0 -479
  145. package/immutablepriorityqueue.gr +0 -360
  146. package/immutablepriorityqueue.md +0 -291
  147. package/immutableset.gr +0 -498
  148. package/immutableset.md +0 -449
  149. package/runtime/debug.gr +0 -2
  150. package/runtime/debug.md +0 -6
  151. package/runtime/unsafe/errors.gr +0 -36
  152. package/runtime/unsafe/errors.md +0 -204
  153. package/sys/process.gr +0 -254
  154. package/sys/random.gr +0 -79
  155. package/sys/random.md +0 -66
package/number.gr CHANGED
@@ -1,15 +1,41 @@
1
+ /*
2
+ * ====================================================
3
+ * Applies to all functions with a comment referring here.
4
+ *
5
+ * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
6
+ *
7
+ * Permission to use, copy, modify, and distribute this
8
+ * software is freely granted, provided that this notice
9
+ * is preserved.
10
+ *
11
+ * ====================================================
12
+ */
13
+
1
14
  /**
2
- * @module Number: Utilities for working with numbers.
3
- * @example import Number from "number"
15
+ * Utilities for working with numbers.
16
+ *
17
+ * @example from "number" include Number
18
+ *
19
+ * @example 1
20
+ * @example -1
21
+ * @example 0.5
22
+ * @example 1/2
23
+ * @example Infinity
24
+ * @example NaN
25
+ *
4
26
  * @since v0.4.0
5
27
  */
28
+ module Number
6
29
 
7
- import WasmI32 from "runtime/unsafe/wasmi32"
8
- import WasmI64 from "runtime/unsafe/wasmi64"
9
- import WasmF32 from "runtime/unsafe/wasmf32"
10
- import WasmF64 from "runtime/unsafe/wasmf64"
11
- import Memory from "runtime/unsafe/memory"
12
- import {
30
+ from "runtime/unsafe/wasmi32" include WasmI32
31
+ from "runtime/unsafe/wasmi64" include WasmI64
32
+ from "runtime/unsafe/wasmf32" include WasmF32
33
+ from "runtime/unsafe/wasmf64" include WasmF64
34
+ from "runtime/unsafe/memory" include Memory
35
+ from "runtime/dataStructures" include DataStructures
36
+ use DataStructures.{ newFloat64, allocateString }
37
+ from "runtime/numbers" include Numbers
38
+ use Numbers.{
13
39
  coerceNumberToWasmF64,
14
40
  reducedInteger,
15
41
  isFloat,
@@ -17,124 +43,100 @@ import {
17
43
  isRational,
18
44
  isBoxedNumber,
19
45
  isNaN,
20
- scalbn,
21
- } from "runtime/numbers"
22
- import Atoi from "runtime/atoi/parse"
23
- import Atof from "runtime/atof/parse"
24
- import { newFloat64, newInt64, allocateString } from "runtime/dataStructures"
25
- import Tags from "runtime/unsafe/tags"
26
- import Exception from "runtime/exception"
27
-
28
- /**
29
- * @section Constants: Number constant values.
30
- */
31
-
32
- /**
33
- * NaN represented as a Number value.
34
- *
35
- * @since v0.5.4
36
- */
37
- export let nan = 0.0 / 0.0
46
+ }
47
+ from "runtime/atoi/parse" include Parse as Atoi
48
+ from "runtime/atof/parse" include Parse as Atof
49
+ from "runtime/unsafe/tags" include Tags
50
+ from "runtime/exception" include Exception
38
51
 
39
- /**
40
- * Infinity represented as a Number value.
41
- *
42
- * @since v0.5.4
43
- */
44
- export let infinity = 1.0 / 0.0
52
+ use Atoi.{ type ParseIntError }
45
53
 
54
+ provide { type ParseIntError }
46
55
  /**
47
56
  * Pi represented as a Number value.
48
57
  *
49
58
  * @since v0.5.2
50
59
  */
51
- export let pi = 3.141592653589793
60
+ provide let pi = 3.141592653589793
52
61
 
53
62
  /**
54
63
  * Tau represented as a Number value.
55
64
  *
56
65
  * @since v0.5.2
57
66
  */
58
- export let tau = 6.283185307179586
67
+ provide let tau = 6.283185307179586
59
68
 
60
69
  /**
61
70
  * Euler's number represented as a Number value.
62
71
  *
63
72
  * @since v0.5.2
64
73
  */
65
- export let e = 2.718281828459045
66
-
67
- /**
68
- * @section Operations: Functions for operating on values of the Number type.
69
- */
74
+ provide let e = 2.718281828459045
70
75
 
71
76
  /**
72
77
  * Computes the sum of its operands.
73
78
  *
74
- * @param x: The first operand
75
- * @param y: The second operand
79
+ * @param num1: The first operand
80
+ * @param num2: The second operand
76
81
  * @returns The sum of the two operands
77
82
  *
78
- * @since v0.4.0
83
+ * @example
84
+ * from Number use { (+) }
85
+ * assert 1 + 2 == 3
86
+ *
87
+ * @since v0.6.0
88
+ * @history v0.4.0: Originally named `add`
79
89
  */
80
- export let add = (+)
90
+ provide let (+) = (+)
81
91
 
82
92
  /**
83
93
  * Computes the difference of its operands.
84
94
  *
85
- * @param x: The first operand
86
- * @param y: The second operand
95
+ * @param num1: The first operand
96
+ * @param num2: The second operand
87
97
  * @returns The difference of the two operands
88
98
  *
89
- * @since v0.4.0
99
+ * @example
100
+ * from Number use { (-) }
101
+ * assert 5 - 2 == 3
102
+ *
103
+ * @since v0.6.0
104
+ * @history v0.4.0: Originally named `sub`
90
105
  */
91
- export let sub = (-)
106
+ provide let (-) = (-)
92
107
 
93
108
  /**
94
109
  * Computes the product of its operands.
95
110
  *
96
- * @param x: The first operand
97
- * @param y: The second operand
111
+ * @param num1: The first operand
112
+ * @param num2: The second operand
98
113
  * @returns The product of the two operands
99
114
  *
100
- * @since v0.4.0
115
+ * @example
116
+ * from Number use { (*) }
117
+ * assert 5 * 4 == 20
118
+ *
119
+ * @since v0.6.0
120
+ * @history v0.4.0: Originally named `mul`
101
121
  */
102
- export let mul = (*)
122
+ provide let (*) = (*)
103
123
 
104
124
  /**
105
125
  * Computes the quotient of its operands.
106
126
  *
107
- * @param x: The dividend
108
- * @param y: The divisor
127
+ * @param num1: The dividend
128
+ * @param num2: The divisor
109
129
  * @returns The quotient of the two operands
110
130
  *
111
- * @since v0.4.0
112
- */
113
- export let div = (/)
114
-
115
- // Exponentiation by squaring https://en.wikipedia.org/wiki/Exponentiation_by_squaring special path for int^int
116
- let rec expBySquaring = (y, x, n) => {
117
- if (n == 0) {
118
- 1
119
- } else if (n == 1) {
120
- x * y
121
- } else if (n % 2 == 0) {
122
- expBySquaring(y, x * x, n / 2)
123
- } else {
124
- expBySquaring(x * y, x * x, (n - 1) / 2)
125
- }
126
- }
127
-
128
- // Math.pow is largely based on https://git.musl-libc.org/cgit/musl/tree/src/math/pow.c
129
- /*
130
- * ====================================================
131
- * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
131
+ * @example
132
+ * from Number use { (/) }
133
+ * assert 10 / 2.5 == 4
132
134
  *
133
- * Permission to use, copy, modify, and distribute this
134
- * software is freely granted, provided that this notice
135
- * is preserved.
136
- * ====================================================
135
+ * @since v0.6.0
136
+ * @history v0.4.0: Originally named `div`
137
137
  */
138
+ provide let (/) = (/)
139
+
138
140
  /**
139
141
  * Computes the exponentiation of the given base and power.
140
142
  *
@@ -142,422 +144,14 @@ let rec expBySquaring = (y, x, n) => {
142
144
  * @param power: The exponent number
143
145
  * @returns The base raised to the given power
144
146
  *
145
- * @since v0.5.4
147
+ * @example
148
+ * from Number use { (**) }
149
+ * assert 10 ** 2 == 100
150
+ *
151
+ * @since v0.6.0
152
+ * @history v0.5.4: Originally named `pow`
146
153
  */
147
- @unsafe
148
- export let pow = (base, power) => {
149
- // TODO(#1476): Move this into runtime/numbers.gr
150
- if (base == 1 && power != 0) {
151
- 1
152
- } else if (
153
- isInteger(WasmI32.fromGrain(base)) && isInteger(WasmI32.fromGrain(power))
154
- ) {
155
- if (power < 0) expBySquaring(1, 1 / base, power * -1)
156
- else expBySquaring(1, base, power)
157
- } else {
158
- // TODO(#553): Refactor once we have early return
159
- // Based on https://git.musl-libc.org/cgit/musl/tree/src/math/pow.c
160
- let (==) = WasmF64.eq
161
- let (!=) = WasmF64.ne
162
- let (<=) = WasmF64.le
163
- let (/) = WasmF64.div
164
- let (*) = WasmF64.mul
165
- let (+) = WasmF64.add
166
- // Constants
167
- let infinity = 1.0W / 0.0W
168
- let nan = 0.0W / 0.0W
169
- let x = coerceNumberToWasmF64(base)
170
- let y = coerceNumberToWasmF64(power)
171
- let mut foundOutput = false, output = 0.0W
172
- // Fast paths
173
- if (WasmF64.abs(y) <= 2.0W) {
174
- if (y == 2.0W) {
175
- foundOutput = true
176
- output = x * x
177
- } else if (y == 0.5W) {
178
- foundOutput = true
179
- if (x != infinity) output = WasmF64.abs(WasmF64.sqrt(x))
180
- else output = infinity
181
- } else if (y == -1.0W) {
182
- foundOutput = true
183
- output = 1.0W / x
184
- } else if (y == 1.0W) {
185
- foundOutput = true
186
- output = x
187
- } else if (y == 0.0W) {
188
- foundOutput = true
189
- output = nan
190
- }
191
- }
192
- // Full calculation
193
- if (foundOutput) {
194
- WasmI32.toGrain(newFloat64(output)): Number
195
- } else {
196
- let dp_h1 = WasmF64.reinterpretI64(0x3FE2B80340000000N)
197
- let dp_l1 = WasmF64.reinterpretI64(0x3E4CFDEB43CFD006N)
198
- let two53 = WasmF64.reinterpretI64(0x4340000000000000N)
199
- let huge = WasmF64.reinterpretI64(0x7E37E43C8800759CN)
200
- let tiny = WasmF64.reinterpretI64(0x01A56E1FC2F8F359N)
201
- let l1 = WasmF64.reinterpretI64(0x3FE3333333333303N)
202
- let l2 = WasmF64.reinterpretI64(0x3FDB6DB6DB6FABFFN)
203
- let l3 = WasmF64.reinterpretI64(0x3FD55555518F264DN)
204
- let l4 = WasmF64.reinterpretI64(0x3FD17460A91D4101N)
205
- let l5 = WasmF64.reinterpretI64(0x3FCD864A93C9DB65N)
206
- let l6 = WasmF64.reinterpretI64(0x3FCA7E284A454EEFN)
207
- let p1 = WasmF64.reinterpretI64(0x3FC555555555553EN)
208
- let p2 = WasmF64.reinterpretI64(0xBF66C16C16BEBD93N)
209
- let p3 = WasmF64.reinterpretI64(0x3F11566AAF25DE2CN)
210
- let p4 = WasmF64.reinterpretI64(0xBEBBBD41C5D26BF1N)
211
- let p5 = WasmF64.reinterpretI64(0x3E66376972BEA4D0N)
212
- let lg2 = WasmF64.reinterpretI64(0x3FE62E42FEFA39EFN)
213
- let lg2_h = WasmF64.reinterpretI64(0x3FE62E4300000000N)
214
- let lg2_l = WasmF64.reinterpretI64(0xBE205C610CA86C39N)
215
- let ovt = WasmF64.reinterpretI64(0x3C971547652B82FEN)
216
- let cp = WasmF64.reinterpretI64(0x3FEEC709DC3A03FDN)
217
- let cp_h = WasmF64.reinterpretI64(0x3FEEC709E0000000N)
218
- let cp_l = WasmF64.reinterpretI64(0xBE3E2FE0145B01F5N)
219
- let ivln2 = WasmF64.reinterpretI64(0x3FF71547652B82FEN)
220
- let ivln2_h = WasmF64.reinterpretI64(0x3FF7154760000000N)
221
- let ivln2_l = WasmF64.reinterpretI64(0x3E54AE0BF85DDF44N)
222
- let inv3 = WasmF64.reinterpretI64(0x3FD5555555555555N)
223
- let (==) = WasmI32.eq
224
- let (!=) = WasmI32.ne
225
- let (>=) = WasmI32.geS
226
- let (<=) = WasmI32.leS
227
- let (&) = WasmI32.and
228
- let (|) = WasmI32.or
229
- let (>) = WasmI32.gtS
230
- let (<) = WasmI32.ltS
231
- let (<<) = WasmI32.shl
232
- let (>>) = WasmI32.shrS
233
- let (-) = WasmI32.sub
234
- let (+) = WasmI32.add
235
- let u_ = WasmI64.reinterpretF64(x)
236
- let hx = WasmI32.wrapI64(WasmI64.shrS(u_, 32N))
237
- let lx = WasmI32.wrapI64(u_)
238
- let u_ = WasmI64.reinterpretF64(y)
239
- let hy = WasmI32.wrapI64(WasmI64.shrS(u_, 32N))
240
- let ly = WasmI32.wrapI64(u_)
241
- let mut ix = hx & 0x7FFFFFFFn
242
- let iy = hy & 0x7FFFFFFFn
243
- if ((iy | ly) == 0n) { // x**0 = 1, even if x is NaN
244
- 1 // return 1
245
- } else if (
246
- // Either Argument is Nan
247
- ix > 0x7FF00000n ||
248
- ix == 0x7FF00000n && lx != 0n ||
249
- iy > 0x7FF00000n ||
250
- iy == 0x7FF00000n && ly != 0n
251
- ) {
252
- WasmI32.toGrain(newFloat64(WasmF64.add(x, y))): Number
253
- } else {
254
- let mut yisint = 0n
255
- let mut k = 0n
256
- if (hx < 0n) {
257
- if (iy >= 0x43400000n) {
258
- yisint = 2n
259
- } else if (iy >= 0x3FF00000n) {
260
- k = (iy >> 20n) - 0x3FFn
261
- let mut offset = 0n
262
- let mut _ly = 0n
263
- if (k > 20n) {
264
- offset = 52n - k
265
- _ly = ly
266
- } else {
267
- offset = 20n - k
268
- _ly = iy
269
- }
270
- let jj = _ly >> offset
271
- if (jj << offset == _ly) yisint = 2n - (jj & 1n)
272
- }
273
- }
274
- if (ly == 0n) {
275
- if (iy == 0x7FF00000n) { // y is +- inf
276
- foundOutput = true
277
- if ((ix - 0x3FF00000n | lx) == 0n) { // C: (-1)**+-inf is 1, JS: NaN
278
- output = nan
279
- } else if (ix >= 0x3FF00000n) { // (|x|>1)**+-inf = inf,0
280
- if (hy >= 0n) output = y else output = 0.0W
281
- } else { // (|x|<1)**+-inf = 0,inf
282
- if (hy >= 0n) output = 0.0W else output = y * -1.0W
283
- }
284
- } else if (iy == 0x3FF00000n) {
285
- foundOutput = true
286
- if (hy >= 0n) output = x else output = 1.0W / x
287
- } else if (hy == 0x3FE00000n) {
288
- foundOutput = true
289
- output = x * x
290
- } else if (hy == 0x3FE00000n) {
291
- if (hx >= 0n) {
292
- foundOutput = true
293
- output = WasmF64.sqrt(x)
294
- }
295
- }
296
- }
297
- if (foundOutput) {
298
- WasmI32.toGrain(newFloat64(output)): Number
299
- } else {
300
- let mut ax = WasmF64.abs(x)
301
- let mut z = 0.0W
302
- if (
303
- lx == 0n && (ix == 0n || ix == 0x7FF00000n || ix == 0x3FF00000n)
304
- ) {
305
- z = ax
306
- if (hy < 0n) z = 1.0W / z
307
- if (hx < 0n) {
308
- if ((ix - 0x3FF00000n | yisint) == 0n) {
309
- let d = WasmF64.sub(z, z)
310
- z = d / d
311
- } else if (yisint == 1n) z *= -1.0W
312
- }
313
- WasmI32.toGrain(newFloat64(z)): Number
314
- } else {
315
- let mut s = 1.0W
316
- if (hx < 0n) {
317
- if (yisint == 0n) {
318
- let d = WasmF64.sub(x, x)
319
- foundOutput = true
320
- output = d / d
321
- } else if (yisint == 1n) s = -1.0W
322
- }
323
- if (foundOutput) {
324
- WasmI32.toGrain(newFloat64(output)): Number
325
- } else {
326
- let mut t1 = 0.0W,
327
- t2 = 0.0W,
328
- p_h = 0.0W,
329
- p_l = 0.0W,
330
- r = 0.0W,
331
- t = 0.0W,
332
- u = 0.0W,
333
- v = 0.0W,
334
- w = 0.0W
335
- let mut j = 0n, n = 0n
336
- if (iy > 0x41E00000n) {
337
- if (iy > 0x43F00000n) {
338
- if (ix <= 0x3FEFFFFFn) {
339
- foundOutput = true
340
- if (hy < 0n) output = huge * huge else output = tiny * tiny
341
- } else if (ix >= 0x3FF00000n) {
342
- foundOutput = true
343
- if (hy > 0n) output = huge * huge else output = tiny * tiny
344
- }
345
- }
346
- if (!foundOutput) {
347
- if (ix < 0x3FEFFFFFn) {
348
- foundOutput = true
349
- if (hy < 0n) {
350
- output = s * huge * huge
351
- } else {
352
- output = s * tiny * tiny
353
- }
354
- } else if (ix > 0x3FF00000n) {
355
- foundOutput = true
356
- if (hy > 0n) {
357
- output = s * huge * huge
358
- } else {
359
- output = s * tiny * tiny
360
- }
361
- } else {
362
- let (-) = WasmF64.sub
363
- let (&) = WasmI64.and
364
- t = ax - 1.0W
365
- w = t * t * (0.5W - t * (inv3 - t * 0.25W))
366
- u = ivln2_h * t
367
- v = t * ivln2_l - w * ivln2
368
- t1 = WasmF64.add(u, v)
369
- t1 = WasmF64.reinterpretI64(
370
- WasmI64.reinterpretF64(t1) & 0xFFFFFFFF00000000N
371
- )
372
- t2 = v - (t1 - u)
373
- }
374
- }
375
- } else {
376
- let mut ss = 0.0W,
377
- s2 = 0.0W,
378
- s_h = 0.0W,
379
- s_l = 0.0W,
380
- t_h = 0.0W,
381
- t_l = 0.0W
382
- n = 0n
383
- if (ix < 0x00100000n) {
384
- let (>>) = WasmI64.shrU
385
- ax *= two53
386
- n -= 53n
387
- ix = WasmI32.wrapI64(WasmI64.reinterpretF64(ax) >> 32N)
388
- }
389
- n += (ix >> 20n) - 0x3FFn
390
- j = ix & 0x000FFFFFn
391
- ix = j | 0x3FF00000n
392
- if (j <= 0x3988En) {
393
- k = 0n
394
- } else if (j < 0xBB67An) {
395
- k = 1n
396
- } else {
397
- k = 0n
398
- n += 1n
399
- ix -= 0x00100000n
400
- }
401
- let (&) = WasmI64.and
402
- let (|) = WasmI64.or
403
- let (<<) = WasmI64.shl
404
- ax = WasmF64.reinterpretI64(
405
- WasmI64.reinterpretF64(ax) & 0xFFFFFFFFN |
406
- WasmI64.extendI32S(ix) << 32N
407
- )
408
- let bp = if (k != 0n) 1.5W else 1.0W
409
- u = WasmF64.sub(ax, bp)
410
- v = 1.0W / WasmF64.add(ax, bp)
411
- ss = u * v
412
- s_h = ss
413
- s_h = WasmF64.reinterpretI64(
414
- WasmI64.reinterpretF64(s_h) & 0xFFFFFFFF00000000N
415
- )
416
- t_h = WasmF64.reinterpretI64(
417
- WasmI64.shl(
418
- WasmI64.extendI32S(
419
- WasmI32.or(WasmI32.shrS(ix, 1n), 0x20000000n) +
420
- 0x00080000n +
421
- WasmI32.shl(k, 18n)
422
- ),
423
- 32N
424
- )
425
- )
426
- let (-) = WasmF64.sub
427
- let (+) = WasmF64.add
428
- t_l = ax - (t_h - bp)
429
- s_l = v * (u - s_h * t_h - s_h * t_l)
430
- s2 = ss * ss
431
- //formatter-ignore
432
- r = s2 * s2 * (l1 + s2 * (l2 + s2 * (l3 + s2 * (l4 + s2 * (l5 + s2 * l6)))))
433
- r += s_l * (s_h + ss)
434
- s2 = s_h * s_h
435
- t_h = 3.0W + s2 + r
436
- t_h = WasmF64.reinterpretI64(
437
- WasmI64.reinterpretF64(t_h) & 0xFFFFFFFF00000000N
438
- )
439
- t_l = r - (t_h - 3.0W - s2)
440
- u = s_h * t_h
441
- v = s_l * t_h + t_l * ss
442
- p_h = u + v
443
- p_h = WasmF64.reinterpretI64(
444
- WasmI64.reinterpretF64(p_h) & 0xFFFFFFFF00000000N
445
- )
446
- p_l = v - (p_h - u)
447
- let z_h = cp_h * p_h
448
- let dp_l = if (k != 0n) dp_l1 else 0.0W
449
- let z_l = cp_l * p_h + p_l * cp + dp_l
450
- t = WasmF64.convertI32S(n)
451
- let dp_h = if (k != 0n) dp_h1 else 0.0W
452
- t1 = z_h + z_l + dp_h + t
453
- t1 = WasmF64.reinterpretI64(
454
- WasmI64.reinterpretF64(t1) & 0xFFFFFFFF00000000N
455
- )
456
- t2 = z_l - (t1 - t - dp_h - z_h)
457
- }
458
- if (foundOutput) {
459
- WasmI32.toGrain(newFloat64(output)): Number
460
- } else {
461
- let (>) = WasmF64.gt
462
- let (&) = WasmI64.and
463
- let (-) = WasmF64.sub
464
- let (+) = WasmF64.add
465
- let (>>) = WasmI64.shrS
466
- let y1 = WasmF64.reinterpretI64(
467
- WasmI64.reinterpretF64(y) & 0xFFFFFFFF00000000N
468
- )
469
- p_l = (y - y1) * t1 + y * t2
470
- p_h = y1 * t1
471
- z = p_l + p_h
472
- let u_ = WasmI64.reinterpretF64(z)
473
- let j = WasmI32.wrapI64(u_ >> 32N)
474
- let i = WasmI32.wrapI64(u_)
475
- if (j >= 0x40900000n) {
476
- if ((WasmI32.sub(j, 0x40900000n) | i) != 0n) {
477
- foundOutput = true
478
- output = s * huge * huge
479
- } else if (p_l + ovt > z - p_h) {
480
- foundOutput = true
481
- output = s * huge * huge
482
- }
483
- } else if (WasmI32.and(j, 0x7FFFFFFFn) >= 0x4090CC00n) {
484
- if (WasmI32.sub(j, 0xC090CC00n | i) != 0n) {
485
- foundOutput = true
486
- output = s * tiny * tiny
487
- } else if (WasmF64.le(p_l, z - p_h)) {
488
- foundOutput = true
489
- output = s * tiny * tiny
490
- }
491
- }
492
- if (foundOutput) {
493
- WasmI32.toGrain(newFloat64(output)): Number
494
- } else {
495
- let (&) = WasmI32.and
496
- let (>>) = WasmI32.shrS
497
- let (-) = WasmI32.sub
498
- let (+) = WasmI32.add
499
- let (>) = WasmI32.gtS
500
- let (*) = WasmI32.mul
501
- let i = j & 0x7FFFFFFFn
502
- k = (i >> 20n) - 0x3FFn
503
- n = 0n
504
- if (i > 0x3FE00000n) {
505
- n = j + (0x00100000n >> k + 1n)
506
- k = ((n & 0x7FFFFFFFn) >> 20n) - 0x3FFn
507
- t = 0.0W
508
- t = WasmF64.reinterpretI64(
509
- WasmI64.shl(
510
- WasmI64.extendI32S(
511
- n & WasmI32.xor(0x000FFFFFn >> k, -1n)
512
- ),
513
- 32N
514
- )
515
- )
516
- n = (n & 0x000FFFFFn | 0x00100000n) >> 20n - k
517
- if (j < 0n) n *= -1n
518
- p_h = WasmF64.sub(p_h, t)
519
- }
520
- let (&) = WasmI64.and
521
- let (|) = WasmI64.or
522
- let (*) = WasmF64.mul
523
- let (-) = WasmF64.sub
524
- let (+) = WasmF64.add
525
- t = WasmF64.add(p_l, p_h)
526
- t = WasmF64.reinterpretI64(
527
- WasmI64.reinterpretF64(t) & 0xFFFFFFFF00000000N
528
- )
529
- u = t * lg2_h
530
- v = (p_l - (t - p_h)) * lg2 + t * lg2_l
531
- z = u + v
532
- w = v - (z - u)
533
- t = z * z
534
- t1 = z - t * (p1 + t * (p2 + t * (p3 + t * (p4 + t * p5))))
535
- r = z * t1 / (t1 - 2.0W) - (w + z * w)
536
- z = 1.0W - (r - z)
537
- let j = WasmI32.add(
538
- WasmI32.wrapI64(
539
- WasmI64.shrS(WasmI64.reinterpretF64(z), 32N)
540
- ),
541
- WasmI32.shl(n, 20n)
542
- )
543
- if (WasmI32.shrS(j, 20n) <= 0n) {
544
- z = scalbn(z, n)
545
- } else {
546
- z = WasmF64.reinterpretI64(
547
- WasmI64.reinterpretF64(z) & 0xFFFFFFFFN |
548
- WasmI64.shl(WasmI64.extendI32S(j), 32N)
549
- )
550
- }
551
- WasmI32.toGrain(newFloat64(s * z)): Number
552
- }
553
- }
554
- }
555
- }
556
- }
557
- }
558
- }
559
- }
560
- }
154
+ provide let (**) = (**)
561
155
 
562
156
  /**
563
157
  * Computes the exponentiation of Euler's number to the given power.
@@ -565,25 +159,32 @@ export let pow = (base, power) => {
565
159
  * @param power: The exponent number
566
160
  * @returns The `Number.e` value raised to the given power
567
161
  *
162
+ * @example Number.exp(1) == Number.e
163
+ * @example Number.exp(10) == 22026.465794806703
164
+ *
568
165
  * @since v0.5.4
569
166
  */
570
- export let exp = power => {
571
- if (power == 0) 1 else pow(e, power)
167
+ provide let exp = power => {
168
+ if (power == 0) 1 else e ** power
572
169
  }
170
+
573
171
  /**
574
172
  * Computes the square root of its operand.
575
173
  *
576
174
  * @param x: The number to square root
577
175
  * @returns The square root of the operand
578
176
  *
177
+ * @example Number.sqrt(25) == 5
178
+ *
579
179
  * @since v0.4.0
580
180
  */
581
181
  @unsafe
582
- export let sqrt = (x: Number) => {
182
+ provide let sqrt = (x: Number) => {
183
+ use WasmF64.{ (==) }
583
184
  let xval = coerceNumberToWasmF64(x)
584
185
  let x = WasmI32.fromGrain(x)
585
186
  let sqrtd = WasmF64.sqrt(xval)
586
- if (!isFloat(x) && WasmF64.eq(sqrtd, WasmF64.trunc(sqrtd))) {
187
+ if (!isFloat(x) && sqrtd == WasmF64.trunc(sqrtd)) {
587
188
  WasmI32.toGrain(reducedInteger(WasmI64.truncF64S(sqrtd))): Number
588
189
  } else {
589
190
  WasmI32.toGrain(newFloat64(sqrtd)): Number
@@ -599,8 +200,10 @@ export let sqrt = (x: Number) => {
599
200
  * @example Number.sign(-10000) == -1
600
201
  * @example Number.sign(222222) == 1
601
202
  * @example Number.sign(0) == 0
203
+ *
204
+ * @since v0.5.0
602
205
  */
603
- export let sign = x => {
206
+ provide let sign = x => {
604
207
  match (x) {
605
208
  x when x < 0 => -1,
606
209
  x when x > 0 => 1,
@@ -615,10 +218,12 @@ export let sign = x => {
615
218
  * @param y: The second operand
616
219
  * @returns The smaller of the two operands
617
220
  *
221
+ * @example Number.min(5, 2) == 2
222
+ *
618
223
  * @since v0.4.0
619
224
  * @history v0.5.4: Handle NaN properly
620
225
  */
621
- export let min = (x: Number, y: Number) => if (compare(x, y) < 0) x else y
226
+ provide let min = (x: Number, y: Number) => if (compare(x, y) < 0) x else y
622
227
 
623
228
  /**
624
229
  * Returns the larger of its operands.
@@ -627,10 +232,12 @@ export let min = (x: Number, y: Number) => if (compare(x, y) < 0) x else y
627
232
  * @param y: The second operand
628
233
  * @returns The larger of the two operands
629
234
  *
235
+ * @example Number.max(5, 2) == 5
236
+ *
630
237
  * @since v0.4.0
631
238
  * @history v0.5.4: Handle NaN properly
632
239
  */
633
- export let max = (x: Number, y: Number) => if (compare(x, y) > 0) x else y
240
+ provide let max = (x: Number, y: Number) => if (compare(x, y) > 0) x else y
634
241
 
635
242
  /**
636
243
  * Rounds its operand up to the next largest integer.
@@ -638,15 +245,18 @@ export let max = (x: Number, y: Number) => if (compare(x, y) > 0) x else y
638
245
  * @param x: The number to round
639
246
  * @returns The next largest integer of the operand
640
247
  *
248
+ * @example Number.ceil(5.5) == 6
249
+ * @example Number.ceil(-5.5) == -5
250
+ *
641
251
  * @since v0.4.0
642
252
  * @history v0.5.4: Handle NaN and Infinity properly
643
253
  */
644
254
  @unsafe
645
- export let ceil = (x: Number) => {
255
+ provide let ceil = (x: Number) => {
646
256
  if (x != x) {
647
- nan
648
- } else if (x == infinity) {
649
- infinity
257
+ NaN
258
+ } else if (x == Infinity) {
259
+ Infinity
650
260
  } else {
651
261
  let xval = coerceNumberToWasmF64(x)
652
262
  let ceiling = WasmI64.truncF64S(WasmF64.ceil(xval))
@@ -660,15 +270,18 @@ export let ceil = (x: Number) => {
660
270
  * @param x: The number to round
661
271
  * @returns The previous integer of the operand
662
272
  *
273
+ * @example Number.floor(5.5) == 5
274
+ * @example Number.floor(-5.5) == -6
275
+ *
663
276
  * @since v0.4.0
664
277
  * @history v0.5.4: Handle NaN and Infinity properly
665
278
  */
666
279
  @unsafe
667
- export let floor = (x: Number) => {
280
+ provide let floor = (x: Number) => {
668
281
  if (x != x) {
669
- nan
670
- } else if (x == infinity) {
671
- infinity
282
+ NaN
283
+ } else if (x == Infinity) {
284
+ Infinity
672
285
  } else {
673
286
  let xval = coerceNumberToWasmF64(x)
674
287
  let floored = WasmI64.truncF64S(WasmF64.floor(xval))
@@ -682,15 +295,17 @@ export let floor = (x: Number) => {
682
295
  * @param x: The number to truncate
683
296
  * @returns The integer part of the operand
684
297
  *
298
+ * @example Number.trunc(5.5) == 5
299
+ *
685
300
  * @since v0.4.0
686
301
  * @history v0.5.4: Handle NaN and Infinity properly
687
302
  */
688
303
  @unsafe
689
- export let trunc = (x: Number) => {
304
+ provide let trunc = (x: Number) => {
690
305
  if (x != x) {
691
- nan
692
- } else if (x == infinity) {
693
- infinity
306
+ NaN
307
+ } else if (x == Infinity) {
308
+ Infinity
694
309
  } else {
695
310
  let xval = coerceNumberToWasmF64(x)
696
311
  let trunced = WasmI64.truncF64S(xval)
@@ -704,15 +319,20 @@ export let trunc = (x: Number) => {
704
319
  * @param x: The number to round
705
320
  * @returns The nearest integer to the operand
706
321
  *
322
+ * @example Number.round(5.5) == 6
323
+ * @example Number.round(5.4) == 5
324
+ * @example Number.round(-5.5) == -6
325
+ * @example Number.round(-5.4) == -5
326
+ *
707
327
  * @since v0.4.0
708
328
  * @history v0.5.4: Handle NaN and Infinity properly
709
329
  */
710
330
  @unsafe
711
- export let round = (x: Number) => {
331
+ provide let round = (x: Number) => {
712
332
  if (x != x) {
713
- nan
714
- } else if (x == infinity) {
715
- infinity
333
+ NaN
334
+ } else if (x == Infinity) {
335
+ Infinity
716
336
  } else {
717
337
  let xval = coerceNumberToWasmF64(x)
718
338
  let rounded = WasmI64.truncF64S(WasmF64.nearest(xval))
@@ -726,9 +346,12 @@ export let round = (x: Number) => {
726
346
  * @param x: The operand
727
347
  * @returns The absolute value of the operand
728
348
  *
349
+ * @example Number.abs(-1) == 1
350
+ * @example Number.abs(5) == 5
351
+ *
729
352
  * @since v0.4.0
730
353
  */
731
- export let abs = (x: Number) => if (0 > x) x * -1 else x
354
+ provide let abs = (x: Number) => if (0 > x) x * -1 else x
732
355
 
733
356
  /**
734
357
  * Returns the negation of its operand.
@@ -736,9 +359,12 @@ export let abs = (x: Number) => if (0 > x) x * -1 else x
736
359
  * @param x: The number to negate
737
360
  * @returns The negated operand
738
361
  *
362
+ * @example Number.neg(-1) == 1
363
+ * @example Number.neg(1) == -1
364
+ *
739
365
  * @since v0.4.0
740
366
  */
741
- export let neg = (x: Number) => x * -1
367
+ provide let neg = (x: Number) => x * -1
742
368
 
743
369
  /**
744
370
  * Checks if a number is a floating point value.
@@ -746,10 +372,17 @@ export let neg = (x: Number) => x * -1
746
372
  * @param x: The number to check
747
373
  * @returns `true` if the value is a floating point number or `false` otherwise
748
374
  *
375
+ * @example Number.isFloat(0.5)
376
+ * @example Number.isFloat(1.0)
377
+ * @example Number.isFloat(Infinity)
378
+ * @example Number.isFloat(NaN)
379
+ * @example Number.isFloat(1/2) == false
380
+ * @example Number.isFloat(1) == false
381
+ *
749
382
  * @since v0.5.3
750
383
  */
751
384
  @unsafe
752
- export let isFloat = (x: Number) => {
385
+ provide let isFloat = (x: Number) => {
753
386
  isFloat(WasmI32.fromGrain(x))
754
387
  }
755
388
 
@@ -759,10 +392,17 @@ export let isFloat = (x: Number) => {
759
392
  * @param x: The number to check
760
393
  * @returns `true` if the value is an integer or `false` otherwise
761
394
  *
395
+ * @example Number.isInteger(1)
396
+ * @example Number.isInteger(0.5) == false
397
+ * @example Number.isInteger(1.0) == false
398
+ * @example Number.isInteger(1/2) == false
399
+ * @example Number.isInteger(Infinity) == false
400
+ * @example Number.isInteger(NaN) == false
401
+ *
762
402
  * @since v0.5.3
763
403
  */
764
404
  @unsafe
765
- export let isInteger = (x: Number) => {
405
+ provide let isInteger = (x: Number) => {
766
406
  isInteger(WasmI32.fromGrain(x))
767
407
  }
768
408
 
@@ -772,10 +412,17 @@ export let isInteger = (x: Number) => {
772
412
  * @param x: The number to check
773
413
  * @returns `true` if the value is a non-integer rational number or `false` otherwise
774
414
  *
415
+ * @example Number.isRational(1/2)
416
+ * @example Number.isRational(0.5) == false
417
+ * @example Number.isRational(1.0) == false
418
+ * @example Number.isRational(1) == false
419
+ * @example Number.isRational(Infinity) == false
420
+ * @example Number.isRational(NaN) == false
421
+ *
775
422
  * @since v0.5.3
776
423
  */
777
424
  @unsafe
778
- export let isRational = (x: Number) => {
425
+ provide let isRational = (x: Number) => {
779
426
  isRational(WasmI32.fromGrain(x))
780
427
  }
781
428
 
@@ -786,23 +433,30 @@ export let isRational = (x: Number) => {
786
433
  * @param x: The number to check
787
434
  * @returns `true` if the value is finite or `false` otherwise
788
435
  *
436
+ * @example Number.isFinite(1/2)
437
+ * @example Number.isFinite(0.5)
438
+ * @example Number.isFinite(1.0)
439
+ * @example Number.isFinite(1)
440
+ * @example Number.isFinite(Infinity) == false
441
+ * @example Number.isFinite(-Infinity) == false
442
+ * @example Number.isFinite(NaN) == false
443
+ *
789
444
  * @since v0.4.0
790
445
  */
791
446
  @unsafe
792
- export let isFinite = (x: Number) => {
447
+ provide let isFinite = (x: Number) => {
448
+ use WasmI32.{ (==) }
793
449
  let asPtr = WasmI32.fromGrain(x)
794
450
  if (isBoxedNumber(asPtr)) {
795
451
  // Boxed numbers can have multiple subtypes, of which float32 and float64 can be infinite.
796
452
  let tag = WasmI32.load(asPtr, 4n)
797
- if (WasmI32.eq(tag, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG)) {
453
+ if (tag == Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) {
454
+ use WasmF64.{ (-), (==) }
798
455
  // uses the fact that all finite floats minus themselves are zero
799
456
  // (NaN - NaN == NaN, inf - inf == NaN,
800
457
  // -inf - -inf == NaN, inf - -inf == inf, -inf - inf == -inf)
801
458
  let wf64 = WasmF64.load(asPtr, 8n)
802
- WasmF64.eq(WasmF64.sub(wf64, wf64), 0.W)
803
- } else if (WasmI32.eq(tag, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG)) {
804
- let wf32 = WasmF32.load(asPtr, 8n)
805
- WasmF32.eq(WasmF32.sub(wf32, wf32), 0.w)
459
+ wf64 - wf64 == 0.0W
806
460
  } else {
807
461
  // Neither rational numbers nor boxed integers can be infinite or NaN.
808
462
  // Grain doesn't allow creating a rational with denominator of zero either.
@@ -820,10 +474,18 @@ export let isFinite = (x: Number) => {
820
474
  * @param x: The number to check
821
475
  * @returns `true` if the value is NaN, otherwise `false`
822
476
  *
477
+ * @example Number.isNaN(NaN)
478
+ * @example Number.isNaN(Infinity) == false
479
+ * @example Number.isNaN(-Infinity) == false
480
+ * @example Number.isNaN(1/2) == false
481
+ * @example Number.isNaN(0.5) == false
482
+ * @example Number.isNaN(1.0) == false
483
+ * @example Number.isNaN(1) == false
484
+ *
823
485
  * @since v0.4.0
824
486
  */
825
487
  @unsafe
826
- export let isNaN = (x: Number) => {
488
+ provide let isNaN = (x: Number) => {
827
489
  let asPtr = WasmI32.fromGrain(x)
828
490
  isNaN(asPtr)
829
491
  }
@@ -835,21 +497,28 @@ export let isNaN = (x: Number) => {
835
497
  * @param x: The number to check
836
498
  * @returns `true` if the value is infinite or `false` otherwise
837
499
  *
500
+ * @example Number.isInfinite(Infinity)
501
+ * @example Number.isInfinite(-Infinity)
502
+ * @example Number.isInfinite(NaN) == false
503
+ * @example Number.isInfinite(1/2) == false
504
+ * @example Number.isInfinite(0.5) == false
505
+ * @example Number.isInfinite(1.0) == false
506
+ * @example Number.isInfinite(1) == false
507
+ *
838
508
  * @since v0.4.0
839
509
  */
840
510
  @unsafe
841
- export let isInfinite = (x: Number) => {
511
+ provide let isInfinite = (x: Number) => {
512
+ use WasmI32.{ (==) }
842
513
  // The following code is equivalent to (!isFinite(x) && !isNaN(x)),
843
514
  // so see those functions to understand what's going on here.
844
515
  let asPtr = WasmI32.fromGrain(x)
845
516
  if (isBoxedNumber(asPtr)) {
846
517
  let tag = WasmI32.load(asPtr, 4n)
847
- if (WasmI32.eq(tag, Tags._GRAIN_FLOAT64_BOXED_NUM_TAG)) {
518
+ if (tag == Tags._GRAIN_FLOAT64_BOXED_NUM_TAG) {
519
+ use WasmF64.{ (-), (==), (!=) }
848
520
  let wf64 = WasmF64.load(asPtr, 8n)
849
- WasmF64.ne(WasmF64.sub(wf64, wf64), 0.W) && WasmF64.eq(wf64, wf64)
850
- } else if (WasmI32.eq(tag, Tags._GRAIN_FLOAT32_BOXED_NUM_TAG)) {
851
- let wf32 = WasmF32.load(asPtr, 8n)
852
- WasmF32.ne(WasmF32.sub(wf32, wf32), 0.w) && WasmF32.eq(wf32, wf32)
521
+ wf64 - wf64 != 0.0W && wf64 == wf64
853
522
  } else {
854
523
  false
855
524
  }
@@ -858,6 +527,38 @@ export let isInfinite = (x: Number) => {
858
527
  }
859
528
  }
860
529
 
530
+ /**
531
+ * Determines whether two values are considered close to each other using a relative and absolute tolerance.
532
+ *
533
+ * @param a: The first value
534
+ * @param b: The second value
535
+ * @param relativeTolerance: The maximum tolerance to use relative to the larger absolute value `a` or `b`
536
+ * @param absoluteTolerance: The absolute tolerance to use, regardless of the values of `a` or `b`
537
+ * @returns `true` if the values are considered close to each other or `false` otherwise
538
+ *
539
+ * @example Number.isClose(1.233, 1.233)
540
+ * @example Number.isClose(1.233, 1.233000001)
541
+ * @example Number.isClose(8.005, 8.450, absoluteTolerance=0.5)
542
+ * @example Number.isClose(4, 4.1, relativeTolerance=0.025)
543
+ * @example Number.isClose(1.233, 1.24) == false
544
+ * @example Number.isClose(1.233, 1.4566) == false
545
+ * @example Number.isClose(8.005, 8.450, absoluteTolerance=0.4) == false
546
+ * @example Number.isClose(4, 4.1, relativeTolerance=0.024) == false
547
+ *
548
+ * @since v0.6.0
549
+ */
550
+ provide let isClose = (a, b, relativeTolerance=1e-9, absoluteTolerance=0.0) => {
551
+ if (a == b) {
552
+ true
553
+ } else if (isFinite(a) && isFinite(b)) {
554
+ abs(a - b) <=
555
+ max(relativeTolerance * max(abs(a), abs(b)), absoluteTolerance)
556
+ } else {
557
+ // NaN and infinities which were not equal
558
+ false
559
+ }
560
+ }
561
+
861
562
  /**
862
563
  * Parses a string representation of an integer into a `Number` using the
863
564
  * specified radix (also known as a number system "base").
@@ -867,24 +568,33 @@ export let isInfinite = (x: Number) => {
867
568
  * favor of the prefix. Underscores that appear in the numeric portion of the
868
569
  * input are ignored.
869
570
  *
870
- * @param input: The string to parse
571
+ * @param string: The string to parse
871
572
  * @param radix: The number system base to use when parsing the input string
872
- * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise
573
+ * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(err)` containing a variant of `ParseIntError`
574
+ *
575
+ * @example Number.parseInt("1", radix=10) == Ok(1)
576
+ * @example Number.parseInt("-1", radix=10) == Ok(-1)
577
+ * @example Number.parseInt("0xf0", radix=16) == Ok(0x0f0)
873
578
  *
874
579
  * @since v0.4.5
580
+ * @history v0.6.0: Switched from a string-based error message to a structured error enum
875
581
  */
876
- export let parseInt = Atoi.parseInt
582
+ provide let parseInt = Atoi.parseInt
877
583
 
878
584
  /**
879
585
  * Parses a string representation of a float into a `Number`. Underscores that appear
880
586
  * in numeric portions of the input are ignored.
881
587
  *
882
- * @param input: The string to parse
588
+ * @param string: The string to parse
883
589
  * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise
884
590
  *
591
+ * @example Number.parseFloat("1") == Ok(1.0)
592
+ * @example Number.parseFloat("-1") == Ok(-1.0)
593
+ * @example Number.parseFloat("-1.5") == Ok(-1.5)
594
+ *
885
595
  * @since v0.5.5
886
596
  */
887
- export let parseFloat = Atof.parseFloat
597
+ provide let parseFloat = Atof.parseFloat
888
598
 
889
599
  /**
890
600
  * Parses a string representation of an integer, float, or rational into a `Number`.
@@ -893,58 +603,59 @@ export let parseFloat = Atof.parseFloat
893
603
  * @param input: The string to parse
894
604
  * @returns `Ok(value)` containing the parsed number on a successful parse or `Err(msg)` containing an error message string otherwise
895
605
  *
606
+ * @example Number.parse("1") == Ok(1)
607
+ * @example Number.parse("-1") == Ok(-1)
608
+ * @example Number.parse("0xf0") == Ok(0x0f0)
609
+ * @example Number.parse("-1.5") == Ok(-1.5)
610
+ *
896
611
  * @since v0.5.5
897
612
  */
898
613
  @unsafe
899
- export let parse = input => {
614
+ provide let parse = input => {
900
615
  match (parseInt(input, 10)) {
901
616
  Ok(number) => Ok(number),
902
- Err(msg) =>
903
- match (parseFloat(input)) {
904
- Ok(number) => Ok(number),
905
- Err(_) => {
906
- // Split the input on a `/` and attempt to parse a rational
907
- let (+) = WasmI32.add
908
- let (-) = WasmI32.sub
909
- let (<) = WasmI32.ltU
910
- let (==) = WasmI32.eq
617
+ Err(msg) => match (parseFloat(input)) {
618
+ Ok(number) => Ok(number),
619
+ Err(_) => {
620
+ // Split the input on a `/` and attempt to parse a rational
621
+ use WasmI32.{ (+), (-), ltU as (<), (==) }
911
622
 
912
- // Search for `/`
913
- let input = WasmI32.fromGrain(input)
914
- let len = WasmI32.load(input, 4n)
915
- let mut slashIdx = -1n
916
- for (let mut i = 0n; i < len; i += 1n) {
917
- if (WasmI32.load8U(input + i, 8n) == 0x2fn) {
918
- slashIdx = i
919
- break
920
- }
623
+ // Search for `/`
624
+ let input = WasmI32.fromGrain(input)
625
+ let len = WasmI32.load(input, 4n)
626
+ let mut slashIdx = -1n
627
+ for (let mut i = 0n; i < len; i += 1n) {
628
+ if (WasmI32.load8U(input + i, 8n) == 0x2fn) {
629
+ slashIdx = i
630
+ break
921
631
  }
632
+ }
922
633
 
923
- if (slashIdx == -1n) {
924
- Err(msg)
925
- } else {
926
- let numeratorLen = slashIdx
927
- let denominatorLen = len - slashIdx - 1n
634
+ if (slashIdx == -1n) {
635
+ Err(msg)
636
+ } else {
637
+ let numeratorLen = slashIdx
638
+ let denominatorLen = len - slashIdx - 1n
928
639
 
929
- let numerator = allocateString(numeratorLen)
930
- Memory.copy(numerator + 8n, input + 8n, numeratorLen)
931
- let numerator = WasmI32.toGrain(numerator): String
640
+ let numerator = allocateString(numeratorLen)
641
+ Memory.copy(numerator + 8n, input + 8n, numeratorLen)
642
+ let numerator = WasmI32.toGrain(numerator): String
932
643
 
933
- let denominator = allocateString(denominatorLen)
934
- Memory.copy(
935
- denominator + 8n,
936
- input + 8n + slashIdx + 1n,
937
- denominatorLen
938
- )
939
- let denominator = WasmI32.toGrain(denominator): String
644
+ let denominator = allocateString(denominatorLen)
645
+ Memory.copy(
646
+ denominator + 8n,
647
+ input + 8n + slashIdx + 1n,
648
+ denominatorLen
649
+ )
650
+ let denominator = WasmI32.toGrain(denominator): String
940
651
 
941
- match ((parseInt(numerator, 10), parseInt(denominator, 10))) {
942
- (Ok(numerator), Ok(denominator)) => Ok(numerator / denominator),
943
- (Err(msg), _) | (_, Err(msg)) => Err(msg),
944
- }
652
+ match ((parseInt(numerator, 10), parseInt(denominator, 10))) {
653
+ (Ok(numerator), Ok(denominator)) => Ok(numerator / denominator),
654
+ (Err(msg), _) | (_, Err(msg)) => Err(msg),
945
655
  }
946
- },
656
+ }
947
657
  },
658
+ },
948
659
  }
949
660
  }
950
661
 
@@ -970,136 +681,283 @@ let chebyshevSine = (radians: Number) => {
970
681
  (radians - pi - pi_minor) * (radians + pi + pi_minor) * p1 * radians
971
682
  }
972
683
 
973
- /**
974
- * Computes the sine of a number (in radians) using Chebyshev polynomials.
975
- *
976
- * @param radians: The input in radians
977
- * @returns The computed sine
978
- *
979
- * @since v0.5.2
980
- * @history v0.5.4: Handle NaN and Infinity
981
- */
982
- export let sin = (radians: Number) => {
983
- if (radians != radians || radians == infinity) {
984
- nan
985
- } else {
986
- let quot = reduceToPiBound(radians)
987
- let bounded = radians - pi * quot
988
- if (quot % 2 == 0) {
989
- chebyshevSine(bounded)
990
- } else {
991
- neg(chebyshevSine(bounded))
992
- }
993
- }
684
+ @unsafe
685
+ let rf = z => {
686
+ // see: musl/src/math/asin.c and SUN COPYRIGHT NOTICE at top of file
687
+ // Operators
688
+ use WasmF64.{ (+), (*), (/) }
689
+ /* coefficients for R(x^2) */
690
+ let pS0 = 1.66666666666666657415e-01W /* 0x3FC55555, 0x55555555 */
691
+ let pS1 = -3.25565818622400915405e-01W /* 0xBFD4D612, 0x03EB6F7D */
692
+ let pS2 = 2.01212532134862925881e-01W /* 0x3FC9C155, 0x0E884455 */
693
+ let pS3 = -4.00555345006794114027e-02W /* 0xBFA48228, 0xB5688F3B */
694
+ let pS4 = 7.91534994289814532176e-04W /* 0x3F49EFE0, 0x7501B288 */
695
+ let pS5 = 3.47933107596021167570e-05W /* 0x3F023DE1, 0x0DFDF709 */
696
+ let qS1 = -2.40339491173441421878e+00W /* 0xC0033A27, 0x1C8A2D4B */
697
+ let qS2 = 2.02094576023350569471e+00W /* 0x40002AE5, 0x9C598AC8 */
698
+ let qS3 = -6.88283971605453293030e-01W /* 0xBFE6066C, 0x1B8D0159 */
699
+ let qS4 = 7.70381505559019352791e-02W /* 0x3FB3B8C5, 0xB12E9282 */
700
+ // Calculations
701
+ let p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))))
702
+ let q = 1.0W + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)))
703
+ p / q
994
704
  }
995
705
 
996
706
  /**
997
- * Computes the cosine of a number (in radians) using Chebyshev polynomials.
707
+ * Computes the inverse sine of the given angle.
998
708
  *
999
- * @param radians: The input in radians
1000
- * @returns The computed cosine
709
+ * @param angle: A number between -1 and 1, representing the angle's sine value
710
+ * @returns The inverse sine (angle in radians between `-pi/2` and `pi/2`) of the given `angle` or `NaN` if the given `angle` is not between`-1` and `1`
1001
711
  *
1002
- * @since v0.5.2
1003
- * @history v0.5.4: Handle NaN and Infinity
712
+ * @example Number.asin(0) == 0
713
+ * @example Number.asin(1) == 1.5707963267948966
714
+ *
715
+ * @since v0.6.0
1004
716
  */
1005
- export let cos = (radians: Number) => {
1006
- if (radians != radians || radians == infinity) {
1007
- nan
717
+ @unsafe
718
+ provide let asin = angle => {
719
+ // see: musl/src/math/asin.c and SUN COPYRIGHT NOTICE at top of file
720
+ let origAngle = Numbers.coerceNumberToWasmF64(angle)
721
+ let mut x = origAngle
722
+
723
+ let pio2_hi = 1.57079632679489655800e+00W /* 0x3FF921FB, 0x54442D18 */
724
+ let pio2_lo = 6.12323399573676603587e-17W /* 0x3C91A626, 0x33145C07 */
725
+
726
+ use WasmI32.{ (&), (|), leU as (<), geU as (>=) }
727
+ use WasmI64.{ (>>>) }
728
+ use WasmF64.{ (+), (-), (*), (/) }
729
+
730
+ let hx = WasmI32.wrapI64(WasmI64.reinterpretF64(x) >>> 32N)
731
+ let ix = hx & 0x7fffffffn
732
+ /* |x| >= 1 or nan */
733
+ if (ix >= 0x3ff00000n) {
734
+ let lx = WasmI32.wrapI64(WasmI64.reinterpretF64(x))
735
+ use WasmI32.{ (-) }
736
+ /* asin(1) = +-pi/2 with inexact */
737
+ if (WasmI32.eqz(ix - 0x3ff00000n | lx))
738
+ return WasmI32.toGrain(newFloat64(x * pio2_hi + 0x1p-120W)): Number
739
+ return WasmI32.toGrain(newFloat64(NaNW)): Number
740
+ }
741
+ /* |x| < 0.5 */
742
+ if (ix < 0x3fe00000n) {
743
+ /* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
744
+ let output = if (ix < 0x3e500000n && ix >= 0x00100000n)
745
+ x
746
+ else
747
+ x + x * rf(x * x)
748
+ return WasmI32.toGrain(newFloat64(output)): Number
749
+ }
750
+ /* 1 > |x| >= 0.5 */
751
+ let z = (1.0W - WasmF64.abs(x)) * 0.5W
752
+ let s = WasmF64.sqrt(z)
753
+ let r = rf(z)
754
+ /* if |x| > 0.975 */
755
+ if (ix >= 0x3fef3333n) {
756
+ x = pio2_hi - (2.0W * (s + s * r) - pio2_lo)
1008
757
  } else {
1009
- sin(pi / 2 + radians)
758
+ use WasmI64.{ (&) }
759
+ /* f+c = sqrt(z) */
760
+ let f = WasmF64.reinterpretI64(
761
+ WasmI64.reinterpretF64(s) & 0xFFFFFFFF00000000N
762
+ )
763
+ let c = (z - f * f) / (s + f)
764
+ x = 0.5W * pio2_hi -
765
+ (2.0W * s * r - (pio2_lo - 2.0W * c) - (0.5W * pio2_hi - 2.0W * f))
1010
766
  }
767
+ x = WasmF64.copySign(x, origAngle)
768
+ return WasmI32.toGrain(newFloat64(x)): Number
1011
769
  }
1012
770
 
1013
771
  /**
1014
- * Computes the tangent of a number (in radians) using Chebyshev polynomials.
772
+ * Computes the inverse cosine of the given angle.
1015
773
  *
1016
- * @param radians: The input in radians
1017
- * @returns The computed tangent
774
+ * @param angle: A number between -1 and 1, representing the angle's cosine value
775
+ * @returns The inverse cosine (angle in radians between `-pi/2` and `pi/2`) of the given `angle` or `NaN` if the given `angle` is not between`-1` and `1`
1018
776
  *
1019
- * @since v0.5.4
777
+ * @example Number.acos(1) == 0
778
+ * @example Number.acos(0) == 1.5707963267948966
779
+ *
780
+ * @since v0.6.0
1020
781
  */
1021
- export let tan = (radians: Number) => {
1022
- if (isNaN(radians) || isInfinite(radians)) {
1023
- nan
1024
- } else {
1025
- sin(radians) / cos(radians)
782
+ @unsafe
783
+ provide let acos = angle => {
784
+ // see: musl/src/math/acos.c and SUN COPYRIGHT NOTICE at top of file
785
+ let origAngle = Numbers.coerceNumberToWasmF64(angle)
786
+ let mut x = origAngle
787
+
788
+ let pio2_hi = 1.57079632679489655800e+00W /* 0x3FF921FB, 0x54442D18 */
789
+ let pio2_lo = 6.12323399573676603587e-17W /* 0x3C91A626, 0x33145C07 */
790
+
791
+ use WasmI32.{ (>>), (&), (|), (!=), leU as (<), geU as (>=), leU as (<=) }
792
+ use WasmI64.{ (>>>) }
793
+ use WasmF64.{ (+), (-), (*), (/) }
794
+
795
+ let hx = WasmI32.wrapI64(WasmI64.reinterpretF64(x) >>> 32N)
796
+ let ix = hx & 0x7fffffffn
797
+
798
+ /* |x| >= 1 or nan */
799
+ if (ix >= 0x3ff00000n) {
800
+ let lx = WasmI32.wrapI64(WasmI64.reinterpretF64(x))
801
+ use WasmI32.{ (-) }
802
+ if (WasmI32.eqz(ix - 0x3ff00000n | lx)) {
803
+ /* acos(1)=0, acos(-1)=pi */
804
+ if (hx >> 31n != 0n)
805
+ return WasmI32.toGrain(newFloat64(2.0W * pio2_hi + 0x1p-120W)): Number
806
+ else
807
+ return 0
808
+ }
809
+ return WasmI32.toGrain(newFloat64(NaNW)): Number
1026
810
  }
811
+ /* |x| < 0.5 */
812
+ if (ix < 0x3fe00000n) {
813
+ /* |x| < 2**-57 */
814
+ let output = if (ix <= 0x3c600000n)
815
+ pio2_hi + 0x1p-120W
816
+ else
817
+ pio2_hi - (x - (pio2_lo - x * rf(x * x)))
818
+ return WasmI32.toGrain(newFloat64(output)): Number
819
+ }
820
+ /* x < -0.5 */
821
+ if (hx >> 31n != 0n) {
822
+ let z = (1.0W + x) * 0.5W
823
+ let s = WasmF64.sqrt(z)
824
+ let w = rf(z) * s - pio2_lo
825
+ return WasmI32.toGrain(newFloat64(2.0W * (pio2_hi - (s + w)))): Number
826
+ }
827
+
828
+ /* x > 0.5 */
829
+ use WasmI64.{ (&) }
830
+ let z = (1.0W - x) * 0.5W
831
+ let s = WasmF64.sqrt(z)
832
+ let df = WasmF64.reinterpretI64(
833
+ WasmI64.reinterpretF64(s) & 0xFFFFFFFF00000000N
834
+ )
835
+ let c = (z - df * df) / (s + df)
836
+ let w = rf(z) * s + c
837
+ return WasmI32.toGrain(newFloat64(2.0W * (df + w))): Number
1027
838
  }
1028
839
 
1029
- // Math.gamma implemented using the Lanczos approximation
1030
- // https://en.wikipedia.org/wiki/Lanczos_approximation
1031
840
  /**
1032
- * Computes the gamma function of a value using Lanczos approximation.
841
+ * Computes the inverse tangent of the given angle.
1033
842
  *
1034
- * @param z: The value to interpolate
1035
- * @returns The gamma of the given value
843
+ * @param angle: A number between -1 and 1, representing the angle's tangent value
844
+ * @returns The inverse tangent (angle in radians between `-pi/2` and `pi/2`) of the given `angle` or `NaN` if the given `angle` is not between`-1` and `1`
1036
845
  *
1037
- * @throws InvalidArgument(String): When `z` is zero
846
+ * @example Number.atan(0) == 0
847
+ * @example Number.atan(1) == 0.7853981633974483
1038
848
  *
1039
- * @since v0.5.4
849
+ * @since v0.6.0
1040
850
  */
1041
- export let rec gamma = z => {
1042
- if (z == 0) {
1043
- throw Exception.InvalidArgument("Gamma of 0 is undefined")
1044
- } else if (isInteger(z) && z > 0) {
1045
- let mut output = 1
1046
- for (let mut i = 1; i < z; i += 1) {
1047
- output *= i
1048
- }
1049
- output
851
+ @unsafe
852
+ provide let atan = angle => {
853
+ // see: musl/src/math/asin.c and SUN COPYRIGHT NOTICE at top of file
854
+ let origAngle = Numbers.coerceNumberToWasmF64(angle)
855
+ let mut x = origAngle
856
+ // Constants
857
+ let atanhi0 = 4.63647609000806093515e-01W // atan(0.5)hi 0x3FDDAC67, 0x0561BB4F
858
+ let atanhi1 = 7.85398163397448278999e-01W // atan(1.0)hi 0x3FE921FB, 0x54442D18
859
+ let atanhi2 = 9.82793723247329054082e-01W // atan(1.5)hi 0x3FEF730B, 0xD281F69B
860
+ let atanhi3 = 1.57079632679489655800e+00W // atan(inf)hi 0x3FF921FB, 0x54442D18
861
+ let atanlo0 = 2.26987774529616870924e-17W // atan(0.5)lo 0x3C7A2B7F, 0x222F65E2
862
+ let atanlo1 = 3.06161699786838301793e-17W // atan(1.0)lo 0x3C81A626, 0x33145C07
863
+ let atanlo2 = 1.39033110312309984516e-17W // atan(1.5)lo 0x3C700788, 0x7AF0CBBD
864
+ let atanlo3 = 6.12323399573676603587e-17W // atan(inf)lo 0x3C91A626, 0x33145C07
865
+ let aT0 = 3.33333333333329318027e-01W // 0x3FD55555, 0x5555550D
866
+ let aT1 = -1.99999999998764832476e-01W // 0xBFC99999, 0x9998EBC4
867
+ let aT2 = 1.42857142725034663711e-01W // 0x3FC24924, 0x920083FF
868
+ let aT3 = -1.11111104054623557880e-01W // 0xBFBC71C6, 0xFE231671
869
+ let aT4 = 9.09088713343650656196e-02W // 0x3FB745CD, 0xC54C206E
870
+ let aT5 = -7.69187620504482999495e-02W // 0xBFB3B0F2, 0xAF749A6D
871
+ let aT6 = 6.66107313738753120669e-02W // 0x3FB10D66, 0xA0D03D51
872
+ let aT7 = -5.83357013379057348645e-02W // 0xBFADDE2D, 0x52DEFD9A
873
+ let aT8 = 4.97687799461593236017e-02W // 0x3FA97B4B, 0x24760DEB
874
+ let aT9 = -3.65315727442169155270e-02W // 0xBFA2B444, 0x2C6A6C2F
875
+ let aT10 = 1.62858201153657823623e-02W // 0x3F90AD3A, 0xE322DA11
876
+ // Operators
877
+ use WasmI32.{ (&), (<), (>=), (==) }
878
+ use WasmI64.{ (>>>) }
879
+ use WasmF64.{ (+), (-), (*), (/) }
880
+ // Calculations
881
+ let ix = WasmI32.wrapI64(WasmI64.reinterpretF64(x) >>> 32N)
882
+ let sx = x
883
+ let ix = ix & 0x7FFFFFFFn
884
+ /* if |x| >= 2^66 */
885
+ if (ix >= 0x44100000n) {
886
+ if (isNaN(angle)) return NaN
887
+ let z = atanhi3 + 0x1p-120W
888
+ return WasmI32.toGrain(newFloat64(WasmF64.copySign(z, sx))): Number
889
+ }
890
+ let mut id = 3n
891
+ /* |x| < 0.4375 */
892
+ if (ix < 0x3FDC0000n) {
893
+ /* |x| < 2^-27 */
894
+ if (ix < 0x3E400000n) return angle
895
+ id = -1n
1050
896
  } else {
1051
- let mut z = z
1052
- let g = 7
1053
- let c = [>
1054
- 0.99999999999980993,
1055
- 676.5203681218851,
1056
- -1259.1392167224028,
1057
- 771.32342877765313,
1058
- -176.61502916214059,
1059
- 12.507343278686905,
1060
- -0.13857109526572012,
1061
- 9.9843695780195716e-6,
1062
- 1.5056327351493116e-7,
1063
- ]
1064
- let mut output = 0
1065
- if (z < 0.5) {
1066
- output = pi / (sin(pi * z) * gamma(1 - z))
1067
- } else if (z == 0.5) {
1068
- // Handle this case separately because it is out of the domain of Number.pow when calculating
1069
- output = 1.7724538509055159
897
+ x = WasmF64.abs(x)
898
+ /* |x| < 1.1875 */
899
+ if (ix < 0x3FF30000n) {
900
+ /* 7/16 <= |x| < 11/16 */
901
+ if (ix < 0x3FE60000n) {
902
+ id = 0n
903
+ x = (2.0W * x - 1.0W) / (2.0W + x)
904
+ } else { /* 11/16 <= |x| < 19/16 */
905
+ id = 1n
906
+ x = (x - 1.0W) / (x + 1.0W)
907
+ }
1070
908
  } else {
1071
- z -= 1
1072
- let mut x = c[0]
1073
- for (let mut i = 1; i < g + 2; i += 1) {
1074
- x += c[i] / (z + i)
909
+ /* |x| < 2.4375 */
910
+ if (ix < 0x40038000n) {
911
+ id = 2n
912
+ x = (x - 1.5W) / (1.0W + 1.5W * x)
913
+ } else { /* 2.4375 <= |x| < 2^66 */
914
+ x = -1.0W / x
1075
915
  }
1076
-
1077
- let t = z + g + 0.5
1078
- output = sqrt(2 * pi) * pow(t, z + 0.5) * exp(t * -1) * x
1079
916
  }
1080
- if (abs(output) == infinity) infinity else output
1081
917
  }
918
+ let z = x * x
919
+ let w = z * z
920
+ let s1 = z * (aT0 + w * (aT2 + w * (aT4 + w * (aT6 + w * (aT8 + w * aT10)))))
921
+ let s2 = w * (aT1 + w * (aT3 + w * (aT5 + w * (aT7 + w * aT9))))
922
+ let s3 = x * (s1 + s2)
923
+ use WasmI32.{ (<) }
924
+ if (id < 0n)
925
+ return WasmI32.toGrain(newFloat64(WasmF64.copySign(x - s3, sx))): Number
926
+ let mut z = 0.0W
927
+ match (id) {
928
+ 0n => z = atanhi0 - (s3 - atanlo0 - x),
929
+ 1n => z = atanhi1 - (s3 - atanlo1 - x),
930
+ 2n => z = atanhi2 - (s3 - atanlo2 - x),
931
+ 3n => z = atanhi3 - (s3 + atanlo3 - x),
932
+ _ => fail "Unreachable",
933
+ }
934
+ return WasmI32.toGrain(newFloat64(WasmF64.copySign(z, sx))): Number
1082
935
  }
1083
936
 
1084
937
  /**
1085
- * Computes the product of consecutive integers for an integer input and computes the gamma function for non-integer inputs.
938
+ * Computes the angle between the positive x-axis and the ray from the origin to the point (x, y).
1086
939
  *
1087
- * @param n: The value to factorialize
1088
- * @returns The factorial of the given value
940
+ * @param y: The given y coordinate
941
+ * @param x: The given x coordinate
942
+ * @returns The angle in radians between the positive x-axis and the point (x, y)
1089
943
  *
1090
- * @throws InvalidArgument(String): When `n` is negative
944
+ * @example Number.atan2(0, 1) == Number.pi
1091
945
  *
1092
- * @since v0.5.4
946
+ * @since v0.6.0
1093
947
  */
1094
- export let rec factorial = n => {
1095
- if (isInteger(n) && n < 0) gamma(abs(n) + 1) * -1 else if (
1096
- !isInteger(n) && n < 0
1097
- ) {
1098
- throw Exception.InvalidArgument(
1099
- "Cannot compute the factorial of a negative non-integer"
1100
- )
1101
- } else {
1102
- gamma(n + 1)
948
+ provide let atan2 = (y, x) => {
949
+ if (x > 0) {
950
+ atan(y / x)
951
+ } else if (x < 0 && y >= 0) {
952
+ atan(y / x) + pi
953
+ } else if (x < 0 && y < 0) {
954
+ atan(y / x) - pi
955
+ } else if (x == 0 && y > 0) {
956
+ pi / 2
957
+ } else if (x == 0 && y < 0) {
958
+ pi / -2
959
+ } else { // x == 0 && y == 0
960
+ 0
1103
961
  }
1104
962
  }
1105
963
 
@@ -1109,9 +967,11 @@ export let rec factorial = n => {
1109
967
  * @param degrees: The value to convert
1110
968
  * @returns The value in radians
1111
969
  *
970
+ * @example Number.toRadians(180) == Number.pi
971
+ *
1112
972
  * @since v0.5.4
1113
973
  */
1114
- export let toRadians = degrees => degrees * (pi / 180)
974
+ provide let toRadians = degrees => degrees * (pi / 180)
1115
975
 
1116
976
  /**
1117
977
  * Converts radians to degrees.
@@ -1119,6 +979,96 @@ export let toRadians = degrees => degrees * (pi / 180)
1119
979
  * @param radians: The value to convert
1120
980
  * @returns The value in degrees
1121
981
  *
982
+ * @example Number.toRadians(Number.pi) == 180
983
+ *
1122
984
  * @since v0.5.4
1123
985
  */
1124
- export let toDegrees = radians => radians * (180 / pi)
986
+ provide let toDegrees = radians => radians * (180 / pi)
987
+
988
+ // TODO(#471): Add examples for clamp
989
+ /**
990
+ * Constrains a number within the given inclusive range.
991
+ *
992
+ * @param range: The inclusive range to clamp within
993
+ * @param input: The number to clamp
994
+ * @returns The constrained number
995
+ *
996
+ * @since v0.6.0
997
+ */
998
+ provide let clamp = (range, input) => {
999
+ if (isNaN(input)) {
1000
+ input
1001
+ } else {
1002
+ let rangeEnd = max(range.rangeStart, range.rangeEnd)
1003
+ let rangeStart = min(range.rangeStart, range.rangeEnd)
1004
+
1005
+ if (input > rangeEnd) {
1006
+ rangeEnd
1007
+ } else if (input < rangeStart) {
1008
+ rangeStart
1009
+ } else {
1010
+ input
1011
+ }
1012
+ }
1013
+ }
1014
+
1015
+ // TODO(#471): Add examples for linearInterpolate
1016
+ /**
1017
+ * Maps a weight between 0 and 1 within the given inclusive range.
1018
+ *
1019
+ * @param range: The inclusive range to interpolate within
1020
+ * @param weight: The weight to interpolate
1021
+ * @returns The blended value
1022
+ *
1023
+ * @throws InvalidArgument(String): When `weight` is not between 0 and 1
1024
+ * @throws InvalidArgument(String): When `range` is not finite
1025
+ * @throws InvalidArgument(String): When `range` includes NaN
1026
+ *
1027
+ * @since v0.6.0
1028
+ */
1029
+ provide let linearInterpolate = (range, weight) => {
1030
+ if (weight < 0 || weight > 1 || isNaN(weight))
1031
+ throw Exception.InvalidArgument("Weight must be between 0 and 1")
1032
+ if (isInfinite(range.rangeStart) || isInfinite(range.rangeEnd))
1033
+ throw Exception.InvalidArgument("The range must be finite")
1034
+ if (isNaN(range.rangeStart) || isNaN(range.rangeEnd))
1035
+ throw Exception.InvalidArgument("The range must not include NaN")
1036
+ (range.rangeEnd - range.rangeStart) * weight + range.rangeStart
1037
+ }
1038
+
1039
+ // TODO(#471): Add examples for linearMap
1040
+ /**
1041
+ * Scales a number from one inclusive range to another inclusive range.
1042
+ * If the number is outside the input range, it will be clamped.
1043
+ *
1044
+ * @param inputRange: The inclusive range you are mapping from
1045
+ * @param outputRange: The inclusive range you are mapping to
1046
+ * @param current: The number to map
1047
+ * @returns The mapped number
1048
+ *
1049
+ * @throws InvalidArgument(String): When `inputRange` is not finite
1050
+ * @throws InvalidArgument(String): When `inputRange` includes NaN
1051
+ * @throws InvalidArgument(String): When `outputRange` is not finite
1052
+ * @throws InvalidArgument(String): When `outputRange` includes NaN
1053
+ *
1054
+ * @since v0.6.0
1055
+ */
1056
+ provide let linearMap = (inputRange, outputRange, current) => {
1057
+ if (isNaN(current)) {
1058
+ current
1059
+ } else {
1060
+ if (isInfinite(inputRange.rangeStart) || isInfinite(inputRange.rangeEnd))
1061
+ throw Exception.InvalidArgument("The inputRange must be finite")
1062
+ if (isNaN(inputRange.rangeStart) || isNaN(inputRange.rangeEnd))
1063
+ throw Exception.InvalidArgument("The inputRange must not include NaN")
1064
+ if (isInfinite(outputRange.rangeStart) || isInfinite(outputRange.rangeEnd))
1065
+ throw Exception.InvalidArgument("The outputRange must be finite")
1066
+ if (isNaN(outputRange.rangeStart) || isNaN(outputRange.rangeEnd))
1067
+ throw Exception.InvalidArgument("The outputRange must not include NaN")
1068
+ let mapped = (current - inputRange.rangeStart) *
1069
+ (outputRange.rangeEnd - outputRange.rangeStart) /
1070
+ (inputRange.rangeEnd - inputRange.rangeStart) +
1071
+ outputRange.rangeStart
1072
+ clamp(outputRange, mapped)
1073
+ }
1074
+ }