@grain/stdlib 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +1 -1
  3. package/array.gr +92 -73
  4. package/array.md +18 -18
  5. package/bigint.gr +497 -0
  6. package/bigint.md +811 -0
  7. package/buffer.gr +56 -217
  8. package/buffer.md +24 -17
  9. package/bytes.gr +103 -205
  10. package/bytes.md +19 -0
  11. package/char.gr +152 -166
  12. package/char.md +200 -0
  13. package/exception.md +6 -0
  14. package/float32.gr +159 -82
  15. package/float32.md +315 -0
  16. package/float64.gr +163 -82
  17. package/float64.md +315 -0
  18. package/hash.gr +53 -49
  19. package/int32.gr +479 -230
  20. package/int32.md +937 -0
  21. package/int64.gr +479 -230
  22. package/int64.md +937 -0
  23. package/list.gr +530 -116
  24. package/list.md +1141 -0
  25. package/map.gr +302 -121
  26. package/map.md +525 -0
  27. package/number.gr +51 -57
  28. package/number.md +37 -3
  29. package/option.gr +25 -25
  30. package/option.md +1 -1
  31. package/package.json +3 -3
  32. package/pervasives.gr +504 -52
  33. package/pervasives.md +1116 -0
  34. package/queue.gr +8 -1
  35. package/queue.md +10 -0
  36. package/random.gr +196 -0
  37. package/random.md +179 -0
  38. package/range.gr +26 -26
  39. package/regex.gr +1833 -842
  40. package/regex.md +11 -11
  41. package/result.md +1 -1
  42. package/runtime/bigint.gr +2045 -0
  43. package/runtime/bigint.md +326 -0
  44. package/runtime/dataStructures.gr +99 -279
  45. package/runtime/dataStructures.md +391 -0
  46. package/runtime/debug.gr +0 -1
  47. package/runtime/debug.md +6 -0
  48. package/runtime/equal.gr +40 -37
  49. package/runtime/equal.md +6 -0
  50. package/runtime/exception.gr +28 -15
  51. package/runtime/exception.md +30 -0
  52. package/runtime/gc.gr +50 -20
  53. package/runtime/gc.md +36 -0
  54. package/runtime/malloc.gr +32 -22
  55. package/runtime/malloc.md +55 -0
  56. package/runtime/numberUtils.gr +297 -142
  57. package/runtime/numberUtils.md +54 -0
  58. package/runtime/numbers.gr +1204 -453
  59. package/runtime/numbers.md +300 -0
  60. package/runtime/string.gr +193 -228
  61. package/runtime/string.md +24 -0
  62. package/runtime/stringUtils.gr +62 -38
  63. package/runtime/stringUtils.md +6 -0
  64. package/runtime/unsafe/constants.gr +17 -0
  65. package/runtime/unsafe/constants.md +72 -0
  66. package/runtime/unsafe/conv.gr +10 -10
  67. package/runtime/unsafe/conv.md +71 -0
  68. package/runtime/unsafe/errors.md +204 -0
  69. package/runtime/unsafe/memory.gr +14 -3
  70. package/runtime/unsafe/memory.md +54 -0
  71. package/runtime/unsafe/printWasm.gr +4 -4
  72. package/runtime/unsafe/printWasm.md +24 -0
  73. package/runtime/unsafe/tags.gr +11 -10
  74. package/runtime/unsafe/tags.md +120 -0
  75. package/runtime/unsafe/wasmf32.gr +9 -2
  76. package/runtime/unsafe/wasmf32.md +168 -0
  77. package/runtime/unsafe/wasmf64.gr +9 -2
  78. package/runtime/unsafe/wasmf64.md +168 -0
  79. package/runtime/unsafe/wasmi32.gr +65 -47
  80. package/runtime/unsafe/wasmi32.md +282 -0
  81. package/runtime/unsafe/wasmi64.gr +78 -50
  82. package/runtime/unsafe/wasmi64.md +300 -0
  83. package/runtime/utils/printing.gr +62 -0
  84. package/runtime/utils/printing.md +18 -0
  85. package/runtime/wasi.gr +200 -46
  86. package/runtime/wasi.md +839 -0
  87. package/set.gr +125 -121
  88. package/set.md +24 -21
  89. package/stack.gr +29 -29
  90. package/stack.md +4 -6
  91. package/string.gr +434 -415
  92. package/string.md +3 -3
  93. package/sys/file.gr +477 -482
  94. package/sys/process.gr +33 -47
  95. package/sys/random.gr +48 -20
  96. package/sys/random.md +38 -0
  97. package/sys/time.gr +12 -28
@@ -0,0 +1,2045 @@
1
+ /* grainc-flags --no-pervasives */
2
+ /**
3
+ * Arbitary-precision integers.
4
+ */
5
+
6
+ // Useful reading:
7
+ // Modern Computer Arithmetic, Richard Brent and Paul Zimmermann, Cambridge University Press, 2010.
8
+ // https://members.loria.fr/PZimmermann/mca/pub226.html
9
+
10
+ /*
11
+ This file is *not* a direct port of `nim-lang/bigints`, but pieces of it are, and it does draw substantial inspiration from it.
12
+ The following is the copyright notice from the `nim-lang/bigints` project (MIT License same as license for Grain standard library):
13
+
14
+ Copyright 2019 Dennis Felsing
15
+ */
16
+ import Memory from "runtime/unsafe/memory"
17
+ import Tags from "runtime/unsafe/tags"
18
+ import {
19
+ _UMAX_I64,
20
+ _SMAX32_I64,
21
+ _SMAX_I64,
22
+ _SMAX_I32,
23
+ } from "runtime/unsafe/constants"
24
+ import WasmI32 from "runtime/unsafe/wasmi32"
25
+ import WasmI64 from "runtime/unsafe/wasmi64"
26
+ import WasmF32 from "runtime/unsafe/wasmf32"
27
+ import WasmF64 from "runtime/unsafe/wasmf64"
28
+ import Exception from "runtime/exception"
29
+ import DS from "runtime/dataStructures"
30
+ import RPrint from "runtime/utils/printing"
31
+
32
+ // things we need which are missing due to --no-pervasives:
33
+ primitive (!): Bool -> Bool = "@not"
34
+ primitive (&&): (Bool, Bool) -> Bool = "@and"
35
+ primitive (||): (Bool, Bool) -> Bool = "@or"
36
+ primitive throw: Exception -> a = "@throw"
37
+
38
+ enum List<a> {
39
+ [],
40
+ [...](a, List<a>),
41
+ }
42
+
43
+ // Layout:
44
+ // -------------------------------------------------------
45
+ // | numtag | tag | size | flags | <reserved> | limbs... |
46
+ // -------------------------------------------------------
47
+ //
48
+ // numtag | i32 | always _GRAIN_BOXED_NUM_HEAP_TAG
49
+ // tag | i32 | always _GRAIN_BIGINT_BOXED_NUM_TAG
50
+ // size | i32 | the number of limbs. If negative, the bigint is negative
51
+ // flags | i16 | bitflags used for algorithm implementations (see below)
52
+ // <res.> | i16 | reserved for future use
53
+ // limbs | i64... |
54
+
55
+ // NOTE: As this layout is similar to GMP's, we have the same type of limitation
56
+ // on integer size (16 GiB, since we reject sizes with MSB of 1; see init())
57
+ // (https://stackoverflow.com/a/62530477)
58
+
59
+ // Flags:
60
+ // 1 - NEGATIVE: set to 1 if the number is negative
61
+
62
+ // Outline of future strategy for fixints:
63
+ // - the <reserved> portion will become the number of limbs, and we'll have an _IS_FIXINT flag (so fixints can be 1-15 64-bit dwords)
64
+ // - fixints will always internally be positive numbers in the range [0, 2^{64*n})
65
+ // - (this should make most operations' implementations work pretty seamlessly)
66
+ // - when printing, we check if the MSB is a 1, and, if so, allocate a new temporary non-fixint with the twos complement and print that
67
+
68
+ @unsafe
69
+ let _IS_NEGATIVE = 1n
70
+
71
+ @unsafe
72
+ let maxu32 = (a, b) => {
73
+ let (<) = WasmI32.ltU
74
+ if (a < b) b else a
75
+ }
76
+
77
+ @unsafe
78
+ let minu32 = (a, b) => {
79
+ let (<) = WasmI32.ltU
80
+ if (a < b) a else b
81
+ }
82
+
83
+ @unsafe
84
+ let minu64 = (a, b) => {
85
+ let (<) = WasmI64.ltU
86
+ if (a < b) a else b
87
+ }
88
+
89
+ // TODO(#1188): use faster abs algos
90
+ // https://stackoverflow.com/questions/664852/which-is-the-fastest-way-to-get-the-absolute-value-of-a-number
91
+
92
+ @unsafe
93
+ let absi32 = n => {
94
+ let (<) = WasmI32.ltS
95
+ if (n < 0n) {
96
+ WasmI32.mul(n, -1n)
97
+ } else {
98
+ n
99
+ }
100
+ }
101
+
102
+ @unsafe
103
+ let absi64 = n => {
104
+ let (<) = WasmI64.ltS
105
+ if (n < 0N) {
106
+ WasmI64.mul(n, -1N)
107
+ } else {
108
+ n
109
+ }
110
+ }
111
+
112
+ @unsafe
113
+ let lnot = n => {
114
+ WasmI32.xor(n, -1n)
115
+ }
116
+
117
+ @unsafe
118
+ let init = (limbs: WasmI32) => {
119
+ let (+) = WasmI32.add
120
+ let (*) = WasmI32.mul
121
+ let (<) = WasmI32.ltS
122
+ let (==) = WasmI32.eq
123
+ let (&) = WasmI32.and
124
+ if (!WasmI32.eqz(limbs & 0x80000000n)) {
125
+ // MSB is 1. We reject these sizes because they will cause overflows
126
+ // in our multiplication/division algorithms. This means that BigInts
127
+ // are limited to 16+17179869176 bytes, or just over 16GiB.
128
+ throw Exception.InvalidArgument(
129
+ "Cannot allocate BigInt with >= 2147483648 limbs"
130
+ )
131
+ }
132
+
133
+ let numtagLen = 4n
134
+ let tagLen = 4n
135
+ let sizeLen = 4n
136
+ let resflagsLen = 4n
137
+ let limbsLen = absi32(limbs) * 8n
138
+ let len = numtagLen + tagLen + sizeLen + resflagsLen + limbsLen
139
+
140
+ let ptr = Memory.malloc(len)
141
+ Memory.fill(ptr, 0n, len)
142
+
143
+ WasmI32.store(ptr, Tags._GRAIN_BOXED_NUM_HEAP_TAG, 0n)
144
+ WasmI32.store(ptr, Tags._GRAIN_BIGINT_BOXED_NUM_TAG, 4n)
145
+ WasmI32.store(ptr, limbs, 8n)
146
+ WasmI32.store(ptr, 0n, 12n)
147
+
148
+ ptr
149
+ }
150
+
151
+ // For debugging
152
+ @unsafe
153
+ export let debugDumpNumber = (num: WasmI32) => {
154
+ //let num = WasmI32.fromGrain(num)
155
+ RPrint.printString("-=-=-=-== debug dump ==-=-=-=-")
156
+ RPrint.printString("Ref Count:")
157
+ RPrint.printNumber(WasmI64.extendI32U(WasmI32.load(WasmI32.sub(num, 8n), 0n)))
158
+ RPrint.printString("Heap Tag:")
159
+ RPrint.printNumber(WasmI64.extendI32U(WasmI32.load(num, 0n)))
160
+ RPrint.printString("Boxed Num Tag:")
161
+ RPrint.printNumber(WasmI64.extendI32U(WasmI32.load(num, 4n)))
162
+ RPrint.printString("Num Limbs:")
163
+ let limbs = WasmI32.load(num, 8n)
164
+ RPrint.printNumber(WasmI64.extendI32U(limbs))
165
+ RPrint.printString("Flags:")
166
+ RPrint.printNumber(
167
+ WasmI64.extendI32U(WasmI32.and(0xffffn, WasmI32.load(num, 12n)))
168
+ )
169
+ RPrint.printString("<reserved>:")
170
+ RPrint.printNumber(
171
+ WasmI64.extendI32U(
172
+ WasmI32.shrU(WasmI32.and(0xffff0000n, WasmI32.load(num, 12n)), 16n)
173
+ )
174
+ )
175
+ RPrint.printString("Limbs:")
176
+ for (let mut i = 0n; WasmI32.ltS(i, limbs); i = WasmI32.add(i, 1n)) {
177
+ // if a nonzero limb is found, then we're at the min
178
+ let (*) = WasmI32.mul
179
+ let (+) = WasmI32.add
180
+ RPrint.printNumber(WasmI64.load(num, (i + 2n) * 8n))
181
+ }
182
+ }
183
+
184
+ @unsafe
185
+ export let getSize = ptr => {
186
+ WasmI32.load(ptr, 8n)
187
+ }
188
+
189
+ @unsafe
190
+ let setSize = (ptr, n) => {
191
+ WasmI32.store(ptr, n, 8n)
192
+ }
193
+
194
+ @unsafe
195
+ export let getFlags = ptr => {
196
+ WasmI32.load(ptr, 12n)
197
+ }
198
+
199
+ @unsafe
200
+ let flagIsSet = (ptr, flag) => {
201
+ WasmI32.ne(WasmI32.and(getFlags(ptr), flag), 0n)
202
+ }
203
+
204
+ @unsafe
205
+ let getFlag = (ptr, flag) => {
206
+ let setFlags = WasmI32.load(ptr, 12n)
207
+ WasmI32.shrU(WasmI32.and(setFlags, flag), WasmI32.ctz(flag))
208
+ }
209
+
210
+ @unsafe
211
+ let setFlag = (ptr, flag, value) => {
212
+ let (*) = WasmI32.mul
213
+ let (|) = WasmI32.or
214
+ let (&) = WasmI32.and
215
+ let (!) = lnot
216
+ WasmI32.store(
217
+ ptr,
218
+ WasmI32.load(ptr, 12n) & !flag |
219
+ flag *
220
+ (if (WasmI32.eqz(value)) {
221
+ 0n
222
+ } else {
223
+ 1n
224
+ }),
225
+ 12n
226
+ )
227
+ }
228
+
229
+ @unsafe
230
+ export let getLimb = (ptr, i) => {
231
+ let (*) = WasmI32.mul
232
+ let (+) = WasmI32.add
233
+ WasmI64.load(ptr, (i + 2n) * 8n)
234
+ }
235
+
236
+ @unsafe
237
+ let setLimb = (ptr, i, v) => {
238
+ let (*) = WasmI32.mul
239
+ let (+) = WasmI32.add
240
+ WasmI64.store(ptr, v, (i + 2n) * 8n)
241
+ }
242
+
243
+ @unsafe
244
+ let getHalfSize = ptr => {
245
+ // for when it's important to know *precisely* the number of half-limbs,
246
+ // excluding trailing zeros
247
+ let size = getSize(ptr)
248
+ let (<<) = WasmI32.shl
249
+ let (>) = WasmI32.gtU
250
+ let (-) = WasmI32.sub
251
+ if (
252
+ size > 0n &&
253
+ WasmI32.eqz(WasmI32.wrapI64(WasmI64.shrU(getLimb(ptr, size - 1n), 32N)))
254
+ ) {
255
+ // last half-limb is trailing zeros
256
+ (size << 1n) - 1n
257
+ } else {
258
+ size << 1n
259
+ }
260
+ }
261
+
262
+ // "Half-limb" operators for 32-bit-based algorithms
263
+ // (some, such as multiplication, are simpler to implement
264
+ // using 32-bit than with 64-bit)
265
+ @unsafe
266
+ let getHalfLimb = (ptr, i) => {
267
+ let (*) = WasmI32.mul
268
+ let (+) = WasmI32.add
269
+ WasmI32.load(ptr, (i + 4n) * 4n)
270
+ }
271
+
272
+ @unsafe
273
+ let setHalfLimb = (ptr, i, v) => {
274
+ let (*) = WasmI32.mul
275
+ let (+) = WasmI32.add
276
+ WasmI32.store(ptr, v, (i + 4n) * 4n)
277
+ }
278
+
279
+ @unsafe
280
+ let clone = (num: WasmI32) => {
281
+ let (+) = WasmI32.add
282
+ let (*) = WasmI32.mul
283
+ let len = getSize(num) * 8n + 16n
284
+ let ret = Memory.malloc(len)
285
+ Memory.copy(ret, num, len)
286
+ ret
287
+ }
288
+
289
+ @unsafe
290
+ let cloneWithLen = (num: WasmI32, len: WasmI32) => {
291
+ let (+) = WasmI32.add
292
+ let (*) = WasmI32.mul
293
+ let oldlen = getSize(num) * 8n + 16n
294
+ let newlen = len * 8n + 16n
295
+ let ret = Memory.malloc(newlen)
296
+ Memory.fill(ret, 0n, newlen)
297
+ Memory.copy(ret, num, if (WasmI32.ltU(oldlen, newlen)) oldlen else newlen)
298
+ WasmI32.store(ret, len, 8n)
299
+ ret
300
+ }
301
+
302
+ /**
303
+ * Remove any trailing zero limbs from the given number
304
+ * (NOTE: Result is not guaranteed to be a new number)
305
+ */
306
+ @unsafe
307
+ let trimNumberInPlace = (num: WasmI32) => {
308
+ let numLimbs = getSize(num)
309
+ let mut neededLimbs = numLimbs
310
+ let (!=) = WasmI64.ne
311
+ let (==) = WasmI32.eq
312
+ let (-) = WasmI32.sub
313
+ let (>=) = WasmI32.geS
314
+ for (let mut i = numLimbs - 1n; i >= 0n; i -= 1n) {
315
+ // if a nonzero limb is found, then we're at the min
316
+ if (getLimb(num, i) != 0N) {
317
+ break
318
+ }
319
+ // otherwise, this is a zero limb, so we don't need it
320
+ neededLimbs -= 1n
321
+ }
322
+ let ret = if (neededLimbs == numLimbs) {
323
+ num
324
+ } else {
325
+ // NOTE: We cheat here. We could clone the number, but instead
326
+ // we have it lie about its length. When the number is freed,
327
+ // it will still free the entire underlying array
328
+ setSize(num, neededLimbs)
329
+ num
330
+ }
331
+ ret
332
+ }
333
+
334
+ @unsafe
335
+ let makeZero = () => {
336
+ init(0n)
337
+ }
338
+
339
+ @unsafe
340
+ export let makeWrappedInt32 = (value: WasmI32) => {
341
+ let ret = init(1n)
342
+ if (WasmI32.ltS(value, 0n)) {
343
+ setFlag(ret, _IS_NEGATIVE, 1n)
344
+ }
345
+ let value = if (WasmI32.ltS(value, 0n)) {
346
+ WasmI32.mul(value, -1n)
347
+ } else {
348
+ value
349
+ }
350
+ setLimb(ret, 0n, WasmI64.extendI32U(value))
351
+ ret
352
+ }
353
+
354
+ @unsafe
355
+ export let makeWrappedUint32 = (value: WasmI32) => {
356
+ let ret = init(1n)
357
+ setLimb(ret, 0n, WasmI64.extendI32U(value))
358
+ ret
359
+ }
360
+
361
+ @unsafe
362
+ export let makeWrappedInt64 = (value: WasmI64) => {
363
+ let ret = init(1n)
364
+ if (WasmI64.ltS(value, 0N)) {
365
+ setFlag(ret, _IS_NEGATIVE, 1n)
366
+ }
367
+ let value = if (WasmI64.ltS(value, 0N)) {
368
+ WasmI64.mul(value, -1N)
369
+ } else {
370
+ value
371
+ }
372
+ setLimb(ret, 0n, value)
373
+ ret
374
+ }
375
+
376
+ @unsafe
377
+ export let makeWrappedUint64 = (value: WasmI64) => {
378
+ let ret = init(1n)
379
+ setLimb(ret, 0n, value)
380
+ ret
381
+ }
382
+
383
+ @unsafe
384
+ export let isNegative = (num: WasmI32) => {
385
+ flagIsSet(num, _IS_NEGATIVE)
386
+ }
387
+
388
+ /**
389
+ * Returns true if the given bigint is equal to zero
390
+ */
391
+ @unsafe
392
+ export let eqz = (num: WasmI32) => {
393
+ let numLimbs = getSize(num)
394
+ let (!=) = WasmI64.ne
395
+ let (-) = WasmI32.sub
396
+ let (>=) = WasmI32.geS
397
+ let mut result = 1n
398
+ for (let mut i = numLimbs - 1n; i >= 0n; i -= 1n) {
399
+ if (getLimb(num, i) != 0N) {
400
+ result = 0n
401
+ break
402
+ }
403
+ }
404
+ WasmI32.ne(result, 0n)
405
+ }
406
+
407
+ @unsafe
408
+ let negateInPlace = (num: WasmI32) => {
409
+ setFlag(num, _IS_NEGATIVE, if (flagIsSet(num, _IS_NEGATIVE)) 0n else 1n)
410
+ num
411
+ }
412
+
413
+ @unsafe
414
+ export let negate = (num: WasmI32) => {
415
+ let ret = clone(num)
416
+ setFlag(ret, _IS_NEGATIVE, if (flagIsSet(ret, _IS_NEGATIVE)) 0n else 1n)
417
+ ret
418
+ }
419
+
420
+ @unsafe
421
+ let absInPlace = (num: WasmI32) => {
422
+ setFlag(num, _IS_NEGATIVE, 0n)
423
+ num
424
+ }
425
+
426
+ @unsafe
427
+ export let abs = (num: WasmI32) => {
428
+ let ret = clone(num)
429
+ setFlag(ret, _IS_NEGATIVE, 0n)
430
+ ret
431
+ }
432
+
433
+ /*
434
+ * Conversions
435
+ */
436
+ @unsafe
437
+ export let canConvertToInt32 = (num: WasmI32) => {
438
+ let (<=) = WasmI64.leU
439
+ let (==) = WasmI32.eq
440
+ let numLimbs = getSize(num)
441
+ if (numLimbs == 0n) {
442
+ true
443
+ } else if (numLimbs == 1n) {
444
+ let limb = getLimb(num, 0n)
445
+ limb <= _SMAX32_I64
446
+ } else {
447
+ false
448
+ }
449
+ }
450
+
451
+ @unsafe
452
+ export let toInt32 = (num: WasmI32) => {
453
+ let (<=) = WasmI64.leU
454
+ let (==) = WasmI32.eq
455
+ let numLimbs = getSize(num)
456
+ if (numLimbs == 0n) {
457
+ 0n
458
+ } else if (numLimbs == 1n) {
459
+ let limb = getLimb(num, 0n)
460
+ if (limb <= _SMAX32_I64) {
461
+ if (flagIsSet(num, _IS_NEGATIVE)) {
462
+ WasmI32.mul(-1n, WasmI32.wrapI64(limb))
463
+ } else {
464
+ WasmI32.wrapI64(limb)
465
+ }
466
+ } else {
467
+ throw Exception.Overflow
468
+ }
469
+ } else {
470
+ throw Exception.Overflow
471
+ }
472
+ }
473
+
474
+ @unsafe
475
+ export let canConvertToInt64 = (num: WasmI32) => {
476
+ let (<=) = WasmI64.leU
477
+ let (==) = WasmI32.eq
478
+ let numLimbs = getSize(num)
479
+ if (numLimbs == 0n) {
480
+ true
481
+ } else if (numLimbs == 1n) {
482
+ let limb = getLimb(num, 0n)
483
+ if (limb <= _SMAX32_I64) {
484
+ true
485
+ } else {
486
+ false
487
+ }
488
+ } else {
489
+ false
490
+ }
491
+ }
492
+
493
+ @unsafe
494
+ export let toInt64 = (num: WasmI32) => {
495
+ let (<=) = WasmI64.leU
496
+ let (==) = WasmI32.eq
497
+ let numLimbs = getSize(num)
498
+ if (numLimbs == 0n) {
499
+ 0N
500
+ } else if (numLimbs == 1n) {
501
+ let limb = getLimb(num, 0n)
502
+ if (limb <= _SMAX32_I64) {
503
+ if (flagIsSet(num, _IS_NEGATIVE)) {
504
+ WasmI64.mul(-1N, limb)
505
+ } else {
506
+ limb
507
+ }
508
+ } else {
509
+ throw Exception.Overflow
510
+ }
511
+ } else {
512
+ throw Exception.Overflow
513
+ }
514
+ }
515
+
516
+ @unsafe
517
+ export let toFloat64 = (num: WasmI32) => {
518
+ // approximate!
519
+ // semi-port of https://github.com/JuliaLang/julia/issues/31293#issuecomment-477220553
520
+ let (<=) = WasmI64.leU
521
+ let (==) = WasmI32.eq
522
+ let (+) = WasmF64.add
523
+ let (*) = WasmF64.mul
524
+ let numLimbs = getSize(num)
525
+ let res = if (eqz(num)) {
526
+ 0.0W
527
+ } else if (WasmI32.gtU(numLimbs, 16n)) {
528
+ // Float64 infinity (bigger than FLOAT_MAX)
529
+ WasmF64.reinterpretI64(
530
+ 0b0111111111110000000000000000000000000000000000000000000000000000N
531
+ )
532
+ } else if (numLimbs == 1n) {
533
+ WasmF64.convertI64U(getLimb(num, 0n))
534
+ } else {
535
+ // We have to convert manually. if anyone has a better way
536
+ // to do this (performance or accuracy-wise), please submit a PR
537
+ // factor == 2^64
538
+ let factor = WasmF64.reinterpretI64(
539
+ 0b0100001111110000000000000000000000000000000000000000000000000000N
540
+ )
541
+ let mut result = 0.W
542
+ for (let mut i = 0n; WasmI32.ltU(i, numLimbs); i = WasmI32.add(i, 1n)) {
543
+ if (!WasmI32.eqz(i)) {
544
+ result *= factor
545
+ }
546
+ result += WasmF64.convertI64U(getLimb(num, i))
547
+ }
548
+ result
549
+ }
550
+ if (flagIsSet(num, _IS_NEGATIVE)) {
551
+ WasmF64.neg(res)
552
+ } else {
553
+ res
554
+ }
555
+ }
556
+
557
+ @unsafe
558
+ export let toFloat32 = (num: WasmI32) => {
559
+ WasmF32.demoteF64(toFloat64(num))
560
+ }
561
+
562
+ /*
563
+ * Comparisons
564
+ */
565
+
566
+ @unsafe
567
+ let cmpUnsignedI64 = (num1: WasmI32, num2: WasmI64) => {
568
+ let (-) = WasmI32.sub
569
+ let (==) = WasmI32.eq
570
+ let (!=) = WasmI32.ne
571
+ let (>=) = WasmI32.geS
572
+ let num1Limbs = getSize(num1)
573
+ let num2Limbs = 1n
574
+ let delta = num1Limbs - num2Limbs
575
+ let num2abs = if (WasmI64.ltS(num2, 0N)) WasmI64.mul(-1N, num2) else num2
576
+ if (delta != 0n) {
577
+ delta
578
+ } else {
579
+ // num1 and num2 have the same size. Compare all limbs, high to low
580
+ let mut result = 0n
581
+ for (let mut i = num1Limbs - 1n; i >= 0n; i -= 1n) {
582
+ let limb1 = getLimb(num1, i)
583
+ let limb2 = if (i == 0n) num2abs else 0N
584
+ if (WasmI64.ltU(limb1, limb2)) {
585
+ result = -1n
586
+ break
587
+ } else if (WasmI64.ltU(limb2, limb1)) {
588
+ result = 1n
589
+ break
590
+ }
591
+ }
592
+ result
593
+ }
594
+ }
595
+
596
+ @unsafe
597
+ export let cmpI64 = (num1: WasmI32, num2: WasmI64) => {
598
+ if (eqz(num1)) {
599
+ if (WasmI64.eqz(num2)) {
600
+ 0n
601
+ } else if (WasmI64.ltS(num2, 0N)) {
602
+ 1n
603
+ } else {
604
+ -1n
605
+ }
606
+ } else if (flagIsSet(num1, _IS_NEGATIVE)) {
607
+ if (WasmI64.eqz(num2) || !WasmI64.ltS(num2, 0N)) {
608
+ -1n
609
+ } else {
610
+ WasmI32.mul(-1n, cmpUnsignedI64(num1, num2))
611
+ }
612
+ } else {
613
+ if (WasmI64.eqz(num2) || WasmI64.ltS(num2, 0N)) {
614
+ 1n
615
+ } else {
616
+ cmpUnsignedI64(num1, num2)
617
+ }
618
+ }
619
+ }
620
+
621
+ @unsafe
622
+ export let cmpF64 = (num1: WasmI32, num2: WasmF64) => {
623
+ let asf64 = toFloat64(num1)
624
+ let (<) = WasmF64.lt
625
+ let (>) = WasmF64.gt
626
+ if (asf64 < num2) {
627
+ -1n
628
+ } else if (asf64 > num2) {
629
+ 1n
630
+ } else {
631
+ 0n
632
+ }
633
+ }
634
+
635
+ @unsafe
636
+ export let cmpF32 = (num1: WasmI32, num2: WasmF32) => {
637
+ cmpF64(num1, WasmF64.promoteF32(num2))
638
+ }
639
+
640
+ @unsafe
641
+ let cmpUnsigned = (num1: WasmI32, num2: WasmI32) => {
642
+ let (-) = WasmI32.sub
643
+ let (!=) = WasmI32.ne
644
+ let (>=) = WasmI32.geS
645
+ let num1Limbs = getSize(num1)
646
+ let num2Limbs = getSize(num2)
647
+ let delta = num1Limbs - num2Limbs
648
+ if (delta != 0n) {
649
+ delta
650
+ } else {
651
+ // num1 and num2 have the same size. Compare all limbs, high to low
652
+ let mut result = 0n
653
+ for (let mut i = num1Limbs - 1n; i >= 0n; i -= 1n) {
654
+ let limb1 = getLimb(num1, i)
655
+ let limb2 = getLimb(num2, i)
656
+ if (WasmI64.ltU(limb1, limb2)) {
657
+ result = -1n
658
+ break
659
+ } else if (WasmI64.ltU(limb2, limb1)) {
660
+ result = 1n
661
+ break
662
+ }
663
+ }
664
+ result
665
+ }
666
+ }
667
+
668
+ @unsafe
669
+ export let cmp = (num1: WasmI32, num2: WasmI32) => {
670
+ if (eqz(num1)) {
671
+ if (eqz(num2)) {
672
+ 0n
673
+ } else if (flagIsSet(num2, _IS_NEGATIVE)) {
674
+ 1n
675
+ } else {
676
+ -1n
677
+ }
678
+ } else if (flagIsSet(num1, _IS_NEGATIVE)) {
679
+ if (eqz(num2) || !flagIsSet(num2, _IS_NEGATIVE)) {
680
+ -1n
681
+ } else {
682
+ cmpUnsigned(num2, num1)
683
+ }
684
+ } else {
685
+ if (eqz(num2) || flagIsSet(num2, _IS_NEGATIVE)) {
686
+ 1n
687
+ } else {
688
+ cmpUnsigned(num1, num2)
689
+ }
690
+ }
691
+ }
692
+
693
+ @unsafe
694
+ export let eq = (num1: WasmI32, num2: WasmI32) => {
695
+ WasmI32.eq(cmp(num1, num2), 0n)
696
+ }
697
+
698
+ @unsafe
699
+ export let ne = (num1: WasmI32, num2: WasmI32) => {
700
+ WasmI32.ne(cmp(num1, num2), 0n)
701
+ }
702
+
703
+ @unsafe
704
+ export let lt = (num1: WasmI32, num2: WasmI32) => {
705
+ WasmI32.ltS(cmp(num1, num2), 0n)
706
+ }
707
+
708
+ @unsafe
709
+ export let lte = (num1: WasmI32, num2: WasmI32) => {
710
+ WasmI32.leS(cmp(num1, num2), 0n)
711
+ }
712
+
713
+ @unsafe
714
+ export let gt = (num1: WasmI32, num2: WasmI32) => {
715
+ WasmI32.gtS(cmp(num1, num2), 0n)
716
+ }
717
+
718
+ @unsafe
719
+ export let gte = (num1: WasmI32, num2: WasmI32) => {
720
+ WasmI32.geS(cmp(num1, num2), 0n)
721
+ }
722
+
723
+ /*
724
+ * String Utilities
725
+ */
726
+
727
+ @unsafe
728
+ let countTrailingZeroBits = (num: WasmI32) => {
729
+ let numLimbs = getSize(num)
730
+ let (!=) = WasmI64.ne
731
+ let (+) = WasmI32.add
732
+ let (<) = WasmI32.ltS
733
+ let mut result = 0n
734
+ for (let mut i = 0n; i < numLimbs; i += 1n) {
735
+ let limb = getLimb(num, i)
736
+ if (limb != 0N) {
737
+ result += WasmI32.wrapI64(WasmI64.ctz(limb))
738
+ break
739
+ } else {
740
+ result += 64n
741
+ }
742
+ }
743
+ result
744
+ }
745
+
746
+ let _DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
747
+ // maximum number of digits that can fully fit a uint64 (for each valid base):
748
+ let _SIZES = [>
749
+ 0,
750
+ 0,
751
+ 64, // 2
752
+ 40, // 3
753
+ 32, // 4
754
+ 27, // 5
755
+ 24, // 6
756
+ 22, // 7
757
+ 21, // 8
758
+ 20, // 9
759
+ 19, // 10
760
+ 18, // 11
761
+ 17, // 12
762
+ 17, // 13
763
+ 16, // 14
764
+ 16, // 15
765
+ 16, // 16
766
+ 15, // 17
767
+ 15, // 18
768
+ 15, // 19
769
+ 14, // 20
770
+ 14, //
771
+ 14, //
772
+ 14, // 23
773
+ 13, // 24
774
+ 13, //
775
+ 13, //
776
+ 13, //
777
+ 13, //
778
+ 13, //
779
+ 13, //
780
+ 12,
781
+ 12,
782
+ 12,
783
+ 12,
784
+ 12,
785
+ 12,
786
+ ]
787
+
788
+ @unsafe
789
+ export let rec bigIntToString = (num: WasmI32, base: WasmI32) => {
790
+ let getDigit = n =>
791
+ WasmI32.load8U(WasmI32.add(WasmI32.fromGrain(_DIGITS), n), 8n)
792
+ if (WasmI32.ltS(base, 2n) || WasmI32.gtS(base, 32n)) {
793
+ throw Exception.InvalidArgument("toString base must be in range [2,32]")
794
+ }
795
+ if (eqz(num)) {
796
+ "0"
797
+ } else {
798
+ let size = DS.untagSimpleNumber(_SIZES[DS.tagSimpleNumber(base)])
799
+ let (==) = WasmI32.eq
800
+ let mut result = []
801
+ if (base == 2n || base == 4n || base == 8n || base == 16n || base == 32n) {
802
+ // if base is a power of two, use optimized path
803
+ let bits = WasmI64.extendI32U(WasmI32.ctz(base))
804
+ let mask = WasmI64.sub(WasmI64.shl(1N, bits), 1N)
805
+ let numLimbs = getSize(num)
806
+ let totalBits = WasmI64.sub(
807
+ WasmI64.mul(64N, WasmI64.extendI32U(numLimbs)),
808
+ WasmI64.clz(getLimb(num, WasmI32.sub(numLimbs, 1n)))
809
+ )
810
+ let mut acc = 0N
811
+ let mut accBits = 0N
812
+ for (let mut i = 0n; WasmI32.ltS(i, numLimbs); i = WasmI32.add(i, 1n)) {
813
+ let limb = getLimb(num, i)
814
+ acc = WasmI64.or(acc, WasmI64.shl(limb, accBits))
815
+ accBits = WasmI64.add(accBits, 64N)
816
+ while (WasmI64.geS(accBits, bits)) {
817
+ result = [
818
+ DS.tagChar(getDigit(WasmI32.wrapI64(WasmI64.and(acc, mask)))),
819
+ ...result
820
+ ]
821
+ acc = WasmI64.shrU(acc, bits)
822
+ if (WasmI64.gtS(accBits, 64N)) {
823
+ acc = WasmI64.shrU(
824
+ limb,
825
+ WasmI64.sub(64N, WasmI64.sub(accBits, bits))
826
+ )
827
+ }
828
+ accBits = WasmI64.sub(accBits, bits)
829
+ }
830
+ }
831
+ if (WasmI64.gtS(acc, 0N)) {
832
+ result = [DS.tagChar(getDigit(WasmI32.wrapI64(acc))), ...result]
833
+ }
834
+ } else {
835
+ let base = WasmI64.extendI32U(base)
836
+ let d = base
837
+ let mut tmp = clone(num)
838
+ setFlag(tmp, _IS_NEGATIVE, 0n)
839
+ while (!eqz(tmp)) {
840
+ let (-) = WasmI32.sub
841
+ let (<<) = WasmI32.shl
842
+ let tmpCopy = tmp
843
+ let numLimbs = getSize(tmpCopy)
844
+ tmp = init(numLimbs)
845
+ let mut c = 0N
846
+ let numHalfLimbs = numLimbs << 1n
847
+ for (let mut i = numHalfLimbs - 1n; WasmI32.geS(i, 0n); i -= 1n) {
848
+ let (+) = WasmI64.add
849
+ let (/) = WasmI64.divU
850
+ let (%) = WasmI64.remU
851
+ let halfLimb = getHalfLimb(tmpCopy, i)
852
+ // we need this if to exclude the trailing 0 half-limb, if it exists
853
+ if (!(halfLimb == 0n && WasmI32.sub(numHalfLimbs, 1n) == i)) {
854
+ let tmpInner = WasmI64.extendI32U(halfLimb) + WasmI64.shl(c, 32N)
855
+ setHalfLimb(tmp, i, WasmI32.wrapI64(tmpInner / d))
856
+ c = tmpInner % d
857
+ }
858
+ }
859
+ Memory.decRef(tmpCopy)
860
+ tmp = trimNumberInPlace(tmp)
861
+ result = [
862
+ DS.tagChar(getDigit(WasmI32.wrapI64(WasmI64.remU(c, base)))),
863
+ ...result
864
+ ]
865
+ }
866
+ }
867
+ while (
868
+ match (result) {
869
+ [c, ...tl] when DS.untagChar(c) == DS.untagChar('0') => true,
870
+ _ => false,
871
+ }
872
+ ) {
873
+ match (result) {
874
+ [c, ...tl] => result = tl,
875
+ _ => void, // <- impossible
876
+ }
877
+ }
878
+ if (flagIsSet(num, _IS_NEGATIVE)) {
879
+ result = ['-', ...result]
880
+ }
881
+ @unsafe
882
+ let rec computeLength = (lst, acc) => {
883
+ match (lst) {
884
+ [] => acc,
885
+ [_, ...tl] => computeLength(tl, WasmI32.add(acc, 1n)),
886
+ }
887
+ }
888
+ let length = computeLength(result, 0n)
889
+ let ret = DS.allocateString(length)
890
+ @unsafe
891
+ let rec populateString = (lst, idx) => {
892
+ match (lst) {
893
+ [] => void,
894
+ [hd, ...tl] => {
895
+ WasmI32.store8(WasmI32.add(ret, idx), DS.untagChar(hd), 8n)
896
+ populateString(tl, WasmI32.add(idx, 1n))
897
+ },
898
+ }
899
+ }
900
+ populateString(result, 0n)
901
+ WasmI32.toGrain(ret): String
902
+ }
903
+ }
904
+
905
+ @unsafe
906
+ export let bigIntToString10 = (num: WasmI32) => {
907
+ bigIntToString(num, 10n)
908
+ }
909
+
910
+ /*
911
+ * Addition and Subtraction
912
+ */
913
+
914
+ @unsafe
915
+ let unsignedAdd = (num1: WasmI32, num2: WasmI32) => {
916
+ let (<) = WasmI32.ltU
917
+ let (>=) = WasmI32.geU
918
+ let (|) = WasmI32.or
919
+ let (&) = WasmI32.and
920
+ let (+) = WasmI64.add
921
+ let (==) = WasmI32.eq
922
+ let num1Limbs = getSize(num1)
923
+ let num2Limbs = getSize(num2)
924
+ let n = maxu32(num1Limbs, num2Limbs)
925
+ let mut dest = init(n)
926
+ let mut carry = 0N
927
+ for (let mut i = 0n; i < n; i = WasmI32.add(i, 1n)) {
928
+ let limb1 = if (i >= num1Limbs) {
929
+ 0N
930
+ } else {
931
+ getLimb(num1, i)
932
+ }
933
+ let limb2 = if (i >= num2Limbs) {
934
+ 0N
935
+ } else {
936
+ getLimb(num2, i)
937
+ }
938
+ let z = limb1 + limb2 + carry
939
+ setLimb(dest, i, z)
940
+ let (<) = WasmI64.ltU
941
+ carry = if (z < limb1 || z < limb2 || z < carry) 1N else 0N
942
+ }
943
+ // handle remaining carry (resize if needed)
944
+ if (WasmI64.ne(carry, 0N)) {
945
+ dest = cloneWithLen(dest, WasmI32.add(n, 1n))
946
+ setLimb(dest, n, carry)
947
+ }
948
+ dest
949
+ }
950
+
951
+ @unsafe
952
+ let unsignedAddInt = (num1: WasmI32, int: WasmI64) => {
953
+ let (<) = WasmI32.ltU
954
+ let (>=) = WasmI32.geU
955
+ let (>) = WasmI32.gtU
956
+ let (|) = WasmI32.or
957
+ let (&) = WasmI32.and
958
+ let (+) = WasmI64.add
959
+ let (==) = WasmI32.eq
960
+ let num1Limbs = getSize(num1)
961
+ let n = maxu32(num1Limbs, 1n)
962
+ let mut dest = init(n)
963
+ let mut carry = 0N
964
+ for (let mut i = 0n; i < n; i = WasmI32.add(i, 1n)) {
965
+ let limb1 = if (i >= num1Limbs) {
966
+ 0N
967
+ } else {
968
+ getLimb(num1, i)
969
+ }
970
+ let limb2 = if (i > 0n) {
971
+ 0N
972
+ } else {
973
+ int
974
+ }
975
+ let z = limb1 + limb2 + carry
976
+ setLimb(dest, i, z)
977
+ let (<) = WasmI64.ltU
978
+ carry = if (z < limb1 || z < limb2 || z < carry) 1N else 0N
979
+ let (<) = WasmI32.ltU
980
+ if (WasmI64.eqz(carry) && i < WasmI32.sub(n, 1n)) {
981
+ // if we're not carrying, then there's nothing left to do but copy
982
+ let (+) = WasmI32.add
983
+ let (*) = WasmI32.mul
984
+ let (-) = WasmI32.sub
985
+ Memory.copy(
986
+ dest + 16n + 8n * (i + 1n),
987
+ num1 + 16n + 8n * (i + 1n),
988
+ 8n * (num1Limbs - (i + 1n))
989
+ )
990
+ break
991
+ }
992
+ }
993
+ // handle remaining carry (resize if needed)
994
+ if (WasmI64.ne(carry, 0N)) {
995
+ dest = cloneWithLen(dest, WasmI32.add(n, 1n))
996
+ setLimb(dest, n, carry)
997
+ }
998
+ dest
999
+ }
1000
+
1001
+ @unsafe
1002
+ let unsignedSubImpl = (num1: WasmI32, num2: WasmI32) => {
1003
+ let (<) = WasmI32.ltU
1004
+ let (>=) = WasmI32.geU
1005
+ let (|) = WasmI32.or
1006
+ let (&) = WasmI32.and
1007
+ let (+) = WasmI64.add
1008
+ let (-) = WasmI64.sub
1009
+ let (==) = WasmI32.eq
1010
+ let num1Limbs = getSize(num1)
1011
+ let num2Limbs = getSize(num2)
1012
+ let m = minu32(num1Limbs, num2Limbs)
1013
+ let n = maxu32(num1Limbs, num2Limbs)
1014
+ let mut dest = init(n)
1015
+ let mut carry = 0N
1016
+ for (let mut i = 0n; i < n; i = WasmI32.add(i, 1n)) {
1017
+ let limb1 = if (i >= num1Limbs) {
1018
+ 0N
1019
+ } else {
1020
+ getLimb(num1, i)
1021
+ }
1022
+ let limb2 = if (i >= num2Limbs) {
1023
+ 0N
1024
+ } else {
1025
+ getLimb(num2, i)
1026
+ }
1027
+ let z1 = limb1 - limb2
1028
+ let (>) = WasmI64.gtU
1029
+ let carry1 = z1 > limb1
1030
+ let z = z1 - carry
1031
+ let carry2 = z > z1
1032
+ setLimb(dest, i, z)
1033
+ carry = if (carry1 || carry2) 1N else 0N
1034
+ }
1035
+ // carry should be nonzero at this point
1036
+ if (num1Limbs < num2Limbs) {
1037
+ setFlag(dest, _IS_NEGATIVE, 1n)
1038
+ }
1039
+ trimNumberInPlace(dest)
1040
+ }
1041
+
1042
+ @unsafe
1043
+ let unsignedSub = (num1: WasmI32, num2: WasmI32) => {
1044
+ let cmpRes = cmpUnsigned(num1, num2)
1045
+ let (>) = WasmI32.gtS
1046
+ let (<) = WasmI32.ltS
1047
+ if (cmpRes > 0n) {
1048
+ unsignedSubImpl(num1, num2)
1049
+ } else if (cmpRes < 0n) {
1050
+ let ret = unsignedSubImpl(num2, num1)
1051
+ negateInPlace(ret)
1052
+ } else {
1053
+ // num1 == num2
1054
+ makeZero()
1055
+ }
1056
+ }
1057
+
1058
+ @unsafe
1059
+ let unsignedSubIntImpl = (num1: WasmI32, int: WasmI64) => {
1060
+ let (<) = WasmI32.ltU
1061
+ let (>=) = WasmI32.geU
1062
+ let (>) = WasmI32.gtU
1063
+ let (|) = WasmI32.or
1064
+ let (&) = WasmI32.and
1065
+ let (+) = WasmI64.add
1066
+ let (-) = WasmI64.sub
1067
+ let (==) = WasmI32.eq
1068
+ let num1Limbs = getSize(num1)
1069
+ let num2Limbs = 1n
1070
+ let n = maxu32(num1Limbs, 1n)
1071
+ let mut dest = init(n)
1072
+ let mut carry = 0N
1073
+ for (let mut i = 0n; i < n; i = WasmI32.add(i, 1n)) {
1074
+ let limb1 = if (i >= num1Limbs) {
1075
+ 0N
1076
+ } else {
1077
+ getLimb(num1, i)
1078
+ }
1079
+ let limb2 = if (i > 0n) {
1080
+ 0N
1081
+ } else {
1082
+ int
1083
+ }
1084
+ let z1 = limb1 - limb2
1085
+ let (>) = WasmI64.gtU
1086
+ let carry1 = z1 > limb1
1087
+ let z = z1 - carry
1088
+ let carry2 = z > z1
1089
+ setLimb(dest, i, z)
1090
+ carry = if (carry1 || carry2) 1N else 0N
1091
+ if (WasmI64.eqz(carry) && i < WasmI32.sub(n, 1n)) {
1092
+ // if we're not carrying, then there's nothing left to do but copy
1093
+ let (+) = WasmI32.add
1094
+ let (*) = WasmI32.mul
1095
+ let (-) = WasmI32.sub
1096
+ Memory.copy(
1097
+ dest + 16n + 8n * (i + 1n),
1098
+ num1 + 16n + 8n * (i + 1n),
1099
+ 8n * (num1Limbs - (i + 1n))
1100
+ )
1101
+ break
1102
+ }
1103
+ }
1104
+ // carry should be nonzero at this point
1105
+ if (num1Limbs < num2Limbs) {
1106
+ setFlag(dest, _IS_NEGATIVE, 1n)
1107
+ }
1108
+ trimNumberInPlace(dest)
1109
+ }
1110
+
1111
+ @unsafe
1112
+ let unsignedSubInt = (num1: WasmI32, int: WasmI64) => {
1113
+ let num1Limbs = getSize(num1)
1114
+ let (==) = WasmI32.eq
1115
+ let (<) = WasmI64.ltU
1116
+ if (num1Limbs == 0n || num1Limbs == 1n && getLimb(num1, 0n) < int) {
1117
+ let ret = init(1n)
1118
+ setLimb(
1119
+ ret,
1120
+ 0n,
1121
+ WasmI64.sub(
1122
+ int,
1123
+ if (num1Limbs == 0n) {
1124
+ 0N
1125
+ } else {
1126
+ getLimb(num1, 0n)
1127
+ }
1128
+ )
1129
+ )
1130
+ trimNumberInPlace(negateInPlace(ret))
1131
+ } else if (num1Limbs == 1n && WasmI64.eq(getLimb(num1, 0n), int)) {
1132
+ makeZero()
1133
+ } else {
1134
+ unsignedSubIntImpl(num1, int)
1135
+ }
1136
+ }
1137
+
1138
+ @unsafe
1139
+ export let add = (num1: WasmI32, num2: WasmI32) => {
1140
+ if (flagIsSet(num1, _IS_NEGATIVE)) {
1141
+ if (flagIsSet(num2, _IS_NEGATIVE)) {
1142
+ let ret = unsignedAdd(num1, num2)
1143
+ setFlag(ret, _IS_NEGATIVE, 1n)
1144
+ ret
1145
+ } else {
1146
+ unsignedSub(num2, num1)
1147
+ }
1148
+ } else {
1149
+ if (flagIsSet(num2, _IS_NEGATIVE)) {
1150
+ unsignedSub(num1, num2)
1151
+ } else {
1152
+ unsignedAdd(num1, num2)
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ @unsafe
1158
+ export let addInt = (num1: WasmI32, int: WasmI64) => {
1159
+ // int is *signed*
1160
+ if (eqz(num1)) {
1161
+ let ret = makeWrappedUint64(int)
1162
+ if (WasmI64.ltS(int, 0N)) {
1163
+ setFlag(ret, _IS_NEGATIVE, 1n)
1164
+ }
1165
+ ret
1166
+ } else if (flagIsSet(num1, _IS_NEGATIVE)) {
1167
+ let ret = if (WasmI64.ltS(int, 0N)) {
1168
+ unsignedAddInt(num1, WasmI64.add(WasmI64.xor(int, _UMAX_I64), 1N))
1169
+ } else {
1170
+ unsignedSubInt(num1, int)
1171
+ }
1172
+ negateInPlace(ret)
1173
+ ret
1174
+ } else {
1175
+ if (WasmI64.ltS(int, 0N)) {
1176
+ unsignedSubInt(num1, WasmI64.add(WasmI64.xor(int, _UMAX_I64), 1N))
1177
+ } else {
1178
+ unsignedAddInt(num1, int)
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ @unsafe
1184
+ export let sub = (num1: WasmI32, num2: WasmI32) => {
1185
+ if (flagIsSet(num1, _IS_NEGATIVE)) {
1186
+ if (flagIsSet(num2, _IS_NEGATIVE)) {
1187
+ unsignedSub(num2, num1)
1188
+ } else {
1189
+ let ret = unsignedAdd(num1, num2)
1190
+ setFlag(ret, _IS_NEGATIVE, 1n)
1191
+ ret
1192
+ }
1193
+ } else {
1194
+ if (flagIsSet(num2, _IS_NEGATIVE)) {
1195
+ unsignedAdd(num1, num2)
1196
+ } else {
1197
+ unsignedSub(num1, num2)
1198
+ }
1199
+ }
1200
+ }
1201
+
1202
+ @unsafe
1203
+ export let subInt = (num1: WasmI32, int: WasmI64) => {
1204
+ // int is *signed*
1205
+ if (eqz(num1)) {
1206
+ let ret = makeWrappedUint64(int)
1207
+ // inverse:
1208
+ if (WasmI64.gtS(int, 0N)) {
1209
+ setFlag(ret, _IS_NEGATIVE, 1n)
1210
+ }
1211
+ ret
1212
+ } else if (flagIsSet(num1, _IS_NEGATIVE)) {
1213
+ let ret = if (WasmI64.ltS(int, 0N)) {
1214
+ unsignedSubInt(num1, WasmI64.add(WasmI64.xor(int, _UMAX_I64), 1N))
1215
+ } else {
1216
+ unsignedAddInt(num1, int)
1217
+ }
1218
+ negateInPlace(ret)
1219
+ ret
1220
+ } else {
1221
+ if (WasmI64.ltS(int, 0N)) {
1222
+ unsignedAddInt(num1, WasmI64.add(WasmI64.xor(int, _UMAX_I64), 1N))
1223
+ } else {
1224
+ unsignedSubInt(num1, int)
1225
+ }
1226
+ }
1227
+ }
1228
+
1229
+ @unsafe
1230
+ export let incr = (num: WasmI32) => {
1231
+ addInt(num, 1N)
1232
+ }
1233
+
1234
+ @unsafe
1235
+ export let decr = (num: WasmI32) => {
1236
+ subInt(num, 1N)
1237
+ }
1238
+
1239
+ /*
1240
+ * Multiplication
1241
+ */
1242
+
1243
+ @unsafe
1244
+ let unsignedMul = (num1: WasmI32, num2: WasmI32) => {
1245
+ // num1 >= num2
1246
+ let (+) = WasmI32.add
1247
+ let (-) = WasmI32.sub
1248
+ let (*) = WasmI64.mul
1249
+ let (<<) = WasmI32.shl
1250
+ let num1Limbs = getSize(num1)
1251
+ let num2Limbs = getSize(num2)
1252
+ let num1HalfLimbs = num1Limbs << 1n
1253
+ let num2HalfLimbs = num2Limbs << 1n
1254
+ let dest = init(num1Limbs + num2Limbs)
1255
+ let mut tmp = 0N
1256
+ // because 64-bit overflow calculation isn't straightforward (and
1257
+ // basically requires doing so anyway), we implement this alg with 32-bit half-limbs
1258
+ for (let mut i = 0n; WasmI32.ltU(i, num1HalfLimbs); i += 1n) {
1259
+ let halfLimb1 = getHalfLimb(num1, i)
1260
+ let halfLimb2 = getHalfLimb(num2, 0n)
1261
+ tmp = WasmI64.add(
1262
+ tmp,
1263
+ WasmI64.extendI32U(halfLimb1) * WasmI64.extendI32U(halfLimb2)
1264
+ )
1265
+ setHalfLimb(dest, i, WasmI32.wrapI64(tmp))
1266
+ tmp = WasmI64.shrU(tmp, 32N)
1267
+ }
1268
+ setHalfLimb(dest, num1HalfLimbs, WasmI32.wrapI64(tmp))
1269
+ for (let mut j = 1n; WasmI32.ltU(j, num2HalfLimbs); j += 1n) {
1270
+ tmp = 0N
1271
+ let halfLimb2 = getHalfLimb(num2, j)
1272
+ if (
1273
+ WasmI32.eq(j, WasmI32.sub(num2HalfLimbs, 1n)) && WasmI32.eq(halfLimb2, 0n)
1274
+ ) {
1275
+ break
1276
+ }
1277
+ for (let mut i = 0n; WasmI32.ltU(i, num1HalfLimbs); i += 1n) {
1278
+ let curHalfLimb = getHalfLimb(dest, j + i)
1279
+ let halfLimb1 = getHalfLimb(num1, i)
1280
+ tmp = WasmI64.add(
1281
+ tmp,
1282
+ WasmI64.add(
1283
+ WasmI64.extendI32U(curHalfLimb),
1284
+ WasmI64.extendI32U(halfLimb1) * WasmI64.extendI32U(halfLimb2)
1285
+ )
1286
+ )
1287
+ setHalfLimb(dest, j + i, WasmI32.wrapI64(tmp))
1288
+ tmp = WasmI64.shrU(tmp, 32N)
1289
+ }
1290
+ let mut pos = j + num1HalfLimbs
1291
+ while (WasmI64.gtU(tmp, 0N)) {
1292
+ tmp = WasmI64.add(tmp, WasmI64.extendI32U(getHalfLimb(dest, pos)))
1293
+ setHalfLimb(dest, pos, WasmI32.wrapI64(tmp))
1294
+ tmp = WasmI64.shrU(tmp, 32N)
1295
+ pos += 1n
1296
+ }
1297
+ }
1298
+ trimNumberInPlace(dest)
1299
+ }
1300
+
1301
+ // TODO(#1189): Replace with Karatsuba multiplication
1302
+ @unsafe
1303
+ export let mul = (num1: WasmI32, num2: WasmI32) => {
1304
+ let ret = if (eqz(num1) || eqz(num2)) {
1305
+ makeZero()
1306
+ } else {
1307
+ let num1Limbs = getSize(num1)
1308
+ let num2Limbs = getSize(num2)
1309
+ if (WasmI32.gtU(num2Limbs, num1Limbs)) {
1310
+ unsignedMul(num2, num1)
1311
+ } else {
1312
+ unsignedMul(num1, num2)
1313
+ }
1314
+ }
1315
+ setFlag(
1316
+ ret,
1317
+ _IS_NEGATIVE,
1318
+ WasmI32.xor(getFlag(num1, _IS_NEGATIVE), getFlag(num2, _IS_NEGATIVE))
1319
+ )
1320
+ ret
1321
+ }
1322
+
1323
+ /*
1324
+ * Bitwise Ops
1325
+ */
1326
+
1327
+ @unsafe
1328
+ export let shl = (num: WasmI32, places: WasmI32) => {
1329
+ // places is *unsigned*
1330
+ let numLimbs = getSize(num)
1331
+ let mut carry = 0N
1332
+ let (+) = WasmI32.add
1333
+ let (-) = WasmI64.sub
1334
+ let (/) = WasmI32.divU
1335
+ let (%) = WasmI32.remU
1336
+ let (<<) = WasmI64.shl
1337
+ let (<) = WasmI32.ltU
1338
+ let (|) = WasmI64.or
1339
+ let (&) = WasmI64.and
1340
+ let a = places / 32n
1341
+ let b = places % 32n
1342
+ let mask = (1N << WasmI64.extendI32U(b)) - 1N << 64N - WasmI64.extendI32U(b)
1343
+ let result = init(numLimbs + a)
1344
+ setFlag(result, _IS_NEGATIVE, getFlag(num, _IS_NEGATIVE))
1345
+ let numHalfLimbs = WasmI32.shl(numLimbs, 1n)
1346
+ for (let mut i = 0n; i < numHalfLimbs; i += 1n) {
1347
+ let acc = WasmI64.extendI32U(getHalfLimb(num, i)) << 32N | carry
1348
+ let (>>) = WasmI64.shrU
1349
+ carry = (acc & mask) >> 32N
1350
+ setHalfLimb(
1351
+ result,
1352
+ i + a,
1353
+ WasmI32.wrapI64(acc << WasmI64.extendI32U(b) >> 32N)
1354
+ )
1355
+ }
1356
+ let (>) = WasmI64.gtU
1357
+ let ret = if (carry > 0N) {
1358
+ let ret = cloneWithLen(result, numLimbs + a + 1n)
1359
+ let (>>) = WasmI64.shrU
1360
+ setHalfLimb(
1361
+ ret,
1362
+ numHalfLimbs + a,
1363
+ WasmI32.wrapI64(carry >> 32N - WasmI64.extendI32U(b))
1364
+ )
1365
+ ret
1366
+ } else {
1367
+ result
1368
+ }
1369
+ trimNumberInPlace(ret)
1370
+ }
1371
+
1372
+ @unsafe
1373
+ export let shrS = (num: WasmI32, places: WasmI32) => {
1374
+ // places is *unsigned*
1375
+ let numLimbs = getSize(num)
1376
+ let mut carry = 0N
1377
+ let (+) = WasmI32.add
1378
+ let (-) = WasmI32.sub
1379
+ let (/) = WasmI32.divU
1380
+ let (%) = WasmI32.remU
1381
+ let (<<) = WasmI32.shl
1382
+ let (>>) = WasmI64.shrU
1383
+ let (>=) = WasmI32.geS
1384
+ let (|) = WasmI64.or
1385
+ let (&) = WasmI64.and
1386
+ let a = places / 32n
1387
+ let b = places % 32n
1388
+ let mask = (1n << b) - 1n
1389
+ let numHalfLimbs = numLimbs << 1n
1390
+ if (WasmI32.geU(a, numHalfLimbs)) {
1391
+ // edge case: we shift right all the way
1392
+ if (flagIsSet(num, _IS_NEGATIVE)) {
1393
+ makeWrappedInt32(-1n)
1394
+ } else {
1395
+ makeZero()
1396
+ }
1397
+ } else {
1398
+ let newHalfLimbs = numHalfLimbs - a
1399
+ let ret = init(
1400
+ WasmI32.add(
1401
+ WasmI32.shrU(newHalfLimbs, 1n),
1402
+ if (WasmI32.eqz(WasmI32.and(newHalfLimbs, 1n))) {
1403
+ 0n
1404
+ } else {
1405
+ 1n
1406
+ }
1407
+ )
1408
+ )
1409
+ setFlag(ret, _IS_NEGATIVE, getFlag(num, _IS_NEGATIVE))
1410
+ for (let mut i = numHalfLimbs - 1n; i >= a; i -= 1n) {
1411
+ let (<<) = WasmI64.shl
1412
+ let (|) = WasmI64.or
1413
+ let acc = carry << 32N | WasmI64.extendI32U(getHalfLimb(num, i))
1414
+ carry = acc & WasmI64.extendI32U(mask)
1415
+ setHalfLimb(ret, i - a, WasmI32.wrapI64(acc >> WasmI64.extendI32U(b)))
1416
+ }
1417
+ let ret = if (flagIsSet(ret, _IS_NEGATIVE)) {
1418
+ let mut underflow = false
1419
+ let (>) = WasmI64.gtU
1420
+ if (carry > 0N) {
1421
+ underflow = true
1422
+ } else {
1423
+ let (<) = WasmI32.ltU
1424
+ for (let mut i = 0n; i < a; i += 1n) {
1425
+ if (0n < getHalfLimb(ret, i)) {
1426
+ underflow = true
1427
+ break
1428
+ }
1429
+ }
1430
+ }
1431
+ if (underflow) {
1432
+ let newRet = decr(ret)
1433
+ Memory.decRef(ret)
1434
+ newRet
1435
+ } else {
1436
+ ret
1437
+ }
1438
+ } else {
1439
+ ret
1440
+ }
1441
+ trimNumberInPlace(ret)
1442
+ }
1443
+ }
1444
+
1445
+ @unsafe
1446
+ let bitwiseNotUnsigned = (num: WasmI32) => {
1447
+ // *not the user-facing bitwise-not*
1448
+ // the user-facing ops need some bit-flipping ability, which this provides,
1449
+ // but `bitwiseNot` is the user-facing bitwise NOT implementation
1450
+ let num1Limbs = getSize(num)
1451
+ let ret = init(num1Limbs)
1452
+ let (+) = WasmI32.add
1453
+ let (<) = WasmI32.ltU
1454
+ let (^) = WasmI64.xor
1455
+ for (let mut i = 0n; i < num1Limbs; i += 1n) {
1456
+ setLimb(ret, i, getLimb(num, i) ^ _UMAX_I64)
1457
+ }
1458
+ ret
1459
+ }
1460
+
1461
+ @unsafe
1462
+ let bitwiseAndPositive =
1463
+ (
1464
+ num1: WasmI32,
1465
+ num2: WasmI32,
1466
+ copyTrailing: WasmI32,
1467
+ ) => {
1468
+ // bitwise and, but both num1 and num2 are assumed to be positive
1469
+ let num1Limbs = getSize(num1)
1470
+ let num2Limbs = getSize(num2)
1471
+ let newLimbs = maxu32(num1Limbs, num2Limbs)
1472
+ let ret = init(newLimbs)
1473
+ let (+) = WasmI32.add
1474
+ let (<) = WasmI32.ltU
1475
+ let (&) = WasmI64.and
1476
+ let (==) = WasmI32.eq
1477
+ let numToProcess = minu32(num1Limbs, num2Limbs) // anything past this is 0
1478
+ for (let mut i = 0n; i < numToProcess; i += 1n) {
1479
+ setLimb(ret, i, getLimb(num1, i) & getLimb(num2, i))
1480
+ }
1481
+ if (numToProcess < newLimbs && copyTrailing == 1n) {
1482
+ // will be a no-op if numToProcess == num1Limbs
1483
+ for (let mut i = numToProcess; i < num1Limbs; i += 1n) {
1484
+ setLimb(ret, i, getLimb(num1, i))
1485
+ }
1486
+ }
1487
+ if (numToProcess < newLimbs && copyTrailing == 2n) {
1488
+ // will be a no-op if numToProcess == num1Limbs
1489
+ for (let mut i = numToProcess; i < num2Limbs; i += 1n) {
1490
+ setLimb(ret, i, getLimb(num2, i))
1491
+ }
1492
+ }
1493
+ ret
1494
+ }
1495
+
1496
+ @unsafe
1497
+ let bitwiseOrPositive = (num1: WasmI32, num2: WasmI32) => {
1498
+ // bitwise or, but both num1 and num2 are assumed to be positive
1499
+ let num1Limbs = getSize(num1)
1500
+ let num2Limbs = getSize(num2)
1501
+ let newLimbs = maxu32(num1Limbs, num2Limbs)
1502
+ let ret = init(newLimbs)
1503
+ let (+) = WasmI32.add
1504
+ let (<) = WasmI32.ltU
1505
+ let (|) = WasmI64.or
1506
+ for (let mut i = 0n; i < newLimbs; i += 1n) {
1507
+ let limb1 = if (i < num1Limbs) getLimb(num1, i) else 0N
1508
+ let limb2 = if (i < num2Limbs) getLimb(num2, i) else 0N
1509
+ setLimb(ret, i, limb1 | limb2)
1510
+ }
1511
+ ret
1512
+ }
1513
+
1514
+ @unsafe
1515
+ let bitwiseXorPositive = (num1: WasmI32, num2: WasmI32) => {
1516
+ // bitwise xor, but both num1 and num2 are assumed to be positive
1517
+ let num1Limbs = getSize(num1)
1518
+ let num2Limbs = getSize(num2)
1519
+ let newLimbs = maxu32(num1Limbs, num2Limbs)
1520
+ let ret = init(newLimbs)
1521
+ let (+) = WasmI32.add
1522
+ let (<) = WasmI32.ltU
1523
+ let (^) = WasmI64.xor
1524
+ for (let mut i = 0n; i < newLimbs; i += 1n) {
1525
+ let limb1 = if (i < num1Limbs) getLimb(num1, i) else 0N
1526
+ let limb2 = if (i < num2Limbs) getLimb(num2, i) else 0N
1527
+ setLimb(ret, i, limb1 ^ limb2)
1528
+ }
1529
+ ret
1530
+ }
1531
+
1532
+ // https://stackoverflow.com/a/32298732
1533
+
1534
+ @unsafe
1535
+ export let bitwiseNot = (num: WasmI32) => {
1536
+ // ~x == -x - 1
1537
+ let numNegated = negate(num)
1538
+ let result = decr(numNegated)
1539
+ Memory.decRef(numNegated)
1540
+ result
1541
+ }
1542
+
1543
+ @unsafe
1544
+ export let bitwiseAnd = (num1: WasmI32, num2: WasmI32) => {
1545
+ let num1 = WasmI32.fromGrain(num1)
1546
+ let num2 = WasmI32.fromGrain(num2)
1547
+ let ret = if (!flagIsSet(num1, _IS_NEGATIVE)) {
1548
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1549
+ // A & B
1550
+ bitwiseAndPositive(num1, num2, 0n)
1551
+ } else {
1552
+ // A & -B == A & ~(B-1)
1553
+ let num2Neg = negate(num2)
1554
+ let num2Sub1 = decr(num2Neg)
1555
+ Memory.decRef(num2Neg)
1556
+ let notNum2Sub1 = bitwiseNotUnsigned(num2Sub1)
1557
+ Memory.decRef(num2Sub1)
1558
+ let combined = bitwiseAndPositive(num1, notNum2Sub1, 1n)
1559
+ Memory.decRef(notNum2Sub1)
1560
+ combined
1561
+ }
1562
+ } else {
1563
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1564
+ // -A & B == ~(A-1) & B
1565
+ let num1Neg = negate(num1) // A
1566
+ let num1Sub1 = decr(num1Neg) // A-1
1567
+ Memory.decRef(num1Neg)
1568
+ let notNum1Sub1 = bitwiseNotUnsigned(num1Sub1) // ~(A-1)
1569
+ Memory.decRef(num1Sub1)
1570
+ let combined = bitwiseAndPositive(notNum1Sub1, num2, 2n) // ~(A-1) & B
1571
+ Memory.decRef(notNum1Sub1)
1572
+ combined
1573
+ } else {
1574
+ // -A & -B == -((A-1) | (B-1) + 1)
1575
+ let num1Neg = negate(num1) // A
1576
+ let num2Neg = negate(num2) // B
1577
+ let num1Sub1 = decr(num1Neg)
1578
+ let num2Sub1 = decr(num2Neg)
1579
+ let orResult = bitwiseOrPositive(num1Sub1, num2Sub1)
1580
+ let ret = incr(orResult)
1581
+ Memory.decRef(num1Neg)
1582
+ Memory.decRef(num2Neg)
1583
+ Memory.decRef(num1Sub1)
1584
+ Memory.decRef(num2Sub1)
1585
+ Memory.decRef(orResult)
1586
+ // avoid extra allocation on negate()
1587
+ setFlag(ret, _IS_NEGATIVE, 1n)
1588
+ ret
1589
+ }
1590
+ }
1591
+ trimNumberInPlace(ret)
1592
+ }
1593
+
1594
+ @unsafe
1595
+ export let bitwiseOr = (num1: WasmI32, num2: WasmI32) => {
1596
+ let num1 = WasmI32.fromGrain(num1)
1597
+ let num2 = WasmI32.fromGrain(num2)
1598
+ let ret = if (!flagIsSet(num1, _IS_NEGATIVE)) {
1599
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1600
+ // A | B
1601
+ bitwiseOrPositive(num1, num2)
1602
+ } else {
1603
+ // A | -B == -(((B-1) & ~A) + 1)
1604
+ let num2Neg = negate(num2) // B
1605
+ let num2Sub1 = decr(num2Neg) // B-1
1606
+ Memory.decRef(num2Neg)
1607
+ let notNum1 = bitwiseNotUnsigned(num1) // ~A
1608
+ let retSub1 = bitwiseAndPositive(notNum1, num2Sub1, 2n) // (B-1) & ~A
1609
+ Memory.decRef(num2Sub1)
1610
+ Memory.decRef(notNum1)
1611
+ let ret = incr(retSub1)
1612
+ Memory.decRef(retSub1)
1613
+ negateInPlace(ret)
1614
+ ret
1615
+ }
1616
+ } else {
1617
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1618
+ // A | -B == -(((A-1) & ~B) + 1)
1619
+ let num1Neg = negate(num1) // A
1620
+ let num1Sub1 = decr(num1Neg) // A-1
1621
+ Memory.decRef(num1Neg)
1622
+ let notNum2 = bitwiseNotUnsigned(num2) // ~B
1623
+ let retSub1 = bitwiseAndPositive(num1Sub1, notNum2, 1n) // (A-1) & ~B
1624
+ Memory.decRef(num1Sub1)
1625
+ Memory.decRef(notNum2)
1626
+ let ret = incr(retSub1)
1627
+ Memory.decRef(retSub1)
1628
+ negateInPlace(ret)
1629
+ ret
1630
+ } else {
1631
+ // -A | -B == -((A-1) & (B-1) + 1)
1632
+ let num1Neg = negate(num1) // A
1633
+ let num2Neg = negate(num2) // B
1634
+ let num1Sub1 = decr(num1Neg) // (A-1)
1635
+ let num2Sub1 = decr(num2Neg) // (B-1)
1636
+ let andResult = bitwiseAndPositive(
1637
+ num1Sub1,
1638
+ num2Sub1,
1639
+ 0n
1640
+ ) // (A-1) & (B-1)
1641
+ let ret = incr(andResult) // ((A-1) & (B-1)) + 1
1642
+ Memory.decRef(num1Neg)
1643
+ Memory.decRef(num2Neg)
1644
+ Memory.decRef(num1Sub1)
1645
+ Memory.decRef(num2Sub1)
1646
+ Memory.decRef(andResult)
1647
+ // avoid extra allocation on negate()
1648
+ setFlag(ret, _IS_NEGATIVE, 1n)
1649
+ ret
1650
+ }
1651
+ }
1652
+ trimNumberInPlace(ret)
1653
+ }
1654
+
1655
+ @unsafe
1656
+ export let bitwiseXor = (num1: WasmI32, num2: WasmI32) => {
1657
+ let num1 = WasmI32.fromGrain(num1)
1658
+ let num2 = WasmI32.fromGrain(num2)
1659
+ let ret = if (!flagIsSet(num1, _IS_NEGATIVE)) {
1660
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1661
+ // A ^ B
1662
+ bitwiseXorPositive(num1, num2)
1663
+ } else {
1664
+ // A ^ -B == A ^ (B-1)+1
1665
+ let num2Neg = negate(num2) // B
1666
+ let num2Sub1 = decr(num2Neg) // B-1
1667
+ Memory.decRef(num2Neg)
1668
+ let retSub1 = bitwiseXorPositive(num2Sub1, num1) // (A^(B-1))
1669
+ Memory.decRef(num2Sub1)
1670
+ let ret = incr(retSub1)
1671
+ Memory.decRef(retSub1)
1672
+ // avoid extra allocation on negate()
1673
+ setFlag(ret, _IS_NEGATIVE, 1n)
1674
+ ret
1675
+ }
1676
+ } else {
1677
+ if (!flagIsSet(num2, _IS_NEGATIVE)) {
1678
+ // -A ^ B == (B^(A-1))+1
1679
+ let num1Neg = negate(num1) // A
1680
+ let num1Sub1 = decr(num1Neg) // A-1
1681
+ Memory.decRef(num1Neg)
1682
+ let retSub1 = bitwiseXorPositive(num1Sub1, num2) // (B^(A-1))
1683
+ Memory.decRef(num1Sub1)
1684
+ let ret = incr(retSub1)
1685
+ Memory.decRef(retSub1)
1686
+ // avoid extra allocation on negate()
1687
+ setFlag(ret, _IS_NEGATIVE, 1n)
1688
+ ret
1689
+ } else {
1690
+ // -A ^ -B == (A-1)^(B-1)
1691
+ let num1Neg = negate(num1) // A
1692
+ let num2Neg = negate(num2) // B
1693
+ let num1Sub1 = decr(num1Neg)
1694
+ let num2Sub1 = decr(num2Neg)
1695
+ let ret = bitwiseXorPositive(num1Sub1, num2Sub1)
1696
+ Memory.decRef(num1Neg)
1697
+ Memory.decRef(num2Neg)
1698
+ Memory.decRef(num1Sub1)
1699
+ Memory.decRef(num2Sub1)
1700
+ ret
1701
+ }
1702
+ }
1703
+ trimNumberInPlace(ret)
1704
+ }
1705
+
1706
+ @unsafe
1707
+ export let countLeadingZeros = (num: WasmI32) => {
1708
+ // if positive, there are an infinite number. if negative, there are none.
1709
+ if (flagIsSet(num, _IS_NEGATIVE)) {
1710
+ 0n
1711
+ } else {
1712
+ _SMAX_I32
1713
+ }
1714
+ }
1715
+
1716
+ @unsafe
1717
+ export let countTrailingZeros = (num: WasmI32) => {
1718
+ // the number of trailing zeros is the same for `x` and `-x` (using two's complement),
1719
+ // so we can safely ignore the sign.
1720
+ let numLimbs = getSize(num)
1721
+ let mut ret = 0N
1722
+ let (+) = WasmI32.add
1723
+ let (<) = WasmI32.ltU
1724
+ for (let mut i = 0n; i < numLimbs; i += 1n) {
1725
+ let limb = getLimb(num, i)
1726
+ let limbtz = WasmI64.ctz(limb)
1727
+ let (!=) = WasmI64.ne
1728
+ let (+) = WasmI64.add
1729
+ ret += limbtz
1730
+ if (limbtz != 64N) {
1731
+ break
1732
+ }
1733
+ }
1734
+ ret
1735
+ }
1736
+
1737
+ @unsafe
1738
+ export let popcnt = (num: WasmI32, flagDest: WasmI32) => {
1739
+ // negative numbers have an infinite number of ones, so we return SMAX
1740
+ if (flagIsSet(num, _IS_NEGATIVE)) {
1741
+ WasmI32.store(flagDest, 1n, 0n)
1742
+ _SMAX32_I64
1743
+ } else {
1744
+ let numLimbs = getSize(num)
1745
+ let mut ret = 0N
1746
+ let (+) = WasmI32.add
1747
+ let (<) = WasmI32.ltU
1748
+ for (let mut i = 0n; i < numLimbs; i += 1n) {
1749
+ let limb = getLimb(num, i)
1750
+ let limbtz = WasmI64.popcnt(limb)
1751
+ let (+) = WasmI64.add
1752
+ ret += limbtz
1753
+ }
1754
+ ret
1755
+ }
1756
+ }
1757
+
1758
+ /*
1759
+ * Division
1760
+ */
1761
+
1762
+ // BigInt GCD
1763
+ @unsafe
1764
+ export let gcd = (num1: WasmI32, num2: WasmI32) => {
1765
+ if (eqz(num1)) {
1766
+ abs(num2)
1767
+ } else if (eqz(num2)) {
1768
+ abs(num1)
1769
+ } else {
1770
+ let mut u = abs(num1)
1771
+ let mut v = abs(num2)
1772
+ let i = countTrailingZeroBits(u)
1773
+ let j = countTrailingZeroBits(v)
1774
+ let k = minu32(i, j)
1775
+ let mut newu = shrS(u, i)
1776
+ let mut newv = shrS(v, j)
1777
+ Memory.decRef(u)
1778
+ Memory.decRef(v)
1779
+ u = newu
1780
+ v = newv
1781
+ let mut ret = 0n
1782
+ while (true) {
1783
+ if (gt(u, v)) {
1784
+ let tmp = v
1785
+ v = u
1786
+ u = tmp
1787
+ }
1788
+ newv = sub(v, u)
1789
+ Memory.decRef(v)
1790
+ v = newv
1791
+ if (eqz(v)) {
1792
+ ret = shl(u, k)
1793
+ break
1794
+ }
1795
+ newv = shrS(v, countTrailingZeroBits(v))
1796
+ Memory.decRef(v)
1797
+ v = newv
1798
+ }
1799
+ ret
1800
+ }
1801
+ }
1802
+
1803
+ // For division, "normalized" refers to the following (from Brent & Zimmermann):
1804
+ //
1805
+ // We say that B = sum_0^{n-1} b_j \beta^j is *normalized* when its most significant
1806
+ // word b_{n-1} satisfies b_{n-1} >= \beta / 2. This is a stricter condition
1807
+ // (for \beta > 2) than simply requiring b_{n-1} to be non-zero.
1808
+ // If B is not normalized, we can compute A' = 2^k A and B' = 2^k B so that
1809
+ // B' is normalized, then divide A' by B' giving A' = Q'B' + R'. The quotient
1810
+ // and remainder of the division of A by B are, respectively, Q := Q' and R := R'/(2^k);
1811
+ // the latter division being exact
1812
+
1813
+ // Brent & Zimmermann v0.5.9 Algorithm 1.6 (Chapter 1.4)
1814
+ @unsafe
1815
+ let baseCaseDivRem = (a: WasmI32, b: WasmI32, result: WasmI32) => {
1816
+ let (+) = WasmI32.add
1817
+ let (-) = WasmI32.sub
1818
+ let (*) = WasmI32.mul
1819
+ let (<<) = WasmI32.shl
1820
+ let (>>) = WasmI32.shrU
1821
+ let (>=) = WasmI32.geS
1822
+ let aOrig = a
1823
+ // b is `n` half-limbs
1824
+ // a is `n+m` half-limbs, m >= 0 (i.e. `a` has at least as many limbs as `b`)
1825
+ // b is normalized
1826
+ // \beta == 2^32 (we use half-limbs in this implementation because it makes calculating qjstar faster)
1827
+ let n = getHalfSize(b)
1828
+ let m = getHalfSize(a) - n
1829
+ let qsize = (if (WasmI32.eqz(WasmI32.and(m + 1n, 1n))) {
1830
+ m + 1n
1831
+ } else {
1832
+ m + 2n
1833
+ }) >>
1834
+ 1n
1835
+ let mut q = init(qsize)
1836
+ let mut a = 0n
1837
+ let bTimesBetaM = shl(
1838
+ b,
1839
+ WasmI32.mul(m, 32n)
1840
+ ) // b * \beta^m == b * (2^32)^m == b*2^(32*m)
1841
+ if (gte(aOrig, bTimesBetaM)) {
1842
+ setHalfLimb(q, m, 1n)
1843
+ a = sub(aOrig, bTimesBetaM)
1844
+ } else {
1845
+ // (clone for sane reference management)
1846
+ a = clone(aOrig)
1847
+ }
1848
+ Memory.decRef(bTimesBetaM)
1849
+ for (let mut j = m - 1n; j >= 0n; j -= 1n) {
1850
+ let (<<) = WasmI64.shl
1851
+ let (|) = WasmI64.or
1852
+ let (/) = WasmI64.divU
1853
+ let anjBeta = WasmI64.extendI32U(getHalfLimb(a, n + j)) <<
1854
+ 32N // a_{n+j}\beta
1855
+ let anj1 = WasmI64.extendI32U(getHalfLimb(a, n + j - 1n)) // a_{n+j-1}
1856
+ let bn1 = WasmI64.extendI32U(getHalfLimb(b, n - 1n)) // b_{n-1}
1857
+ let qjstar = (anjBeta | anj1) / bn1 // q_j^\ast (quotient selection)
1858
+ let mut qj = WasmI32.wrapI64(
1859
+ qjstar
1860
+ ) // min(q_j^ast, \beta - 1) (equiv. to qjstar & _UMAX_I6432)
1861
+ let bTimesBetaJ = shl(
1862
+ b,
1863
+ WasmI32.mul(j, 32n)
1864
+ ) // b * \beta^j == b * (2^32)^j == b*2^(32*j)
1865
+ let qjWrapped = makeWrappedUint32(qj)
1866
+ let qjTimesBTimesBetaJ = mul(bTimesBetaJ, qjWrapped)
1867
+ Memory.decRef(qjWrapped)
1868
+ let anew = sub(a, qjTimesBTimesBetaJ)
1869
+ Memory.decRef(a)
1870
+ a = anew
1871
+ while (flagIsSet(a, _IS_NEGATIVE)) {
1872
+ qj -= 1n
1873
+ let anew = add(a, bTimesBetaJ)
1874
+ Memory.decRef(a)
1875
+ a = anew
1876
+ }
1877
+ Memory.decRef(bTimesBetaJ)
1878
+ setHalfLimb(q, j, qj)
1879
+ }
1880
+ WasmI32.store(result, trimNumberInPlace(q), 0n) // Q := q
1881
+ WasmI32.store(result, trimNumberInPlace(a), 4n) // R := a
1882
+ }
1883
+
1884
+ @unsafe
1885
+ let baseCaseDivRemUnnormalized = (a: WasmI32, b: WasmI32, result: WasmI32) => {
1886
+ // wrapper around baseCaseDivRem which accepts unnormalized b
1887
+ // b is `n` half-limbs; n > 0
1888
+ // a is `n+m` half-limbs, m >= 0 (i.e. `a` has at least as many limbs as `b`)
1889
+ let (-) = WasmI32.sub
1890
+ let (>) = WasmI32.gtU
1891
+ let n = getHalfSize(b)
1892
+ //assert !WasmI32.eqz(n)
1893
+ let mostSignificantHalfLimb = getHalfLimb(b, n - 1n)
1894
+ let k = WasmI32.clz(mostSignificantHalfLimb)
1895
+ if (k > 0n) {
1896
+ let anew = shl(a, k)
1897
+ let bnew = shl(b, k)
1898
+ baseCaseDivRem(anew, bnew, result)
1899
+ Memory.decRef(anew)
1900
+ Memory.decRef(bnew)
1901
+ let rold = WasmI32.load(result, 4n)
1902
+ let rnew = shrS(rold, k)
1903
+ Memory.decRef(rold)
1904
+ WasmI32.store(result, rnew, 4n) // R := R' / 2^k
1905
+ } else {
1906
+ baseCaseDivRem(a, b, result)
1907
+ }
1908
+ }
1909
+
1910
+ @unsafe
1911
+ let division =
1912
+ (
1913
+ num1: WasmI32,
1914
+ num2: WasmI32,
1915
+ destContainer: WasmI32,
1916
+ divMod: Bool,
1917
+ ) => {
1918
+ if (eqz(num2)) {
1919
+ throw Exception.DivisionByZero
1920
+ }
1921
+ let num1HalfLimbs = getHalfSize(num1)
1922
+ let num2HalfLimbs = getHalfSize(num2)
1923
+ let mut q = 0n
1924
+ let mut r = 0n
1925
+ if (eqz(num1)) {
1926
+ q = makeZero()
1927
+ r = makeZero()
1928
+ } else if (WasmI32.ltU(num1HalfLimbs, num2HalfLimbs)) {
1929
+ q = makeZero()
1930
+ r = clone(num1)
1931
+ } else if (WasmI32.eq(num2HalfLimbs, 1n)) {
1932
+ let d = getLimb(num2, 0n)
1933
+ let (+) = WasmI32.add
1934
+ let (<<) = WasmI32.shl
1935
+ q = init(getSize(num1))
1936
+ let mut r2 = 0N
1937
+ for (
1938
+ let mut i = WasmI32.sub(num1HalfLimbs, 1n);
1939
+ WasmI32.geS(i, 0n);
1940
+ i = WasmI32.sub(i, 1n)
1941
+ ) {
1942
+ let (+) = WasmI64.add
1943
+ let (/) = WasmI64.divU
1944
+ let (%) = WasmI64.remU
1945
+ let (==) = WasmI32.eq
1946
+ let halfLimb = getHalfLimb(num1, i)
1947
+ if (!(halfLimb == 0n && WasmI32.sub(num1HalfLimbs, 1n) == i)) {
1948
+ let tmp = WasmI64.extendI32U(halfLimb) + WasmI64.shl(r2, 32N)
1949
+ setHalfLimb(q, i, WasmI32.wrapI64(tmp / d))
1950
+ r2 = tmp % d
1951
+ }
1952
+ }
1953
+ q = trimNumberInPlace(q)
1954
+ r = init(1n)
1955
+ setLimb(r, 0n, r2)
1956
+ } else {
1957
+ let result = Memory.malloc(8n) // holder for q and r
1958
+ let num1abs = abs(num1)
1959
+ let num2abs = abs(num2)
1960
+ baseCaseDivRemUnnormalized(num1abs, num2abs, result)
1961
+ Memory.decRef(num1abs)
1962
+ Memory.decRef(num2abs)
1963
+ q = WasmI32.load(result, 0n)
1964
+ r = WasmI32.load(result, 4n)
1965
+ Memory.free(result)
1966
+ if (
1967
+ flagIsSet(num1, _IS_NEGATIVE) && !flagIsSet(num2, _IS_NEGATIVE) ||
1968
+ !flagIsSet(num1, _IS_NEGATIVE) && flagIsSet(num2, _IS_NEGATIVE)
1969
+ ) {
1970
+ // fix sign for negative div by positive & vice versa
1971
+ q = negateInPlace(q)
1972
+ }
1973
+ }
1974
+ setFlag(
1975
+ q,
1976
+ _IS_NEGATIVE,
1977
+ WasmI32.xor(getFlag(num1, _IS_NEGATIVE), getFlag(num2, _IS_NEGATIVE))
1978
+ )
1979
+ if (flagIsSet(num1, _IS_NEGATIVE) && !eqz(r)) {
1980
+ setFlag(r, _IS_NEGATIVE, 1n)
1981
+ }
1982
+ // convert truncated division to floor division
1983
+ // https://en.wikipedia.org/wiki/Modulo_operation
1984
+ if (
1985
+ divMod &&
1986
+ (flagIsSet(r, _IS_NEGATIVE) && !flagIsSet(num2, _IS_NEGATIVE) ||
1987
+ !flagIsSet(r, _IS_NEGATIVE) && flagIsSet(num2, _IS_NEGATIVE))
1988
+ ) {
1989
+ let newr = add(r, num2)
1990
+ Memory.decRef(r)
1991
+ let newq = decr(q)
1992
+ Memory.decRef(q)
1993
+ r = newr
1994
+ q = newq
1995
+ }
1996
+ // Finished. Return appropriate number
1997
+ WasmI32.store(destContainer, q, 0n)
1998
+ WasmI32.store(destContainer, r, 4n)
1999
+ }
2000
+
2001
+ @unsafe
2002
+ export let quotRem = (num1: WasmI32, num2: WasmI32, dest: WasmI32) => {
2003
+ division(num1, num2, dest, false)
2004
+ }
2005
+
2006
+ @unsafe
2007
+ export let divMod = (num1: WasmI32, num2: WasmI32, dest: WasmI32) => {
2008
+ division(num1, num2, dest, true)
2009
+ }
2010
+
2011
+ @unsafe
2012
+ export let quot = (num1: WasmI32, num2: WasmI32) => {
2013
+ let dest = Memory.malloc(8n)
2014
+ division(num1, num2, dest, false)
2015
+ let ret = WasmI32.load(dest, 0n)
2016
+ Memory.free(dest)
2017
+ ret
2018
+ }
2019
+
2020
+ @unsafe
2021
+ export let div = (num1: WasmI32, num2: WasmI32) => {
2022
+ let dest = Memory.malloc(8n)
2023
+ division(num1, num2, dest, true)
2024
+ let ret = WasmI32.load(dest, 0n)
2025
+ Memory.free(dest)
2026
+ ret
2027
+ }
2028
+
2029
+ @unsafe
2030
+ export let rem = (num1: WasmI32, num2: WasmI32) => {
2031
+ let dest = Memory.malloc(8n)
2032
+ division(num1, num2, dest, false)
2033
+ let ret = WasmI32.load(dest, 4n)
2034
+ Memory.free(dest)
2035
+ ret
2036
+ }
2037
+
2038
+ @unsafe
2039
+ export let mod = (num1: WasmI32, num2: WasmI32) => {
2040
+ let dest = Memory.malloc(8n)
2041
+ division(num1, num2, dest, true)
2042
+ let ret = WasmI32.load(dest, 4n)
2043
+ Memory.free(dest)
2044
+ ret
2045
+ }