@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.
- package/CHANGELOG.md +87 -0
- package/LICENSE +1 -1
- package/array.gr +92 -73
- package/array.md +18 -18
- package/bigint.gr +497 -0
- package/bigint.md +811 -0
- package/buffer.gr +56 -217
- package/buffer.md +24 -17
- package/bytes.gr +103 -205
- package/bytes.md +19 -0
- package/char.gr +152 -166
- package/char.md +200 -0
- package/exception.md +6 -0
- package/float32.gr +159 -82
- package/float32.md +315 -0
- package/float64.gr +163 -82
- package/float64.md +315 -0
- package/hash.gr +53 -49
- package/int32.gr +479 -230
- package/int32.md +937 -0
- package/int64.gr +479 -230
- package/int64.md +937 -0
- package/list.gr +530 -116
- package/list.md +1141 -0
- package/map.gr +302 -121
- package/map.md +525 -0
- package/number.gr +51 -57
- package/number.md +37 -3
- package/option.gr +25 -25
- package/option.md +1 -1
- package/package.json +3 -3
- package/pervasives.gr +504 -52
- package/pervasives.md +1116 -0
- package/queue.gr +8 -1
- package/queue.md +10 -0
- package/random.gr +196 -0
- package/random.md +179 -0
- package/range.gr +26 -26
- package/regex.gr +1833 -842
- package/regex.md +11 -11
- package/result.md +1 -1
- package/runtime/bigint.gr +2045 -0
- package/runtime/bigint.md +326 -0
- package/runtime/dataStructures.gr +99 -279
- package/runtime/dataStructures.md +391 -0
- package/runtime/debug.gr +0 -1
- package/runtime/debug.md +6 -0
- package/runtime/equal.gr +40 -37
- package/runtime/equal.md +6 -0
- package/runtime/exception.gr +28 -15
- package/runtime/exception.md +30 -0
- package/runtime/gc.gr +50 -20
- package/runtime/gc.md +36 -0
- package/runtime/malloc.gr +32 -22
- package/runtime/malloc.md +55 -0
- package/runtime/numberUtils.gr +297 -142
- package/runtime/numberUtils.md +54 -0
- package/runtime/numbers.gr +1204 -453
- package/runtime/numbers.md +300 -0
- package/runtime/string.gr +193 -228
- package/runtime/string.md +24 -0
- package/runtime/stringUtils.gr +62 -38
- package/runtime/stringUtils.md +6 -0
- package/runtime/unsafe/constants.gr +17 -0
- package/runtime/unsafe/constants.md +72 -0
- package/runtime/unsafe/conv.gr +10 -10
- package/runtime/unsafe/conv.md +71 -0
- package/runtime/unsafe/errors.md +204 -0
- package/runtime/unsafe/memory.gr +14 -3
- package/runtime/unsafe/memory.md +54 -0
- package/runtime/unsafe/printWasm.gr +4 -4
- package/runtime/unsafe/printWasm.md +24 -0
- package/runtime/unsafe/tags.gr +11 -10
- package/runtime/unsafe/tags.md +120 -0
- package/runtime/unsafe/wasmf32.gr +9 -2
- package/runtime/unsafe/wasmf32.md +168 -0
- package/runtime/unsafe/wasmf64.gr +9 -2
- package/runtime/unsafe/wasmf64.md +168 -0
- package/runtime/unsafe/wasmi32.gr +65 -47
- package/runtime/unsafe/wasmi32.md +282 -0
- package/runtime/unsafe/wasmi64.gr +78 -50
- package/runtime/unsafe/wasmi64.md +300 -0
- package/runtime/utils/printing.gr +62 -0
- package/runtime/utils/printing.md +18 -0
- package/runtime/wasi.gr +200 -46
- package/runtime/wasi.md +839 -0
- package/set.gr +125 -121
- package/set.md +24 -21
- package/stack.gr +29 -29
- package/stack.md +4 -6
- package/string.gr +434 -415
- package/string.md +3 -3
- package/sys/file.gr +477 -482
- package/sys/process.gr +33 -47
- package/sys/random.gr +48 -20
- package/sys/random.md +38 -0
- 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
|
+
}
|