/home/travis/build/MoarVM/MoarVM/src/math/bigintops.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | #include <math.h> |
3 | | |
4 | | #ifndef MANTISSA_BITS_IN_DOUBLE |
5 | | #define MANTISSA_BITS_IN_DOUBLE 53 |
6 | | #endif |
7 | | #ifndef MAX_BIGINT_BITS_IN_DOUBLE |
8 | | #define MAX_BIGINT_BITS_IN_DOUBLE 1023 |
9 | | #endif |
10 | | |
11 | | #ifndef MAX |
12 | 0 | #define MAX(x,y) ((x)>(y)?(x):(y)) |
13 | | #endif |
14 | | |
15 | | #ifndef MIN |
16 | 124 | #define MIN(x,y) ((x)<(y)?(x):(y)) |
17 | | #endif |
18 | | |
19 | 476 | MVM_STATIC_INLINE void adjust_nursery(MVMThreadContext *tc, MVMP6bigintBody *body) { |
20 | 476 | if (MVM_BIGINT_IS_BIG(body)) { |
21 | 124 | int used = USED(body->u.bigint); |
22 | 124 | int adjustment = MIN(used, 32768) & ~0x7; |
23 | 124 | if (adjustment && (char *)tc->nursery_alloc_limit - adjustment > (char *)tc->nursery_alloc) { |
24 | 3 | tc->nursery_alloc_limit = (char *)(tc->nursery_alloc_limit) - adjustment; |
25 | 3 | } |
26 | 124 | } |
27 | 476 | } |
28 | | |
29 | | /* Taken from mp_set_long, but portably accepts a 64-bit number. */ |
30 | 20 | int MVM_bigint_mp_set_uint64(mp_int * a, MVMuint64 b) { |
31 | 20 | int x, res; |
32 | 20 | |
33 | 20 | mp_zero (a); |
34 | 20 | |
35 | 20 | /* set four bits at a time */ |
36 | 340 | for (x = 0; x < sizeof(MVMuint64) * 2; x++) { |
37 | 320 | /* shift the number up four bits */ |
38 | 320 | if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { |
39 | 0 | return res; |
40 | 0 | } |
41 | 320 | |
42 | 320 | /* OR in the top four bits of the source */ |
43 | 320 | a->dp[0] |= (b >> ((sizeof(MVMuint64)) * 8 - 4)) & 15; |
44 | 320 | |
45 | 320 | /* shift the source up to the next four bits */ |
46 | 320 | b <<= 4; |
47 | 320 | |
48 | 320 | /* ensure that digits are not clamped off */ |
49 | 320 | a->used += 1; |
50 | 320 | } |
51 | 20 | mp_clamp(a); |
52 | 20 | return MP_OKAY; |
53 | 20 | } |
54 | | |
55 | | /* |
56 | | * Convert to double, assumes IEEE-754 conforming double. Taken from |
57 | | * https://github.com/czurnieden/libtommath/blob/master/bn_mp_get_double.c |
58 | | * and slightly modified to fit MoarVM's setup. |
59 | | */ |
60 | | static const int mp_get_double_digits_needed |
61 | | = ((MANTISSA_BITS_IN_DOUBLE + DIGIT_BIT) / DIGIT_BIT) + 1; |
62 | | static const double mp_get_double_multiplier = (double)(MP_MASK + 1); |
63 | | |
64 | 11 | static MVMnum64 mp_get_double(mp_int *a, int shift) { |
65 | 11 | MVMnum64 d; |
66 | 11 | int i, limit, final_shift; |
67 | 11 | d = 0.0; |
68 | 11 | |
69 | 11 | mp_clamp(a); |
70 | 11 | i = a->used; |
71 | 11 | limit = (i <= mp_get_double_digits_needed) |
72 | 7 | ? 0 : i - mp_get_double_digits_needed; |
73 | 11 | |
74 | 29 | while (i-- > limit) { |
75 | 18 | d += a->dp[i]; |
76 | 18 | d *= mp_get_double_multiplier; |
77 | 18 | } |
78 | 11 | |
79 | 11 | if (a->sign == MP_NEG) |
80 | 4 | d *= -1.0; |
81 | 11 | final_shift = i * DIGIT_BIT - shift; |
82 | 11 | if (final_shift < 0) { |
83 | 9 | while (final_shift < -1023) { |
84 | 1 | d *= pow(2.0, -1023); |
85 | 1 | final_shift += 1023; |
86 | 1 | } |
87 | 8 | } |
88 | 3 | else { |
89 | 3 | while (final_shift > 1023) { |
90 | 0 | d *= pow(2.0, 1023); |
91 | 0 | final_shift -= 1023; |
92 | 0 | } |
93 | 3 | } |
94 | 11 | d *= pow(2.0, final_shift); |
95 | 11 | return d; |
96 | 11 | } |
97 | | |
98 | 325 | static void from_num(MVMnum64 d, mp_int *a) { |
99 | 325 | MVMnum64 d_digit = pow(2, DIGIT_BIT); |
100 | 325 | MVMnum64 da = fabs(d); |
101 | 325 | MVMnum64 upper; |
102 | 325 | MVMnum64 lower; |
103 | 325 | MVMnum64 lowest; |
104 | 325 | MVMnum64 rest; |
105 | 325 | int digits = 0; |
106 | 325 | |
107 | 325 | mp_zero(a); |
108 | 325 | |
109 | 347 | while (da > d_digit * d_digit * d_digit) {; |
110 | 22 | da /= d_digit; |
111 | 22 | digits++; |
112 | 22 | } |
113 | 325 | mp_grow(a, digits + 3); |
114 | 325 | |
115 | 325 | /* populate the top 3 digits */ |
116 | 325 | upper = da / (d_digit*d_digit); |
117 | 325 | rest = fmod(da, d_digit*d_digit); |
118 | 325 | lower = rest / d_digit; |
119 | 325 | lowest = fmod(rest,d_digit ); |
120 | 325 | if (upper >= 1) { |
121 | 2 | MVM_bigint_mp_set_uint64(a, (MVMuint64) upper); |
122 | 2 | mp_mul_2d(a, DIGIT_BIT , a); |
123 | 2 | DIGIT(a, 0) = (mp_digit) lower; |
124 | 2 | mp_mul_2d(a, DIGIT_BIT , a); |
125 | 323 | } else { |
126 | 323 | if (lower >= 1) { |
127 | 0 | MVM_bigint_mp_set_uint64(a, (MVMuint64) lower); |
128 | 0 | mp_mul_2d(a, DIGIT_BIT , a); |
129 | 0 | a->used = 2; |
130 | 323 | } else { |
131 | 323 | a->used = 1; |
132 | 323 | } |
133 | 323 | } |
134 | 325 | DIGIT(a, 0) = (mp_digit) lowest; |
135 | 325 | |
136 | 325 | /* shift the rest */ |
137 | 325 | mp_mul_2d(a, DIGIT_BIT * digits, a); |
138 | 325 | if (d < 0) |
139 | 3 | mp_neg(a, a); |
140 | 325 | mp_clamp(a); |
141 | 325 | mp_shrink(a); |
142 | 325 | } |
143 | | |
144 | | /* Returns the body of a P6bigint, containing the bigint/smallint union, for |
145 | | * operations that want to explicitly handle the two. */ |
146 | 2.68k | static MVMP6bigintBody * get_bigint_body(MVMThreadContext *tc, MVMObject *obj) { |
147 | 2.68k | if (IS_CONCRETE(obj)) |
148 | 2.68k | return (MVMP6bigintBody *)REPR(obj)->box_funcs.get_boxed_ref(tc, |
149 | 2.68k | STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_P6bigint); |
150 | 2.68k | else |
151 | 0 | MVM_exception_throw_adhoc(tc, |
152 | 0 | "Can only perform big integer operations on concrete objects"); |
153 | 2.68k | } |
154 | | |
155 | | /* Checks if a bigint can be stored small. */ |
156 | 802 | static int can_be_smallint(const mp_int *i) { |
157 | 802 | if (USED(i) != 1) |
158 | 110 | return 0; |
159 | 692 | return MVM_IS_32BIT_INT(DIGIT(i, 0)); |
160 | 802 | } |
161 | | |
162 | | /* Forces a bigint, even if we only have a smallint. Takes a parameter that |
163 | | * indicates where to allocate a temporary mp_int if needed. */ |
164 | 435 | static mp_int * force_bigint(const MVMP6bigintBody *body, mp_int **tmp) { |
165 | 435 | if (MVM_BIGINT_IS_BIG(body)) { |
166 | 160 | return body->u.bigint; |
167 | 160 | } |
168 | 275 | else { |
169 | 275 | MVMint64 value = body->u.smallint.value; |
170 | 275 | mp_int *i = MVM_malloc(sizeof(mp_int)); |
171 | 275 | mp_init(i); |
172 | 275 | if (value >= 0) { |
173 | 261 | mp_set_long(i, value); |
174 | 261 | } |
175 | 14 | else { |
176 | 14 | mp_set_long(i, -value); |
177 | 14 | mp_neg(i, i); |
178 | 14 | } |
179 | 374 | while (*tmp) |
180 | 99 | tmp++; |
181 | 275 | *tmp = i; |
182 | 275 | return i; |
183 | 275 | } |
184 | 435 | } |
185 | | |
186 | | /* Clears an array that may contain tempory big ints. */ |
187 | 220 | static void clear_temp_bigints(mp_int *const *ints, MVMint32 n) { |
188 | 220 | MVMint32 i; |
189 | 655 | for (i = 0; i < n; i++) |
190 | 435 | if (ints[i]) { |
191 | 275 | mp_clear(ints[i]); |
192 | 275 | MVM_free(ints[i]); |
193 | 275 | } |
194 | 220 | } |
195 | | |
196 | | /* Stores an int64 in a bigint result body, either as a 32-bit smallint if it |
197 | | * is in range, or a big integer if not. */ |
198 | 213 | static void store_int64_result(MVMP6bigintBody *body, MVMint64 result) { |
199 | 213 | if (MVM_IS_32BIT_INT(result)) { |
200 | 195 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
201 | 195 | body->u.smallint.value = (MVMint32)result; |
202 | 195 | } |
203 | 18 | else { |
204 | 18 | mp_int *i = MVM_malloc(sizeof(mp_int)); |
205 | 18 | mp_init(i); |
206 | 18 | if (result >= 0) { |
207 | 18 | MVM_bigint_mp_set_uint64(i, (MVMuint64)result); |
208 | 18 | } |
209 | 0 | else { |
210 | 0 | MVM_bigint_mp_set_uint64(i, (MVMuint64)-result); |
211 | 0 | mp_neg(i, i); |
212 | 0 | } |
213 | 18 | body->u.bigint = i; |
214 | 18 | } |
215 | 213 | } |
216 | | |
217 | | /* Stores an bigint in a bigint result body, either as a 32-bit smallint if it |
218 | | * is in range, or a big integer if not. Clears and frees the passed bigint if |
219 | | * it is not being used. */ |
220 | 572 | static void store_bigint_result(MVMP6bigintBody *body, mp_int *i) { |
221 | 572 | if (can_be_smallint(i)) { |
222 | 418 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
223 | 418 | body->u.smallint.value = SIGN(i) == MP_NEG ? -DIGIT(i, 0) : DIGIT(i, 0); |
224 | 418 | mp_clear(i); |
225 | 418 | MVM_free(i); |
226 | 418 | } |
227 | 154 | else { |
228 | 154 | body->u.bigint = i; |
229 | 154 | } |
230 | 572 | } |
231 | | |
232 | | /* Bitops on libtomath (no two's complement API) are horrendously inefficient and |
233 | | * really should be hand-coded to work DIGIT-by-DIGIT with in-loop carry |
234 | | * handling. For now we have these fixups. |
235 | | * |
236 | | * The following inverts the bits of a negative bigint, adds 1 to that, and |
237 | | * appends sign-bit extension DIGITs to it to give us a 2s compliment |
238 | | * representation in memory. Do not call it on positive bigints. |
239 | | */ |
240 | 0 | static void grow_and_negate(const mp_int *a, int size, mp_int *b) { |
241 | 0 | int i; |
242 | 0 | /* Always add an extra DIGIT so we can tell positive values |
243 | 0 | * with a one in the highest bit apart from negative values. |
244 | 0 | */ |
245 | 0 | int actual_size = MAX(size, USED(a)) + 1; |
246 | 0 |
|
247 | 0 | SIGN(b) = MP_ZPOS; |
248 | 0 | mp_grow(b, actual_size); |
249 | 0 | USED(b) = actual_size; |
250 | 0 | for (i = 0; i < USED(a); i++) { |
251 | 0 | DIGIT(b, i) = (~DIGIT(a, i)) & MP_MASK; |
252 | 0 | } |
253 | 0 | for (; i < actual_size; i++) { |
254 | 0 | DIGIT(b, i) = MP_MASK; |
255 | 0 | } |
256 | 0 | /* Note: This add cannot cause another grow assuming nobody ever |
257 | 0 | * tries to use tommath -0 for anything, and nobody tries to use |
258 | 0 | * this on positive bigints. |
259 | 0 | */ |
260 | 0 | mp_add_d(b, 1, b); |
261 | 0 | } |
262 | | |
263 | | static void two_complement_bitop(mp_int *a, mp_int *b, mp_int *c, |
264 | 0 | int (*mp_bitop)(mp_int *, mp_int *, mp_int *)) { |
265 | 0 |
|
266 | 0 | mp_int d; |
267 | 0 | mp_int e; |
268 | 0 | mp_int *f; |
269 | 0 | mp_int *g; |
270 | 0 |
|
271 | 0 | f = a; |
272 | 0 | g = b; |
273 | 0 | if (MP_NEG == SIGN(a)) { |
274 | 0 | mp_init(&d); |
275 | 0 | grow_and_negate(a, USED(b), &d); |
276 | 0 | f = &d; |
277 | 0 | } |
278 | 0 | if (MP_NEG == SIGN(b)) { |
279 | 0 | mp_init(&e); |
280 | 0 | grow_and_negate(b, USED(a), &e); |
281 | 0 | g = &e; |
282 | 0 | } |
283 | 0 | /* f and g now guaranteed to each point to positive bigints containing |
284 | 0 | * a two's complement representation of the values in a and b. If either |
285 | 0 | * a or b was negative, the representation is one tomath "digit" longer |
286 | 0 | * than it need be and sign extended. |
287 | 0 | */ |
288 | 0 | mp_bitop(f, g, c); |
289 | 0 | if (f == &d) mp_clear(&d); |
290 | 0 | if (g == &e) mp_clear(&e); |
291 | 0 | /* Use the fact that tomath clamps to detect results that should be |
292 | 0 | * signed. If we created extra tomath "digits" and they resulted in |
293 | 0 | * sign bits of 0, they have been clamped away. If the resulting sign |
294 | 0 | * bits were 1, they remain, and c will have more digits than either of |
295 | 0 | * original operands. Note this only works because we do not |
296 | 0 | * support NOR/NAND/NXOR, and so two zero sign bits can never create 1s. |
297 | 0 | */ |
298 | 0 | if (USED(c) > MAX(USED(a),USED(b))) { |
299 | 0 | int i; |
300 | 0 | for (i = 0; i < USED(c); i++) { |
301 | 0 | DIGIT(c, i) = (~DIGIT(c, i)) & MP_MASK; |
302 | 0 | } |
303 | 0 | mp_add_d(c, 1, c); |
304 | 0 | mp_neg(c, c); |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | static void two_complement_shl(mp_int *result, mp_int *value, MVMint64 count) { |
309 | 0 | if (count >= 0) { |
310 | 0 | mp_mul_2d(value, count, result); |
311 | 0 | } |
312 | 0 | else if (MP_NEG == SIGN(value)) { |
313 | 0 | /* fake two's complement semantics on top of sign-magnitude |
314 | 0 | * algorithm appears to work [citation needed] |
315 | 0 | */ |
316 | 0 | mp_add_d(value, 1, result); |
317 | 0 | mp_div_2d(result, -count, result, NULL); |
318 | 0 | mp_sub_d(result, 1, result); |
319 | 0 | } |
320 | 0 | else { |
321 | 0 | mp_div_2d(value, -count, result, NULL); |
322 | 0 | } |
323 | 0 | } |
324 | | |
325 | | #define MVM_BIGINT_UNARY_OP(opname, SMALLINT_OP) \ |
326 | 66 | void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \ |
327 | 66 | MVMP6bigintBody *bb = get_bigint_body(tc, result); \ |
328 | 66 | if (!IS_CONCRETE(source)) { \ |
329 | 0 | store_int64_result(bb, 0); \ |
330 | 0 | } \ |
331 | 66 | else { \ |
332 | 66 | MVMP6bigintBody *ba = get_bigint_body(tc, source); \ |
333 | 66 | if (MVM_BIGINT_IS_BIG(ba)) { \ |
334 | 11 | mp_int *ia = ba->u.bigint; \ |
335 | 11 | mp_int *ib = MVM_malloc(sizeof(mp_int)); \ |
336 | 11 | mp_init(ib); \ |
337 | 11 | mp_##opname(ia, ib); \ |
338 | 11 | store_bigint_result(bb, ib); \ |
339 | 11 | adjust_nursery(tc, bb); \ |
340 | 11 | } \ |
341 | 55 | else { \ |
342 | 55 | MVMint64 sb; \ |
343 | 55 | MVMint64 sa = ba->u.smallint.value; \ |
344 | 55 | SMALLINT_OP; \ |
345 | 55 | store_int64_result(bb, sb); \ |
346 | 55 | } \ |
347 | 66 | } \ |
348 | 66 | } Line | Count | Source | 326 | 64 | void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \ | 327 | 64 | MVMP6bigintBody *bb = get_bigint_body(tc, result); \ | 328 | 64 | if (!IS_CONCRETE(source)) { \ | 329 | 0 | store_int64_result(bb, 0); \ | 330 | 0 | } \ | 331 | 64 | else { \ | 332 | 64 | MVMP6bigintBody *ba = get_bigint_body(tc, source); \ | 333 | 64 | if (MVM_BIGINT_IS_BIG(ba)) { \ | 334 | 11 | mp_int *ia = ba->u.bigint; \ | 335 | 11 | mp_int *ib = MVM_malloc(sizeof(mp_int)); \ | 336 | 11 | mp_init(ib); \ | 337 | 11 | mp_##opname(ia, ib); \ | 338 | 11 | store_bigint_result(bb, ib); \ | 339 | 11 | adjust_nursery(tc, bb); \ | 340 | 11 | } \ | 341 | 53 | else { \ | 342 | 53 | MVMint64 sb; \ | 343 | 53 | MVMint64 sa = ba->u.smallint.value; \ | 344 | 53 | SMALLINT_OP; \ | 345 | 53 | store_int64_result(bb, sb); \ | 346 | 53 | } \ | 347 | 64 | } \ | 348 | 64 | } |
Line | Count | Source | 326 | 2 | void MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result, MVMObject *source) { \ | 327 | 2 | MVMP6bigintBody *bb = get_bigint_body(tc, result); \ | 328 | 2 | if (!IS_CONCRETE(source)) { \ | 329 | 0 | store_int64_result(bb, 0); \ | 330 | 0 | } \ | 331 | 2 | else { \ | 332 | 2 | MVMP6bigintBody *ba = get_bigint_body(tc, source); \ | 333 | 2 | if (MVM_BIGINT_IS_BIG(ba)) { \ | 334 | 0 | mp_int *ia = ba->u.bigint; \ | 335 | 0 | mp_int *ib = MVM_malloc(sizeof(mp_int)); \ | 336 | 0 | mp_init(ib); \ | 337 | 0 | mp_##opname(ia, ib); \ | 338 | 0 | store_bigint_result(bb, ib); \ | 339 | 0 | adjust_nursery(tc, bb); \ | 340 | 0 | } \ | 341 | 2 | else { \ | 342 | 2 | MVMint64 sb; \ | 343 | 2 | MVMint64 sa = ba->u.smallint.value; \ | 344 | 2 | SMALLINT_OP; \ | 345 | 2 | store_int64_result(bb, sb); \ | 346 | 2 | } \ | 347 | 2 | } \ | 348 | 2 | } |
|
349 | | |
350 | | #define MVM_BIGINT_BINARY_OP(opname) \ |
351 | 1 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ |
352 | 1 | MVMP6bigintBody *ba, *bb, *bc; \ |
353 | 1 | MVMObject *result; \ |
354 | 1 | mp_int *tmp[2] = { NULL, NULL }; \ |
355 | 1 | mp_int *ia, *ib, *ic; \ |
356 | 1 | MVMROOT2(tc, a, b, { \ |
357 | 1 | result = MVM_repr_alloc_init(tc, result_type);\ |
358 | 1 | }); \ |
359 | 1 | ba = get_bigint_body(tc, a); \ |
360 | 1 | bb = get_bigint_body(tc, b); \ |
361 | 1 | bc = get_bigint_body(tc, result); \ |
362 | 1 | ia = force_bigint(ba, tmp); \ |
363 | 1 | ib = force_bigint(bb, tmp); \ |
364 | 1 | ic = MVM_malloc(sizeof(mp_int)); \ |
365 | 1 | mp_init(ic); \ |
366 | 1 | mp_##opname(ia, ib, ic); \ |
367 | 1 | store_bigint_result(bc, ic); \ |
368 | 1 | clear_temp_bigints(tmp, 2); \ |
369 | 1 | adjust_nursery(tc, bc); \ |
370 | 1 | return result; \ |
371 | 1 | } |
372 | | |
373 | | #define MVM_BIGINT_BINARY_OP_SIMPLE(opname, SMALLINT_OP) \ |
374 | 205 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ |
375 | 205 | MVMP6bigintBody *ba, *bb, *bc; \ |
376 | 205 | MVMObject *result; \ |
377 | 205 | ba = get_bigint_body(tc, a); \ |
378 | 205 | bb = get_bigint_body(tc, b); \ |
379 | 205 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ |
380 | 70 | mp_int *tmp[2] = { NULL, NULL }; \ |
381 | 70 | mp_int *ia, *ib, *ic; \ |
382 | 70 | MVMROOT2(tc, a, b, { \ |
383 | 70 | result = MVM_repr_alloc_init(tc, result_type);\ |
384 | 70 | }); \ |
385 | 70 | ba = get_bigint_body(tc, a); \ |
386 | 70 | bb = get_bigint_body(tc, b); \ |
387 | 70 | bc = get_bigint_body(tc, result); \ |
388 | 70 | ia = force_bigint(ba, tmp); \ |
389 | 70 | ib = force_bigint(bb, tmp); \ |
390 | 70 | ic = MVM_malloc(sizeof(mp_int)); \ |
391 | 70 | mp_init(ic); \ |
392 | 70 | mp_##opname(ia, ib, ic); \ |
393 | 70 | store_bigint_result(bc, ic); \ |
394 | 70 | clear_temp_bigints(tmp, 2); \ |
395 | 70 | adjust_nursery(tc, bc); \ |
396 | 70 | } \ |
397 | 135 | else { \ |
398 | 135 | MVMint64 sc; \ |
399 | 135 | MVMint64 sa = ba->u.smallint.value; \ |
400 | 135 | MVMint64 sb = bb->u.smallint.value; \ |
401 | 135 | SMALLINT_OP; \ |
402 | 135 | result = MVM_intcache_get(tc, result_type, sc); \ |
403 | 135 | if (result) \ |
404 | 0 | return result; \ |
405 | 135 | result = MVM_repr_alloc_init(tc, result_type);\ |
406 | 135 | bc = get_bigint_body(tc, result); \ |
407 | 135 | store_int64_result(bc, sc); \ |
408 | 135 | } \ |
409 | 205 | return result; \ |
410 | 205 | } Line | Count | Source | 374 | 103 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 375 | 103 | MVMP6bigintBody *ba, *bb, *bc; \ | 376 | 103 | MVMObject *result; \ | 377 | 103 | ba = get_bigint_body(tc, a); \ | 378 | 103 | bb = get_bigint_body(tc, b); \ | 379 | 103 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 380 | 47 | mp_int *tmp[2] = { NULL, NULL }; \ | 381 | 47 | mp_int *ia, *ib, *ic; \ | 382 | 47 | MVMROOT2(tc, a, b, { \ | 383 | 47 | result = MVM_repr_alloc_init(tc, result_type);\ | 384 | 47 | }); \ | 385 | 47 | ba = get_bigint_body(tc, a); \ | 386 | 47 | bb = get_bigint_body(tc, b); \ | 387 | 47 | bc = get_bigint_body(tc, result); \ | 388 | 47 | ia = force_bigint(ba, tmp); \ | 389 | 47 | ib = force_bigint(bb, tmp); \ | 390 | 47 | ic = MVM_malloc(sizeof(mp_int)); \ | 391 | 47 | mp_init(ic); \ | 392 | 47 | mp_##opname(ia, ib, ic); \ | 393 | 47 | store_bigint_result(bc, ic); \ | 394 | 47 | clear_temp_bigints(tmp, 2); \ | 395 | 47 | adjust_nursery(tc, bc); \ | 396 | 47 | } \ | 397 | 56 | else { \ | 398 | 56 | MVMint64 sc; \ | 399 | 56 | MVMint64 sa = ba->u.smallint.value; \ | 400 | 56 | MVMint64 sb = bb->u.smallint.value; \ | 401 | 56 | SMALLINT_OP; \ | 402 | 56 | result = MVM_intcache_get(tc, result_type, sc); \ | 403 | 56 | if (result) \ | 404 | 0 | return result; \ | 405 | 56 | result = MVM_repr_alloc_init(tc, result_type);\ | 406 | 56 | bc = get_bigint_body(tc, result); \ | 407 | 56 | store_int64_result(bc, sc); \ | 408 | 56 | } \ | 409 | 103 | return result; \ | 410 | 103 | } |
Line | Count | Source | 374 | 1 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 375 | 1 | MVMP6bigintBody *ba, *bb, *bc; \ | 376 | 1 | MVMObject *result; \ | 377 | 1 | ba = get_bigint_body(tc, a); \ | 378 | 1 | bb = get_bigint_body(tc, b); \ | 379 | 1 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 380 | 0 | mp_int *tmp[2] = { NULL, NULL }; \ | 381 | 0 | mp_int *ia, *ib, *ic; \ | 382 | 0 | MVMROOT2(tc, a, b, { \ | 383 | 0 | result = MVM_repr_alloc_init(tc, result_type);\ | 384 | 0 | }); \ | 385 | 0 | ba = get_bigint_body(tc, a); \ | 386 | 0 | bb = get_bigint_body(tc, b); \ | 387 | 0 | bc = get_bigint_body(tc, result); \ | 388 | 0 | ia = force_bigint(ba, tmp); \ | 389 | 0 | ib = force_bigint(bb, tmp); \ | 390 | 0 | ic = MVM_malloc(sizeof(mp_int)); \ | 391 | 0 | mp_init(ic); \ | 392 | 0 | mp_##opname(ia, ib, ic); \ | 393 | 0 | store_bigint_result(bc, ic); \ | 394 | 0 | clear_temp_bigints(tmp, 2); \ | 395 | 0 | adjust_nursery(tc, bc); \ | 396 | 0 | } \ | 397 | 1 | else { \ | 398 | 1 | MVMint64 sc; \ | 399 | 1 | MVMint64 sa = ba->u.smallint.value; \ | 400 | 1 | MVMint64 sb = bb->u.smallint.value; \ | 401 | 1 | SMALLINT_OP; \ | 402 | 1 | result = MVM_intcache_get(tc, result_type, sc); \ | 403 | 1 | if (result) \ | 404 | 0 | return result; \ | 405 | 1 | result = MVM_repr_alloc_init(tc, result_type);\ | 406 | 1 | bc = get_bigint_body(tc, result); \ | 407 | 1 | store_int64_result(bc, sc); \ | 408 | 1 | } \ | 409 | 1 | return result; \ | 410 | 1 | } |
Line | Count | Source | 374 | 101 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 375 | 101 | MVMP6bigintBody *ba, *bb, *bc; \ | 376 | 101 | MVMObject *result; \ | 377 | 101 | ba = get_bigint_body(tc, a); \ | 378 | 101 | bb = get_bigint_body(tc, b); \ | 379 | 101 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 380 | 23 | mp_int *tmp[2] = { NULL, NULL }; \ | 381 | 23 | mp_int *ia, *ib, *ic; \ | 382 | 23 | MVMROOT2(tc, a, b, { \ | 383 | 23 | result = MVM_repr_alloc_init(tc, result_type);\ | 384 | 23 | }); \ | 385 | 23 | ba = get_bigint_body(tc, a); \ | 386 | 23 | bb = get_bigint_body(tc, b); \ | 387 | 23 | bc = get_bigint_body(tc, result); \ | 388 | 23 | ia = force_bigint(ba, tmp); \ | 389 | 23 | ib = force_bigint(bb, tmp); \ | 390 | 23 | ic = MVM_malloc(sizeof(mp_int)); \ | 391 | 23 | mp_init(ic); \ | 392 | 23 | mp_##opname(ia, ib, ic); \ | 393 | 23 | store_bigint_result(bc, ic); \ | 394 | 23 | clear_temp_bigints(tmp, 2); \ | 395 | 23 | adjust_nursery(tc, bc); \ | 396 | 23 | } \ | 397 | 78 | else { \ | 398 | 78 | MVMint64 sc; \ | 399 | 78 | MVMint64 sa = ba->u.smallint.value; \ | 400 | 78 | MVMint64 sb = bb->u.smallint.value; \ | 401 | 78 | SMALLINT_OP; \ | 402 | 78 | result = MVM_intcache_get(tc, result_type, sc); \ | 403 | 78 | if (result) \ | 404 | 0 | return result; \ | 405 | 78 | result = MVM_repr_alloc_init(tc, result_type);\ | 406 | 78 | bc = get_bigint_body(tc, result); \ | 407 | 78 | store_int64_result(bc, sc); \ | 408 | 78 | } \ | 409 | 101 | return result; \ | 410 | 101 | } |
|
411 | | |
412 | | #define MVM_BIGINT_BINARY_OP_2(opname, SMALLINT_OP) \ |
413 | 4 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ |
414 | 4 | MVMP6bigintBody *ba = get_bigint_body(tc, a); \ |
415 | 4 | MVMP6bigintBody *bb = get_bigint_body(tc, b); \ |
416 | 4 | MVMP6bigintBody *bc; \ |
417 | 4 | MVMObject *result; \ |
418 | 4 | MVMROOT2(tc, a, b, { \ |
419 | 4 | result = MVM_repr_alloc_init(tc, result_type);\ |
420 | 4 | }); \ |
421 | 4 | bc = get_bigint_body(tc, result); \ |
422 | 4 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ |
423 | 0 | mp_int *tmp[2] = { NULL, NULL }; \ |
424 | 0 | mp_int *ia = force_bigint(ba, tmp); \ |
425 | 0 | mp_int *ib = force_bigint(bb, tmp); \ |
426 | 0 | mp_int *ic = MVM_malloc(sizeof(mp_int)); \ |
427 | 0 | mp_init(ic); \ |
428 | 0 | two_complement_bitop(ia, ib, ic, mp_##opname); \ |
429 | 0 | store_bigint_result(bc, ic); \ |
430 | 0 | clear_temp_bigints(tmp, 2); \ |
431 | 0 | adjust_nursery(tc, bc); \ |
432 | 0 | } \ |
433 | 4 | else { \ |
434 | 4 | MVMint64 sc; \ |
435 | 4 | MVMint64 sa = ba->u.smallint.value; \ |
436 | 4 | MVMint64 sb = bb->u.smallint.value; \ |
437 | 4 | SMALLINT_OP; \ |
438 | 4 | store_int64_result(bc, sc); \ |
439 | 4 | } \ |
440 | 4 | return result; \ |
441 | 4 | } Line | Count | Source | 413 | 1 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 414 | 1 | MVMP6bigintBody *ba = get_bigint_body(tc, a); \ | 415 | 1 | MVMP6bigintBody *bb = get_bigint_body(tc, b); \ | 416 | 1 | MVMP6bigintBody *bc; \ | 417 | 1 | MVMObject *result; \ | 418 | 1 | MVMROOT2(tc, a, b, { \ | 419 | 1 | result = MVM_repr_alloc_init(tc, result_type);\ | 420 | 1 | }); \ | 421 | 1 | bc = get_bigint_body(tc, result); \ | 422 | 1 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 423 | 0 | mp_int *tmp[2] = { NULL, NULL }; \ | 424 | 0 | mp_int *ia = force_bigint(ba, tmp); \ | 425 | 0 | mp_int *ib = force_bigint(bb, tmp); \ | 426 | 0 | mp_int *ic = MVM_malloc(sizeof(mp_int)); \ | 427 | 0 | mp_init(ic); \ | 428 | 0 | two_complement_bitop(ia, ib, ic, mp_##opname); \ | 429 | 0 | store_bigint_result(bc, ic); \ | 430 | 0 | clear_temp_bigints(tmp, 2); \ | 431 | 0 | adjust_nursery(tc, bc); \ | 432 | 0 | } \ | 433 | 1 | else { \ | 434 | 1 | MVMint64 sc; \ | 435 | 1 | MVMint64 sa = ba->u.smallint.value; \ | 436 | 1 | MVMint64 sb = bb->u.smallint.value; \ | 437 | 1 | SMALLINT_OP; \ | 438 | 1 | store_int64_result(bc, sc); \ | 439 | 1 | } \ | 440 | 1 | return result; \ | 441 | 1 | } |
Line | Count | Source | 413 | 1 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 414 | 1 | MVMP6bigintBody *ba = get_bigint_body(tc, a); \ | 415 | 1 | MVMP6bigintBody *bb = get_bigint_body(tc, b); \ | 416 | 1 | MVMP6bigintBody *bc; \ | 417 | 1 | MVMObject *result; \ | 418 | 1 | MVMROOT2(tc, a, b, { \ | 419 | 1 | result = MVM_repr_alloc_init(tc, result_type);\ | 420 | 1 | }); \ | 421 | 1 | bc = get_bigint_body(tc, result); \ | 422 | 1 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 423 | 0 | mp_int *tmp[2] = { NULL, NULL }; \ | 424 | 0 | mp_int *ia = force_bigint(ba, tmp); \ | 425 | 0 | mp_int *ib = force_bigint(bb, tmp); \ | 426 | 0 | mp_int *ic = MVM_malloc(sizeof(mp_int)); \ | 427 | 0 | mp_init(ic); \ | 428 | 0 | two_complement_bitop(ia, ib, ic, mp_##opname); \ | 429 | 0 | store_bigint_result(bc, ic); \ | 430 | 0 | clear_temp_bigints(tmp, 2); \ | 431 | 0 | adjust_nursery(tc, bc); \ | 432 | 0 | } \ | 433 | 1 | else { \ | 434 | 1 | MVMint64 sc; \ | 435 | 1 | MVMint64 sa = ba->u.smallint.value; \ | 436 | 1 | MVMint64 sb = bb->u.smallint.value; \ | 437 | 1 | SMALLINT_OP; \ | 438 | 1 | store_int64_result(bc, sc); \ | 439 | 1 | } \ | 440 | 1 | return result; \ | 441 | 1 | } |
Line | Count | Source | 413 | 2 | MVMObject * MVM_bigint_##opname(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { \ | 414 | 2 | MVMP6bigintBody *ba = get_bigint_body(tc, a); \ | 415 | 2 | MVMP6bigintBody *bb = get_bigint_body(tc, b); \ | 416 | 2 | MVMP6bigintBody *bc; \ | 417 | 2 | MVMObject *result; \ | 418 | 2 | MVMROOT2(tc, a, b, { \ | 419 | 2 | result = MVM_repr_alloc_init(tc, result_type);\ | 420 | 2 | }); \ | 421 | 2 | bc = get_bigint_body(tc, result); \ | 422 | 2 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { \ | 423 | 0 | mp_int *tmp[2] = { NULL, NULL }; \ | 424 | 0 | mp_int *ia = force_bigint(ba, tmp); \ | 425 | 0 | mp_int *ib = force_bigint(bb, tmp); \ | 426 | 0 | mp_int *ic = MVM_malloc(sizeof(mp_int)); \ | 427 | 0 | mp_init(ic); \ | 428 | 0 | two_complement_bitop(ia, ib, ic, mp_##opname); \ | 429 | 0 | store_bigint_result(bc, ic); \ | 430 | 0 | clear_temp_bigints(tmp, 2); \ | 431 | 0 | adjust_nursery(tc, bc); \ | 432 | 0 | } \ | 433 | 2 | else { \ | 434 | 2 | MVMint64 sc; \ | 435 | 2 | MVMint64 sa = ba->u.smallint.value; \ | 436 | 2 | MVMint64 sb = bb->u.smallint.value; \ | 437 | 2 | SMALLINT_OP; \ | 438 | 2 | store_int64_result(bc, sc); \ | 439 | 2 | } \ | 440 | 2 | return result; \ | 441 | 2 | } |
|
442 | | |
443 | | MVM_BIGINT_UNARY_OP(abs, { sb = labs(sa); }) |
444 | | MVM_BIGINT_UNARY_OP(neg, { sb = -sa; }) |
445 | | |
446 | | /* unused */ |
447 | | /* MVM_BIGINT_UNARY_OP(sqrt) */ |
448 | | |
449 | | MVM_BIGINT_BINARY_OP_SIMPLE(add, { sc = sa + sb; }) |
450 | | MVM_BIGINT_BINARY_OP_SIMPLE(sub, { sc = sa - sb; }) |
451 | | MVM_BIGINT_BINARY_OP_SIMPLE(mul, { sc = sa * sb; }) |
452 | | MVM_BIGINT_BINARY_OP(lcm) |
453 | | |
454 | 1 | MVMObject *MVM_bigint_gcd(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { |
455 | 1 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
456 | 1 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
457 | 1 | MVMP6bigintBody *bc; |
458 | 1 | MVMObject *result; |
459 | 1 | |
460 | 1 | MVMROOT2(tc, a, b, { |
461 | 1 | result = MVM_repr_alloc_init(tc, result_type); |
462 | 1 | }); |
463 | 1 | |
464 | 1 | bc = get_bigint_body(tc, result); |
465 | 1 | |
466 | 1 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { |
467 | 0 | mp_int *tmp[2] = { NULL, NULL }; |
468 | 0 | mp_int *ia = force_bigint(ba, tmp); |
469 | 0 | mp_int *ib = force_bigint(bb, tmp); |
470 | 0 | mp_int *ic = MVM_malloc(sizeof(mp_int)); |
471 | 0 | mp_init(ic); |
472 | 0 | mp_gcd(ia, ib, ic); |
473 | 0 | store_bigint_result(bc, ic); |
474 | 0 | clear_temp_bigints(tmp, 2); |
475 | 0 | adjust_nursery(tc, bc); |
476 | 1 | } else { |
477 | 1 | MVMint32 sa = ba->u.smallint.value; |
478 | 1 | MVMint32 sb = bb->u.smallint.value; |
479 | 1 | MVMint32 t; |
480 | 1 | sa = abs(sa); |
481 | 1 | sb = abs(sb); |
482 | 3 | while (sb != 0) { |
483 | 2 | t = sb; |
484 | 2 | sb = sa % sb; |
485 | 2 | sa = t; |
486 | 2 | } |
487 | 1 | store_int64_result(bc, sa); |
488 | 1 | } |
489 | 1 | |
490 | 1 | return result; |
491 | 1 | } |
492 | | |
493 | | MVM_BIGINT_BINARY_OP_2(or , { sc = sa | sb; }) |
494 | | MVM_BIGINT_BINARY_OP_2(xor, { sc = sa ^ sb; }) |
495 | | MVM_BIGINT_BINARY_OP_2(and, { sc = sa & sb; }) |
496 | | |
497 | 119 | MVMint64 MVM_bigint_cmp(MVMThreadContext *tc, MVMObject *a, MVMObject *b) { |
498 | 119 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
499 | 119 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
500 | 119 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { |
501 | 16 | mp_int *tmp[2] = { NULL, NULL }; |
502 | 16 | mp_int *ia = force_bigint(ba, tmp); |
503 | 16 | mp_int *ib = force_bigint(bb, tmp); |
504 | 16 | MVMint64 r = (MVMint64)mp_cmp(ia, ib); |
505 | 16 | clear_temp_bigints(tmp, 2); |
506 | 16 | return r; |
507 | 16 | } |
508 | 103 | else { |
509 | 103 | MVMint64 sa = ba->u.smallint.value; |
510 | 103 | MVMint64 sb = bb->u.smallint.value; |
511 | 52 | return sa == sb ? 0 : sa < sb ? -1 : 1; |
512 | 103 | } |
513 | 119 | } |
514 | | |
515 | 6 | MVMObject * MVM_bigint_mod(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { |
516 | 6 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
517 | 6 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
518 | 6 | MVMP6bigintBody *bc; |
519 | 6 | |
520 | 6 | MVMObject *result; |
521 | 6 | |
522 | 6 | MVMROOT2(tc, a, b, { |
523 | 6 | result = MVM_repr_alloc_init(tc, result_type); |
524 | 6 | }); |
525 | 6 | |
526 | 6 | bc = get_bigint_body(tc, result); |
527 | 6 | |
528 | 6 | /* XXX the behavior of C's mod operator is not correct |
529 | 6 | * for our purposes. So we rely on mp_mod for all our modulus |
530 | 6 | * calculations for now. */ |
531 | 6 | if (1 || MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { |
532 | 6 | mp_int *tmp[2] = { NULL, NULL }; |
533 | 6 | mp_int *ia = force_bigint(ba, tmp); |
534 | 6 | mp_int *ib = force_bigint(bb, tmp); |
535 | 6 | mp_int *ic = MVM_malloc(sizeof(mp_int)); |
536 | 6 | int mp_result; |
537 | 6 | |
538 | 6 | mp_init(ic); |
539 | 6 | |
540 | 6 | mp_result = mp_mod(ia, ib, ic); |
541 | 6 | clear_temp_bigints(tmp, 2); |
542 | 6 | |
543 | 6 | if (mp_result == MP_VAL) { |
544 | 0 | MVM_exception_throw_adhoc(tc, "Division by zero"); |
545 | 0 | } |
546 | 6 | store_bigint_result(bc, ic); |
547 | 6 | adjust_nursery(tc, bc); |
548 | 0 | } else { |
549 | 0 | store_int64_result(bc, ba->u.smallint.value % bb->u.smallint.value); |
550 | 0 | } |
551 | 6 | |
552 | 6 | return result; |
553 | 6 | } |
554 | | |
555 | 10 | MVMObject *MVM_bigint_div(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMObject *b) { |
556 | 10 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
557 | 10 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
558 | 10 | MVMP6bigintBody *bc; |
559 | 10 | mp_int *ia, *ib, *ic; |
560 | 10 | int cmp_a; |
561 | 10 | int cmp_b; |
562 | 10 | mp_int remainder; |
563 | 10 | mp_int intermediate; |
564 | 10 | MVMObject *result; |
565 | 10 | |
566 | 10 | int mp_result; |
567 | 10 | |
568 | 10 | if (!MVM_BIGINT_IS_BIG(bb) && bb->u.smallint.value == 1 && STABLE(a) == STABLE(b)) { |
569 | 0 | return a; |
570 | 0 | } |
571 | 10 | |
572 | 10 | MVMROOT2(tc, a, b, { |
573 | 10 | result = MVM_repr_alloc_init(tc, result_type); |
574 | 10 | }); |
575 | 10 | |
576 | 10 | bc = get_bigint_body(tc, result); |
577 | 10 | |
578 | 10 | /* we only care about MP_LT or !MP_LT, so we give MP_GT even for 0. */ |
579 | 10 | if (MVM_BIGINT_IS_BIG(ba)) { |
580 | 1 | cmp_a = !mp_iszero(ba->u.bigint) && SIGN(ba->u.bigint) == MP_NEG ? MP_LT : MP_GT; |
581 | 9 | } else { |
582 | 5 | cmp_a = ba->u.smallint.value < 0 ? MP_LT : MP_GT; |
583 | 9 | } |
584 | 10 | if (MVM_BIGINT_IS_BIG(bb)) { |
585 | 0 | cmp_b = !mp_iszero(bb->u.bigint) && SIGN(bb->u.bigint) == MP_NEG ? MP_LT : MP_GT; |
586 | 10 | } else { |
587 | 6 | cmp_b = bb->u.smallint.value < 0 ? MP_LT : MP_GT; |
588 | 10 | } |
589 | 10 | |
590 | 10 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { |
591 | 1 | mp_int *tmp[2] = { NULL, NULL }; |
592 | 1 | ia = force_bigint(ba, tmp); |
593 | 1 | ib = force_bigint(bb, tmp); |
594 | 1 | |
595 | 1 | ic = MVM_malloc(sizeof(mp_int)); |
596 | 1 | mp_init(ic); |
597 | 1 | |
598 | 1 | /* if we do a div with a negative, we need to make sure |
599 | 1 | * the result is floored rather than rounded towards |
600 | 1 | * zero, like C and libtommath would do. */ |
601 | 1 | if ((cmp_a == MP_LT) ^ (cmp_b == MP_LT)) { |
602 | 1 | mp_init(&remainder); |
603 | 1 | mp_init(&intermediate); |
604 | 1 | mp_result = mp_div(ia, ib, &intermediate, &remainder); |
605 | 1 | if (mp_result == MP_VAL) { |
606 | 0 | mp_clear(&remainder); |
607 | 0 | mp_clear(&intermediate); |
608 | 0 | clear_temp_bigints(tmp, 2); |
609 | 0 | MVM_exception_throw_adhoc(tc, "Division by zero"); |
610 | 0 | } |
611 | 1 | if (mp_iszero(&remainder) == 0) { |
612 | 1 | mp_sub_d(&intermediate, 1, ic); |
613 | 0 | } else { |
614 | 0 | mp_copy(&intermediate, ic); |
615 | 0 | } |
616 | 1 | mp_clear(&remainder); |
617 | 1 | mp_clear(&intermediate); |
618 | 0 | } else { |
619 | 0 | mp_result = mp_div(ia, ib, ic, NULL); |
620 | 0 | if (mp_result == MP_VAL) { |
621 | 0 | clear_temp_bigints(tmp, 2); |
622 | 0 | MVM_exception_throw_adhoc(tc, "Division by zero"); |
623 | 0 | } |
624 | 0 | } |
625 | 1 | store_bigint_result(bc, ic); |
626 | 1 | clear_temp_bigints(tmp, 2); |
627 | 1 | adjust_nursery(tc, bc); |
628 | 9 | } else { |
629 | 9 | MVMint32 num = ba->u.smallint.value; |
630 | 9 | MVMint32 denom = bb->u.smallint.value; |
631 | 9 | MVMint32 value; |
632 | 9 | if ((cmp_a == MP_LT) ^ (cmp_b == MP_LT)) { |
633 | 5 | if (denom == 0) { |
634 | 0 | MVM_exception_throw_adhoc(tc, "Division by zero"); |
635 | 0 | } |
636 | 5 | if ((num % denom) != 0) { |
637 | 3 | value = num / denom - 1; |
638 | 2 | } else { |
639 | 2 | value = num / denom; |
640 | 2 | } |
641 | 4 | } else { |
642 | 4 | value = num / denom; |
643 | 4 | } |
644 | 9 | store_int64_result(bc, value); |
645 | 9 | } |
646 | 10 | |
647 | 10 | return result; |
648 | 10 | } |
649 | | |
650 | | MVMObject * MVM_bigint_pow(MVMThreadContext *tc, MVMObject *a, MVMObject *b, |
651 | 117 | MVMObject *num_type, MVMObject *int_type) { |
652 | 117 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
653 | 117 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
654 | 117 | MVMObject *r = NULL; |
655 | 117 | |
656 | 117 | mp_int *tmp[2] = { NULL, NULL }; |
657 | 117 | mp_int *base = force_bigint(ba, tmp); |
658 | 117 | mp_int *exponent = force_bigint(bb, tmp); |
659 | 117 | mp_digit exponent_d = 0; |
660 | 117 | |
661 | 117 | if (mp_iszero(exponent) || (MP_EQ == mp_cmp_d(base, 1))) { |
662 | 19 | r = MVM_repr_box_int(tc, int_type, 1); |
663 | 19 | } |
664 | 98 | else if (SIGN(exponent) == MP_ZPOS) { |
665 | 97 | exponent_d = mp_get_int(exponent); |
666 | 97 | if ((MP_GT == mp_cmp_d(exponent, exponent_d))) { |
667 | 7 | if (mp_iszero(base)) { |
668 | 1 | r = MVM_repr_box_int(tc, int_type, 0); |
669 | 1 | } |
670 | 6 | else if (mp_get_int(base) == 1) { |
671 | 2 | r = MVM_repr_box_int(tc, int_type, MP_ZPOS == SIGN(base) || mp_iseven(exponent) ? 1 : -1); |
672 | 2 | } |
673 | 4 | else { |
674 | 4 | MVMnum64 inf; |
675 | 4 | if (MP_ZPOS == SIGN(base) || mp_iseven(exponent)) { |
676 | 3 | inf = MVM_num_posinf(tc); |
677 | 3 | } |
678 | 1 | else { |
679 | 1 | inf = MVM_num_neginf(tc); |
680 | 1 | } |
681 | 4 | r = MVM_repr_box_num(tc, num_type, inf); |
682 | 4 | } |
683 | 7 | } |
684 | 90 | else { |
685 | 90 | mp_int *ic = MVM_malloc(sizeof(mp_int)); |
686 | 90 | MVMP6bigintBody *resbody; |
687 | 90 | mp_init(ic); |
688 | 90 | MVM_gc_mark_thread_blocked(tc); |
689 | 90 | mp_expt_d(base, exponent_d, ic); |
690 | 90 | MVM_gc_mark_thread_unblocked(tc); |
691 | 90 | r = MVM_repr_alloc_init(tc, int_type); |
692 | 90 | resbody = get_bigint_body(tc, r); |
693 | 90 | store_bigint_result(resbody, ic); |
694 | 90 | adjust_nursery(tc, resbody); |
695 | 90 | } |
696 | 97 | } |
697 | 1 | else { |
698 | 1 | MVMnum64 f_base = mp_get_double(base, 0); |
699 | 1 | MVMnum64 f_exp = mp_get_double(exponent, 0); |
700 | 1 | r = MVM_repr_box_num(tc, num_type, pow(f_base, f_exp)); |
701 | 1 | } |
702 | 117 | clear_temp_bigints(tmp, 2); |
703 | 117 | return r; |
704 | 117 | } |
705 | | |
706 | 4 | MVMObject *MVM_bigint_shl(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMint64 n) { |
707 | 4 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
708 | 4 | MVMP6bigintBody *bb; |
709 | 4 | MVMObject *result; |
710 | 4 | |
711 | 4 | MVMROOT(tc, a, { |
712 | 4 | result = MVM_repr_alloc_init(tc, result_type); |
713 | 4 | }); |
714 | 4 | |
715 | 4 | bb = get_bigint_body(tc, result); |
716 | 4 | |
717 | 4 | if (MVM_BIGINT_IS_BIG(ba) || n >= 31) { |
718 | 0 | mp_int *tmp[1] = { NULL }; |
719 | 0 | mp_int *ia = force_bigint(ba, tmp); |
720 | 0 | mp_int *ib = MVM_malloc(sizeof(mp_int)); |
721 | 0 | mp_init(ib); |
722 | 0 | two_complement_shl(ib, ia, n); |
723 | 0 | store_bigint_result(bb, ib); |
724 | 0 | clear_temp_bigints(tmp, 1); |
725 | 0 | adjust_nursery(tc, bb); |
726 | 4 | } else { |
727 | 4 | MVMint64 value; |
728 | 4 | if (n < 0) |
729 | 3 | value = ((MVMint64)ba->u.smallint.value) >> -n; |
730 | 4 | else |
731 | 1 | value = ((MVMint64)ba->u.smallint.value) << n; |
732 | 4 | store_int64_result(bb, value); |
733 | 4 | } |
734 | 4 | |
735 | 4 | return result; |
736 | 4 | } |
737 | | /* Checks if a MVMP6bigintBody is negative. Handles cases where it is stored as |
738 | | * a small int as well as cases when it is stored as a bigint */ |
739 | 0 | int BIGINT_IS_NEGATIVE (MVMP6bigintBody *ba) { |
740 | 0 | mp_int *mp_a = ba->u.bigint; |
741 | 0 | if (MVM_BIGINT_IS_BIG(ba)) { |
742 | 0 | return SIGN(mp_a) == MP_NEG; |
743 | 0 | } |
744 | 0 | else { |
745 | 0 | return ba->u.smallint.value < 0; |
746 | 0 | } |
747 | 0 | } |
748 | 4 | MVMObject *MVM_bigint_shr(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a, MVMint64 n) { |
749 | 4 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
750 | 4 | MVMP6bigintBody *bb; |
751 | 4 | MVMObject *result; |
752 | 4 | |
753 | 4 | MVMROOT(tc, a, { |
754 | 4 | result = MVM_repr_alloc_init(tc, result_type); |
755 | 4 | }); |
756 | 4 | |
757 | 4 | bb = get_bigint_body(tc, result); |
758 | 4 | |
759 | 4 | if (MVM_BIGINT_IS_BIG(ba) || n < 0) { |
760 | 0 | mp_int *tmp[1] = { NULL }; |
761 | 0 | mp_int *ia = force_bigint(ba, tmp); |
762 | 0 | mp_int *ib = MVM_malloc(sizeof(mp_int)); |
763 | 0 | mp_init(ib); |
764 | 0 | two_complement_shl(ib, ia, -n); |
765 | 0 | store_bigint_result(bb, ib); |
766 | 0 | clear_temp_bigints(tmp, 1); |
767 | 0 | adjust_nursery(tc, bb); |
768 | 4 | } else if (n >= 32) { |
769 | 0 | store_int64_result(bb, BIGINT_IS_NEGATIVE(ba) ? -1 : 0); |
770 | 4 | } else { |
771 | 4 | MVMint32 value = ba->u.smallint.value; |
772 | 4 | value = value >> n; |
773 | 4 | store_int64_result(bb, value); |
774 | 4 | } |
775 | 4 | |
776 | 4 | return result; |
777 | 4 | } |
778 | | |
779 | 1 | MVMObject *MVM_bigint_not(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a) { |
780 | 1 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
781 | 1 | MVMP6bigintBody *bb; |
782 | 1 | MVMObject *result; |
783 | 1 | |
784 | 1 | MVMROOT(tc, a, { |
785 | 1 | result = MVM_repr_alloc_init(tc, result_type); |
786 | 1 | }); |
787 | 1 | |
788 | 1 | bb = get_bigint_body(tc, result); |
789 | 1 | |
790 | 1 | if (MVM_BIGINT_IS_BIG(ba)) { |
791 | 0 | mp_int *ia = ba->u.bigint; |
792 | 0 | mp_int *ib = MVM_malloc(sizeof(mp_int)); |
793 | 0 | mp_init(ib); |
794 | 0 | /* two's complement not: add 1 and negate */ |
795 | 0 | mp_add_d(ia, 1, ib); |
796 | 0 | mp_neg(ib, ib); |
797 | 0 | store_bigint_result(bb, ib); |
798 | 0 | adjust_nursery(tc, bb); |
799 | 1 | } else { |
800 | 1 | MVMint32 value = ba->u.smallint.value; |
801 | 1 | value = ~value; |
802 | 1 | store_int64_result(bb, value); |
803 | 1 | } |
804 | 1 | |
805 | 1 | return result; |
806 | 1 | } |
807 | | |
808 | 1 | void MVM_bigint_expmod(MVMThreadContext *tc, MVMObject *result, MVMObject *a, MVMObject *b, MVMObject *c) { |
809 | 1 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
810 | 1 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
811 | 1 | MVMP6bigintBody *bc = get_bigint_body(tc, c); |
812 | 1 | MVMP6bigintBody *bd = get_bigint_body(tc, result); |
813 | 1 | |
814 | 1 | mp_int *tmp[3] = { NULL, NULL, NULL }; |
815 | 1 | |
816 | 1 | mp_int *ia = force_bigint(ba, tmp); |
817 | 1 | mp_int *ib = force_bigint(bb, tmp); |
818 | 1 | mp_int *ic = force_bigint(bc, tmp); |
819 | 1 | mp_int *id = MVM_malloc(sizeof(mp_int)); |
820 | 1 | mp_init(id); |
821 | 1 | |
822 | 1 | mp_exptmod(ia, ib, ic, id); |
823 | 1 | store_bigint_result(bd, id); |
824 | 1 | clear_temp_bigints(tmp, 3); |
825 | 1 | adjust_nursery(tc, bd); |
826 | 1 | } |
827 | | |
828 | 229 | void MVM_bigint_from_str(MVMThreadContext *tc, MVMObject *a, const char *buf) { |
829 | 229 | MVMP6bigintBody *body = get_bigint_body(tc, a); |
830 | 229 | mp_int *i = alloca(sizeof(mp_int)); |
831 | 229 | mp_init(i); |
832 | 229 | mp_read_radix(i, buf, 10); |
833 | 229 | adjust_nursery(tc, body); |
834 | 229 | if (can_be_smallint(i)) { |
835 | 130 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
836 | 130 | body->u.smallint.value = SIGN(i) == MP_NEG ? -DIGIT(i, 0) : DIGIT(i, 0); |
837 | 130 | mp_clear(i); |
838 | 130 | } |
839 | 99 | else { |
840 | 99 | mp_int *i_cpy = MVM_malloc(sizeof(mp_int)); |
841 | 99 | memcpy(i_cpy, i, sizeof(mp_int)); |
842 | 99 | body->u.bigint = i_cpy; |
843 | 99 | } |
844 | 229 | } |
845 | 1.14k | #define can_fit_into_8bit(g) ((-128 <= (g) && (g) <= 127)) |
846 | 229 | MVMObject * MVM_coerce_sI(MVMThreadContext *tc, MVMString *s, MVMObject *type) { |
847 | 229 | char *buf = NULL; |
848 | 229 | int is_malloced = 0; |
849 | 229 | MVMStringIndex i; |
850 | 229 | MVMObject *a = MVM_repr_alloc_init(tc, type); |
851 | 229 | if (s->body.num_graphs < 120) { |
852 | 229 | buf = alloca(s->body.num_graphs + 1); |
853 | 229 | } |
854 | 0 | else { |
855 | 0 | buf = MVM_malloc(s->body.num_graphs + 1); |
856 | 0 | is_malloced = 1; |
857 | 0 | } |
858 | 229 | /* We just ignore synthetics since parsing will fail if a synthetic is |
859 | 229 | * encountered anyway. */ |
860 | 229 | switch (s->body.storage_type) { |
861 | 128 | case MVM_STRING_GRAPHEME_ASCII: |
862 | 128 | case MVM_STRING_GRAPHEME_8: |
863 | 128 | memcpy(buf, s->body.storage.blob_8, sizeof(MVMGrapheme8) * s->body.num_graphs); |
864 | 128 | break; |
865 | 0 | case MVM_STRING_GRAPHEME_32: |
866 | 0 | for (i = 0; i < s->body.num_graphs; i++) { |
867 | 0 | buf[i] = can_fit_into_8bit(s->body.storage.blob_32[i]) |
868 | 0 | ? s->body.storage.blob_32[i] |
869 | 0 | : '?'; /* Add a filler bogus char if it can't fit */ |
870 | 0 | } |
871 | 0 | break; |
872 | 101 | case MVM_STRING_STRAND: { |
873 | 101 | MVMGraphemeIter gi; |
874 | 101 | MVM_string_gi_init(tc, &gi, s); |
875 | 1.24k | for (i = 0; i < s->body.num_graphs; i++) { |
876 | 1.14k | MVMGrapheme32 g = MVM_string_gi_get_grapheme(tc, &gi); |
877 | 1.14k | buf[i] = can_fit_into_8bit(g) ? g : '?'; |
878 | 1.14k | } |
879 | 101 | break; |
880 | 128 | } |
881 | 0 | default: |
882 | 0 | if (is_malloced) MVM_free(buf); |
883 | 0 | MVM_exception_throw_adhoc(tc, "String corruption found in MVM_coerce_sI"); |
884 | 229 | } |
885 | 229 | buf[s->body.num_graphs] = 0; |
886 | 229 | |
887 | 229 | MVM_bigint_from_str(tc, a, buf); |
888 | 229 | if (is_malloced) MVM_free(buf); |
889 | 229 | return a; |
890 | 229 | } |
891 | | |
892 | 1 | MVMObject * MVM_bigint_from_bigint(MVMThreadContext *tc, MVMObject *result_type, MVMObject *a) { |
893 | 1 | MVMP6bigintBody *a_body; |
894 | 1 | MVMP6bigintBody *r_body; |
895 | 1 | MVMObject *result; |
896 | 1 | |
897 | 1 | MVMROOT(tc, a, { |
898 | 1 | result = MVM_repr_alloc_init(tc, result_type); |
899 | 1 | }); |
900 | 1 | |
901 | 1 | a_body = get_bigint_body(tc, a); |
902 | 1 | r_body = get_bigint_body(tc, result); |
903 | 1 | |
904 | 1 | if (MVM_BIGINT_IS_BIG(a_body)) { |
905 | 0 | mp_int *i = MVM_malloc(sizeof(mp_int)); |
906 | 0 | mp_init_copy(i, a_body->u.bigint); |
907 | 0 | store_bigint_result(r_body, i); |
908 | 0 | adjust_nursery(tc, r_body); |
909 | 0 | } |
910 | 1 | else { |
911 | 1 | r_body->u.smallint = a_body->u.smallint; |
912 | 1 | r_body->u.smallint.flag = a_body->u.smallint.flag; |
913 | 1 | r_body->u.smallint.value = a_body->u.smallint.value; |
914 | 1 | } |
915 | 1 | |
916 | 1 | return result; |
917 | 1 | } |
918 | | |
919 | 482 | MVMString * MVM_bigint_to_str(MVMThreadContext *tc, MVMObject *a, int base) { |
920 | 482 | MVMP6bigintBody *body = get_bigint_body(tc, a); |
921 | 482 | if (MVM_BIGINT_IS_BIG(body)) { |
922 | 146 | mp_int *i = body->u.bigint; |
923 | 146 | int len; |
924 | 146 | char *buf; |
925 | 146 | MVMString *result; |
926 | 146 | int is_malloced = 0; |
927 | 146 | mp_radix_size(i, base, &len); |
928 | 146 | if (len < 120) { |
929 | 146 | buf = alloca(len); |
930 | 146 | } |
931 | 0 | else { |
932 | 0 | buf = MVM_malloc(len); |
933 | 0 | is_malloced = 1; |
934 | 0 | } |
935 | 146 | mp_toradix_n(i, buf, base, len); |
936 | 146 | result = MVM_string_ascii_decode(tc, tc->instance->VMString, buf, len - 1); |
937 | 146 | if (is_malloced) MVM_free(buf); |
938 | 146 | return result; |
939 | 146 | } |
940 | 336 | else { |
941 | 336 | if (base == 10) { |
942 | 277 | return MVM_coerce_i_s(tc, body->u.smallint.value); |
943 | 277 | } |
944 | 59 | else { |
945 | 59 | /* It's small, but shove it through bigint lib, as it knows how to |
946 | 59 | * get other bases right. */ |
947 | 59 | mp_int i; |
948 | 59 | int len, is_malloced = 0; |
949 | 59 | char *buf; |
950 | 59 | MVMString *result; |
951 | 59 | |
952 | 59 | MVMint64 value = body->u.smallint.value; |
953 | 59 | mp_init(&i); |
954 | 59 | if (value >= 0) { |
955 | 55 | mp_set_long(&i, value); |
956 | 55 | } |
957 | 4 | else { |
958 | 4 | mp_set_long(&i, -value); |
959 | 4 | mp_neg(&i, &i); |
960 | 4 | } |
961 | 59 | |
962 | 59 | mp_radix_size(&i, base, &len); |
963 | 59 | if (len < 120) { |
964 | 59 | buf = alloca(len); |
965 | 59 | } |
966 | 0 | else { |
967 | 0 | buf = MVM_malloc(len); |
968 | 0 | is_malloced = 1; |
969 | 0 | } |
970 | 59 | mp_toradix_n(&i, buf, base, len); |
971 | 59 | result = MVM_string_ascii_decode(tc, tc->instance->VMString, buf, len - 1); |
972 | 59 | if (is_malloced) MVM_free(buf); |
973 | 59 | mp_clear(&i); |
974 | 59 | |
975 | 59 | return result; |
976 | 59 | } |
977 | 336 | } |
978 | 482 | } |
979 | | |
980 | 8 | MVMnum64 MVM_bigint_to_num(MVMThreadContext *tc, MVMObject *a) { |
981 | 8 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
982 | 8 | |
983 | 8 | if (MVM_BIGINT_IS_BIG(ba)) { |
984 | 7 | mp_int *ia = ba->u.bigint; |
985 | 7 | return mp_get_double(ia, 0); |
986 | 1 | } else { |
987 | 1 | return (double)ba->u.smallint.value; |
988 | 1 | } |
989 | 8 | } |
990 | | |
991 | 325 | MVMObject *MVM_bigint_from_num(MVMThreadContext *tc, MVMObject *result_type, MVMnum64 n) { |
992 | 325 | MVMObject * const result = MVM_repr_alloc_init(tc, result_type); |
993 | 325 | MVMP6bigintBody *ba = get_bigint_body(tc, result); |
994 | 325 | mp_int *ia = MVM_malloc(sizeof(mp_int)); |
995 | 325 | mp_init(ia); |
996 | 325 | from_num(n, ia); |
997 | 325 | store_bigint_result(ba, ia); |
998 | 325 | return result; |
999 | 325 | } |
1000 | | |
1001 | 11 | MVMnum64 MVM_bigint_div_num(MVMThreadContext *tc, MVMObject *a, MVMObject *b) { |
1002 | 11 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
1003 | 11 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
1004 | 11 | MVMnum64 c; |
1005 | 11 | |
1006 | 11 | if (MVM_BIGINT_IS_BIG(ba) || MVM_BIGINT_IS_BIG(bb)) { |
1007 | 2 | mp_int *tmp[2] = { NULL, NULL }; |
1008 | 2 | mp_int *ia = force_bigint(ba, tmp); |
1009 | 2 | mp_int *ib = force_bigint(bb, tmp); |
1010 | 2 | |
1011 | 2 | mp_clamp(ib); |
1012 | 2 | if (ib->used == 0) { /* zero-denominator special case */ |
1013 | 0 | if (ia->sign == MP_NEG) |
1014 | 0 | c = MVM_NUM_NEGINF; |
1015 | 0 | else |
1016 | 0 | c = MVM_NUM_POSINF; |
1017 | 0 | /* |
1018 | 0 | * we won't have NaN case here, since the branch requires at |
1019 | 0 | * least one bigint to be big |
1020 | 0 | */ |
1021 | 0 | } |
1022 | 2 | else { |
1023 | 2 | mp_int scaled; |
1024 | 2 | int bbits = mp_count_bits(ib)+64; |
1025 | 2 | |
1026 | 2 | if (mp_init(&scaled) != MP_OKAY) |
1027 | 0 | MVM_exception_throw_adhoc(tc, |
1028 | 0 | "Failed to initialize bigint for scaled divident"); |
1029 | 2 | if (mp_mul_2d(ia, bbits, &scaled) != MP_OKAY) |
1030 | 0 | MVM_exception_throw_adhoc(tc, "Failed to scale divident"); |
1031 | 2 | // simply re-use &scaled for result |
1032 | 2 | if (mp_div(&scaled, ib, &scaled, NULL) != MP_OKAY) |
1033 | 0 | MVM_exception_throw_adhoc(tc, |
1034 | 0 | "Failed to preform bigint division"); |
1035 | 2 | c = mp_get_double(&scaled, bbits); |
1036 | 2 | mp_clear(&scaled); |
1037 | 2 | } |
1038 | 2 | clear_temp_bigints(tmp, 2); |
1039 | 9 | } else { |
1040 | 9 | c = (double)ba->u.smallint.value / (double)bb->u.smallint.value; |
1041 | 9 | } |
1042 | 11 | return c; |
1043 | 11 | } |
1044 | | |
1045 | 1 | MVMObject * MVM_bigint_rand(MVMThreadContext *tc, MVMObject *type, MVMObject *b) { |
1046 | 1 | MVMObject *result; |
1047 | 1 | MVMP6bigintBody *ba; |
1048 | 1 | MVMP6bigintBody *bb = get_bigint_body(tc, b); |
1049 | 1 | |
1050 | 1 | MVMint8 use_small_arithmetic = 0; |
1051 | 1 | MVMint8 have_to_negate = 0; |
1052 | 1 | MVMint32 smallint_max = 0; |
1053 | 1 | |
1054 | 1 | if (MVM_BIGINT_IS_BIG(bb)) { |
1055 | 1 | if (can_be_smallint(bb->u.bigint)) { |
1056 | 0 | use_small_arithmetic = 1; |
1057 | 0 | smallint_max = DIGIT(bb->u.bigint, 0); |
1058 | 0 | have_to_negate = SIGN(bb->u.bigint) == MP_NEG; |
1059 | 0 | } |
1060 | 0 | } else { |
1061 | 0 | use_small_arithmetic = 1; |
1062 | 0 | smallint_max = bb->u.smallint.value; |
1063 | 0 | } |
1064 | 1 | |
1065 | 1 | if (use_small_arithmetic) { |
1066 | 0 | if (MP_GEN_RANDOM_MAX >= abs(smallint_max)) { |
1067 | 0 | mp_digit result_int = MP_GEN_RANDOM(); |
1068 | 0 | result_int = result_int % smallint_max; |
1069 | 0 | if(have_to_negate) |
1070 | 0 | result_int *= -1; |
1071 | 0 |
|
1072 | 0 | MVMROOT2(tc, type, b, { |
1073 | 0 | result = MVM_repr_alloc_init(tc, type); |
1074 | 0 | }); |
1075 | 0 |
|
1076 | 0 | ba = get_bigint_body(tc, result); |
1077 | 0 | store_int64_result(ba, result_int); |
1078 | 0 | } else { |
1079 | 0 | use_small_arithmetic = 0; |
1080 | 0 | } |
1081 | 0 | } |
1082 | 1 | |
1083 | 1 | if (!use_small_arithmetic) { |
1084 | 1 | mp_int *tmp[1] = { NULL }; |
1085 | 1 | mp_int *rnd = MVM_malloc(sizeof(mp_int)); |
1086 | 1 | mp_int *max = force_bigint(bb, tmp); |
1087 | 1 | |
1088 | 1 | MVMROOT2(tc, type, b, { |
1089 | 1 | result = MVM_repr_alloc_init(tc, type); |
1090 | 1 | }); |
1091 | 1 | |
1092 | 1 | ba = get_bigint_body(tc, result); |
1093 | 1 | |
1094 | 1 | mp_init(rnd); |
1095 | 1 | mp_rand(rnd, USED(max) + 1); |
1096 | 1 | |
1097 | 1 | mp_mod(rnd, max, rnd); |
1098 | 1 | store_bigint_result(ba, rnd); |
1099 | 1 | clear_temp_bigints(tmp, 1); |
1100 | 1 | adjust_nursery(tc, ba); |
1101 | 1 | } |
1102 | 1 | |
1103 | 1 | return result; |
1104 | 1 | } |
1105 | | |
1106 | 6 | MVMint64 MVM_bigint_is_prime(MVMThreadContext *tc, MVMObject *a, MVMint64 b) { |
1107 | 6 | /* mp_prime_is_prime returns True for 1, and I think |
1108 | 6 | * it's worth special-casing this particular number :-) |
1109 | 6 | */ |
1110 | 6 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
1111 | 6 | |
1112 | 6 | if (MVM_BIGINT_IS_BIG(ba) || ba->u.smallint.value != 1) { |
1113 | 5 | mp_int *tmp[1] = { NULL }; |
1114 | 5 | mp_int *ia = force_bigint(ba, tmp); |
1115 | 5 | if (mp_cmp_d(ia, 1) == MP_EQ) { |
1116 | 0 | clear_temp_bigints(tmp, 1); |
1117 | 0 | return 0; |
1118 | 0 | } |
1119 | 5 | else { |
1120 | 5 | int result; |
1121 | 5 | mp_prime_is_prime(ia, b, &result); |
1122 | 5 | clear_temp_bigints(tmp, 1); |
1123 | 5 | return result; |
1124 | 5 | } |
1125 | 1 | } else { |
1126 | 1 | /* we only reach this if we have a smallint that's equal to 1. |
1127 | 1 | * which we define as not-prime. */ |
1128 | 1 | return 0; |
1129 | 1 | } |
1130 | 6 | } |
1131 | | |
1132 | | /* concatenating with "" ensures that only literal strings are accepted as argument. */ |
1133 | | #define STR_WITH_LEN(str) ("" str ""), (sizeof(str) - 1) |
1134 | | |
1135 | 33 | MVMObject * MVM_bigint_radix(MVMThreadContext *tc, MVMint64 radix, MVMString *str, MVMint64 offset, MVMint64 flag, MVMObject *type) { |
1136 | 33 | MVMObject *result; |
1137 | 33 | MVMint64 chars = MVM_string_graphs(tc, str); |
1138 | 33 | MVMuint16 neg = 0; |
1139 | 33 | MVMint64 ch; |
1140 | 33 | |
1141 | 33 | mp_int zvalue; |
1142 | 33 | mp_int zbase; |
1143 | 33 | |
1144 | 33 | MVMObject *value_obj; |
1145 | 33 | mp_int *value; |
1146 | 33 | MVMP6bigintBody *bvalue; |
1147 | 33 | |
1148 | 33 | MVMObject *base_obj; |
1149 | 33 | mp_int *base; |
1150 | 33 | MVMP6bigintBody *bbase; |
1151 | 33 | |
1152 | 33 | MVMObject *pos_obj; |
1153 | 33 | MVMint64 pos = -1; |
1154 | 33 | |
1155 | 33 | if (radix > 36) { |
1156 | 0 | MVM_exception_throw_adhoc(tc, "Cannot convert radix of %"PRId64" (max 36)", radix); |
1157 | 0 | } |
1158 | 33 | |
1159 | 33 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&str); |
1160 | 33 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&type); |
1161 | 33 | |
1162 | 33 | /* initialize the object */ |
1163 | 33 | result = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); |
1164 | 33 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&result); |
1165 | 33 | |
1166 | 33 | mp_init(&zvalue); |
1167 | 33 | mp_init(&zbase); |
1168 | 33 | mp_set_int(&zbase, 1); |
1169 | 33 | |
1170 | 33 | value_obj = MVM_repr_alloc_init(tc, type); |
1171 | 33 | MVM_repr_push_o(tc, result, value_obj); |
1172 | 33 | MVM_gc_root_temp_push(tc, (MVMCollectable **)&value_obj); |
1173 | 33 | |
1174 | 33 | base_obj = MVM_repr_alloc_init(tc, type); |
1175 | 33 | MVM_repr_push_o(tc, result, base_obj); |
1176 | 33 | |
1177 | 33 | bvalue = get_bigint_body(tc, value_obj); |
1178 | 33 | bbase = get_bigint_body(tc, base_obj); |
1179 | 33 | |
1180 | 33 | value = MVM_malloc(sizeof(mp_int)); |
1181 | 33 | base = MVM_malloc(sizeof(mp_int)); |
1182 | 33 | |
1183 | 33 | mp_init(value); |
1184 | 33 | mp_init(base); |
1185 | 33 | |
1186 | 33 | mp_set_int(base, 1); |
1187 | 33 | |
1188 | 33 | ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0; |
1189 | 33 | if ((flag & 0x02) && (ch == '+' || ch == '-')) { |
1190 | 4 | neg = (ch == '-'); |
1191 | 4 | offset++; |
1192 | 4 | ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0; |
1193 | 4 | } |
1194 | 33 | |
1195 | 155 | while (offset < chars) { |
1196 | 155 | if (ch >= '0' && ch <= '9') ch = ch - '0'; /* fast-path for ASCII 0..9 */ |
1197 | 22 | else if (ch >= 'a' && ch <= 'z') ch = ch - 'a' + 10; |
1198 | 18 | else if (ch >= 'A' && ch <= 'Z') ch = ch - 'A' + 10; |
1199 | 15 | else if (ch >= 0xFF21 && ch <= 0xFF3A) ch = ch - 0xFF21 + 10; /* uppercase fullwidth */ |
1200 | 11 | else if (ch >= 0xFF41 && ch <= 0xFF5A) ch = ch - 0xFF41 + 10; /* lowercase fullwidth */ |
1201 | 7 | else if (ch > 0 && MVM_unicode_codepoint_get_property_int(tc, ch, MVM_UNICODE_PROPERTY_NUMERIC_TYPE) |
1202 | 7 | == MVM_UNICODE_PVALUE_Numeric_Type_DECIMAL) { |
1203 | 4 | /* as of Unicode 9.0.0, characters with the 'de' Numeric Type (and are |
1204 | 4 | * thus also of General Category Nd, since 4.0.0) are contiguous |
1205 | 4 | * sequences of 10 chars whose Numeric Values ascend from 0 through 9. |
1206 | 4 | */ |
1207 | 4 | |
1208 | 4 | /* the string returned for NUMERIC_VALUE_NUMERATOR contains an integer |
1209 | 4 | * value. We can use numerator because they all are from 0-9 and have |
1210 | 4 | * denominator of 1 */ |
1211 | 4 | ch = fast_atoi(MVM_unicode_codepoint_get_property_cstr(tc, ch, MVM_UNICODE_PROPERTY_NUMERIC_VALUE_NUMERATOR)); |
1212 | 4 | } |
1213 | 3 | else break; |
1214 | 152 | if (ch >= radix) break; |
1215 | 144 | mp_mul_d(&zvalue, radix, &zvalue); |
1216 | 144 | mp_add_d(&zvalue, ch, &zvalue); |
1217 | 144 | mp_mul_d(&zbase, radix, &zbase); |
1218 | 144 | offset++; pos = offset; |
1219 | 144 | if (ch != 0 || !(flag & 0x04)) { mp_copy(&zvalue, value); mp_copy(&zbase, base); } |
1220 | 144 | if (offset >= chars) break; |
1221 | 122 | ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset); |
1222 | 122 | if (ch != '_') continue; |
1223 | 4 | offset++; |
1224 | 4 | if (offset >= chars) break; |
1225 | 4 | ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset); |
1226 | 4 | } |
1227 | 33 | |
1228 | 33 | mp_clear(&zvalue); |
1229 | 33 | mp_clear(&zbase); |
1230 | 33 | |
1231 | 33 | if (neg || flag & 0x01) { |
1232 | 5 | mp_neg(value, value); |
1233 | 5 | } |
1234 | 33 | |
1235 | 33 | store_bigint_result(bvalue, value); |
1236 | 33 | store_bigint_result(bbase, base); |
1237 | 33 | |
1238 | 33 | adjust_nursery(tc, bvalue); |
1239 | 33 | adjust_nursery(tc, bbase); |
1240 | 33 | |
1241 | 33 | pos_obj = MVM_repr_box_int(tc, type, pos); |
1242 | 33 | MVM_repr_push_o(tc, result, pos_obj); |
1243 | 33 | |
1244 | 33 | MVM_gc_root_temp_pop_n(tc, 4); |
1245 | 33 | |
1246 | 33 | return result; |
1247 | 33 | } |
1248 | | |
1249 | | /* returns 1 if a is too large to fit into an INTVAL without loss of |
1250 | | information */ |
1251 | 6 | MVMint64 MVM_bigint_is_big(MVMThreadContext *tc, MVMObject *a) { |
1252 | 6 | MVMP6bigintBody *ba = get_bigint_body(tc, a); |
1253 | 6 | |
1254 | 6 | if (MVM_BIGINT_IS_BIG(ba)) { |
1255 | 1 | mp_int *b = ba->u.bigint; |
1256 | 1 | MVMint64 is_big = b->used > 1; |
1257 | 1 | /* XXX somebody please check that on a 32 bit platform */ |
1258 | 1 | if ( sizeof(MVMint64) * 8 > DIGIT_BIT && is_big == 0 && DIGIT(b, 0) & ~0x7FFFFFFFUL) |
1259 | 0 | is_big = 1; |
1260 | 1 | return is_big; |
1261 | 5 | } else { |
1262 | 5 | /* if it's in a smallint, it's 32 bits big at most and fits into an INTVAL easily. */ |
1263 | 5 | return 0; |
1264 | 5 | } |
1265 | 6 | } |
1266 | | |
1267 | 4 | MVMint64 MVM_bigint_bool(MVMThreadContext *tc, MVMObject *a) { |
1268 | 4 | MVMP6bigintBody *body = get_bigint_body(tc, a); |
1269 | 4 | if (MVM_BIGINT_IS_BIG(body)) |
1270 | 1 | return !mp_iszero(body->u.bigint); |
1271 | 4 | else |
1272 | 3 | return body->u.smallint.value != 0; |
1273 | 4 | } |